32-bit RISC-V Linux をビルドして Spike で動かす
最近は自作 32-bit RISC-V CPU で Linux を動かしていた。その前段階として 32-bit RISC-V Linux をビルドして Spike で動かしたのでそのときの備忘録。
Spike でただ Linux を動かすだけなら Buildroot の Spike 用の設定ファイルを使えば簡単なのだが (ただし、ビルド時間が恐ろしく長い...)、今回は以下のような諸事情があって一からビルドした。
- OpenSBI に手を加えて自作 CPU 向けの firmware を追加したい
- 命令セットとして RV32IMA_Zicsr_Zifencei_Zicntr を使いたい
- デフォルトだと RV32IMAFDC_Zicsr_Zifencei_Zicntr_Zihpm (RV32GC)
- 自作 CPU では単精度/倍精度浮動小数点拡張 (F/D) と圧縮命令拡張 (C)、ハードウェアパフォーマンスカウンタ拡張 (Zihpm) をサポートしていない
大まかな手順としては、まず Buildroot で initrd を生成し、次に Linux をビルドして Image を生成、その次に OpenSBI をビルドして firmware を生成、最後にそれを Spike に渡して Linux を動かすという流れになる。
なお、Buildroot と Linux のビルド方法は自作 CPU のものと同じ。自作 CPU では OpenSBI だけ少し修正して Linux を動かしている。
Spike で 32-bit RISC-V Linux を動かすときは以下の資料が参考になる。
環境
- Docker で
amd64/ubuntu:latestの環境を用意してコマンドの動作確認をした。
Architecture: x86_64 OS : Ubuntu 24.04.1 LTS
前準備
- 環境変数などの設定。
$ mkdir $HOME/work && export WORK=$_ $ mkdir $WORK/riscv32 && export RISCV32=$_ $ export PATH=$RISCV32/bin:$PATH $ sudo apt update && sudo apt upgrade -y
RISC-V GNU Compiler Toolchain
- RISC-V GNU Compiler Toolchain をインストールする。
- Buildroot、Linux、OpenSBI を RV32IMA_Zicsr_Zifencei_Zicntr 向けにコンパイルするためのクロスコンパイラ。
- RISC-V GNU Compiler Toolchain - GitHub
$ cd $WORK $ sudo apt install -y autoconf automake autotools-dev curl python3 python3-pip libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev ninja-build git cmake libglib2.0-dev libslirp-dev $ git clone https://github.com/riscv/riscv-gnu-toolchain $ cd riscv-gnu-toolchain $ ./configure --prefix=$RISCV32 --with-arch=rv32ima_zicsr_zifencei_zicntr --with-abi=ilp32 $ make linux -j $(nproc)
Spike
- Spike をインストールする。
- Spike は RISC-V の公式が提供している命令セットシミュレータ (Instruction Set Simulator, ISS)。
- Spike RISC-V ISA Simulator - GitHub
$ cd $WORK $ sudo apt install -y device-tree-compiler $ git clone https://github.com/riscv-software-src/riscv-isa-sim.git $ mkdir riscv-isa-sim/build && cd $_ $ ../configure --prefix=$RISCV32 --with-target=riscv32-unknown-linux-gnu $ make -j $(nproc) $ make install
Buildroot
- 組み込みシステム用の Linux 環境を構築するプロセスを簡素化、自動化してくれるもの。
- 今回は初期 RAM ディスク (initrd・initramfs) を生成するためだけに使用する。
- Buildroot を使用すると root 権限を使わなくても initrd が作れて便利。
- 初期 RAM ディスクは本命のルートファイルシステムをマウントするために使用するミニファイルシステムらしい。
- Spike は cpio 形式の initrd を使うのがデフォルトらしいので慣習に従っておく。
- 参考) https://www.kernel.org/doc/Documentation/filesystems/ramfs-rootfs-initramfs.txt
- https://buildroot.org/downloads/
$ cd $WORK $ sudo apt install -y cpio libncurses-dev rsync unzip wget $ wget https://buildroot.org/downloads/buildroot-2024.11-rc2.tar.xz $ tar Jxvf buildroot-2024.11-rc2.tar.xz $ cd buildroot-2024.11-rc2 $ make menuconfig - Target options ---> - Target Architecture (RISCV) ---> - Target Architecture Variant (Custom architecture) ---> - [*] Integer Multiplication and Division (M) - [*] Atomic Instructions (A) - Target Architecture Size (32-bit) ---> - Toolchain ---> - Toolchain type (External toolchain) ---> - ($(RISCV32)) Toolchain path - (riscv32-unknown-linux-gnu) Toolchain prefix - External toolchain kernel headers series (6.6.x) ---> - External toolchain C library (glibc) ---> - [*] Toolchain has SSP support? - [*] Toolchain has SSP strong support? - [ ] Toolchain has RPC support? - [*] Toolchain has C++ support? - [*] Toolchain has Fortran support? - [*] Toolchain has OpenMP support? - Filesystem images ---> - [*] cpio the root filesystem (for use as an initial RAM filesystem) - Compression method (gzip) ---> - [ ] tar the root filesystem $ make
$WORK/buildroot-2024.11-rc2/output/images/にrootfs.cpioとrootfs.cpio.gzが生成される。- 末尾に
.gzがついているのがgzipで圧縮されたもの。今回はこちらを使用する。
Linux
- 最新版の Linux Kernel 6.12.1 (2024/12/01 時点) をビルドする。
- 今回は
tinyconfigと32-bit.configを使用する。 - 参考) https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/README.rst
- 参考) RISC-VでLinuxの動くマルチコアをつくるのにやったこと (2. ソフト編)
- Linux - GitHub
- https://kernel.org/
$ cd $WORK $ wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.1.tar.xz $ tar Jxvf linux-6.12.1.tar.xz $ cd linux-6.12.1 $ make mrproper $ make ARCH=riscv CROSS_COMPILE=riscv32-unknown-linux-gnu- tinyconfig $ make ARCH=riscv CROSS_COMPILE=riscv32-unknown-linux-gnu- 32-bit.config $ make ARCH=riscv CROSS_COMPILE=riscv32-unknown-linux-gnu- menuconfig - General setup ---> - [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support - ($(WORK)/buildroot-2024.11-rc2/output/images/rootfs.cpio.gz) Initramfs source file(s) - [*] Support initial ramdisk/ramfs compressed using gzip (NEW) - [ ] Support initial ramdisk/ramfs compressed using bzip2 - [ ] Support initial ramdisk/ramfs compressed using LZMA - [ ] Support initial ramdisk/ramfs compressed using XZ - [ ] Support initial ramdisk/ramfs compressed using LZO - [ ] Support initial ramdisk/ramfs compressed using LZ4 - [ ] Support initial ramdisk/ramfs compressed using ZSTD - [*] Configure standard kernel features (expert users) ---> - [*] Enable support for printk - [*] Enable futex support - Platform type ---> # - [*] Symmetric Multi-Processing # Enable if using multi-core - [ ] Emit compressed instructions when building Linux # You can disable it by disabling “Boot options ---> UEFI runtime support” first - Unaligned Accesses Support (Emulate unaligned access where system support is missing) ---> - Boot options ---> - [ ] UEFI runtime support - [*] Permit falling back to parsing riscv,isa for extension support by default - Executable file formats ---> - [*] Kernel support for ELF binaries - [*] Write ELF core dumps with partial segments (NEW) - [*] Kernel support for scripts starting with #! - [ ] Kernel support for flat binaries - [*] Kernel support for MISC binaries - [*] Enable core dump support - Device Drivers ---> - Generic Driver Options ---> - [*] Maintain a devtmpfs filesystem to mount at /dev - [*] Automount devtmpfs at /dev, after the kernel mounted the rootfs - Character devices ---> - [*] Enable TTY - [*] Virtual terminal (NEW) - [*] Enable character translations in console (NEW) - [*] Support for console on virtual terminal (NEW) - [ ] Support for binding and unbinding console drivers (NEW) - [*] Unix98 PTY support (NEW) - [*] Legacy (BSD) PTY support (NEW) - (256) Maximum number of legacy PTY in use (NEW) - [*] Allow legacy TIOCSTI usage (NEW) - [*] Automatically load TTY Line Disciplines (NEW) - Serial drivers ---> - [*] Early console using RISC-V SBI - [*] RISC-V SBI console support - File systems ---> - Pseudo filesystems ---> - [*] /proc file system support - [ ] /proc/kcore support (NEW) - [*] Sysctl support (/proc/sys) (NEW) - [*] Enable /proc page monitoring (NEW) - [ ] Include /proc/<pid>/task/<tid>/children file (NEW) - [*] sysfs file system support - Kernel hacking ---> - printk and dmesg options ---> - [*] Show timing information on printks $ make ARCH=riscv CROSS_COMPILE=riscv32-unknown-linux-gnu- -j $(nproc)
$WORK/linux-6.12.1/にvmlinux、$WORK/linux-6.12.1/arch/riscv/boot/にImageが生成される。vmlinuxは ELF ファイルであり、Imageはvmlinuxをobjcopy -O binary vmlinuxしたバイナリファイル。- OpenSBI には
Imageを payload として渡す。 - Linux のアセンブリが見たい場合は
riscv32-unknown-linux-gnu-objdump -d vmlinux > vmlinux.dumpなどとしてディスアセンブリするといい。
OpenSBI
- RISC-V Open Source Supervisor Binary Interface (OpenSBI)
- RISC-V Supervisor Binary Interface (SBI) のオープンソース実装が OpenSBI らしい。
- Machine-mode で実行されるプラットフォーム固有のファームウェア。
- Supervisor-mode からシステムコール形式で呼び出すことができる。
- ブートローダー的な役割もある。
- 先ほど生成した Linux の
Imageを payload として渡す。 - 参考) OpenSBI Deep Dive - Western Digital
- OpenSBI - GitHub
$ cd $WORK $ git clone https://github.com/riscv-software-src/opensbi.git $ cd opensbi $ make CROSS_COMPILE=riscv32-unknown-linux-gnu- PLATFORM_RISCV_XLEN=32 PLATFORM_RISCV_ABI=ilp32 PLATFORM_RISCV_ISA=rv32ima_zicsr_zifencei_zicntr PLATFORM=generic FW_PAYLOAD_PATH=$WORK/linux-6.12.1/arch/riscv/boot/Image
$WORK/opensbi/build/platform/generic/firmware/にfw_payload.elfが生成される。- これが Buildroot の initrd、Linux Kernel、OpenSBI をすべて含んだ ELF ファイル。
- これを Spike で実行する。
Running Linux on Spike
- Spike で
fw_payload.elfを実行して Linux を動かす。 - exit する方法: Ctrl + c → q → Enter
$ spike --isa=rv32ima_zicsr_zifencei_zicntr -m256 --bootargs 'root=/dev/ram console=hvc0 earlycon=sbi' $WORK/opensbi/build/platform/generic/firmware/fw_payload.elf OpenSBI v1.5-137-ga387a8d ____ _____ ____ _____ / __ \ / ____| _ \_ _| | | | |_ __ ___ _ __ | (___ | |_) || | | | | | '_ \ / _ \ '_ \ \___ \| _ < | | | |__| | |_) | __/ | | |____) | |_) || |_ \____/| .__/ \___|_| |_|_____/|____/_____| | | |_| Platform Name : ucbbar,spike-bare 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 : htif Platform Shutdown Device : htif Platform Suspend Device : --- Platform CPPC Device : --- Firmware Base : 0x80000000 Firmware Size : 321 KB Firmware RW Offset : 0x40000 Firmware RW Size : 65 KB Firmware Heap Offset : 0x47000 Firmware Heap Size : 37 KB (total), 2 KB (reserved), 10 KB (used), 24 KB (free) Firmware Scratch Size : 4096 B (total), 244 B (used), 3852 B (free) Runtime SBI Version : 2.0 Domain0 Name : root Domain0 Boot HART : 0 Domain0 HARTs : 0* Domain0 Region00 : 0x10000000-0x10000fff M: (I,R,W) S/U: (R,W) Domain0 Region01 : 0x80040000-0x8005ffff M: (R,W) S/U: () Domain0 Region02 : 0x02080000-0x020bffff M: (I,R,W) S/U: () Domain0 Region03 : 0x80000000-0x8003ffff M: (R,X) S/U: () Domain0 Region04 : 0x02000000-0x0207ffff M: (I,R,W) S/U: () Domain0 Region05 : 0x0c000000-0x0cffffff M: (I,R,W) S/U: (R,W) Domain0 Region06 : 0x00000000-0xffffffff M: () S/U: (R,W,X) Domain0 Next Address : 0x80400000 Domain0 Next Arg1 : 0x82200000 Domain0 Next Mode : S-mode Domain0 SysReset : yes Domain0 SysSuspend : yes Boot HART ID : 0 Boot HART Domain : root Boot HART Priv Version : v1.12 Boot HART Base ISA : rv32ima Boot HART ISA Extensions : zicntr,sdtrig Boot HART PMP Count : 16 Boot HART PMP Granularity : 2 bits Boot HART PMP Address Bits: 32 Boot HART MHPM Info : 0 (0x00000000) Boot HART Debug Triggers : 4 triggers Boot HART MIDELEG : 0x00000222 Boot HART MEDELEG : 0x0004b109 [ 0.000000] Linux version 6.12.1 (root@bbe998f21521) (riscv32-unknown-linux-gnu-gcc () 14.2.0, GNU ld (GNU Binutils) 2.43.1) #1 Sun Dec 1 16:38:35 JST 2024 [ 0.000000] OF: fdt: Ignoring memory range 0x80000000 - 0x80400000 [ 0.000000] Machine model: ucbbar,spike-bare [ 0.000000] SBI specification v2.0 detected [ 0.000000] SBI implementation ID=0x1 Version=0x10005 [ 0.000000] SBI TIME extension detected [ 0.000000] SBI IPI extension detected [ 0.000000] SBI RFENCE extension detected [ 0.000000] SBI SRST extension detected [ 0.000000] SBI DBCN extension detected [ 0.000000] earlycon: sbi0 at I/O port 0x0 (options '') [ 0.000000] printk: legacy bootconsole [sbi0] enabled [ 0.000000] OF: reserved mem: 0x80000000..0x8003ffff (256 KiB) nomap non-reusable mmode_resv1@80000000 [ 0.000000] OF: reserved mem: 0x80040000..0x8005ffff (128 KiB) nomap non-reusable mmode_resv0@80040000 [ 0.000000] Zone ranges: [ 0.000000] Normal [mem 0x0000000080400000-0x000000008fffffff] [ 0.000000] Movable zone start for each node [ 0.000000] Early memory node ranges [ 0.000000] node 0: [mem 0x0000000080400000-0x000000008fffffff] [ 0.000000] Initmem setup node 0 [mem 0x0000000080400000-0x000000008fffffff] [ 0.000000] Falling back to deprecated "riscv,isa" [ 0.000000] riscv: base ISA extensions aim [ 0.000000] riscv: ELF capabilities aim [ 0.000000] Kernel command line: root=/dev/ram console=hvc0 earlycon=sbi [ 0.000000] Dentry cache hash table entries: 32768 (order: 5, 131072 bytes, linear) [ 0.000000] Inode-cache hash table entries: 16384 (order: 4, 65536 bytes, linear) [ 0.000000] Built 1 zonelists, mobility grouping on. Total pages: 64512 [ 0.000000] mem auto-init: stack:all(zero), heap alloc:off, heap free:off [ 0.000000] SLUB: HWalign=64, Order=0-1, MinObjects=0, CPUs=1, Nodes=1 [ 0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0 [ 0.000000] riscv-intc: 32 local interrupts mapped [ 0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x24e6a1710, max_idle_ns: 440795202120 ns [ 0.000000] sched_clock: 64 bits at 10MHz, resolution 100ns, wraps every 4398046511100ns [ 0.000090] Console: colour dummy device 80x25 [ 0.000110] Calibrating delay loop (skipped), value calculated using timer frequency.. 20.00 BogoMIPS (lpj=40000) [ 0.000130] pid_max: default: 32768 minimum: 301 [ 0.000165] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear) [ 0.000185] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear) [ 0.016100] ASID allocator using 9 bits (512 entries) [ 0.020135] Memory: 249124K/258048K available (1482K kernel code, 476K rwdata, 178K rodata, 3849K init, 184K bss, 8688K reserved, 0K cma-reserved) [ 0.020220] devtmpfs: initialized [ 0.020630] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns [ 0.020655] futex hash table entries: 256 (order: -1, 3072 bytes, linear) [ 0.021395] clocksource: Switched to clocksource riscv_clocksource [ 0.029425] workingset: timestamp_bits=30 max_order=16 bucket_order=0 [ 0.037245] riscv-plic: plic@c000000: mapped 31 interrupts with 1 handlers for 2 contexts. [ 0.070295] printk: legacy console [hvc0] enabled [ 0.070295] printk: legacy console [hvc0] enabled [ 0.070330] printk: legacy bootconsole [sbi0] disabled [ 0.070330] printk: legacy bootconsole [sbi0] disabled [ 0.077680] clk: Disabling unused clocks [ 0.211175] Freeing unused kernel image (initmem) memory: 3848K [ 0.211210] Kernel memory protection not selected by kernel config. [ 0.211240] Run /init as init process Saving 256 bits of non-creditable seed for next boot Starting syslogd: OK Starting klogd: OK Running sysctl: OK Starting network: ip: socket: Function not implemented ip: socket: Function not implemented FAIL Starting crond: OK crond (busybox 1.36.1) started, log level 8 Welcome to Buildroot buildroot login: root root login[60]: root login on 'console' # uname -a uname -a Linux buildroot 6.12.1 #1 Sun Dec 1 16:38:35 JST 2024 riscv32 GNU/Linux # cat /proc/cpuinfo cat /proc/cpuinfo processor : 0 hart : 0 isa : rv32ima_zicntr_zicsr_zifencei_zihpm mmu : sv32 mvendorid : 0x0 marchid : 0x5 mimpid : 0x0 hart isa : rv32ima_zicntr_zicsr_zifencei_zihpm
- Spike で Linux 動いた!