明日にはでっかい太陽が昇るかもしれません。

「覚悟」とは!! 暗闇の荒野に!!進むべき道を切り開く事だッ!

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 の処理を追いかければ、何かヒントがみつかるかも??