QEMUでRISC-Vベアメタルプログラミング(はじめてのC編)
ソース
メイン処理。グローバル変数を定義して、そこに 0x4649(ヨロシク)と 0x5963(ご苦労さん) をセットする。
code:main.c
int my_data;
int your_data;
int main() {
// グローバル変数へ値を書き込む
my_data = 0x4649; // 4649 = ヨロシク
your_data = 0x5963; // 5963 = ご苦労さん
return 0;
}
Cのmain関数を呼び出すスタートアップ処理が必要なのでそれも用意する。こちらはアセンブリ言語で作成。
スタートアップ処理では以下を行う。
スタックポインタ(SP)に _stack_top の値をセット(_stack_topについては後述)
main関数を呼び出し
mainから帰ってきたら無限ループ
code:startup.S
.text
.globl _start
.globl _stack_top
_start:
la sp, _stack_top
call main
loop:
j loop
スタートアップ処理の中でスタックポインタ SP にセットする値はリンカスクリプトの中で定義する必要がある。
スタック領域の最下位アドレスに _stack_top というシンボルを定義して、このアドレスをスタートアップ処理の中でスタックポインタ(SP)にセットする。
code:qemu-virtio.ld
OUTPUT_ARCH("riscv")
ENTRY(_start)
MEMORY
{
ram (rwx) : ORIGIN = 0x80000000, LENGTH = 128M
}
SECTIONS
{
.text : {
*(.text)
} >ram
.rodata : {
*(.rodata)
} >ram
.data : {
. = ALIGN(4096);
*(.data)
} >ram
.bss : {
. = ALIGN(16);
*(.bss)
*(.sbss)
} >ram
.stack : {
. = ALIGN(16);
. = . + 4096;
PROVIDE(_stack_top = .);
} >ram
}
QEMUではセクションで定義した範囲でしかRAMにアクセスできないっぽいので、スタックに利用する4096バイト分のメモリを確保している。
code:qemu-virtio.ld
...
.stack : {
. = ALIGN(16);
. = . + 4096;
PROVIDE(_stack_top = .);
} >ram
...
ビルド
startup.Sとmain.cをビルドして c-hello.elf を生成する。
code:sh
$ riscv64-unknown-elf-gcc -O0 -march=rv32if -mabi=ilp32f -c startup.S
$ riscv64-unknown-elf-gcc -O0 -march=rv32if -mabi=ilp32f -c main.c
$ riscv64-unknown-elf-ld -m elf32lriscv -b elf32-littleriscv -Tqemu-virtio.ld startup.o main.o -o c-hello.elf
アドレスを確認
c-hello.elf の中身をのぞいて、グローバル変数「my_data」と「your_data」のアドレスを確認する。
「my_data」のアドレスは「0x80001000番地」、「your_data」のアドレスは「0x80001004番地」とのこと。
code:sh
$ riscv64-unknown-elf-readelf -a c-hello.elf | grep ".*_data"
10: 80001004 4 OBJECT GLOBAL DEFAULT 3 your_data
11: 80001000 4 OBJECT GLOBAL DEFAULT 3 my_data
実行
作成したELFファイルを指定してQEMUを実行する。
code:sh
$ qemu-system-riscv32 -M virt -monitor stdio -bios none -device loader,file=c-hello.elf
メモリの値を確認
グローバル変数「my_data」と「your_data」に値が設定されているはずなので、QEMUモニタから「xpコマンド」でメモリの内容を確認する。
https://gyazo.com/9f713fad391991da955e973bd5810e66
0x80001000番地(my_data)に「0x4649」が、
0x80001004番地(your_data)に「0x5963」がセットされていることが確認できる。
xpコマンド
xpコマンドの引数は以下のとおり。フォーマットは省略できる。
code:qemu
xp /フォーマット アドレス
code:qemu
// 物理メモリ上の値を(xp) 0x80001000番地を始点として(0x80001000)、16進数表記で2ワード(=8バイト)表示する
(qemu) xp /2x 0x80001000
0000000080001000: 0x00004649 0x00005963
参考
RISC-V OSを作ろう (1) ~ブート処理
リンカスクリプトを理解しよう
QEMU "-bios" vs. "-kernel" vs. "-device loader,file=..."
Generic Loader
The ‘loader’ device allows the user to load multiple images or values into QEMU at startup.