From 44a0ead7f2c034ec1df1b4ec490f50f89514e69c Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Sun, 18 Oct 2026 01:46:47 +0200 Subject: [PATCH] feat(adapter-riscv): boot on QEMU virt with UART hello world --- .cargo/config.toml | 3 ++ Cargo.toml | 5 +++ crates/adapter-riscv/Cargo.toml | 17 +++++++++ crates/adapter-riscv/linker.ld | 36 +++++++++++++++++++ crates/adapter-riscv/src/main.rs | 61 ++++++++++++++++++++++++++++++++ crates/adapter-riscv/src/uart.rs | 60 +++++++++++++++++++++++++++++++ 6 files changed, 182 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 crates/adapter-riscv/Cargo.toml create mode 100644 crates/adapter-riscv/linker.ld create mode 100644 crates/adapter-riscv/src/main.rs create mode 100644 crates/adapter-riscv/src/uart.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..e800c83 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,3 @@ +[target.riscv64gc-unknown-none-elf] +runner = "qemu-system-riscv64 -machine virt -nographic -bios default -kernel" +rustflags = ["-C", "link-arg=-Tcrates/adapter-riscv/linker.ld", "-C", "panic=abort"] diff --git a/Cargo.toml b/Cargo.toml index 010f18d..7977fb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "crates/adapter-riscv", "crates/apps/snake", "crates/domain", "crates/elf", @@ -84,3 +85,7 @@ snake = { path = "crates/apps/snake" } strip = true lto = true opt-level = "z" +panic = "abort" + +[profile.dev] +panic = "abort" diff --git a/crates/adapter-riscv/Cargo.toml b/crates/adapter-riscv/Cargo.toml new file mode 100644 index 0000000..0a651ed --- /dev/null +++ b/crates/adapter-riscv/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "adapter-riscv" +version = "0.1.0" +edition = "2024" + +[dependencies] +types = { workspace = true } +domain = { workspace = true } +platform = { workspace = true } +kernel = { workspace = true } + +[profile.release] +panic = "abort" +opt-level = "s" + +[profile.dev] +panic = "abort" diff --git a/crates/adapter-riscv/linker.ld b/crates/adapter-riscv/linker.ld new file mode 100644 index 0000000..1727e95 --- /dev/null +++ b/crates/adapter-riscv/linker.ld @@ -0,0 +1,36 @@ +OUTPUT_ARCH(riscv) +ENTRY(_start) + +SECTIONS { + . = 0x80200000; + + .text : { + *(.text.entry) + *(.text .text.*) + } + + . = ALIGN(4096); + .rodata : { + *(.rodata .rodata.*) + } + + . = ALIGN(4096); + .data : { + *(.data .data.*) + } + + . = ALIGN(4096); + .bss : { + _bss_start = .; + *(.bss .bss.*) + *(.sbss .sbss.*) + _bss_end = .; + } + + . = ALIGN(4096); + _stack_bottom = .; + . += 0x10000; + _stack_top = .; + + _heap_start = .; +} diff --git a/crates/adapter-riscv/src/main.rs b/crates/adapter-riscv/src/main.rs new file mode 100644 index 0000000..a672161 --- /dev/null +++ b/crates/adapter-riscv/src/main.rs @@ -0,0 +1,61 @@ +#![no_std] +#![no_main] + +mod uart; + +use core::arch::global_asm; +use uart::Uart; + +global_asm!( + r#" +.section .text.entry +.globl _start +_start: + la sp, _stack_top + call kmain +spin: + wfi + j spin +"# +); + +unsafe extern "C" { + static _bss_start: u8; + static _bss_end: u8; +} + +fn zero_bss() { + unsafe { + let start = &_bss_start as *const u8 as *mut u8; + let end = &_bss_end as *const u8 as *mut u8; + let len = end as usize - start as usize; + core::ptr::write_bytes(start, 0, len); + } +} + +#[unsafe(no_mangle)] +extern "C" fn kmain() -> ! { + zero_bss(); + + let uart = Uart::new(); + uart.write_str("k-os: booted on RISC-V\n"); + + loop { + unsafe { core::arch::asm!("wfi") }; + } +} + +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + let uart = Uart::new(); + uart.write_str("PANIC: "); + if let Some(location) = info.location() { + uart.write_str(location.file()); + uart.put_char(b':'); + uart.write_dec(location.line() as usize); + } + uart.put_char(b'\n'); + loop { + unsafe { core::arch::asm!("wfi") }; + } +} diff --git a/crates/adapter-riscv/src/uart.rs b/crates/adapter-riscv/src/uart.rs new file mode 100644 index 0000000..6b6e55d --- /dev/null +++ b/crates/adapter-riscv/src/uart.rs @@ -0,0 +1,60 @@ +const UART_BASE: usize = 0x10000000; + +pub struct Uart { + base: usize, +} + +impl Uart { + pub fn new() -> Self { + Self { base: UART_BASE } + } + + pub fn put_char(&self, byte: u8) { + let ptr = self.base as *mut u8; + unsafe { core::ptr::write_volatile(ptr, byte) }; + } + + pub fn write_str(&self, s: &str) { + for byte in s.bytes() { + self.put_char(byte); + } + } + + pub fn write_hex(&self, mut value: usize) { + self.write_str("0x"); + if value == 0 { + self.put_char(b'0'); + return; + } + let mut buf = [0u8; 16]; + let mut i = 0; + while value > 0 { + let digit = (value & 0xF) as u8; + buf[i] = if digit < 10 { b'0' + digit } else { b'a' + digit - 10 }; + value >>= 4; + i += 1; + } + while i > 0 { + i -= 1; + self.put_char(buf[i]); + } + } + + pub fn write_dec(&self, mut value: usize) { + if value == 0 { + self.put_char(b'0'); + return; + } + let mut buf = [0u8; 20]; + let mut i = 0; + while value > 0 { + buf[i] = b'0' + (value % 10) as u8; + value /= 10; + i += 1; + } + while i > 0 { + i -= 1; + self.put_char(buf[i]); + } + } +}