daisukeの技術ブログ

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

QEMUで組み込みLinux(Buildroot+BusyBox+U-Boot)で起動する

前回 は、QEMU で Arm の組み込み Linux(Buildroot+BusyBox)で、U-Boot を追加して、U-Boot の起動まで確認しました。

今回は、U-Boot の設定をして、カーネル、ルートファイルシステムを起動していきます。

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

はじめに

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

QEMUを動かすの記事一覧
・第1回:STM32(ARM Cortex-M)をQEMUで動かす(環境構築編)
・第2回:STM32(ARM Cortex-M)をQEMUで動かす(ソースコード確認編)
・第3回:STM32(ARM Cortex-M)をQEMUで動かす(スタートアップルーチン編)
・第4回:STM32(ARM Cortex-M)をQEMUで動かす(リンカスクリプト編)
・第5回:STM32(ARM Cortex-M)のELFファイルの内容を確認する
・第6回:STM32(ARM Cortex-M)のELFファイル⇔バイナリの変換を行う
・第7回:STM32(ARM Cortex-M)のバイナリから構築したELFファイルをQEMUで動かす
・第8回:QEMUのビルドに必要なxpm(xPack Project Manager)について学ぶ
・第9回:QEMUをソースからビルドして動かす
・第10回:QEMUのソースコードを変更してSTM32の動作を変える
・第11回:QEMUに似たRenodeというOSSの組込みデバイスエミュレータを試す
・第12回:QEMUに似たRenodeでSTM32をGDBを使ってデバッグする
・第13回:QEMUに似たRenodeでSTM32をバイナリファイルで動かす
・第14回:QEMUに似たRenodeをソースからビルドする
・第15回:QEMUに似たRenodeでVSCodeを使ってデバッグする
・第16回:QEMUに似たRenodeでVSCodeを使ってRenode自体をデバッグする
・第17回:QEMUで組み込みLinux(Buildroot+BusyBox)をやってみる
・第18回:QEMUで組み込みLinux(Buildroot+BusyBox)をやってみる、の補足
・第19回:QEMUで組み込みLinux(Buildroot+BusyBox)にU-Bootを追加する
・第20回:QEMUで組み込みLinux(Buildroot+BusyBox+U-Boot)で起動する ← 今回

以下は、Buildroot の Documentation のリンクです。

https://buildroot.org/downloads/manual/manual.html

環境は、VirtualBox に入れた Ubuntu 22.04 です。

Buildroot の menuconfig では、qemu_arm_vexpress_defconfig を指定して、U-Boot を有効にして、U-Boot の Board defconfig に、express_ca9x4 を指定しています。

U-Boot 単体の起動は確認できたので、今回は、その続きをやっていきます。

U-Bootで起動する

QEMU の実行(start-qemu.sh の最後の行)と、U-Boot の設定が出来れば、カーネルとルートファイルシステムは起動するはずです。

ここでは、まず、U-Boot の設定について調査を行います。

U-Bootの設定を変更する

まず、start-qemu.sh の最後の行は以下のようにしました。最小限です。

実際は1行で書いてますが、見にくいので、ここでは改行を入れています。

exec qemu-system-arm -M vexpress-a9 -smp 1 -m 256 ¥
  -kernel ../build/uboot-2024.01/u-boot ¥
  ${EXTRA_ARGS} "$@"

start-qemu.sh を実行すると、U-Boot が起動します。Hit any key to stop autoboot: と出るので、何らかのキーを押します。

すると、U-Boot のコマンドが実行できる状態(=>)になります。

$ ./start-qemu.sh
VNC server running on 127.0.0.1:5900

U-Boot 2024.01 (Jun 25 2024 - 23:42:33 +0900)

DRAM:  256 MiB
WARNING: Caches not enabled
Core:  18 devices, 10 uclasses, devicetree: embed
Flash: 128 MiB
MMC:   mmci@5000: 0
Loading Environment from Flash... *** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Net:   eth0: ethernet@3,02000000
Hit any key to stop autoboot:  0
=>

現在の変数の設定値を確認します。

=> printenv
arch=arm
baudrate=38400
board=vexpress
board_name=vexpress
boot_a_script=load ${devtype} ${devnum}:${distro_bootpart} ${scriptaddr} ${prefix}${script}; source ${scriptaddr}
boot_efi_binary=load ${devtype} ${devnum}:${distro_bootpart} ${kernel_addr_r} efi/boot/bootarm.efi; if fdt addr -q ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r};else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi
boot_efi_bootmgr=if fdt addr -q ${fdt_addr_r}; then bootefi bootmgr ${fdt_addr_r};else bootefi bootmgr;fi
boot_extlinux=sysboot ${devtype} ${devnum}:${distro_bootpart} any ${scriptaddr} ${prefix}${boot_syslinux_conf}
boot_prefixes=/ /boot/
boot_script_dhcp=boot.scr.uimg
boot_scripts=boot.scr.uimg boot.scr
boot_syslinux_conf=extlinux/extlinux.conf
boot_targets=mmc1 mmc0 pxe dhcp
bootargs=console=tty0 console=ttyAMA0,38400n8
bootcmd=run distro_bootcmd; run bootflash
bootcmd_dhcp=devtype=dhcp; if dhcp ${scriptaddr} ${boot_script_dhcp}; then source ${scriptaddr}; fi;setenv efi_fdtfile ${fdtfile}; if test -z "${fdtfile}" -a -n "${soc}"; then setenv efi_fdtfile ${soc}-${board}${boardver}.dtb; fi; setenv efi_old_vci ${bootp_vci};setenv efi_old_arch ${bootp_arch};setenv bootp_vci PXEClient:Arch:00010:UNDI:003000;setenv bootp_arch 0xa;if dhcp ${kernel_addr_r}; then tftpboot ${fdt_addr_r} dtb/${efi_fdtfile};if fdt addr -q ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r}; else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi;fi;setenv bootp_vci ${efi_old_vci};setenv bootp_arch ${efi_old_arch};setenv efi_fdtfile;setenv efi_old_arch;setenv efi_old_vci;
bootcmd_mmc0=devnum=0; run mmc_boot
bootcmd_mmc1=devnum=1; run mmc_boot
bootcmd_pxe=dhcp; if pxe get; then pxe boot; fi
bootdelay=2
bootflash=run flashargs; cp ${ramdisk_addr} ${ramdisk_addr_r} ${maxramdisk}; bootm ${kernel_addr} ${ramdisk_addr_r}
console=ttyAMA0,38400n8
cpu=armv7
distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done
dram=1024M
efi_dtb_prefixes=/ /dtb/ /dtb/current/
ethaddr=52:54:00:12:34:56
fdt_addr_r=0x60000000
fdtcontroladdr=6087ef20
fdtfile=vexpress-v2p-ca9.dtb
flashargs=setenv bootargs root=${root} console=${console} mem=${dram} mtdparts=${mtd} mmci.fmax=190000 devtmpfs.mount=0  vmalloc=256M
kernel_addr_r=0x60100000
load_efi_dtb=load ${devtype} ${devnum}:${distro_bootpart} ${fdt_addr_r} ${prefix}${efi_fdtfile}
loadaddr=0x60100000
mmc_boot=if mmc dev ${devnum}; then devtype=mmc; run scan_dev_for_boot_part; fi
mtd=armflash:1M@0x800000(uboot),7M@0x1000000(kernel),24M@0x2000000(initrd)
root=/dev/sda1 rw
scan_dev_for_boot=echo Scanning ${devtype} ${devnum}:${distro_bootpart}...; for prefix in ${boot_prefixes}; do run scan_dev_for_extlinux; run scan_dev_for_scripts; done;run scan_dev_for_efi;
scan_dev_for_boot_part=part list ${devtype} ${devnum} -bootable devplist; env exists devplist || setenv devplist 1; for distro_bootpart in ${devplist}; do if fstype ${devtype} ${devnum}:${distro_bootpart} bootfstype; then part uuid ${devtype} ${devnum}:${distro_bootpart} distro_bootpart_uuid ; run scan_dev_for_boot; fi; done; setenv devplist
scan_dev_for_efi=setenv efi_fdtfile ${fdtfile}; if test -z "${fdtfile}" -a -n "${soc}"; then setenv efi_fdtfile ${soc}-${board}${boardver}.dtb; fi; for prefix in ${efi_dtb_prefixes}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${efi_fdtfile}; then run load_efi_dtb; fi;done;run boot_efi_bootmgr;if test -e ${devtype} ${devnum}:${distro_bootpart} efi/boot/bootarm.efi; then echo Found EFI removable media binary efi/boot/bootarm.efi; run boot_efi_binary; echo EFI LOAD FAILED: continuing...; fi; setenv efi_fdtfile
scan_dev_for_extlinux=if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${boot_syslinux_conf}; then echo Found ${prefix}${boot_syslinux_conf}; run boot_extlinux; echo EXTLINUX FAILED: continuing...; fi
scan_dev_for_scripts=for script in ${boot_scripts}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${script}; then echo Found U-Boot script ${prefix}${script}; run boot_a_script; echo SCRIPT FAILED: continuing...; fi; done
stderr=serial
stdin=serial
stdout=serial
ubifs_boot=if ubi part ${bootubipart} ${bootubioff} && ubifsmount ubi0:${bootubivol}; then devtype=ubi; devnum=ubi0; bootfstype=ubifs; distro_bootpart=${bootubivol}; run scan_dev_for_boot; ubifsumount; fi
vendor=armltd

Environment size: 4302/262140 bytes

ついでに、ヘルプも見ておきます。

=> help
?         - alias for 'help'
base      - print or set address offset
bdinfo    - print Board Info structure
blkcache  - block cache diagnostics and control
bootefi   - Boots an EFI payload from memory
bootelf   - Boot from an ELF image in memory
bootflow  - Boot flows
bootm     - boot application image from memory
bootp     - boot image via network using BOOTP/TFTP protocol
bootvx    - Boot vxWorks from an ELF image
bootz     - boot Linux zImage image from memory
cmp       - memory compare
cp        - memory copy
crc32     - checksum calculation
dhcp      - boot image via network using DHCP/TFTP protocol
echo      - echo args to console
eficonfig - provide menu-driven UEFI variable maintenance interface
env       - environment handling commands
erase     - erase FLASH memory
exit      - exit script
ext2load  - load binary file from a Ext2 filesystem
ext2ls    - list files in a directory (default /)
ext4load  - load binary file from a Ext4 filesystem
ext4ls    - list files in a directory (default /)
ext4size  - determine a file's size
false     - do nothing, unsuccessfully
fatinfo   - print information about filesystem
fatload   - load binary file from a dos filesystem
fatls     - list files in a directory (default /)
fatmkdir  - create a directory
fatrm     - delete a file
fatsize   - determine a file's size
fatwrite  - write file into a dos filesystem
fdt       - flattened device tree utility commands
flinfo    - print FLASH memory information
fstype    - Look up a filesystem type
fstypes   - List supported filesystem types
go        - start application at address 'addr'
help      - print command description/usage
iminfo    - print header information for application image
ln        - Create a symbolic link
load      - load binary file from a filesystem
loop      - infinite loop on address range
ls        - list files in a directory (default /)
md        - memory display
mii       - MII utility commands
mm        - memory modify (auto-incrementing address)
mmc       - MMC sub system
mmcinfo   - display MMC info
mw        - memory write (fill)
net       - NET sub-system
nm        - memory modify (constant address)
panic     - Panic with optional message
part      - disk partition related commands
ping      - send ICMP ECHO_REQUEST to network host
printenv  - print environment variables
protect   - enable or disable FLASH write protection
pxe       - get and boot from pxe files
random    - fill memory with random pattern
reset     - Perform RESET of the CPU
run       - run commands in an environment variable
save      - save file to a filesystem
saveenv   - save environment variables to persistent storage
setenv    - set environment variables
showvar   - print local hushshell variables
size      - determine a file's size
source    - run script from memory
sysboot   - command to get and boot from syslinux files
test      - minimal test like /bin/sh
tftpboot  - load file via network using TFTP protocol
true      - do nothing, successfully
ubi       - ubi commands
ubifsload - load file from an UBIFS filesystem
ubifsls   - list files in a directory
ubifsmount- mount UBIFS volume
ubifsumount- unmount UBIFS volume
version   - print monitor, compiler and linker version

ざっと眺めたところで、今回も参考にさせて頂くサイトです。

leavatail.hatenablog.com

U-Boot の bootz を使って、起動しています。

U-Boot の公式サイトのドキュメントを見ます。

docs.u-boot.org

U-Boot の bootz は、zImage 形式のカーネルが起動できるようです。

そして、以下を指定します。

  • 第1引数:カーネルを配置するアドレス(0x62000000)
  • 第2引数:initramfs を配置するアドレス(0x63008000)
  • 第3引数:DTB を配置するアドレス(0x63000000)

括弧内は、参考サイトの指定してるアドレスです。

参考サイトは、initramfs を使用されてますが、こちらはまだ準備していません。

公式サイトを見ると、その場合は「-」を指定すればいいと書かれていました。

bootz 0x62000000 - 0x63000000 を実行すれば良さそうです。

bootz は、メモリに配置されているカーネル、initramfs、DTB を起動してくれるコマンドで、誰かがメモリに配置する必要があります。

参考サイトでは、QEMU の起動時のパラメータで、それぞれを指定のアドレスで配置しています。おそらく、U-Boot の load コマンドで配置する方法もあると思います。

QEMUの実行パラメータを変更する

start-qemu.sh を変更します。最小限にしてましたが、いったんデフォルトに戻して、カーネルと DTB を配置する設定を追加します。

実際は1行で書いてますが、見にくいので、ここでは改行を入れています。

#exec qemu-system-arm -M vexpress-a9 -smp 1 -m 256 -kernel ../build/uboot-2024.01/u-boot ${EXTRA_ARGS} "$@"
exec qemu-system-arm -M vexpress-a9 -smp 1 -m 256 \
  -kernel ../build/uboot-2024.01/u-boot \
  -device loader,file=zImage,addr=0x62000000 \
  -device loader,file=vexpress-v2p-ca9.dtb,addr=0x63000000 \
  -drive file=rootfs.ext2,if=sd,format=raw \
  -append "console=ttyAMA0,115200 rootwait root=/dev/mmcblk0" \
  -net nic,model=lan9118 -net user ${EXTRA_ARGS} "$@"

起動してみる

早速、起動してみます。

Hit any key to stop autoboot: と出たら、何らかのキーを押して、U-Boot で、以下を実行します。

=> bootz  0x62000000 - 0x63000000

Kernel image @ 0x62000000 [ 0x000000 - 0x5153d0 ]
## Flattened Device Tree blob at 63000000
   Booting using the fdt blob at 0x63000000
Working FDT set to 63000000
   Loading Device Tree to 6eb16000, end 6eb1c700 ... OK
Working FDT set to 6eb16000

Starting kernel ...

Booting Linux on physical CPU 0x0
Linux version 6.1.44 (daisuke@daisuke-VirtualBox) (arm-buildroot-linux-gnueabihf-gcc.br_real (Buildroot 2024.02.3) 12.3.0, GNU ld (GNU Binutils) 2.40) #1 SMP Tue Jun 25 23:48:48 JST 2024
CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
OF: fdt: Machine model: V2P-CA9
OF: fdt: Ignoring memory block 0x80000000 - 0x80000004
Memory policy: Data cache writeback
Reserved memory: created DMA memory pool at 0x4c000000, size 8 MiB
OF: reserved mem: initialized node vram@4c000000, compatible id shared-dma-pool
cma: Reserved 16 MiB at 0x6f000000
Zone ranges:
  Normal   [mem 0x0000000060000000-0x000000006fffffff]
Movable zone start for each node
Early memory node ranges
  node   0: [mem 0x0000000060000000-0x000000006fffffff]
Initmem setup node 0 [mem 0x0000000060000000-0x000000006fffffff]
(省略)
drm-clcd-pl111 10020000.clcd: initializing Versatile Express PL111
/dev/root: Can't open blockdev
VFS: Cannot open root device "(null)" or unknown-block(0,0): error -6
Please append a correct "root=" boot option; here are the available partitions:
1f00          131072 mtdblock0
 (driver?)
1f01           32768 mtdblock1
 (driver?)
b300           65536 mmcblk0
 driver: mmcblk
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 6.1.44 #1
Hardware name: ARM-Versatile Express
 unwind_backtrace from show_stack+0x10/0x14
 show_stack from dump_stack_lvl+0x40/0x4c
 dump_stack_lvl from panic+0x108/0x318
 panic from mount_block_root+0x1c4/0x264
 mount_block_root from prepare_namespace+0x150/0x18c
 prepare_namespace from kernel_init+0x18/0x12c
 kernel_init from ret_from_fork+0x14/0x2c
Exception stack(0x90825fb0 to 0x90825ff8)
5fa0:                                     00000000 00000000 00000000 00000000
5fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
5fe0: 00000000 00000000 00000000 00000000 00000013 00000000
---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---

カーネルはうまく起動しましたが、ルートファイルシステムを起動するところでエラーが出ています。

U-Boot がカーネルを起動するので、カーネルのパラメータは、QEMU に指定するのではなく、U-Boot に指定する必要がありそうです。

start-qemu.sh は変えなくても良さそう(使われないので)ですが、一応、-append "xxx" の部分を削除しました。

U-Boot のカーネルパラメータの設定方法は、先ほど貼った printenv を見ると、bootargs という変数に setenv で設定すれば良さそうです。

以下のような感じでしょうか、やってみます。

=> setenv bootargs "console=ttyAMA0,115200 rootwait root=/dev/mmcblk0"

$ ./start-qemu.sh
VNC server running on 127.0.0.1:5900

U-Boot 2024.01 (Jun 25 2024 - 23:42:33 +0900)

DRAM:  256 MiB
WARNING: Caches not enabled
Core:  18 devices, 10 uclasses, devicetree: embed
Flash: 128 MiB
MMC:   mmci@5000: 0
Loading Environment from Flash... *** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Net:   eth0: ethernet@3,02000000
Hit any key to stop autoboot:  0
=> setenv bootargs "console=ttyAMA0,115200 rootwait root=/dev/mmcblk0"
=> bootz  0x62000000 - 0x63000000
Kernel image @ 0x62000000 [ 0x000000 - 0x5153d0 ]
## Flattened Device Tree blob at 63000000
   Booting using the fdt blob at 0x63000000
Working FDT set to 63000000
   Loading Device Tree to 6eb16000, end 6eb1c700 ... OK
Working FDT set to 6eb16000

Starting kernel ...
(省略)
Run /sbin/init as init process
random: crng init done
EXT4-fs (mmcblk0): re-mounted. Quota mode: disabled.
Seeding 256 bits and crediting
Saving 256 bits of creditable seed for next boot
Starting syslogd: OK
Starting klogd: OK
Running sysctl: OK
Starting network: Generic PHY 4e000000.ethernet-ffffffff:01: attached PHY driver (mii_bus:phy_addr=4e000000.ethernet-ffffffff:01, irq=POLL)
smsc911x 4e000000.ethernet eth0: SMSC911x/921x identified at 0x90960000, IRQ: 30
drm-clcd-pl111 10020000.clcd: DVI muxed to daughterboard 1 (core tile) CLCD
drm-clcd-pl111 10020000.clcd: initializing Versatile Express PL111
udhcpc: started, v1.36.1
udhcpc: broadcasting discover
udhcpc: broadcasting select for 10.0.2.15, server 10.0.2.2
udhcpc: lease of 10.0.2.15 obtained from 10.0.2.2, lease time 86400
deleting routers
adding dns 10.0.2.3
OK

Welcome to Buildroot
buildroot login:

うまく起動しました。

何となく、ダブルクォーテーション(")はいらなかったような気がするので、試してみます。

入力したところだけ貼っておきます。同じように正しく起動できたようです。

$ ./start-qemu.sh
=> setenv bootargs console=ttyAMA0,115200 rootwait root=/dev/mmcblk0
=> bootz  0x62000000 - 0x63000000

確か、U-Boot は、デフォルトで、run bootcmd を実行すると思うので、bootcmd 変数を変更します。あと、毎回入力するのは面倒なので、saveenv で保存します。

$ ./start-qemu.sh
=> setenv bootargs console=ttyAMA0,115200 rootwait root=/dev/mmcblk0
=> setenv bootcmd bootz  0x62000000 - 0x63000000
=> printenv
bootargs=console=ttyAMA0,115200 rootwait root=/dev/mmcblk0
bootcmd=bootz 0x62000000 - 0x63000000
=> saveenv
Saving Environment to Flash... . done
Un-Protected 1 sectors
Erasing Flash...
. done
Erased 1 sectors
Writing to Flash... done
. done
Protected 1 sectors
OK
=> reset

バッチリ起動しました!

これで、毎回入力しなくても、自動で起動すると思ったのですが、そうではありませんでした。

実機なら、Flash に書き込むので、設定を覚えてくれますが、QEMU を使ってるので、QEMU を起動し直すと、設定は忘れられてしまうようです。

ファイルに書いてるわけでもないので、それはそうかな、と思いました。

まぁ、でも、今回は久しぶりに上手くいって満足です!

おわりに

今回は、U-Boot の設定を行い、U-Boot からカーネル、ルートファイルシステム(Buildroot)を起動するところまでやりました。

U-Boot のロゴを探したところ、、、ありました!

U-Boot のロゴというものを、初めて知りました(笑)。では、ありがたくアイキャッチに使わせて頂きます。

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

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

今回は以上です!

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