ARMv8のベアメタル開発環境構築③

いよいよCortex-A53向けの環境を立ち上げていく。

# ボードの選定
QEMUでサポートしているマシンリストは以下のコマンドで取得できる。
```sh
$ qemu-system-aarch64 -machine help
Supported machines are:
akita                Sharp SL-C1000 (Akita) PDA (PXA270)
ast2500-evb          Aspeed AST2500 EVB (ARM1176)
ast2600-evb          Aspeed AST2600 EVB (Cortex-A7)
(省略)
raspi0               Raspberry Pi Zero (revision 1.2)
raspi1ap             Raspberry Pi A+ (revision 1.1)
raspi2b              Raspberry Pi 2B (revision 1.1)
raspi3ap             Raspberry Pi 3A+ (revision 1.0)
raspi3b              Raspberry Pi 3B (revision 1.2)
(省略)

```
A53をサポートしているボードが意外と少ない。当初virt-6.2というQEMUが提供する仮想マシンの利用を考えたが、UARTがあるのかどうかよくわからなかったので、結局ラズパイ3Bにした。

続いてラズパイの仕様確認。3BではBroadcom BCM2837というSoCを使用しているため、これの仕様書を調べる。
https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf

UART(PL011)は13章に記載があり、オフセットアドレスは0x7E2_0100…なのだが、結局これでは動かなかった。この仕様書表紙こそBCM2837って書いてあるけど2ページ目以降のヘッダがBCM2835になっている。
どうもBCM2837のデータシートは公開されていないらしく、上位アドレスを0x3Fに読み替える必要があるらしい。さらに、0x7E2_0100は多分誤植で正しくは0x7E20_1000と思われる。ということでラズパイ3BのUART(PL011)のオフセットアドレスは0x3F20_1000として、以降のポーティングを進める。

# ソフトウェアのポーティング
上記で見たように、UARTDRレジスタのオフセットアドレスを書き換える。ほかはそのまま。

```test.c
volatile unsigned int * const UART0DR = (unsigned int *)0x3F201000;
 
void print_uart0(const char *s) {
 while(*s != '\0') { /* Loop until end of string */
 *UART0DR = (unsigned int)(*s); /* Transmit char */
 s++; /* Next char */
 }
}
 
void c_entry() {
 print_uart0("Hello world!\n");
}
```

つづいてstartup.sの書き換え。前回のようにスタックポインタに即値をロードできないらしく、汎用レジスタを介してスタックポインタの初期化を行う。

```c:startup.s
.global _Reset
_Reset:
 LDR x0, =stack_top
 MOV sp, x0
 BL c_entry
 B .
```

リンカはそのまま。これでビルド。
```sh
aarch64-none-elf-as -mcpu=cortex-a53 -g startup.s -o startup.o
aarch64-none-elf-gcc -c -mcpu=cortex-a53 -g test.c -o test.o
aarch64-none-elf-ld -T test.ld test.o startup.o -o test.elf
aarch64-none-elf-objcopy -O binary test.elf test.bin
```
# 動作確認

以下のコマンドで実行。マシンとしてラズパイ3Bを指定している。
```sh
$ qemu-system-aarch64 -M raspi3b -cpu cortex-a53 -nographic -kernel test.elf
Hello world!
HHHello world!
QEMU: Terminated
```

とりあえず動いたものの、表示が何やらおかしい。ラズパイ3BはCA53の4コア構成だが、これが関係しているのだろうか。

CPUIDが0でなければスリープさせるよう、startup.sを書き換えることにする。
肝になるのはMPIDR_EL1というシステムレジスタで、これの下位ビットを見て特定のCPU以外はwfe(wait for event)命令を実行する。
https://developer.arm.com/documentation/ddi0500/j/System-Control/AArch64-register-descriptions/Multiprocessor-Affinity-Register

以下が変更後のstartup.s。適宜コメントを入れている。
```:startup.s
.global _Reset
_Reset:
    // Initialization of stack pointer (sp)
    LDR x0, =stack_top
    MOV sp, x0

    // sleep other than core0
 	mrs     x0, mpidr_el1	/* Load MPIDR to x0 */
	and	x0, x0, #0xffff		/* x0 = MPIDR[15:0] = MPIDR.CPU_ID */
    cmp	x0, #0x0	        /* check CPU_ID (0x0: core0) */
    b.ne sleep_core         /* sleep core 1-3 */

    // entry main func
    BL c_entry
    B .

// for core 1-3
sleep_core:
    wfe
    b .
```

追加したのは// sleep other than core0の部分で、CPUIDを取得し0でなければsleep_coreにブランチさせる。sleep_coreではwfe命令で省電力状態に移行後、無限ループで待機させている。
システムレジスタであるMPIDR_EL1には直接アクセスすることができず、専用のロード命令であるmrsでいったん汎用レジスタにロードする必要がある。

この状態で再度実行してみると、Hello world!は一回しか表示されなくなった。

```sh
$ qemu-system-aarch64 -M raspi3b -cpu cortex-a53 -nographic -kernel test.elf
Hello world!
QEMU: Terminated
```