U-Boot (aarch64) on Qemu を動作させたい (3)
確認していくと、どうやら u-boot の _start (arch/arm/cpu/armv8/start.S:22) がちゃんと処理されていることが確認できた。
確認手順は、
$ qemu-system-aarch64 \ -S -gdb tcp::1234 \ -machine $MACHINE \ -kernel u-boot/u-boot \ -nographic
してから、
$ aarch64-linux-gnu-gdb u-boot ... Reading symbols from u-boot...done. (gdb) target remote localhost:1234 Remote debugging using localhost:1234 _start () at arch/arm/cpu/armv8/start.S:22 22 b reset
で OK。
u-boot の動きを追いかけると、、、
board_init_f (boot_flags=<optimized out>) at common/board_f.c:1083
までは動作していて、
relocate_code () at arch/arm/lib/relocate_64.S:24 24 mov x29, sp (gdb) s 25 str x0, [sp, #16] (gdb) s 29 ldr x1, =__image_copy_start /* x1 <- SRC &__image_copy_start */ (gdb) p __image_copy_start $6 = 0x80000 <_start> "\n"
リロケーションも上手く行っているように見えるが、、、
relocate_code()
を抜けるとハングしているので、リロケーションが上手くできていない??
relocate_code () at arch/arm/lib/relocate_64.S:76 76 ret (gdb) s ^C Thread 1 received signal SIGINT, Interrupt. 0x0000000000000000 in ?? () (gdb) bt #0 0x0000000000000000 in ?? () #1 0x0000000000000000 in ?? () Backtrace stopped: not enough registers or memory available to unwind further
直前のコードで怪しそうなのは、、、
26 /* 27 * Copy u-boot from flash to RAM 28 */ 29 ldr x1, =__image_copy_start /* x1 <- SRC &__image_copy_start */ 30 subs x9, x0, x1 /* x9 <- relocation offset */ 31 b.eq relocate_done /* skip relocation */ 32 ldr x2, =__image_copy_end /* x2 <- SRC &__image_copy_end */ 33 34 copy_loop: 35 ldp x10, x11, [x1], #16 /* copy from source address [x1] */
で、実際の処理は b.eq で relocate_done にジャンプしており、コピーが行われていない。 (正しくはコピーが行われるべき??)
__image_copy_start は
(gdb) p __image_copy_start $1 = 0x80000 <_start> "\n" (gdb) p/x *(__image_copy_start + 0) $14 = 0xa (gdb) p/x *(__image_copy_start + 1) $15 = 0x0 (gdb) p/x *(__image_copy_start + 2) $16 = 0x0 (gdb) p/x *(__image_copy_start + 3) $17 = 0x14 (gdb) p/x *(__image_copy_start + 4) $18 = 0x1f (gdb) p/x *(__image_copy_start + 5) $19 = 0x20 (gdb) p/x *(__image_copy_start + 6) $20 = 0x3 (gdb) p/x *(__image_copy_start + 7) $21 = 0xd5
となっており、 u-boot.bin のデータが先頭から配置されていた。
レジスタの状態はというと、
x0 0x0 0 x1 0x0 0 x2 0x0 0 x3 0x0 0 x4 0x0 0 x5 0x2e 46 x6 0x170 368 x7 0x0 0 x8 0x0 0 x9 0x0 0
となっており、 subs x9, x0, x1
で x9 が 0 になったため、 b.eq で relocate_done にジャンプする結果になったようだ。
relocate_code()
の処理を真面目に追いかけると、
16 /* 17 * void relocate_code (addr_moni) 18 * 19 * This function relocates the monitor code. 20 * x0 holds the destination address. 21 */ 22 ENTRY(relocate_code) # x29 0x80088 524424 # x30 0x82588 533896 # sp 0x0 0x0 # pc 0x825d0 0x825d0 <relocate_code> 23 stp x29, x30, [sp, #-32]! /* create a stack frame */ /** つまり、 x29 および x30 のデータを [sp, #-32] に push している? */ # STP Xt1, Xt2, [Xn|SP, #imm]! ; 64 ビット汎用レジスタ、プレインデクス # Xt1 転送される最初の汎用レジスタ(64 ビット)名を 0 ~ 31 の範囲内で指定します。 # Xt2 転送される 2 つ目の汎用レジスタ(64 ビット)名を 0 ~ 31 の範囲内で指定します。 # Xn|SP 64 ビットの汎用ベースレジスタ名またはスタックポインタ名を 0 ~ 31 の範囲内で指定します。 # imm 64 ビット汎用レジスタ。 # ポストインデクスおよびプレインデクスバリアントは符号付きイミディエートバイトのオフセットであるため、 # -512 ~ 504 の範囲で 8 の倍数となります。 # 符号付きオフセットバリアントはオプションの符号付きイミディエートバイトのオフセットであるため、 # -512 ~ 504 の範囲で 8 の倍数で指定します。デフォルトで 0 になります。 # x29 0x80088 524424 # x30 0x82588 533896 # sp 0xffffffffffffffe0 0xffffffffffffffe0 /** sp が負数って良いの?? */ # pc 0x825d4 0x825d4 <relocate_code+4> 24 mov x29, sp /** x29 = sp */ # x29 0xffffffffffffffe0 -32 # x30 0x82588 533896 # sp 0xffffffffffffffe0 0xffffffffffffffe0 # pc 0x825d8 0x825d8 <relocate_code+8> 25 str x0, [sp, #16] /** x0 = *(sp + 16) */ 26 /* 27 * Copy u-boot from flash to RAM 28 */ # x0 0x0 0 # x1 0x0 0 # x29 0xffffffffffffffe0 -32 # x30 0x82588 533896 # sp 0xffffffffffffffe0 0xffffffffffffffe0 # pc 0x825dc 0x825dc <relocate_code+12> 29 ldr x1, =__image_copy_start /* x1 <- SRC &__image_copy_start */ # x0 0x0 0 # x1 0x0 0 # x29 0xffffffffffffffe0 -32 # x30 0x82588 533896 # sp 0xffffffffffffffe0 0xffffffffffffffe0 # pc 0x825e0 0x825e0 <relocate_code+16> 30 subs x9, x0, x1 /* x9 <- relocation offset */ 31 b.eq relocate_done /* skip relocation */
雰囲気は、 L25 の str の結果 (x0) と L29 の ldr の結果 (x1) が 0 になっているのがおかしい気がする。
そもそも、 L23 の stp の結果がスタックにちゃんと入っていない気がする。。
(gdb) x/8 -32 0xffffffffffffffe0: 0x00000000 0x00000000 0x00000000 0x00000000 0xfffffffffffffff0: 0x00000000 0x00000000 0x00000000 0x00000000
ちなみに、期待通り動作している xilinx_zcu102 では、
x29 0x8000090 134217872 x30 0x7ff1fc30 2146565168 sp 0x7df1adb0 0x7df1adb0 pc 0x8002c7c 0x8002c7c <relocate_code+4> (gdb) x/8 0x7df1adb0 0x7df1adb0: 134217872 0 2146565168 0 0x7df1adc0: 0 0 0 0
うん、期待通り SP のアドレスに x29 と x30 が push されている。
ということは、 raspi3 の場合に SP が 0 になっていることが問題の原因??
解析を続けると、 arch/arm/lib/crt0_64.S で SP が 0 になる処理を発見した。
88 #if !defined(CONFIG_SPL_BUILD) 89 /* 90 * Set up intermediate environment (new sp and gd) and call 91 * relocate_code(addr_moni). Trick here is that we'll return 92 * 'here' but relocated. 93 */ 94 ldr x0, [x18, #GD_START_ADDR_SP] /* x0 <- gd->start_addr_sp */ 95 bic sp, x0, #0xf /* 16-byte alignment for ABI compliance */
L94 で x0 に 0 が入り、 L95 で sp = x0 が行われていた。
先ほどと同じく、 xilinx_zcu102 で確認すると、
94 ldr x0, [x18, #GD_START_ADDR_SP] /* x0 <- gd->start_addr_sp */ (gdb) info register sp x0 x18 sp 0x7ff7e90 0x7ff7e90 x0 0x0 0 x18 0x7ff7e90 134184592 (gdb) s 95 bic sp, x0, #0xf /* 16-byte alignment for ABI compliance */ (gdb) info register sp x0 x18 sp 0x7ff7e90 0x7ff7e90 x0 0x7df1add0 2112990672 x18 0x7ff7e90 134184592 (gdb) x/32 0x7ff7e90 0x7ff7e90: 0x7df1af58 0x00000000 0x00000100 0x00000000 0x7ff7ea0: 0x0001c200 0x00000000 0x00000000 0x00000000 0x7ff7eb0: 0x00000000 0x00000000 0x00000000 0x00000000 0x7ff7ec0: 0x00000000 0x00000000 0x00000001 0x00000000 0x7ff7ed0: 0x08057220 0x00000000 0x00000000 0x00000000 0x7ff7ee0: 0x80000000 0x00000000 0x7ff1d000 0x00000000 0x7ff7ef0: 0x80000000 0x00000000 0x000c2d20 0x00000000 0x7ff7f00: 0x7df1add0 0x00000000 0x7df1add0 0x00000000
やはり L94 で x0 に適切な値が代入されていた。 ( x0 = *(x18 + 0x70))
それで、 raspi3 はというと、、、
(gdb) s 95 bic sp, x0, #0xf /* 16-byte alignment for ABI compliance */ (gdb) info register sp x0 x18 sp 0x7ffdd20 0x7ffdd20 x0 0x0 0 x18 0x7ffdd20 134208800 (gdb) x/32 0x7ffdd20 0x7ffdd20: 0 0 0 0 0x7ffdd30: 0 0 0 0 0x7ffdd40: 0 0 0 0 0x7ffdd50: 0 0 0 0 0x7ffdd60: 0 0 0 0 0x7ffdd70: 0 0 0 0 0x7ffdd80: 0 0 0 0 0x7ffdd90: 0 0 0 0
コードを見ると、 x18 には global_data が入っているのが期待動作らしく、 raspi3 のように global_data がオール 0 というのはやはりおかしそう。
global_data の処理を追いかければ、何かヒントがみつかるかも??