Contents

"Writing an OS in 1000 lines"の環境構築をしたときのメモ

同じ内容をZennのスクラップにも書いたんですが、こっちにも一応書いとく

本の記述に従い環境構築をしてみた
※手元の環境はUbuntu 20.04.6 LTS (WSL2)

しかし、5. ブートの説明通りにrun.shを記述し実行したところ、以下のようなエラーが出た

1
2
3
4
❯ ./run.sh
+ QEMU=qemu-system-riscv32
+ qemu-system-riscv32 -machine virt -bios default -nographic -serial mon:stdio --no-reboot
qemu-system-riscv32: Unable to load the RISC-V firmware "opensbi-riscv32-virt-fw_jump.bin"

パッと見た感じ、QEMUが-bios defaultで呼び出すファームウェアが、本の執筆当時とは変わっていそう(?)

そこで、run.shを以下のように変更した

1
2
3
4
5
6
7
8
#!/bin/bash
set -xue

# QEMUの実行バイナリへのパス
QEMU=qemu-system-riscv32

# QEMUを起動
$QEMU -machine virt -bios opensbi-riscv32-generic-fw_dynamic.bin -nographic -serial mon:stdio --no-reboot

すると、エラー自体は出なくなったが、QEMUを起動しても何も表示されない

1
2
3
4
5
6
❯ ./run.sh
+ QEMU=qemu-system-riscv32
+ qemu-system-riscv32 -machine virt -bios opensbi-riscv32-generic-fw_dynamic.bin -nographic -serial mon:stdio --no-reboot



そこで、以下の手順を試した

  1. aptで入れたQEMU関係のパッケージを全てアンインストール
  2. riscv-gnu-toolchainをソースコードからビルドする
  3. QEMUをソースコードからビルドする

aptで入れたQEMU関係のパッケージを全てアンインストール

1
sudo apt-get remove --purge "qemu-*"

riscv-gnu-toolchainをソースコードからビルドする

ビルドに必要なパッケージをインストールしておく。

1
sudo apt install -y texinfo bison flex libgmp-dev

環境によっては、上記以外にも足りないパッケージがあるかもしれないので、ビルド実行後にエラーが出たら適宜インストールしてから再実行する。

以下のコマンドを実行し、ソースコードからriscv-gnu-toolchainをビルドする。

1
2
3
4
5
cd ~
git clone --depth=1 https://github.com/riscv-collab/riscv-gnu-toolchain.git
cd riscv-gnu-toolchain/
./configure --prefix=/opt/riscv32 --with-arch=rv32i --with-abi=ilp32
sudo make

QEMUをソースコードからビルドする

この本が書かれたのが2023年の8月頃なので、当時の安定リリースであるQEMUのv8.0系をソースコードからビルドし、インストールする。

ビルドに必要なパッケージをインストールしておく。

1
sudo apt install -y pkg-config ninja-build libglib2.0 libpixman-1-dev

環境によっては、上記以外にも足りないパッケージがあるかもしれないので、ビルド実行後にエラーが出たら適宜インストールしてから再実行する。

以下のコマンドを実行する。

1
2
3
4
5
6
mkdir ~/qemu && cd ~/qemu
git clone --depth=1 --branch stable-8.0 https://github.com/qemu/qemu.git
sudo mkdir /opt/qemu-system-riscv32
sudo ./configure --target-list=riscv32-softmmu --prefix=/opt/qemu-system-riscv32/
sudo make -j $(nproc)
sudo make install

ビルドが完了すると、上の--prefixで指定したパスにqemu-system-riscv32という実行バイナリが生成されているはず。そのディレクトリに対してPATHを通す。

すなわち.bashrcに以下を追加する。

1
export PATH="$PATH:/opt/qemu-system-riscv32/bin"

動作確認

本の記述通りにrun.shを記述し、実行した。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
❯ ./run.sh 
+ QEMU=qemu-system-riscv32
+ qemu-system-riscv32 -machine virt -bios default -nographic -serial mon:stdio --no-reboot

OpenSBI v1.2
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name             : riscv-virtio,qemu
Platform Features         : medeleg
Platform HART Count       : 1
Platform IPI Device       : aclint-mswi
Platform Timer Device     : aclint-mtimer @ 10000000Hz
Platform Console Device   : uart8250
Platform HSM Device       : ---
Platform PMU Device       : ---
Platform Reboot Device    : sifive_test
Platform Shutdown Device  : sifive_test
Firmware Base             : 0x80000000
Firmware Size             : 208 KB
Runtime SBI Version       : 1.0

Domain0 Name              : root
Domain0 Boot HART         : 0
Domain0 HARTs             : 0*
Domain0 Region00          : 0x02000000-0x0200ffff (I)
Domain0 Region01          : 0x80000000-0x8003ffff ()
Domain0 Region02          : 0x00000000-0xffffffff (R,W,X)
Domain0 Next Address      : 0x00000000
Domain0 Next Arg1         : 0x87e00000
Domain0 Next Mode         : S-mode
Domain0 SysReset          : yes

Boot HART ID              : 0
Boot HART Domain          : root
Boot HART Priv Version    : v1.12
Boot HART Base ISA        : rv32imafdch
Boot HART ISA Extensions  : time,sstc
Boot HART PMP Count       : 16
Boot HART PMP Granularity : 4
Boot HART PMP Address Bits: 32
Boot HART MHPM Count      : 16
Boot HART MIDELEG         : 0x00001666
Boot HART MEDELEG         : 0x00f0b509

ひとまず正しく動いてそう

ldがriscv32向けのelfを吐けない

本の5.ブートの通りにkernel.c, kernel.ldを作成し、run.shでビルドしようとすると、以下のエラーが出た

1
2
3
4
5
6
7
8
❯ ./run.sh
+ QEMU=qemu-system-riscv32
+ CC=clang
+ CFLAGS='-std=c11 -O2 -g3 -Wall -Wextra --target=riscv32 -ffreestanding -nostdlib'
+ clang -std=c11 -O2 -g3 -Wall -Wextra --target=riscv32 -ffreestanding -nostdlib -Wl,-Tkernel.ld -Wl,-Map=kernel.map -o kernel.elf kernel.c
/usr/bin/ld: unrecognised emulation mode: elf32lriscv
Supported emulations: elf_x86_64 elf32_x86_64 elf_i386 elf_iamcu elf_l1om elf_k1om i386pep i386pe
clang: error: ld command failed with exit code 1 (use -v to see invocation)

手元の環境のClangはリンカとしてldを呼んでいるが、ldのターゲットにriscv32がない模様

一旦以下のようにld.lldを別途呼び出すようにしたら、ビルドできるようになった (Clangが呼び出すリンカを変更できないか探ったが、よく分からなかった)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#!/bin/bash
set -xue

# QEMUの実行バイナリへのパス
QEMU=qemu-system-riscv32

CC=clang
LINKER=ld.lld

# コンパイルオプション
CFLAGS="-c -std=c11 -O2 -g3 -Wall -Wextra --target=riscv32 -ffreestanding -nostdlib -mno-relax"

# カーネルをビルド
$CC $CFLAGS -o kernel.o kernel.c
$LINKER -m elf32lriscv -L/lib -Tkernel.ld -Map=kernel.map kernel.o -o kernel.elf

# QEMUを起動
$QEMU -machine virt -bios default -nographic -serial mon:stdio --no-reboot -kernel kernel.elf

参考にさせていただいたサイト