LoginSignup
2
1

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-03-14

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

ボードの選定

QEMUでサポートしているマシンリストは以下のコマンドで取得できる。

$ 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の書き換え。前回のようにスタックポインタに即値をロードできないらしく、汎用レジスタを介してスタックポインタの初期化を行う。

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

リンカはそのまま。これでビルド。

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を指定している。

$ 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!は一回しか表示されなくなった。

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1