RISC-V bare metal
Table of Contents
1. RISC-V bare metal (without OpenSBI)
You may follow osdev wiki pages.
2. RISC-V bare metal with OpenSBI
2.1. What is OpenSBI ?
2.2. setup OpenSBI
Debian 12's opensbi packages has older version and may not work as expected. Install from source:
git clone https://github.com/riscv-software-src/opensbi.git cd opensbi make ARCH=riscv CROSS_COMPILE=riscv64-unknown-elf- PLATFORM=generic
2.3. get device tree
You may need to install device-tree-compiler package.
$ qemu-system-riscv64 -machine virt -machine dumpdtb=riscv64-virt.dtb $ dtc -I dtb -O dts -o riscv64-virt.dts riscv64-virt.dtb
open riscv64-virt.dts
... memory@80000000 { device_type = "memory"; reg = <0x00 0x80000000 0x00 0x8000000>; }; ... soc { #address-cells = <0x02>; #size-cells = <0x02>; compatible = "simple-bus"; ranges; ... serial@10000000 { interrupts = <0x0a>; interrupt-parent = <0x03>; clock-frequency = "\08@"; reg = <0x00 0x10000000 0x00 0x100>; compatible = "ns16550a"; }; ... } ...
ns16550a - https://en.wikipedia.org/wiki/16550_UART
2.4. example code
startup.S
.global start .section .startup start: /* setup stack */ la sp, stack_top call kmain .end
linker.ld
OpenSBI will be loaded at 0x80000000 by QEMU.
(note: Therefore, in case of bare metal without OpenSBI, we need to load kernel at 0x80000000.)
We are using OpenSBI, so we will try to load our kernel at 0x80100000 (or later address).
. = 0x80100000; SECTIONS { .text : ALIGN(4K) { startup.o(.startup) *(.init); *(.text); } .bss : ALIGN(4K) { PROVIDE(bss_start = .); *(.bss); . += 4096; PROVIDE(stack_top = .); . += 4096; PROVIDE(global_pointer = .); PROVIDE(bss_end = .); } .rodata : ALIGN(4K) { *(.rodata); } .data : ALIGN(4K) { *(.data); } }
kernel.c
UART address from device tree - "serial@10000000"
The following example based on - https://wiki.osdev.org/RISC-V_Bare_Bones
#include <stdint.h> unsigned char *uart = (unsigned char *)0x10000000; void putchar_uart(char c) { *uart = c; return; } void print_uart(const char * str) { while(*str != '\0') { putchar_uart(*str); ++str; } return; } void kmain(void) { print_uart("Hello world!\n"); while(1) { // Read input from the UART putchar_uart(*uart); } return; }
Makefile
CROSS_COMPILE=riscv64-unknown-elf- startup_kernel: startup.o linker.ld kernel.o ${CROSS_COMPILE}ld -T linker.ld --no-dynamic-linker -static -nostdlib -s -o startup startup.o kernel.o startup.o: startup.S ${CROSS_COMPILE}as -o startup.o -c startup.S kernel.o: kernel.c ${CROSS_COMPILE}gcc -Wall -Wextra -c -mcmodel=medany -o kernel.o -c kernel.c -ffreestanding all: startup_kernel clean: rm -f *.o rm -f startup
2.5. test kernel
$ qemu-system-riscv64 -M virt -m 128M -nographic -bios PATH_TO_opensbi/build/platform/generic/firmware/fw_dynamic.bin \ -kernel startup
OpenSBI v1.4-8-gbb90a9e ____ _____ ____ _____ / __ \ / ____| _ \_ _| | | | |_ __ ___ _ __ | (___ | |_) || | | | | | '_ \ / _ \ '_ \ \___ \| _ < | | | |__| | |_) | __/ | | |____) | |_) || |_ \____/| .__/ \___|_| |_|_____/|____/_____| | | |_| 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 : syscon-reboot Platform Shutdown Device : syscon-poweroff Platform Suspend Device : --- Platform CPPC Device : --- Firmware Base : 0x80000000 Firmware Size : 323 KB Firmware RW Offset : 0x40000 Firmware RW Size : 67 KB Firmware Heap Offset : 0x48000 Firmware Heap Size : 35 KB (total), 2 KB (reserved), 10 KB (used), 22 KB (free) Firmware Scratch Size : 4096 B (total), 336 B (used), 3760 B (free) Runtime SBI Version : 2.0 Domain0 Name : root Domain0 Boot HART : 0 Domain0 HARTs : 0* Domain0 Region00 : 0x0000000000100000-0x0000000000100fff M: (I,R,W) S/U: (R,W) Domain0 Region01 : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W) Domain0 Region02 : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: () Domain0 Region03 : 0x0000000080040000-0x000000008005ffff M: (R,W) S/U: () Domain0 Region04 : 0x0000000080000000-0x000000008003ffff M: (R,X) S/U: () Domain0 Region05 : 0x000000000c400000-0x000000000c5fffff M: (I,R,W) S/U: (R,W) Domain0 Region06 : 0x000000000c000000-0x000000000c3fffff M: (I,R,W) S/U: (R,W) Domain0 Region07 : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X) Domain0 Next Address : 0x0000000080100000 Domain0 Next Arg1 : 0x0000000087e00000 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 : rv64imafdch Boot HART ISA Extensions : sstc,zicntr,zihpm,sdtrig Boot HART PMP Count : 16 Boot HART PMP Granularity : 2 bits Boot HART PMP Address Bits: 54 Boot HART MHPM Info : 16 (0x0007fff8) Boot HART Debug Triggers : 2 triggers Boot HART MIDELEG : 0x0000000000001666 Boot HART MEDELEG : 0x0000000000f0b509 Hello world!
ctrl+a x to exit
3. get default RISC-V linker
for further experiments
$ riscv64-unknown-linux-gnu-ld --verbose > riscv64-virt.ld
4. Debugging
$ qemu-system-riscv64 -M virt -m 128M -nographic -bios PATH_TO_opensbi/build/platform/generic/firmware/fw_dynamic.bin \ -kernel startup -s -S
In another shell
$ riscv64-unknown-linux-gnu-gdb startup (gdb) gdb-multiarch (gdb) set architecture riscv:rv64 (gdb) set disassemble-next-line on (gdb) target remote :1234 Press c to continue booting
5. useful links
sifive examples:
https://github.com/sifive/freedom-e-sdk.git
OSDEV:
https://wiki.osdev.org/RISC-V_Bare_Bones
https://wiki.osdev.org/RISC-V_Meaty_Skeleton_with_QEMU_virt_board
Bare metal programming RISC-V:
https://popovicu.com/posts/bare-metal-programming-risc-v/
https://popovicu.com/posts/risc-v-sbi-and-full-boot-process/
https://github.com/popovicu/risc-v-bare-metal-fake-bios
RISC-V from scratch:
https://github.com/twilco/riscv-from-scratch
Uncovering the Mysteries of Linux Boot on RISC-V QEMU Machines - A Deep Dive into the Boot Process:
interrupt:
https://github.com/s094392/riscv-bare-metal
https://mullerlee.cyou/2020/07/09/riscv-exception-interrupt/
RISC-V Assembler Reference
https://michaeljclark.github.io/asm.html
GNU linker ld (GNU Binutils)
https://sourceware.org/binutils/docs/ld.html
Debug Console Extension (EID #0x4442434E "DBCN")
https://github.com/riscv-non-isa/riscv-sbi-doc/blob/master/src/ext-debug-console.adoc
RISC-V – A Baremetal Introduction using C++
https://riscv.org/news/2021/08/risc-v-a-baremetal-introduction-using-c-phil-mulholland/
sifive.com blog
https://www.sifive.com/blog/all-aboard-part-1-compiler-args
https://www.sifive.com/blog/all-aboard-part-2-relocations
https://www.sifive.com/blog/all-aboard-part-3-linker-relaxation-in-riscv-toolchain
https://www.sifive.com/blog/all-aboard-part-4-risc-v-code-models
https://www.sifive.com/blog/all-aboard-part-5-risc-v-multilib
https://www.sifive.com/blog/all-aboard-part-6-booting-a-risc-v-linux-kernel
https://www.sifive.com/blog/all-aboard-part-7-entering-and-exiting-the-linux-kernel-on-risc-v
https://www.sifive.com/blog/all-aboard-part-8-the-risc-v-linux-port-is-upstream
https://www.sifive.com/blog/all-aboard-part-9-paging-and-mmu-in-risc-v-linux-kernel
https://www.sifive.com/blog/all-aboard-part-10-how-to-contribute-to-the-risc-v-software-ecosystem
https://www.sifive.com/blog/all-aboard-part-11-risc-v-hackathon-presented-by-sifive