💮

Zircon の QEMU 用ブートローダ(aarch64)

2021/10/17に公開

本記事では、aarch64 用 Zircon カーネルを QEMU 上で起動させるときの動作を紹介します。
Zircon の QEMU ブート(aarch64) の続きです。

qemu-boot-shim

主な処理

  • 命令・データキャッシュと MMU を無効にする
  • QEMU が用意した DTB から必要な情報を抜き出し、fuchsia-ssh.zbi へ追記する
  • fuchsia-ssh.zbi 中の physboot を ZBI データの後方へコピーする
  • physboot へジャンプする

主要ソースコードの関係

主要なソースコード

boot-shim.S の概要

キャッシュと MMU を無効化

SCTLR_EL2 レジスタの I,C,M ビットをクリアして、キャッシュと MMU を無効化します。

Bit Description
I, bit [12] Instruction access Cacheability control, for accesses at EL2
C, bit [2] Cacheability control, for data accesses
M, bit [0] MMU enable for EL2 stage 1 address translation

— D13.2.114 SCTLR_EL2, System Control Register (EL2), Arm® Architecture Reference Manual

SCTLR_EL2 レジスタ変更後、

  • システムレジスタ変更の反映のために、ISB を実行
  • キャッシュアクセスを完了するために、DSB SY を実行

スタックの用意

boot-shim のメモリマップ
Address Size Section
0x00000000 0x0090 .text.boot0
0x00000090 0x16AC .text
0x00001740 0x03B9 .rodata
0x00001AFC 0x0004 .data
0x00001B00 0x1010 .bss
スタック
Address Symbol
0x00001B10 stack
0x00002B10 stack_end

.bss セクション内の 4096 Byte がスタック領域です。

実際のアドレスは、boot-shim がロードされた 0x40080000 にオフセット 0x00002B10 を加算した、0x40082B10 となります。
SP レジスタに 0x40082B10 をセットして、スタックとして使用可能にします。

boot_shim()を呼び出す

boot-shim.c の boot_shim() を実行します。

引数
Register Value Description
x0 0x49800000 Device Tree Blob
戻り値
Register Value Description
x0 0x48000000 ZBI データ
x1 0x4989436c physboot のエントリポイント(_start)

戻り値は、x0、x1 レジスタに格納されています。

physboot エントリポイントへジャンプ

physboot へ渡す情報
Register Value Description
x0 0x48000000 ZBI データ
x1 0x4989436c physboot のエントリポイント(_start)
CPU の状態
Function Status
Exception Level EL2
IRQ Mask
FIQ Mask
I-Cache Disable
D-Cache Disable
MMU Disable
メモリの状態
Load Address Size Description
0x40000000 40 qemu bootloader
0x40080000 65,536 qemu-boot-shim.bin
0x48000000 25,719,600 fucnshia-ssh.zbi+α
0x49800000 1,048,576 Device Tree Blob
0x49890000 659,088 physboot.zbi
  • DTB は fuchsia-ssh.zbi+α の α 部分にコピーされます

boot-shim.c の概要

device tree から initrd のアドレス取得

chosen {
        linux,initrd-end = < 0x497887e0 >;
        linux,initrd-start = < 0x48000000 >;
  • initrd(fuchsia-ssh.zbi)のロードアドレス 0x48000000
その他の情報

initrd 以外にも、device tree から必要なものだけ device_tree_context 構造体に格納する。

typedef struct {
  dt_slice_t devicetree;
  node_t node;
  uintptr_t initrd_start;
  size_t memory_base;
  size_t memory_size;
  char* cmdline;
  size_t cmdline_length;
  uint32_t cpu_count;
  int gic_version;
} device_tree_context_t;
  • devicetreenode
    device tree 解析中に使用する一時変数
  • initrd_start
    initrd(fuchsia-ssh.zbi)のロードアドレス
  • memory_basememory_size
    RAM の開始アドレス、サイズ
  • cmdlinecmdline_length
    コマンドラインオプション、サイズ
  • cpu_count
    CPU 数
  • gic_version
    GIC のバージョン

ZBI から physboot を探す

Name Size Description
ZBI Header 32 ZBI の Container Header
Boot Item Header 32 physboot の Header
Boot Item Payload physboot の Payload
...

ZBI 中の最初の Boot Item がタイプ ZBI_TYPE_KERNEL_ARM64(physboot)であることを確認します。

ZBI データにボード情報を追加

ZBI データの末尾に、ボード情報を個別の Boot Item として追記します。

ZBI に追加する情報は次のとおりです。

Boot Item Type Data Description
ZBI_TYPE_CPU_TOPOLOGY cluster_id, cpu_id, ... CPU トポロジー情報
ZBI_TYPE_NVRAM base, length ウォームブート時にデータを保持したままにするメモリ領域。クラッシュログ用
ZBI_TYPE_KERNEL_DRIVER
(KDRV_PL011_UART)
mmio_phys,irq UART ドライバ設定
ZBI_TYPE_KERNEL_DRIVER
(KDRV_ARM_GIC_V3)
mmio_phys,gicd_offs,... Generic Interrupt Controller ドライバ設定
ZBI_TYPE_KERNEL_DRIVER
(KDRV_ARM_PSCI)
- Power State Coordination Interface ドライバ設定
ZBI_TYPE_KERNEL_DRIVER
(KDRV_ARM_GENERIC_TIMER)
irq_phys,irq_virt タイマドライバ設定
ZBI_TYPE_PLATFORM_ID vendor_id,product_id ベンダ ID、プロダクト ID
ZBI_TYPE_SERIAL_NUMBER serial_number シリアル番号
ZBI_TYPE_MEM_CONFIG
(ZBI_MEM_RANGE_PERIPHERAL)
memory_base, memory_size 周辺機器のメモリマップド I/O 領域
ZBI_TYPE_MEM_CONFIG
(ZBI_MEM_RANGE_RAM)
memory_base, memory_size RAM の領域
ZBI_TYPE_CMDLINE cmdline コマンドラインオプション
ZBI_TYPE_DEVICETREE DTB Device Tree Blob
  • QEMU の virt ボードが提供するドライバなどを決め打ちで指定しています
  • DTB から取得するのは、qemu コマンドの引数で決定する情報、qemu が決定する情報でした
  • 追加前サイズ 24,676,320 → 追加後サイズ 25,719,600
    大部分は DTB

physboot を ZBI データの後方へコピー

fuchsia-ssh.zbi の後方、4KiB 境界にコピーします。

+----------+-------------------+---------+--------+---------+
|ZBI Header|Boot Item(physboot)|Boot Item|  ...   |Boot Item|
+----------+-------------------+---------+--------+---------+
|                              |                                         ^
+--------------+---------------+                                         |
               |                            copy                         |
               +---------------------------------------------------------+
  • fuchsia-ssh.zbi+α

    図中の赤い部分(physboot.zbi と呼称)を、アドレス 0x49890000 へコピーします。
    コピー先 ZBI Header の length を更新します。

Address Description
コピー元 0x48000000 fucnshia-ssh.zbi+α
コピー先 0x49890000 physboot.zbi
コピー後のメモリ
Load Address Size Description
0x48000000 25,719,600 fucnshia-ssh.zbi+α
0x49890000 659,088 physboot.zbi

まとめ

  • QEMU と実機の差異を吸収

    • physboot 以降は、QEMU・実機ともに同じコードを実行する
    • 実機では U-Boot が行う処理を qemu-boot-shim で隠蔽する
  • QEMU が用意した状態を、physboot 呼び出しに適した形に変更する

    • QEMU が用意した device tree を ZBI データに移す

Discussion