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

$ 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

$ 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

$ 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.cpiorootfs.cpio.gz が生成される。
  • 末尾に .gz がついているのが gzip で圧縮されたもの。今回はこちらを使用する。

Linux

$ 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 ファイルであり、Imagevmlinuxobjcopy -O binary vmlinux したバイナリファイル。
  • OpenSBI には Image を payload として渡す。
  • Linuxアセンブリが見たい場合は riscv32-unknown-linux-gnu-objdump -d vmlinux > vmlinux.dump などとしてディスアセンブリするといい。

OpenSBI

$ 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 動いた!