LinuxカーネルのuImageのフォーマット/作成手順/ブート開始
ARM Linuxを動かすとき、u-bootを使うのでuImageをよく作っているのだけど、その造りを知らなかったので調べた。
uImageのフォーマット
Ubuntu14.04でuImageをつくるツールをインストールすると、昔はuboot-mkimage
だったのが、u-boot-tools
に変わっていた。
u-boot-tools
に含まれるmkimge
ツールでと使うとuImageの作ったりヘッダ情報を調べたりできる。ヘッダ情報を見る場合はl
オプションをつける。
$ mkimage -l uImage Image Name: Linux-3.16.0 Created: Sun Aug 31 00:55:42 2014 Image Type: ARM Linux Kernel Image (uncompressed) Data Size: 2782856 Bytes = 2717.63 kB = 2.65 MB Load Address: 40008000 Entry Point: 40008000
uImageのヘッダ情報は、ココらへんで定義されている。 サイズは64バイトで、各メンバの数値の意味は同じファイルに定義されている。
typedef struct image_header { __be32 ih_magic; /* Image Header Magic Number */ __be32 ih_hcrc; /* Image Header CRC Checksum */ __be32 ih_time; /* Image Creation Timestamp */ __be32 ih_size; /* Image Data Size */ __be32 ih_load; /* Data Load Address */ __be32 ih_ep; /* Entry Point Address */ __be32 ih_dcrc; /* Image Data CRC Checksum */ uint8_t ih_os; /* Operating System */ uint8_t ih_arch; /* CPU architecture */ uint8_t ih_type; /* Image Type */ uint8_t ih_comp; /* Compression Type */ uint8_t ih_name[IH_NMLEN]; /* Image Name IH_NMLEN=32 */ } image_header_t;
uImageの作り方
uImageのつくり方を調べるために、make
時にn
オプションをつける。このオプションをつけると、make
時のコマンドが表示される。
$ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- LOADADDR=0x40008000 uImage -n
結果を見ると、uImageを作るときのコマンドはファイルに保存されていることがわかる。linux/arch/arm/boot/.uImage.cmd
に保存されていて、mkuboot.sh
は、mkimage
を呼び出すだけである。
$ ./scripts/mkuboot.sh -A arm -O linux -C none -T kernel -a 0x40008000 -e 0x40008000 -n 'Linux-3.16.0' -d arch/arm/boot/zImage arch/arm/boot/uImage
mkimage
のd
オプションはuImageに含めるデータの実体でありzImageを指定している。他のオプションはuImageの先頭につけるヘッダ情報を指定しているだけである。
次にzImageについて調べる。uImageと同様にlinux/arch/arm/boot/.zImage.cmd
に作成コマンドが保存さている。
$ arm-none-linux-gnueabi-objcopy -O binary -R .comment -S arch/arm/boot/compressed/vmlinux arch/arm/boot/zImage
zImageは、vmlinuxから.comment
セクションの削除および、シンボルと再配置情報を落としたものであることが分かる。
最後にvmlinuxであるが、これの作成コマンドはlinux/arch/arm/boot/compressed/.vmlinux.cmd
に保存されている。
arm-none-linux-gnueabi-ld -EL --defsym _kernel_bss_size=237028 -p --no-undefined -X -T arch/arm/boot/compressed/vmlinux.lds arch/arm/boot/compressed/head.o arch/arm/boot/compressed/piggy.gzip.o arch/arm/boot/compressed/misc.o arch/arm/boot/compressed/decompress.o arch/arm/boot/compressed/string.o arch/arm/boot/compressed/hyp-stub.o arch/arm/boot/compressed/fdt_rw.o arch/arm/boot/compressed/fdt_ro.o arch/arm/boot/compressed/fdt_wip.o arch/arm/boot/compressed/fdt.o arch/arm/boot/compressed/atags_to_fdt.o arch/arm/boot/compressed/lib1funcs.o arch/arm/boot/compressed/ashldi3.o arch/arm/boot/compressed/bswapsdi2.o -o arch/arm/boot/compressed/vmlinux
ブート開始
pcduinoというARM Cortex A8ベースのボードで確認する。
手順は、http://linux-sunxi.org/Mainline_Kernel_Howtoに従った。
まずカーネルのビルドをする。
LOADADDR
は、普通はconfigファイルで定義されているのかなと思うけど、今回ターゲットにしているpcduinoのconfigには定義されていなかったようである。
$ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- LOADADDR=0x40008000 uImage dtbs -n
次に、make
で作ったuImageとdtbを以下のようなu-bootコマンドで、pcduinoのメモリ上にロードする。
ext4load mmc 0:2 0x46000000 /uImage ext4load mmc 0:2 0x49000000 /sun4i-a10-pcduino.dtb setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait ${extra} bootm 0x46000000 - 0x49000000
u-bootのbootm
コマンドを実行すると、カーネルの起動が開始される。
bootm
で指定したアドレスのうち、0x46000000
はカーネルをロードするアドレス、0x49000000
はdtbをロードするアドレスになる。
u-bootは、uImageのヘッダを除いた部分を0x46000000
からLOADADDR
にコピーする(ココらへん)。
あとは、LOADADDR
から実行を開始すればカーネルがスタートする(ココらへん)。
カーネルの最初に実行される部分は、zImageの最初の部分となる。その部分は、vmlinux
の作成コマンドからlinux/arch/arm/kernel/vmlinux.lds
を見ればよいことが分かる。
エントリポイントとなる_start
は、.start
の最初の部分を指している。これは、linux/arch/arm/boot/compressed/head.S
のstart
にあたる。
このあとは長い長いカーネルの実行が開始される。