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

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

docker コンテナ内で byobu (tmux) を利用する

Tmux and Screen | Intuitive documentation for using Docker containerization

最近は docker exec で直接コンテナ内に入っているけど、 ssh 経由の場合などと対処を混同しちゃうので、まとめておく。

$ docker exec -ti YOUR_CONTAINER_NAME script -q -c "/bin/bash" /dev/null

でコンテナ内に入って、

### or tmux
$ byobu 

とすれば、コンテナ内で tmux を正常に起動できる。

U-Boot (aarch64) on Qemu を動作させたい (9)

github.com

結論からいうと、公式の U-Boot をターゲット向けビルドのモジュールのまま Qemu 上で起動することができるようになった。

ここでの公式は、オリジナル U-Boot に NXP のカスタマイズが入った今回のターゲットボードである LS1046A RDB 向け U-Boot を指す。

前回 PTE の情報がなにかおかしそう、というところまでわかっていたが、リロケーション前に early_mmu_setup()set_ttbr_tcr_mair() で設定する TCR の値がリロケーション後のメモリマップと合っていないっぽいことがわかった。

get_tcr() で、メモリマップに定義されたアドレスの最大値からアドレス幅を求めて、 T0SZ の値を出しているのだが、リロケーション前のアドレスマップは LS1046A RDB で定義されている 480 GiB の DDR Region3 領域を使用しないようになっていたため、リロケーション前に設定した TCR (T0SZ) を使用したリロケーション後の PTE アクセスで不整合が発生していたように見える。

なので、リロケーション前のアドレスマップにも DDR Region3 領域を追加すると無事プロンプトが使えるところまで起動させることができるようになった。

... 省略 ...
DRAM:  Detected UDIMM                                                                                                                                           [1155/19018]
1.9 GiB (DDR4, 32-bit, CL=1, ECC off)
Waking secondary cores to start from fbd1f000
Not all cores (0xf) are up (0x1)
Did not wake secondary cores
Using SERDES1 Protocol: 4403 (0x1133)
Using SERDES2 Protocol: 21849 (0x5559)
ERROR: Stopped after 0 portals
ERROR: Stopped after 0 portals
NAND:  fsl_ifc_chip_init: address did not match any chip selects
0 MiB
MMC:   FSL_SDHC: 0
MMC: no card present
mmc_init: -123, time 1002
*** Warning - MMC init failed, using default environment

EEPROM: wait_for_sr_state: failed sr=a1 cr=f8 state=202
i2c_init_transfer: failed for chip 0x53 retry=0
wait_for_sr_state: failed sr=a1 cr=f8 state=202
i2c_init_transfer: failed for chip 0x53 retry=1
wait_for_sr_state: failed sr=a1 cr=f8 state=202
i2c_init_transfer: failed for chip 0x53 retry=2
i2c_init_transfer: give up i2c_regs=0x2180000
Read failed.
In:    serial
Out:   serial
Err:   serial
AHCI 0000.0000 1 slots 1 ports ? Gbps 0x0 impl SATA mode
flags: 
Found 0 device(s).
SCSI:  Net:   
MMC read: dev # 0, block # 18432, count 128 ...
MMC: no card present
mmc_init: -123, time 1001
MMC: block number 0x4880 exceeds max(0x0)
Fman1: Data at 00000000fbc25d30 is not a firmware
PCIe0: pcie@3400000 Endpoint: no link
PCIe1: pcie@3500000 Endpoint: no link
PCIe2: pcie@3600000 Endpoint: no link
No ethernet found.
Hit any key to stop autoboot:  0 
=> q
Unknown command 'q' - try 'help'

だが、 SDHCが未実装だったり、コアが 1 つしか動作していなかったりと、エミュレーションとしては不十分だが、 U-Boot の機能実装に対するテストが行えるようにはなったのではないかと思う。

と、ここまでは NXP の提供する NXP-SDK v2.0 update 17.03 に含まれる U-Boot で確認していたのだが、冒頭のリポジトリの最新コードを使用すると、リロケーション前のアドレスマップに対する修正なしに (Qemu で動かすための修正なしに!) すんなり起動してしまった。。

最新のコードでもアドレスマップに対する変更は入っていないし、(そもそもNXP-SDK v2.0 のコードも実機で動作しているものなのでアドレスマップが間違っているということはないと思うし、) どの差分が影響しているかは追っていないが、実機向けのバイナリがそのまま Qemu で起動できたのはとても嬉しかった。

最終目標は、仕事で使用しているカスタムボードのバイナリを動作させることだが、ベースシステムのバイナリを動作させられたという重要なマイルストーンを達成したので GitHub にアップしておく。

github.com

U-Boot (aarch64) on Qemu を動作させたい (8)

ハングの原因を追いかけると、リロケーション後に特定のメモリ領域 (キャッシュ関連) に値が入っていないため、例外が発行されていることがわかった。

U-Boot 的には final_mmu_setup() > enable_caches() 内の asm volatile ("msr tcr_el3, %0" : : "r" (tcr) : "memory") で例外が発生している。

Qemu 的には、 get_phys_addr_lpae() でエラーが発生している。

get_phys_addr_lpae() では、用途はわからないが特定のアドレス領域を読んでいて、そこから CPU の持つ機能を判断しているように見えたけど、そこで領域が 0 クリア状態のためエラーに流れていた。

さらなる調査のために、 gdb でメモリアドレスを監視する手段を確認すると、 watch コマンドで監視できることがわかった。

### アドレスは uint64_t として読み出されているため、監視する型を uint64_t にする
(gdb) watch *((uint64_t *)0x7fffd93f0000)
Hardware watchpoint 3: *((uint64_t*)0x7fffd93f0000)
(gdb) c
Continuing.


Hardware watchpoint 3: *((uint64_t*)0x7fffd93f0000)

Old value = 0
New value = 1006571523
0x00007fffdd42a8d0 in code_gen_buffer ()

調べを進めると、 PTE とプログラムの動きがあっていないような感じがする。ここからどう追うか。。

U-Boot (aarch64) on Qemu を動作させたい (7)

なんとか、 DDR の SDP も実装し、 board_f.c の init_sequence_f の初期化シーケンスについては抜けられるようになった。

だが、どこかでハングが発生して、プロンプトまでは到達していない。

u-boot の処理では、プログラムのリロケーションが実行されており、ブレイクポイントは張れなくなっている。

調べると、 gdb ではリロケーション後のシンボル読み込みにも対応していることがわかった。

### リロケーション後のアドレスを確認する
(gdb) p/x gd->relocaddr
$1 = 0xffcfb000

### 現在のシンボルの破棄
(gdb) symbol-file
Discard symbol table from `/path/to/u-boot-qoriq-2016.09+fslgit-r0/build_ls1046ardb_emmc/u-boot'? (y or n) y
No symbol file now.

### リロケーション後アドレスにシンボルを読み込み
(gdb) add-symbol-file u-boot 0xffcfb000
add symbol table from file "u-boot" at
        .text_addr = 0xffcfb000
(y or n) y
Reading symbols from u-boot...done.

### ブレイクポイントの貼り直し
(gdb) b ../arch/arm/lib/crt0_64.S:110
Breakpoint 6 at 0xffd00c90: file ../arch/arm/lib/crt0_64.S, line 110.

### ブレイクポイントの動作確認
(gdb) c
Continuing.

Breakpoint 6, _main () at ../arch/arm/lib/crt0_64.S:110
110             bl      c_runtime_cpu_setup             /* still call old routine */

U-Boot (aarch64) on Qemu を動作させたい (6)

現在は、仕事で触れている freescale の ls1046a Reference Design Board の U-Boot を Qemu で動作させるべく、仮想ボードを作成中。

CPU, RAM, UART と何とか整えてきて、i2c の調整を行っています。

U-Boot 2016.092.0+ga06b20925c (Oct 22 2017 - 21:04:46 +0900)

SoC:  LS1046AE Rev1.0 (0x87070010)
Clock Configuration:
       CPU0(   ):800  MHz  CPU1(   ):800  MHz  CPU2(   ):800  MHz  
       CPU3(   ):800  MHz  
       Bus:      600  MHz  DDR:      2100 MT/s  FMAN:     800  MHz
Reset Configuration Word (RCW):
       00000000: 0c150010 0e000000 00000000 00000000
       00000010: 11335559 40000012 60040000 c1000000
       00000020: 00000000 00000000 00000000 00238800
       00000030: 20124000 00003000 00000096 00000001
Model: LS1046A RDB Board
Board: LS1046ARDB, boot from Invalid setting of SW5
CPLD:  V0.0
PCBA:  V0.0
SERDES Reference Clocks:
SD1_CLK1 = 100.00MHZ, SD1_CLK2 = 100.00MHZ
I2C:   ready
DRAM:  wait_for_sr_state: failed sr=a1 cr=38 state=202
i2c_init_transfer: failed for chip 0x36 retry=0
wait_for_sr_state: failed sr=a1 cr=38 state=202
i2c_init_transfer: failed for chip 0x36 retry=1
wait_for_sr_state: failed sr=a1 cr=38 state=202
i2c_init_transfer: failed for chip 0x36 retry=2
i2c_init_transfer: give up i2c_regs=0x2180000
wait_for_sr_state: failed sr=a1 cr=38 state=202
i2c_init_transfer: failed for chip 0x51 retry=0
wait_for_sr_state: failed sr=a1 cr=38 state=202
i2c_init_transfer: failed for chip 0x51 retry=1
wait_for_sr_state: failed sr=a1 cr=38 state=202
i2c_init_transfer: failed for chip 0x51 retry=2
i2c_init_transfer: give up i2c_regs=0x2180000
DDR: failed to read SPD from address 81
Error: No valid SPD detected.
16 EiB (DDR not enabled)

"Synchronous Abort" handler, esr 0x96000045
ELR:     fff638e8
LR:      ffefd214
x0 : 00000007ffe00000 x1 : 0000000000000000
x2 : 0000000000001000 x3 : 00000007ffe01000
x4 : 00000007ffe00000 x5 : 0000000000000000
x6 : 00000000000001e8 x7 : 0000000000000001
x8 : 0000000082070550 x9 : 000000007defb000
x10: 0000000000000403 x11: 0000000082059274
x12: 0000000000000002 x13: 0000000000000000
x14: 0000000000000000 x15: 0000000082003228
x16: 0000000000000000 x17: 0000000000000000
x18: 00000000ffdf8d70 x19: 00000007ffe00000
x20: 00000007ffe00000 x21: 0000000000000000
x22: 00000000fff89490 x23: 0000000000000000
x24: 0000000000000000 x25: 0000000000000000
x26: 0000000000000000 x27: 0000000000000000
x28: 0000000082001cd8 x29: 00000000ffdf6680

Resetting CPU ...

resetting ...
guts: Unknown register read: b0
guts: Unknown register write: b0 = 2

最終的には Abort してしまっているけど、原因がどこかは現時点では未調査。

タイムアウトの原因を GDB で追いかけると、 i2c_init_transfer_() から tx_byte() に入り、 I2DR にスレーブアドレスを書き込んだあとに、 I2SR_IIF ビット (割り込み) が立たないため、タイムアウトが発生していた。

Qemu 側も GDB で追いかけると、imx_i2c.c から i2c/core.c の i2c_start_transfer() を呼び出したところで QTAILQ_FOREACH() マクロの処理が行われていないため、 I2SR_IIF ビットを立てる処理が実施されていないことがわかった。

QTAILQ_FOREACH() マクロは、 &bus->qbus.children.tqh_first にデータがある場合に実行される処理で、タイムアウトの場合は NULL となっていたため、 bus->current_devs.lh_first も空のままになり、下の QLIST_EMPTY で return 1 となっていた。

    if (QLIST_EMPTY(&bus->current_devs)) {
        QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
            DeviceState *qdev = kid->child;
            I2CSlave *candidate = I2C_SLAVE(qdev);
            if ((candidate->address == address) || (bus->broadcast)) {
                node = g_malloc(sizeof(struct I2CNode));
                node->elt = candidate;
                QLIST_INSERT_HEAD(&bus->current_devs, node, next);
                if (!bus->broadcast) {
                    break;
                }
            }
        }
        bus_scanned = true;
    }

    if (QLIST_EMPTY(&bus->current_devs)) {
        return 1;
    }

bus->qbus.children.tqh_first にデータが入る条件を調べると、 qdev_set_parent_bus() が呼ばれた場合であることがわかった。

qdev_set_parent_bus() は、システムバズに仮想デバイスをつなぐ場合に呼び出している処理で、特定のバスに仮想デバイスを接続する機能を持っている。

ということは、現在は i2c バスにデバイスが接続されていないから、スレーブアドレスの書き込みでタイムアウトになっている、ということ?

backtrace を確認すると、 DDRSPD アクセスに使用していることがわかった。

DDR の情報が取得できないので、 Abort につながっているようだ。

U-Boot (aarch64) on Qemu を動作させたい (5)

u-boot.bin をダンプすると、 init_sequence_f のアドレスには関数ポインタが格納されていることを確認した。

0004cb70  1c 07 09 00 00 00 00 00  48 9c 09 00 00 00 00 00  |........H.......|
0004cb80  00 08 09 00 00 00 00 00  38 2d 08 00 00 00 00 00  |........8-......|
0004cb90  d4 08 09 00 00 00 00 00  08 08 09 00 00 00 00 00  |................|
0004cba0  b8 08 09 00 00 00 00 00  34 34 08 00 00 00 00 00  |........44......|
0004cbb0  e8 4e 0b 00 00 00 00 00  44 27 09 00 00 00 00 00  |.N......D'......|
(gdb) p setup_mon_len
$1 = {int (void)} 0x9071c <setup_mon_len>
(gdb) p initf_malloc
$2 = {int (void)} 0x99c48 <initf_malloc>
(gdb) p initf_console_record
$4 = {int (void)} 0x90800 <initf_console_record>

と、ここで気づいたが、エラーになるからと、 u-boot.bin ではなく、 u-boot (elf) を -kernel に渡していたが、結構ファイルサイズが違うことに気づく。

もしかして、と、 u-boot の init_sequence_f あたりをダンプすると、

0005cb70  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0005cd70  00 00 00 00 00 00 00 00  01 00 00 00 01 00 00 00  |................|
0005cd80  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
0005cd90  00 00 00 00 00 00 00 00  01 00 00 00 00 00 00 00  |................|
0005cda0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

なんか、すごく見覚えがあるデータ配置 (ところどころの 1 具合が) 出たので、間違いない気がする。

CONFIG_REMAKE_ELF を追加して、 u-boot.elf を作成し、同じアドレスをダンプすると、

0005cb70  1c 07 09 00 00 00 00 00  48 9c 09 00 00 00 00 00  |........H.......|
0005cb80  00 08 09 00 00 00 00 00  38 2d 08 00 00 00 00 00  |........8-......|
0005cb90  d4 08 09 00 00 00 00 00  08 08 09 00 00 00 00 00  |................|

となり、期待通りの結果になった!

エラーは残っているものの、無事、 U-Boot (aarch64) on Qemu を動作させることができた。 u-boot / qemu の動作およびデバッグについてもいい勉強になった。

U-Boot 2016.09-dirty (Oct 10 2017 - 04:57:49 +0900)                                                                                                           [51/18819]

DRAM:  960 MiB
RPI 3 Model B (0xa32081)
MMC:   bcm2835_sdhci: 0
Card did not respond to voltage select!
** Bad device mmc 0 **
Using default environment

In:    serial
Out:   lcd
Err:   lcd
Net:   Net Initialization Skipped
No ethernet found.
starting USB...
USB0:   Core Release: 0.000
SNPSID invalid (not DWC2 OTG device): 00000000
Port not available.
Hit any key to stop autoboot:  0 
Card did not respond to voltage select!
starting USB...
USB0:   Core Release: 0.000                                                                                                                                   [25/18819]
SNPSID invalid (not DWC2 OTG device): 00000000
Port not available.
No ethernet found.
missing environment variable: pxeuuid
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/00000000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/0000000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/000000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/00000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/0000
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/000                                                                                                                              [4/18819]
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/00
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/0
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/default-arm-bcm283x
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/default-arm
No ethernet found.
missing environment variable: bootfile
Retrieving file: pxelinux.cfg/default
No ethernet found.
Config file not found
starting USB...
USB0:   Core Release: 0.000
SNPSID invalid (not DWC2 OTG device): 00000000
U-Boot> print
arch=arm
baudrate=115200
board=rpi
board_name=3 Model B
board_rev=0x8
board_rev_scheme=1
board_revision=0xA32081

ただ、 UART については raspi3 で有効になる CONFIG_BCM283X_MU_SERIAL の動かし方がわからないので CONFIG_PL01X_SERIAL を有効にしてしのいでいる。

 /* Console UART */
 #ifdef CONFIG_BCM2837
-#define CONFIG_BCM283X_MU_SERIAL
+#define CONFIG_PL01X_SERIAL
 #else
 #define CONFIG_PL01X_SERIAL
 #endif

U-Boot (aarch64) on Qemu を動作させたい (4)

更に処理を追いかけると、 board_init_f() (common/board_f.c) で static に取られた変数の init_sequence_f のデータが空になっていることがわかった。。

本来は、ボードの初期化メソッドのテーブルなので、関数ポインタが入っているべきところ。

どゆこと??

board_init_f (boot_flags=0) at common/board_f.c:1072
1072            gd->flags = boot_flags;
(gdb) 
1075            if (initcall_run_list(init_sequence_f))
(gdb) 
1073            gd->have_console = 0;
(gdb) 
1075            if (initcall_run_list(init_sequence_f))
(gdb) p init_sequence_f
$1 = {0x0 <repeats 37 times>}

再び、 xilinx_zcu102 で確認すると、

_start () at arch/arm/cpu/armv8/start.S:22
22              b       reset
Breakpoint 1 at 0x8000000: file arch/arm/cpu/armv8/start.S, line 22.
Breakpoint 2 at 0x8002be8: file arch/arm/lib/crt0_64.S, line 75.
(gdb) x/32 init_sequence_f
0x8072588 <init_sequence_f>:    134298688       0       134533344       0
0x8072598 <init_sequence_f+16>: 134333800       0       134298840       0
0x80725a8 <init_sequence_f+32>: 134299264       0       134299100       0
0x80725b8 <init_sequence_f+48>: 134298848       0       134299072       0
0x80725c8 <init_sequence_f+64>: 134543948       0       134307552       0
0x80725d8 <init_sequence_f+80>: 134299012       0       134413964       0
0x80725e8 <init_sequence_f+96>: 134329008       0       134532500       0
0x80725f8 <init_sequence_f+112>:        134537568       0       134299256       0

動かす前からちゃんと展開されているので、 Qemu 側が firm を正しく展開できていない気配も出てきた。