daisukeの技術ブログ

AI、機械学習、最適化、Pythonなどについて、技術調査、技術書の理解した内容、ソフトウェア/ツール作成について書いていきます

QEMUのソースコードを変更してSTM32の動作を変える

前回は、だいぶ苦労して、QEMU をソースからビルドしました。

今回は、QEMU のソースコードを変更して、STM32 の動作を変えてみます。

それでは、やっていきます。

参考文献

はじめに

「QEMUを動かす」の記事一覧です。良かったら参考にしてください。

QEMUを動かすの記事一覧

Interface 2022年 7月号 に、応用例として、QEMU のソースコードを変更して、STM32 の動作を変更する内容が書かれています。

第2回の「STM32(ARM Cortex-M)をQEMUで動かす(ソースコード確認編) - daisukeの技術ブログ」で評価ボードのボタンを押して、ルーレットゲームの動作を確認しました。そのボタンの動作を変更する内容が書かれています。

具体的には、通常のボタンは、ボタンが押されてない状態→ボタンが押された状態→ボタンが押されてない状態 と変化していきますが、ボタンが押された状態で、ボタンの状態の更新処理を削除(コメントアウト)して、ボタンが押しっぱなしの状態を作るというものです。

それでは、やっていきます。

QEMUのソースを変更する

まず、ソースコードを特定します。

Cortex-M 用のハードウェアのソースコードは以下にあります。

~/Work/xpack-dev-tools/qemu-arm-xpack.git/build/linux-x64/sources/qemu-2.8.0-17-legacy.git/hw/cortexm

このディレクトリの button-gpio.c が変更対象のソースコードになります。

関数一覧です。先頭の1つだけが公開関数で、それ以外は、非公開の関数です。

void button_gpio_create_from_info()
static void button_gpio_down_callback()
static void button_gpio_up_callback()
static void button_gpio_instance_init_callback()
static void button_gpio_realize_callback()
static void button_gpio_reset_callback()
static void button_gpio_class_init_callback()
static void button_gpio_type_init()

対象の関数は、button_gpio_up_callback() で、ボタンがアップされた(押された状態から離された状態になった)ときに呼ばれるコールバック関数のようです。

この関数の内容は以下です。

// Action when the button is released.
static void button_gpio_up_callback(ButtonState *button)
{
    qemu_log_function_name();

    ButtonGPIOState *state = BUTTON_GPIO_STATE(button);

    button->value = state->active_low ? 1 : 0;

    printf("[%s up=%d]", object_get_canonical_path_component(OBJECT(button)),
            button->value);

    cm_irq_set(state->irq_out, button->value);
}

Interface 2022年 7月号 の通りに変更します。

変更後のソースです。

変更箇所は2か所で、ボタンの値を設定している1行をコメントアウトしたのと、printf() で出力する文字列に、**fault injection** を追加しました。

これにより、ボタンが離されたのに、その状態に移行しないので、ボタンが押されっぱなしの状態になるということのようです。

// Action when the button is released.
static void button_gpio_up_callback(ButtonState *button)
{
    qemu_log_function_name();

    ButtonGPIOState *state = BUTTON_GPIO_STATE(button);

    //button->value = state->active_low ? 1 : 0;

    printf("[%s up=%d] **fault injection**", object_get_canonical_path_component(OBJECT(button)),
            button->value);

    cm_irq_set(state->irq_out, button->value);
}

QEMUのソースをビルドする

差分ビルドを試してみる

まずは、差分のビルドで、変更したソースが反映されるかを確認したいと思います。単純に、deep-clean を実行せずに、それ以降の内容だけを実行してみます。

xpm run docker-prepare --config linux-x64 -C ~/Work/xpack-dev-tools/qemu-arm-xpack.git && \
xpm run docker-link-deps --config linux-x64 -C ~/Work/xpack-dev-tools/qemu-arm-xpack.git && \
xpm run docker-build-develop --config linux-x64 -C ~/Work/xpack-dev-tools/qemu-arm-xpack.git

これなら、3分ぐらいでビルドが完了します。

実行してみましたが、残念ながら、変更は反映されていませんでした。

package.jsonを読む

ビルド手順は以下に従っています。

github.com

ビルド手順は以下となっています。

実際のビルドは最後の4つの xpm の実行です。

rm -f ~/Work/xpack-dev-tools/qemu-arm-xpack.git/package-lock.json && \
git -C ~/Work/xpack-dev-tools/qemu-arm-xpack.git pull && \
xpm run install -C ~/Work/xpack-dev-tools/qemu-arm-xpack.git && \
git -C ~/Work/xpack-dev-tools/xbb-helper-xpack.git pull && \
xpm link -C ~/Work/xpack-dev-tools/xbb-helper-xpack.git && \
xpm run link-deps -C ~/Work/xpack-dev-tools/qemu-arm-xpack.git && \
\
xpm run deep-clean --config linux-x64 -C ~/Work/xpack-dev-tools/qemu-arm-xpack.git && \
xpm run docker-prepare --config linux-x64 -C ~/Work/xpack-dev-tools/qemu-arm-xpack.git && \
xpm run docker-link-deps --config linux-x64 -C ~/Work/xpack-dev-tools/qemu-arm-xpack.git && \
xpm run docker-build-develop --config linux-x64 -C ~/Work/xpack-dev-tools/qemu-arm-xpack.git

それぞれ、何をしているかを、package.json を詳しく見ます。

deep-clean

~/Work/xpack-dev-tools/qemu-arm-xpack.git のビルド中に作られる3つのディレクトリ buildxpacksnode_modules を全て削除しているようです。build には先ほど変更したソースコードも含まれるので、deep-clean を実行すると変更は反映されません。

2つ目の実行は、そのディレクトリは存在しないので何もしないと思います。

"deep-clean": [
  "rm -rf build xpacks node_modules package-lock.json",
  "rm -rf ${HOME}/Work/xpack-dev-tools-build/{{ properties.appLcName }}-[0-9]*-*"
],

docker-prepare

ディレクトリの作成と、Dockerコンテナの再作成、コンテナの環境構築、xpm install で必要なライブラリのインストールまでを行っていそうです。

"docker-prepare": [
  "mkdir -pv ${HOME}/Work/xpack-dev-tools/xbb-helper-xpack.git",
  "mkdir -pv ${HOME}/.local/xPacks ${HOME}/.cache/xPacks ${HOME}/Work/cache",
  "mkdir -pv ${HOME}/.wine",
  "docker rm --force {{ properties.containerName }}",
  "docker create --name {{ properties.containerName }} --tty --hostname docker --volume $(pwd):$(pwd) --volume ${HOME}/Work/xpack-dev-tools/xbb-helper-xpack.git:${HOME}/Work/xpack-dev-tools/xbb-helper-xpack.git --volume ${HOME}/.cache/xPacks:${HOME}/.cache/xPacks --volume ${HOME}/Work/cache:${HOME}/Work/cache --volume ${HOME}/.wine:${HOME}/.wine --workdir $(pwd) {{ properties.dockerImage }}",
  "docker start {{ properties.containerName }}",
  "docker exec {{ properties.containerName }} {{ properties.force32 }} npm install --location=global xpm@{{ properties.xpm-version }}",
  "docker exec {{ properties.containerName }} {{ properties.force32 }} userdel node",
  "docker exec {{ properties.containerName }} {{ properties.force32 }} groupadd --gid $(id -g) --force $(id -gn)",
  "docker exec {{ properties.containerName }} {{ properties.force32 }} useradd --home-dir ${HOME} --uid $(id -u) --gid $(id -g) $(id -un) --create-home",
  "docker exec {{ properties.containerName }} {{ properties.force32 }} chown --recursive $(id -u):$(id -g) ${HOME}",
  "docker exec --user $(id -un) {{ properties.containerName }} {{ properties.force32 }} bash -c 'lsb_release -sd && whoami && pwd && ls -lLA && ls -l ${HOME}'",
  "docker exec --user $(id -un) {{ properties.containerName }} {{ properties.force32 }} xpm install",
  "docker exec --user $(id -un) {{ properties.containerName }} {{ properties.force32 }} xpm install --config {{ configuration.name }}"
],

docker-link-deps

あまり意味は分かってませんが、xpm のリンクを実行していそうです。

"docker-link-deps": [
  "docker exec --user $(id -un) {{ properties.containerName }} {{ properties.force32 }} xpm link -C ${HOME}/Work/xpack-dev-tools/xbb-helper-xpack.git",
  "docker exec --user $(id -un) {{ properties.containerName }} {{ properties.force32 }} xpm run link-deps"
],

docker-build-develop

最後の xpm の実行です。ここで、ソースのクローンとビルドの両方を実行していそうです。

ソースのクローンの後に、ソースの変更をしたいので、xpm コマンドのレベルでは差分ビルドは出来ないことになります。

Dockerコンテナ上で、xpm run build-develop を実行しているので、これを、もう少し細かく見ていきます。

"docker-build-develop": [
  "docker exec --user $(id -un) {{ properties.containerName }} {{ properties.force32 }} xpm run build-develop --config {{ configuration.name }}"
],

build-develop

build-develop は、以下となっています。関係するプロパティも貼っておきました。

scripts/build.sh を引数付きで実行しているようです。

"build-develop": "{{ properties.commandBashBuild }} --develop",
"commandBashBuild": "bash {{ properties.dbg }} scripts/build.sh --target {{ configuration.name }} --build-folder {{ properties.buildFolderRelativePathPosix }}",
"dbg": ""
"name": "@xpack-dev-tools/qemu-arm",
"buildFolderRelativePathPosix": "{{ 'build' | path_posix_join: configuration.name | downcase }}",

scripts/build.shを読む

以下は、scripts/build.sh の重要な部分です。

他のファイルもざっと見たところ、最後から2番目の build_common_parse_options がコマンドライン引数の処理で、build_common_run が実際のビルドが実行されるところのようです。それ以外は、変数か関数を定義しているだけのようでした。

分割して実行できるといいかなと思ってましたが、そんな簡単には出来なさそうです。

source "${scripts_folder_path}/application.sh"

# Common definitions.
source "${helper_folder_path}/scripts/build-common.sh"

source "${scripts_folder_path}/versioning.sh"

if [ ${#XBB_APPLICATION_COMMON_DEPENDENCIES[@]} -ne 0 ]
then
  for dependency in ${XBB_APPLICATION_COMMON_DEPENDENCIES[@]}
  do
    echo "Including ${helper_folder_path}/dependencies/${dependency}.sh..."
    source "${helper_folder_path}/dependencies/${dependency}.sh"
  done
fi

if [ ${#XBB_APPLICATION_DEPENDENCIES[@]} -ne 0 ]
then
  for dependency in ${XBB_APPLICATION_DEPENDENCIES[@]}
  do
    echo "Including ${scripts_folder_path}/dependencies/${dependency}.sh..."
    source "${scripts_folder_path}/dependencies/${dependency}.sh"
  done
fi

# -----------------------------------------------------------------------------

help_message="    bash $0 [--win] [--debug] [--develop] [--jobs N] [--help]"
build_common_parse_options "${help_message}" "$@"

build_common_run

make を実行してるなら、docker-build-develop を実行したときに、変更されたファイルがビルド対象になって差分ビルドが出来るはずですが、なぜか変更が反映されません。この理由を明らかにする方向で考えます。

シェルスクリプトを見ていると、~/Work/xpack-dev-tools/qemu-arm-xpack.git/scripts/dependencies/qemu-arm-legacy.sh というのを見つけました。これが STM32 を含む、マイコン用の QEMU のビルドのようです。うまく動けば、差分ビルドが出来そうです。

このシェルスクリプトを詳しく見ると、stampファイルというのが作られて、それがあると何もしない、無ければ、ビルドするパスに入る、のような感じになっています。

・・・省略・・・
  local qemu_arm_legacy_stamp_file_path="${XBB_STAMPS_FOLDER_PATH}/stamp-${qemu_arm_legacy_folder_name}-installed"
  if [ ! -f "${qemu_arm_legacy_stamp_file_path}" ] || [ "${XBB_IS_DEBUG}" == "y" ]
  then
    (
・・・省略・・・
        echo
        echo "Running qemu arm legacy make..."

        # Build.
        run_verbose make -j ${XBB_JOBS} V=1

        run_verbose make install
        run_verbose make install-gme
・・・省略・・・

差分ビルドをやってみる

試しに、~/Work/xpack-dev-tools/qemu-arm-xpack.git/build/linux-x64/x86_64-pc-linux-gnu/stamps/stamp-qemu-legacy-2.8.0-17-installed を削除して、ボタンが押しっぱなしの状態になるソースコードの変更を行って、ビルドを実行してみます。

変更したソースコードが動作に反映された
変更したソースコードが動作に反映された

ようやく変更したソースコードが動作に反映されました。stamps ディレクトリのファイルの有無によって、ビルドが実行されるかどうかを制御しているようです。

今回は以上になります。

おわりに

今回も、だいぶ苦労して、QEMU のソースコードの変更を反映することが出来ました。

この記事が誰かの役に立てば嬉しいです。

最後になりましたが、エンジニアグループのランキングに参加中です。

気楽にポチッとよろしくお願いいたします🙇

今回は以上です!

最後までお読みいただき、ありがとうございました。