From 86eb2d3252709fb2c5a95e39cba3e03f5b26e8eb Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Fri, 17 Jan 2025 02:25:49 +0200 Subject: [PATCH] rv64: boot into upper half --- Cargo.lock | 16 ++ Cargo.toml | 6 +- etc/ld/riscv/riscv64-unknown-qemu.ld | 57 ++++++ etc/riscv64-unknown-none.json | 25 +++ kernel/Cargo.toml | 4 + kernel/arch/Cargo.toml | 15 +- kernel/arch/riscv64/Cargo.toml | 18 ++ kernel/arch/riscv64/src/context.rs | 54 ++++++ kernel/arch/riscv64/src/lib.rs | 109 +++++++++++ kernel/arch/riscv64/src/mem/mod.rs | 120 ++++++++++++ kernel/arch/riscv64/src/mem/table.rs | 169 ++++++++++++++++ kernel/arch/riscv64/src/registers.rs | 183 ++++++++++++++++++ kernel/arch/src/lib.rs | 2 + kernel/build.rs | 1 + kernel/driver/bus/pci/src/lib.rs | 5 +- kernel/lib/memtables/src/lib.rs | 6 + kernel/lib/memtables/src/riscv64.rs | 19 ++ kernel/libk/src/task/binary/elf.rs | 2 + kernel/src/arch/mod.rs | 16 +- kernel/src/arch/riscv64/boot/entry.S | 59 ++++++ kernel/src/arch/riscv64/boot/mod.rs | 125 ++++++++++++ kernel/src/arch/riscv64/mod.rs | 48 +++++ kernel/src/util/mod.rs | 4 + kernel/tools/gentables/src/main.rs | 7 +- lib/abi/src/arch/mod.rs | 5 + lib/abi/src/arch/riscv64.rs | 19 ++ lib/qemu/src/lib.rs | 7 + lib/qemu/src/riscv64.rs | 60 ++++++ lib/runtime/src/process/thread_local/mod.rs | 9 +- .../src/process/thread_local/riscv64.rs | 12 ++ lib/runtime/src/sys/mod.rs | 5 +- lib/runtime/src/sys/riscv64.rs | 53 +++++ xtask/src/build/mod.rs | 5 + xtask/src/check.rs | 5 + xtask/src/env.rs | 6 + xtask/src/qemu.rs | 26 ++- 36 files changed, 1262 insertions(+), 20 deletions(-) create mode 100644 etc/ld/riscv/riscv64-unknown-qemu.ld create mode 100644 etc/riscv64-unknown-none.json create mode 100644 kernel/arch/riscv64/Cargo.toml create mode 100644 kernel/arch/riscv64/src/context.rs create mode 100644 kernel/arch/riscv64/src/lib.rs create mode 100644 kernel/arch/riscv64/src/mem/mod.rs create mode 100644 kernel/arch/riscv64/src/mem/table.rs create mode 100644 kernel/arch/riscv64/src/registers.rs create mode 100644 kernel/lib/memtables/src/riscv64.rs create mode 100644 kernel/src/arch/riscv64/boot/entry.S create mode 100644 kernel/src/arch/riscv64/boot/mod.rs create mode 100644 kernel/src/arch/riscv64/mod.rs create mode 100644 lib/abi/src/arch/riscv64.rs create mode 100644 lib/qemu/src/riscv64.rs create mode 100644 lib/runtime/src/process/thread_local/riscv64.rs create mode 100644 lib/runtime/src/sys/riscv64.rs diff --git a/Cargo.lock b/Cargo.lock index bcb0de7c..eaa4cf25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -967,6 +967,7 @@ dependencies = [ "kernel-arch-hosted", "kernel-arch-i686", "kernel-arch-interface", + "kernel-arch-riscv64", "kernel-arch-x86_64", ] @@ -1018,6 +1019,20 @@ dependencies = [ "yggdrasil-abi", ] +[[package]] +name = "kernel-arch-riscv64" +version = "0.1.0" +dependencies = [ + "bitflags 2.6.0", + "device-api", + "kernel-arch-interface", + "libk-mm-interface", + "memtables", + "static_assertions", + "tock-registers 0.9.0", + "yggdrasil-abi", +] + [[package]] name = "kernel-arch-x86" version = "0.1.0" @@ -2658,6 +2673,7 @@ dependencies = [ "kernel-arch-aarch64", "kernel-arch-i686", "kernel-arch-interface", + "kernel-arch-riscv64", "kernel-arch-x86", "kernel-arch-x86_64", "libk", diff --git a/Cargo.toml b/Cargo.toml index 2bf0d7bf..79a56571 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,8 +16,9 @@ members = [ "lib/abi", "lib/libyalloc", "lib/runtime", - "lib/qemu" -, "lib/abi-serde"] + "lib/qemu", + "lib/abi-serde" +] [workspace.dependencies] chrono = { version = "0.4.38", default-features = false, features = ["alloc"] } @@ -53,6 +54,7 @@ abi-generator.path = "tool/abi-generator" # Kernel parts kernel-arch-interface.path = "kernel/arch/interface" kernel-arch-aarch64.path = "kernel/arch/aarch64" +kernel-arch-riscv64.path = "kernel/arch/riscv64" kernel-arch-x86_64.path = "kernel/arch/x86_64" kernel-arch-i686.path = "kernel/arch/i686" kernel-arch-x86.path = "kernel/arch/x86" diff --git a/etc/ld/riscv/riscv64-unknown-qemu.ld b/etc/ld/riscv/riscv64-unknown-qemu.ld new file mode 100644 index 00000000..084e019c --- /dev/null +++ b/etc/ld/riscv/riscv64-unknown-qemu.ld @@ -0,0 +1,57 @@ +ENTRY(__rv64_entry); + +KERNEL_PHYS_BASE = 0x80000000; +KERNEL_VIRT_OFFSET = 0xFFFFFFF000000000; + +SECTIONS { + . = KERNEL_PHYS_BASE; + PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET); + + .text.entry : { + *(.text.entry) + } + + . = ALIGN(16); + . = . + KERNEL_VIRT_OFFSET; + + .text : AT(. - KERNEL_VIRT_OFFSET) { + KEEP(*(.text.vectors)); + *(.text*) + } + + . = ALIGN(4K); + .rodata : AT(. - KERNEL_VIRT_OFFSET) { + *(.rodata*) + *(.eh_frame*) + + . = ALIGN(16); + PROVIDE(__init_array_start = .); + KEEP(*(.init_array*)) + PROVIDE(__init_array_end = .); + } + + . = ALIGN(4K); + .data.tables : AT (. - KERNEL_VIRT_OFFSET) { + KEEP(*(.data.tables)) + } + + . = ALIGN(4K); + .data : AT(. - KERNEL_VIRT_OFFSET) { + *(.data*) + . = ALIGN(8); + PROVIDE(__global_pointer = . + 0x800 - KERNEL_VIRT_OFFSET); + *(.got*) + } + + . = ALIGN(4K); + PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET); + .bss : AT(. - KERNEL_VIRT_OFFSET) { + *(COMMON) + *(.bss*) + } + . = ALIGN(4K); + PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET); + PROVIDE(__bss_size = __bss_end_phys - __bss_start_phys); + + PROVIDE(__kernel_end = .); +}; diff --git a/etc/riscv64-unknown-none.json b/etc/riscv64-unknown-none.json new file mode 100644 index 00000000..f8d0cec4 --- /dev/null +++ b/etc/riscv64-unknown-none.json @@ -0,0 +1,25 @@ +{ + "arch": "riscv64", + "os": "none", + "abi": "softfloat", + "cpu": "generic-rv64", + "llvm-target": "riscv64", + "data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", + "max-atomic-width": 64, + "target-pointer-width": "64", + "features": "+m,+a,+c", + + "disable-redzone": true, + "executables": true, + "panic-strategy": "abort", + "dynamic-linking": true, + "relocation-model": "pic", + "eh-frame-header": false, + + "crt-objects-fallback": "false", + "emit-debug-gdb-scripts": false, + "llvm-abiname": "lp64", + + "linker": "rust-lld", + "linker-flavor": "ld.lld" +} diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 86e08f90..f7b7712f 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -53,6 +53,9 @@ aarch64-cpu.workspace = true device-tree.workspace = true kernel-arch-aarch64.workspace = true +[target.'cfg(target_arch = "riscv64")'.dependencies] +kernel-arch-riscv64.workspace = true + [target.'cfg(target_arch = "x86_64")'.dependencies] yboot-proto.workspace = true kernel-arch-x86_64.workspace = true @@ -81,6 +84,7 @@ kernel-arch-x86_64.workspace = true kernel-arch-i686.workspace = true kernel-arch-x86.workspace = true kernel-arch-aarch64.workspace = true +kernel-arch-riscv64.workspace = true [features] default = ["fb_console"] diff --git a/kernel/arch/Cargo.toml b/kernel/arch/Cargo.toml index 9c082c31..c663fef8 100644 --- a/kernel/arch/Cargo.toml +++ b/kernel/arch/Cargo.toml @@ -3,21 +3,22 @@ name = "kernel-arch" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [target.'cfg(all(target_os = "none", target_arch = "x86_64"))'.dependencies] -kernel-arch-x86_64 = { path = "x86_64" } +kernel-arch-x86_64.path = "x86_64" [target.'cfg(all(target_os = "none", target_arch = "aarch64"))'.dependencies] -kernel-arch-aarch64 = { path = "aarch64" } +kernel-arch-aarch64.path = "aarch64" [target.'cfg(all(target_os = "none", target_arch = "x86"))'.dependencies] -kernel-arch-i686 = { path = "i686" } +kernel-arch-i686.path = "i686" + +[target.'cfg(all(target_os = "none", target_arch = "riscv64"))'.dependencies] +kernel-arch-riscv64.path = "riscv64" [target.'cfg(not(target_os = "none"))'.dependencies] -kernel-arch-hosted = { path = "hosted" } +kernel-arch-hosted.path = "hosted" [dependencies] -kernel-arch-interface = { path = "interface" } +kernel-arch-interface.path = "interface" cfg-if.workspace = true diff --git a/kernel/arch/riscv64/Cargo.toml b/kernel/arch/riscv64/Cargo.toml new file mode 100644 index 00000000..64fb8549 --- /dev/null +++ b/kernel/arch/riscv64/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "kernel-arch-riscv64" +version = "0.1.0" +edition = "2024" + +[dependencies] +yggdrasil-abi.workspace = true +kernel-arch-interface.workspace = true +libk-mm-interface.workspace = true +memtables.workspace = true +device-api = { workspace = true, features = ["derive"] } + +tock-registers.workspace = true +bitflags.workspace = true +static_assertions.workspace = true + +[lints] +workspace = true diff --git a/kernel/arch/riscv64/src/context.rs b/kernel/arch/riscv64/src/context.rs new file mode 100644 index 00000000..ad00f2ce --- /dev/null +++ b/kernel/arch/riscv64/src/context.rs @@ -0,0 +1,54 @@ +use core::marker::PhantomData; + +use kernel_arch_interface::{ + mem::{KernelTableManager, PhysicalMemoryAllocator}, + task::{TaskContext, UserContextInfo}, +}; +use libk_mm_interface::address::PhysicalAddress; +use yggdrasil_abi::error::Error; + +pub struct TaskContextImpl { + _pd: PhantomData<(K, PA)>, +} + +impl> + TaskContext for TaskContextImpl +{ + const USER_STACK_EXTRA_ALIGN: usize = 0; + const SIGNAL_STACK_EXTRA_ALIGN: usize = 0; + + fn user(context: UserContextInfo) -> Result { + let _ = context; + todo!() + } + + fn kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result { + let _ = entry; + let _ = arg; + todo!() + } + + fn set_thread_pointer(&self, tp: usize) { + let _ = tp; + todo!() + } + + fn align_stack_for_entry(sp: usize) -> usize { + let _ = sp; + todo!() + } + + unsafe fn enter(&self) -> ! { + todo!() + } + + unsafe fn switch(&self, from: &Self) { + let _ = from; + todo!() + } + + unsafe fn switch_and_drop(&self, thread: *const ()) { + let _ = thread; + todo!() + } +} diff --git a/kernel/arch/riscv64/src/lib.rs b/kernel/arch/riscv64/src/lib.rs new file mode 100644 index 00000000..51310abf --- /dev/null +++ b/kernel/arch/riscv64/src/lib.rs @@ -0,0 +1,109 @@ +#![feature(decl_macro)] +#![no_std] + +extern crate alloc; + +use alloc::vec::Vec; +use device_api::interrupt::{LocalInterruptController, MessageInterruptController}; +use kernel_arch_interface::{ + cpu::{CpuImpl, IpiQueue}, + task::Scheduler, + Architecture, +}; + +pub mod mem; +pub use mem::{KernelTableManagerImpl, ProcessAddressSpaceImpl}; +pub mod context; +pub use context::TaskContextImpl; +use registers::MSTATUS; +use tock_registers::interfaces::{ReadWriteable, Readable}; +pub mod registers; + +pub struct ArchitectureImpl; + +impl Architecture for ArchitectureImpl { + type PerCpuData = (); + type CpuFeatures = (); + type BreakpointType = u32; + + const BREAKPOINT_VALUE: Self::BreakpointType = 0; + + fn halt() -> ! { + loop {} + } + + unsafe fn set_local_cpu(cpu: *mut ()) { + let _ = cpu; + loop {} + } + + fn local_cpu() -> *mut () { + loop {} + } + + unsafe fn init_local_cpu(id: Option, data: Self::PerCpuData) { + let _ = id; + let _ = data; + loop {} + } + + unsafe fn init_ipi_queues(queues: Vec>) { + let _ = queues; + loop {} + } + + fn ipi_queue(cpu_id: u32) -> Option<&'static IpiQueue> { + let _ = cpu_id; + loop {} + } + + #[inline] + unsafe fn set_interrupt_mask(mask: bool) -> bool { + let old = Self::interrupt_mask(); + if mask { + MSTATUS.modify(MSTATUS::MIE::CLEAR); + } else { + MSTATUS.modify(MSTATUS::MIE::SET); + } + old + } + + #[inline] + fn interrupt_mask() -> bool { + MSTATUS.matches_all(MSTATUS::MIE::SET) + } + + fn wait_for_interrupt() { + loop {} + } + + fn cpu_count() -> usize { + loop {} + } + + fn cpu_index() -> u32 { + loop {} + } + + fn cpu_enabled_features(cpu: &CpuImpl) -> Option<&Self::CpuFeatures> { + let _ = cpu; + loop {} + } + + fn cpu_available_features(cpu: &CpuImpl) -> Option<&Self::CpuFeatures> { + let _ = cpu; + loop {} + } + + fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> { + loop {} + } + + fn message_interrupt_controller() -> Option<&'static dyn MessageInterruptController> { + loop {} + } + + fn idle_task() -> extern "C" fn(usize) -> ! { + loop {} + } +} diff --git a/kernel/arch/riscv64/src/mem/mod.rs b/kernel/arch/riscv64/src/mem/mod.rs new file mode 100644 index 00000000..85a57a6a --- /dev/null +++ b/kernel/arch/riscv64/src/mem/mod.rs @@ -0,0 +1,120 @@ +use core::marker::PhantomData; + +use kernel_arch_interface::{ + mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping}, + split_spinlock, +}; +use libk_mm_interface::{ + address::PhysicalAddress, + process::ProcessAddressSpaceManager, + table::{page_index, MapAttributes, TableAllocator}, +}; +use static_assertions::const_assert_eq; +use table::{L1, L2}; +use yggdrasil_abi::error::Error; + +pub use memtables::riscv64::FixedTables; + +pub mod table; + +split_spinlock! { + use crate::ArchitectureImpl; + use crate::mem::FixedTables; + use libk_mm_interface::KernelImageObject; + + #[link_section = ".data.tables"] + #[used] + static KERNEL_TABLES: KernelImageObject = + unsafe { KernelImageObject::new(FixedTables::zeroed()) }; +} + +pub const KERNEL_VIRT_OFFSET: usize = 0xFFFFFFF0_00000000; +pub const KERNEL_PHYS_BASE: usize = 0x80000000; +pub const SIGN_EXTEND_MASK: usize = 0xFFFFFFC0_00000000; + +pub const KERNEL_START_L1I: usize = page_index::(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE); +pub const KERNEL_L2I: usize = page_index::(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE); +const_assert_eq!(KERNEL_START_L1I, 450); +const_assert_eq!(KERNEL_L2I, 0); + +/// Any VAs above this one are sign-extended +pub const USER_BOUNDARY: usize = 0x40_00000000; + +#[derive(Debug)] +pub struct KernelTableManagerImpl; + +pub struct ProcessAddressSpaceImpl { + _pd: PhantomData, +} + +impl KernelTableManager for KernelTableManagerImpl { + fn virtualize(phys: u64) -> usize { + let _ = phys; + loop {} + } + + fn physicalize(virt: usize) -> u64 { + let _ = virt; + loop {} + } + + unsafe fn map_device_pages( + base: u64, + count: usize, + attrs: DeviceMemoryAttributes, + ) -> Result, Error> { + let _ = base; + let _ = count; + let _ = attrs; + loop {} + } + + unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping) { + let _ = mapping; + loop {} + } + + unsafe fn unmap_physical_address(virt: usize) { + let _ = virt; + loop {} + } +} + +impl ProcessAddressSpaceManager for ProcessAddressSpaceImpl { + const LOWER_LIMIT_PFN: usize = 0; + const UPPER_LIMIT_PFN: usize = 0; + + fn new() -> Result { + todo!() + } + + unsafe fn map_page( + &mut self, + address: usize, + physical: PhysicalAddress, + flags: MapAttributes, + ) -> Result<(), Error> { + let _ = address; + let _ = physical; + let _ = flags; + todo!() + } + + unsafe fn unmap_page(&mut self, address: usize) -> Result { + let _ = address; + todo!() + } + + fn translate(&self, address: usize) -> Result<(PhysicalAddress, MapAttributes), Error> { + let _ = address; + todo!() + } + + fn as_address_with_asid(&self) -> u64 { + todo!() + } + + unsafe fn clear(&mut self) { + todo!() + } +} diff --git a/kernel/arch/riscv64/src/mem/table.rs b/kernel/arch/riscv64/src/mem/table.rs new file mode 100644 index 00000000..cbc5c7d5 --- /dev/null +++ b/kernel/arch/riscv64/src/mem/table.rs @@ -0,0 +1,169 @@ +use core::{ + marker::PhantomData, + ops::{Index, IndexMut}, +}; + +use bitflags::bitflags; +use libk_mm_interface::{ + address::PhysicalAddress, + pointer::{PhysicalRef, PhysicalRefMut}, + table::{EntryLevel, NextPageTable, NonTerminalEntryLevel, TableAllocator}, +}; +use yggdrasil_abi::error::Error; + +use super::KernelTableManagerImpl; + +bitflags! { + pub struct PageAttributes: u64 { + const N = 1 << 63; + /// Dirty bit + const D = 1 << 7; + /// Access bit + const A = 1 << 6; + /// Global mapping bit, implies all lower levels are also global + const G = 1 << 5; + /// U-mode access permission + const U = 1 << 4; + /// Execute permission + const X = 1 << 3; + /// Write permission + const W = 1 << 2; + /// Read-permission + const R = 1 << 1; + /// Valid bit + const V = 1 << 0; + } + + // X W R Meaning + // 0 0 0 Pointer to next level of page table + // 0 0 1 Read-only page + // 0 1 0 --- + // 0 1 1 Read-write page + // 1 0 0 Execute only + // 1 0 1 Read-execute page + // 1 1 0 --- + // 1 1 1 Read-write-execute page +} + +/// L3 - entry is 4KiB +#[derive(Debug, Clone, Copy)] +pub struct L3; +/// L2 - entry is 2MiB +#[derive(Debug, Clone, Copy)] +pub struct L2; +/// L1 - entry is 1GiB +#[derive(Debug, Clone, Copy)] +pub struct L1; + +impl EntryLevel for L3 { + const SHIFT: usize = 12; +} + +impl EntryLevel for L2 { + const SHIFT: usize = 21; +} + +impl EntryLevel for L1 { + const SHIFT: usize = 30; +} + +#[repr(C, align(0x1000))] +pub struct PageTable { + entries: [PageEntry; 512], +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct PageEntry(u64, PhantomData); + +impl NonTerminalEntryLevel for L1 { + type NextLevel = L2; +} +impl NonTerminalEntryLevel for L2 { + type NextLevel = L3; +} + +impl PageTable { + pub const fn zeroed() -> Self { + Self { + entries: [PageEntry::INVALID; 512], + } + } +} + +impl PageEntry { + pub const INVALID: Self = Self(0, PhantomData); + + pub const fn is_present(&self) -> bool { + self.0 & PageAttributes::V.bits() != 0 + } + + pub fn attributes(self) -> PageAttributes { + PageAttributes::from_bits_retain(self.0) + } +} + +impl NextPageTable for PageTable { + type NextLevel = PageTable; + type TableRef = PhysicalRef<'static, PageTable, KernelTableManagerImpl>; + type TableRefMut = PhysicalRefMut<'static, PageTable, KernelTableManagerImpl>; + + fn get(&self, _index: usize) -> Option { + loop {} + } + + fn get_mut(&mut self, _index: usize) -> Option { + loop {} + } + + fn get_mut_or_alloc( + &mut self, + _index: usize, + ) -> Result { + loop {} + } +} + +impl PageEntry { + pub fn block(address: PhysicalAddress, attrs: PageAttributes) -> Self { + // TODO validate address alignment + Self( + (address.into_u64() >> 2) | (PageAttributes::R | PageAttributes::V | attrs).bits(), + PhantomData, + ) + } + + pub fn table(address: PhysicalAddress, mut attrs: PageAttributes) -> Self { + attrs.remove(PageAttributes::R | PageAttributes::W | PageAttributes::X); + Self( + (address.into_u64() >> 2) | (PageAttributes::V | attrs).bits(), + PhantomData, + ) + } +} + +impl PageEntry { + pub fn page(address: PhysicalAddress, attrs: PageAttributes) -> Self { + Self( + (address.into_u64() >> 2) | (PageAttributes::R | PageAttributes::V | attrs).bits(), + PhantomData, + ) + } + + pub fn as_page(&self) -> Option { + loop {} + } +} + +impl Index for PageTable { + type Output = PageEntry; + + fn index(&self, index: usize) -> &Self::Output { + &self.entries[index] + } +} + +impl IndexMut for PageTable { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.entries[index] + } +} diff --git a/kernel/arch/riscv64/src/registers.rs b/kernel/arch/riscv64/src/registers.rs new file mode 100644 index 00000000..d6964093 --- /dev/null +++ b/kernel/arch/riscv64/src/registers.rs @@ -0,0 +1,183 @@ +macro impl_csr_read($struct:ident, $repr:ty, $reg:ident, $register:ty) { + impl tock_registers::interfaces::Readable for $struct { + type T = $repr; + type R = $register; + + #[inline] + fn get(&self) -> $repr { + let mut value: $repr; + unsafe { + core::arch::asm!(concat!("csrr {0}, ", stringify!($reg)), out(reg) value); + } + value + } + } +} +macro impl_csr_write($struct:ident, $repr:ty, $reg:ident, $register:ty) { + impl tock_registers::interfaces::Writeable for $struct { + type T = $repr; + type R = $register; + + #[inline] + fn set(&self, value: $repr) { + unsafe { + core::arch::asm!(concat!("csrw ", stringify!($reg), ", {0}"), in(reg) value); + } + } + } +} + +pub mod misa { + use tock_registers::{interfaces::Readable, register_bitfields}; + + use super::{impl_csr_read, impl_csr_write}; + + register_bitfields!( + u64, + pub MISA [ + A OFFSET(0) NUMBITS(1) [], + C OFFSET(2) NUMBITS(1) [], + D OFFSET(3) NUMBITS(1) [], + E OFFSET(4) NUMBITS(1) [], + F OFFSET(5) NUMBITS(1) [], + H OFFSET(6) NUMBITS(1) [], + I OFFSET(7) NUMBITS(1) [], + M OFFSET(12) NUMBITS(1) [], + Q OFFSET(16) NUMBITS(1) [], + S OFFSET(17) NUMBITS(1) [], + U OFFSET(18) NUMBITS(1) [], + X OFFSET(23) NUMBITS(1) [], + ] + ); + + pub struct Reg; + + impl_csr_read!(Reg, u64, misa, MISA::Register); + impl_csr_write!(Reg, u64, misa, MISA::Register); + + impl Reg { + pub fn is_valid(&self) -> bool { + self.get() != 0 + } + } + + pub const MISA: Reg = Reg; +} + +pub mod mstatus { + use tock_registers::register_bitfields; + + use super::{impl_csr_read, impl_csr_write}; + + register_bitfields!( + u64, + pub MSTATUS [ + /// Interrupt enable for S-mode + SIE OFFSET(1) NUMBITS(1) [], + /// Interrupt enable for M-mode + MIE OFFSET(3) NUMBITS(1) [], + /// Stored SIE state on S-mode trap delegation + SPIE OFFSET(5) NUMBITS(1) [], + /// U-mode big endian + UBE OFFSET(6) NUMBITS(1) [], + /// TODO: something written here on trap to M-mode + MPIE OFFSET(7) NUMBITS(1) [], + /// TODO: something for nested traps + SPP OFFSET(8) NUMBITS(1) [], + /// Vector register dirty status + VS OFFSET(9) NUMBITS(2) [], + /// Original mode before being trapped into M-mode + MPP OFFSET(11) NUMBITS(2) [ + U = 0, + S = 1, + M = 3 + ], + /// Float register dirty status + FS OFFSET(13) NUMBITS(2) [], + /// U-mode extension dirty status + XS OFFSET(15) NUMBITS(2) [], + /// Effective privilege mode at which loads and stores execute. + /// + /// When MPRV = 0, loads and stores behave as normal + /// MPRV = 1, loads/stores are translated and protected + MPRV OFFSET(17) NUMBITS(1) [], + /// Permit supervisor user memory access + /// + /// When SUM = 0, S-mode access to pages accessible by U-mode will fault + SUM OFFSET(18) NUMBITS(1) [], + MXR OFFSET(19) NUMBITS(1) [], + /// Trap virtual memory + /// + /// When TVM = 1, attempts to read/write satp CSR, execute sfence.vma or sinval.vma + /// in S-mode will raise an illegal instruction exception + TVM OFFSET(20) NUMBITS(1) [], + /// Timeout wait + /// + /// When TW = 1, wfi executed in lower privilege level which does not complete + /// within some implementation-specific timeout, raises an illegal + /// instruction exception + TW OFFSET(21) NUMBITS(1) [], + TSR OFFSET(22) NUMBITS(1) [], + /// U-mode XLEN value + UXL OFFSET(32) NUMBITS(2) [], + /// S-mode XLEN value + SXL OFFSET(34) NUMBITS(2) [], + /// S-mode big endian + SBE OFFSET(36) NUMBITS(1) [], + /// M-mode big endian + MBE OFFSET(37) NUMBITS(1) [], + SD OFFSET(63) NUMBITS(1) [], + ] + ); + + pub struct Reg; + + impl_csr_read!(Reg, u64, mstatus, MSTATUS::Register); + impl_csr_write!(Reg, u64, mstatus, MSTATUS::Register); + + pub const MSTATUS: Reg = Reg; +} + +pub mod mepc { + use super::{impl_csr_read, impl_csr_write}; + + pub struct Reg; + + impl_csr_read!(Reg, u64, mepc, ()); + impl_csr_write!(Reg, u64, mepc, ()); + + pub const MEPC: Reg = Reg; +} + +pub mod satp { + use tock_registers::register_bitfields; + + use super::{impl_csr_read, impl_csr_write}; + + register_bitfields!( + u64, + pub SATP [ + PPN OFFSET(0) NUMBITS(44) [], + ASID OFFSET(44) NUMBITS(16) [], + MODE OFFSET(60) NUMBITS(4) [ + Bare = 0, + Sv39 = 8, + Sv48 = 9, + Sv57 = 10, + Sv64 = 11, + ], + ] + ); + + pub struct Reg; + + impl_csr_read!(Reg, u64, satp, SATP::Register); + impl_csr_write!(Reg, u64, satp, SATP::Register); + + pub const SATP: Reg = Reg; +} + +pub use mepc::MEPC; +pub use misa::MISA; +pub use mstatus::MSTATUS; +pub use satp::SATP; diff --git a/kernel/arch/src/lib.rs b/kernel/arch/src/lib.rs index 8f9e80f1..cd5d6c20 100644 --- a/kernel/arch/src/lib.rs +++ b/kernel/arch/src/lib.rs @@ -28,6 +28,8 @@ cfg_if! { extern crate kernel_arch_x86_64 as imp; } else if #[cfg(target_arch = "x86")] { extern crate kernel_arch_i686 as imp; + } else if #[cfg(target_arch = "riscv64")] { + extern crate kernel_arch_riscv64 as imp; } else { compile_error!("Unsupported architecture"); } diff --git a/kernel/build.rs b/kernel/build.rs index bfcc5896..b5fe8a4e 100644 --- a/kernel/build.rs +++ b/kernel/build.rs @@ -92,6 +92,7 @@ fn main() { "x86" => (), "x86_64" => build_x86_64(), "aarch64" => (), + "riscv64" => (), _ => panic!("Unknown target arch: {:?}", arch), } } diff --git a/kernel/driver/bus/pci/src/lib.rs b/kernel/driver/bus/pci/src/lib.rs index 25a3eb83..65e1571a 100644 --- a/kernel/driver/bus/pci/src/lib.rs +++ b/kernel/driver/bus/pci/src/lib.rs @@ -122,7 +122,10 @@ struct BusAddressAllocator { offset_32: u32, } -#[cfg_attr(any(target_arch = "x86_64", target_arch = "x86"), allow(dead_code))] +#[cfg_attr( + any(target_arch = "x86_64", target_arch = "x86", target_arch = "riscv64"), + allow(dead_code) +)] impl BusAddressAllocator { pub fn from_ranges(ranges: &[PciAddressRange]) -> Self { let mut range_32 = None; diff --git a/kernel/lib/memtables/src/lib.rs b/kernel/lib/memtables/src/lib.rs index fa3334e1..a899d9d7 100644 --- a/kernel/lib/memtables/src/lib.rs +++ b/kernel/lib/memtables/src/lib.rs @@ -14,6 +14,12 @@ pub mod x86_64; #[cfg(all(not(feature = "all"), target_arch = "x86_64"))] pub use x86_64::FixedTables; +// RISC-V 64-bit +#[cfg(any(feature = "all", target_arch = "riscv64"))] +pub mod riscv64; +#[cfg(all(not(feature = "all"), target_arch = "riscv64"))] +pub use riscv64::FixedTables; + #[cfg(feature = "all")] pub mod any; diff --git a/kernel/lib/memtables/src/riscv64.rs b/kernel/lib/memtables/src/riscv64.rs new file mode 100644 index 00000000..4920333c --- /dev/null +++ b/kernel/lib/memtables/src/riscv64.rs @@ -0,0 +1,19 @@ +use bytemuck::{Pod, Zeroable}; + +use crate::RawTable; + +pub const KERNEL_L3_COUNT: usize = 8; + +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct FixedTables { + _dummy: RawTable, +} + +impl FixedTables { + pub const fn zeroed() -> Self { + Self { + _dummy: RawTable::zeroed(), + } + } +} diff --git a/kernel/libk/src/task/binary/elf.rs b/kernel/libk/src/task/binary/elf.rs index 763ad1a9..8507039e 100644 --- a/kernel/libk/src/task/binary/elf.rs +++ b/kernel/libk/src/task/binary/elf.rs @@ -30,6 +30,8 @@ cfg_if! { const EXPECTED_ELF_MACHINE: u16 = elf::abi::EM_AARCH64; } else if #[cfg(target_arch = "x86")] { const EXPECTED_ELF_MACHINE: u16 = elf::abi::EM_386; + } else if #[cfg(target_arch = "riscv64")] { + const EXPECTED_ELF_MACHINE: u16 = elf::abi::EM_RISCV; } } diff --git a/kernel/src/arch/mod.rs b/kernel/src/arch/mod.rs index 108b5bfd..5fd4754a 100644 --- a/kernel/src/arch/mod.rs +++ b/kernel/src/arch/mod.rs @@ -7,10 +7,6 @@ use device_api::{ interrupt::{IpiDeliveryTarget, IpiMessage}, ResetDevice, }; -// use device_api::{ -// interrupt::{IpiDeliveryTarget, IpiMessage}, -// ResetDevice, -// }; use kernel_arch::{Architecture, ArchitectureImpl}; use libk_mm::table::EntryLevel; @@ -32,7 +28,17 @@ pub mod i686; #[cfg(any(target_arch = "x86", rust_analyzer))] pub use i686::{I686 as PlatformImpl, PLATFORM}; -#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "x86")))] +#[cfg(any(target_arch = "riscv64", rust_analyzer))] +pub mod riscv64; +#[cfg(any(target_arch = "riscv64", rust_analyzer))] +pub use riscv64::{Riscv64 as PlatformImpl, PLATFORM}; + +#[cfg(not(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "x86" +)))] compile_error!("Unsupported architecture"); /// Architecture-specific lowest level of page mapping diff --git a/kernel/src/arch/riscv64/boot/entry.S b/kernel/src/arch/riscv64/boot/entry.S new file mode 100644 index 00000000..0bfe3f3c --- /dev/null +++ b/kernel/src/arch/riscv64/boot/entry.S @@ -0,0 +1,59 @@ +.macro LOAD_PCREL label, register, symbol +\label: auipc \register, %pcrel_hi(\symbol) + addi \register, \register, %pcrel_lo(\label) +.endm + +.section .text.entry +.option norvc +.type __rv64_entry, @function +.global __rv64_entry +__rv64_entry: + // Jump to parking place if hard id is not zero + csrr t0, mhartid + bnez t0, .spin_loop + + // Reset translation control + csrw satp, zero + + // Zero the .bss + LOAD_PCREL .L00, t0, __bss_start_phys + LOAD_PCREL .L01, t1, __bss_end_phys + +1: bgeu t0, t1, 2f + sd zero, (t0) + addi t0, t0, 4 + j 1b +2: + + // Setup boot stack + LOAD_PCREL .L02, sp, {boot_stack_bottom} + {boot_stack_size} - {kernel_virt_offset} + + // Jump to entry + LOAD_PCREL .L03, t0, {entry_mmode_lower} - {kernel_virt_offset} + + jr t0 + +3: wfi + j 3b + +.spin_loop: + wfi + j .spin_loop + +.size __rv64_entry, . - __rv64_entry + +.section .text +.global __rv64_smode_entry +.type __rv64_smode_entry, @function +.p2align 4 +__rv64_smode_entry: + // Set up the stack again + LOAD_PCREL .L04, sp, {boot_stack_bottom} + {boot_stack_size} + // Enter kernel proper + LOAD_PCREL .L05, t0, {entry_smode_lower} + + jr t0 + +1: wfi + j 1b +.size __rv64_smode_entry, . - __rv64_smode_entry diff --git a/kernel/src/arch/riscv64/boot/mod.rs b/kernel/src/arch/riscv64/boot/mod.rs new file mode 100644 index 00000000..73330640 --- /dev/null +++ b/kernel/src/arch/riscv64/boot/mod.rs @@ -0,0 +1,125 @@ +use core::arch::global_asm; + +use kernel_arch_riscv64::{ + mem::{ + table::{PageAttributes, PageEntry, PageTable, L1}, + KERNEL_VIRT_OFFSET, + }, + registers::{MEPC, MSTATUS, SATP}, +}; +use libk_mm::{address::PhysicalAddress, table::EntryLevel}; +use tock_registers::interfaces::{ReadWriteable, Writeable}; + +const BOOT_STACK_SIZE: usize = 65536; + +#[repr(C, align(0x10))] +struct BootStack([u8; N]); + +impl BootStack { + pub const fn zeroed() -> Self { + Self([0; N]) + } +} + +#[link_section = ".bss"] +static mut BOOT_STACK: BootStack = BootStack::zeroed(); +static mut TABLE: PageTable = PageTable::zeroed(); + +unsafe fn long_jump(pc: usize, sp: usize) -> ! { + core::arch::asm!(r#" + mv sp, {sp} + jr {pc} + "#, pc = in(reg) pc, sp = in(reg) sp, options(noreturn)); +} + +unsafe extern "C" fn __rv64_bsp_smode_entry_lower() -> ! { + // TODO move this to kernel-arch-riscv64, like in other archs + for i in 0..4 { + TABLE[i] = PageEntry::block( + PhysicalAddress::from_usize(i << L1::SHIFT), + PageAttributes::W | PageAttributes::X, + ); + } + // TODO magic numbers + // Map kernel + TABLE[450] = PageEntry::block( + PhysicalAddress::from_usize(0x8000_0000), + PageAttributes::W | PageAttributes::X, + ); + + let address = (&raw const TABLE).addr(); + let address = if address >= KERNEL_VIRT_OFFSET { + address - KERNEL_VIRT_OFFSET + } else { + address + }; + + SATP.modify(SATP::PPN.val((address as u64) >> 12) + SATP::MODE::Sv39); + + let stack = (&raw const BOOT_STACK).addr() + KERNEL_VIRT_OFFSET; + let pc = __rv64_bsp_entry_upper as usize + KERNEL_VIRT_OFFSET; + let sp = stack + BOOT_STACK_SIZE; + + long_jump(pc, sp) +} + +unsafe extern "C" fn __rv64_bsp_entry_upper() -> ! { + loop {} +} + +// Drop to S-mode +unsafe extern "C" fn __rv64_bsp_mmode_entry_lower() -> ! { + extern "C" { + fn __rv64_smode_entry() -> !; + } + + MSTATUS.modify( + // Mask S-mode interrupts + MSTATUS::SIE::CLEAR + + MSTATUS::MPIE::CLEAR + // UXLEN=SXLEN=64 + + MSTATUS::UXL.val(2) + + MSTATUS::SXL.val(2) + // Little endian + + MSTATUS::UBE::CLEAR + + MSTATUS::SBE::CLEAR + // Don't trap S-mode VM insns, sret + U-mode wfi + + MSTATUS::TVM::CLEAR + + MSTATUS::TW::CLEAR + + MSTATUS::TSR::CLEAR + // Disable effective privilege modification + + MSTATUS::MPRV::CLEAR + // Enable S-mode access to U-mode pages + + MSTATUS::SUM::SET + // Make mret return to S-mode + + MSTATUS::MPP::S, + ); + + let entry = __rv64_smode_entry as usize - KERNEL_VIRT_OFFSET; + MEPC.set(entry as u64); + + // Modify pmpcfg/pmpaddr to allow lower-level execution + unsafe { + let mut pmpcfg0: u64; + core::arch::asm!("csrr {0}, pmpcfg0", out(reg) pmpcfg0); + + let pmpaddr0: u64 = 0xFFFFffffFFFFffff; + + pmpcfg0 &= !0xFF; + // A = 1, X, W, R + pmpcfg0 |= 0xF; + + core::arch::asm!("csrw pmpaddr0, {0}; csrw pmpcfg0, {1}", in(reg) pmpaddr0, in(reg) pmpcfg0); + } + + core::arch::asm!("mret", options(noreturn)); +} + +global_asm!( + include_str!("entry.S"), + entry_mmode_lower = sym __rv64_bsp_mmode_entry_lower, + entry_smode_lower = sym __rv64_bsp_smode_entry_lower, + boot_stack_bottom = sym BOOT_STACK, + kernel_virt_offset = const KERNEL_VIRT_OFFSET, + boot_stack_size = const BOOT_STACK_SIZE, +); diff --git a/kernel/src/arch/riscv64/mod.rs b/kernel/src/arch/riscv64/mod.rs new file mode 100644 index 00000000..ae2a7d9a --- /dev/null +++ b/kernel/src/arch/riscv64/mod.rs @@ -0,0 +1,48 @@ +#![allow(missing_docs)] +use abi::error::Error; +use alloc::sync::Arc; +use device_api::{ + interrupt::{IpiDeliveryTarget, IpiMessage}, + ResetDevice, +}; +use kernel_arch_riscv64::mem::KERNEL_VIRT_OFFSET; +use libk_mm::table::EntryLevel; + +use super::Platform; + +pub mod boot; + +pub struct Riscv64; + +#[derive(Debug, Clone, Copy)] +pub struct L3; +impl EntryLevel for L3 { + const SIZE: usize = 4096; + const SHIFT: usize = 12; +} + +impl Platform for Riscv64 { + const KERNEL_VIRT_OFFSET: usize = KERNEL_VIRT_OFFSET; + type L3 = L3; + + unsafe fn reset(&self) -> ! { + loop {} + } + + unsafe fn send_ipi(&self, target: IpiDeliveryTarget, msg: IpiMessage) -> Result { + let _ = target; + let _ = msg; + loop {} + } + + unsafe fn start_application_processors(&self) { + loop {} + } + + fn register_reset_device(&self, reset: Arc) -> Result<(), Error> { + let _ = reset; + loop {} + } +} + +pub static PLATFORM: Riscv64 = Riscv64; diff --git a/kernel/src/util/mod.rs b/kernel/src/util/mod.rs index 3558d3f8..75cd0a7a 100644 --- a/kernel/src/util/mod.rs +++ b/kernel/src/util/mod.rs @@ -52,6 +52,10 @@ pub const fn arch_str() -> &'static str { { "x86_64" } + #[cfg(target_arch = "riscv64")] + { + "riscv64" + } #[cfg(target_arch = "x86")] { "i686" diff --git a/kernel/tools/gentables/src/main.rs b/kernel/tools/gentables/src/main.rs index c23fd894..c76ec3a2 100644 --- a/kernel/tools/gentables/src/main.rs +++ b/kernel/tools/gentables/src/main.rs @@ -9,7 +9,7 @@ use std::{ use clap::Parser; use elf::{ - abi::{EM_386, EM_AARCH64, EM_X86_64, PT_LOAD}, + abi::{EM_386, EM_AARCH64, EM_RISCV, EM_X86_64, PT_LOAD}, endian::AnyEndian, ElfStream, }; @@ -115,6 +115,7 @@ fn find_tables(elf: &mut ElfStream) -> Result<(u64 let section_size = match elf.ehdr.e_machine { EM_AARCH64 => size_of::(), EM_X86_64 => size_of::(), + EM_RISCV => size_of::(), _ => unimplemented!(), }; let (shdrs, strtab) = elf.section_headers_with_strtab()?; @@ -237,6 +238,10 @@ fn build_tables( )? .build() .map(into_any), + EM_RISCV => { + // TODO + std::process::exit(0); + } _ => todo!(), }?; diff --git a/lib/abi/src/arch/mod.rs b/lib/abi/src/arch/mod.rs index 3d1dcde2..bd82f953 100644 --- a/lib/abi/src/arch/mod.rs +++ b/lib/abi/src/arch/mod.rs @@ -17,6 +17,11 @@ pub(crate) mod i686; #[cfg(target_arch = "x86")] use i686 as arch_impl; +#[cfg(target_arch = "riscv64")] +pub(crate) mod riscv64; +#[cfg(target_arch = "riscv64")] +use riscv64 as arch_impl; + pub use arch_impl::SavedFrame; pub trait FrameOps { diff --git a/lib/abi/src/arch/riscv64.rs b/lib/abi/src/arch/riscv64.rs new file mode 100644 index 00000000..7beb0b41 --- /dev/null +++ b/lib/abi/src/arch/riscv64.rs @@ -0,0 +1,19 @@ +#![allow(missing_docs)] + +use super::FrameOps; + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, Default)] +#[repr(C)] +pub struct SavedFrame {} + +impl FrameOps for SavedFrame { + fn set_user_ip(&mut self, value: usize) { + let _ = value; + todo!() + } + + fn user_ip(&self) -> usize { + todo!() + } +} diff --git a/lib/qemu/src/lib.rs b/lib/qemu/src/lib.rs index a25dd601..995f8f75 100644 --- a/lib/qemu/src/lib.rs +++ b/lib/qemu/src/lib.rs @@ -10,6 +10,7 @@ use device::{QemuDevice, QemuSerialTarget}; pub mod aarch64; pub mod i386; +pub mod riscv64; pub mod x86_64; pub mod device; @@ -82,6 +83,12 @@ impl Qemu { } } +impl Qemu { + pub fn new_riscv64() -> Self { + Qemu::new(riscv64::QemuRiscv64) + } +} + impl Qemu { pub fn new(arch: A) -> Self { Self { diff --git a/lib/qemu/src/riscv64.rs b/lib/qemu/src/riscv64.rs new file mode 100644 index 00000000..49320e14 --- /dev/null +++ b/lib/qemu/src/riscv64.rs @@ -0,0 +1,60 @@ +use std::{path::PathBuf, process::Command}; + +use crate::{Architecture, IntoArgs}; + +#[derive(Debug)] +pub enum Cpu { + Rv64, +} + +#[derive(Debug)] +pub enum Machine { + Virt, +} + +#[derive(Debug)] +pub struct QemuRiscv64; + +#[derive(Debug)] +pub struct Image { + pub kernel: PathBuf, +} + +impl IntoArgs for Machine { + fn add_args(&self, command: &mut Command) { + command.arg("-M"); + match self { + Self::Virt => command.arg("virt"), + }; + } +} + +impl IntoArgs for Cpu { + fn add_args(&self, command: &mut Command) { + command.arg("-cpu"); + match self { + Self::Rv64 => command.arg("rv64,zicsr=true,zifencei=true"), + }; + } +} + +impl IntoArgs for Image { + fn add_args(&self, command: &mut Command) { + command.arg("-kernel"); + command.arg(&self.kernel); + command.args(["-bios", "none"]); + } +} + +impl Architecture for QemuRiscv64 { + type CpuType = Cpu; + type ImageType = Image; + type MachineType = Machine; + const DEFAULT_COMMAND: &'static str = "qemu-system-riscv64"; +} + +impl IntoArgs for QemuRiscv64 { + fn add_args(&self, command: &mut Command) { + let _ = command; + } +} diff --git a/lib/runtime/src/process/thread_local/mod.rs b/lib/runtime/src/process/thread_local/mod.rs index f0d4d847..56808929 100644 --- a/lib/runtime/src/process/thread_local/mod.rs +++ b/lib/runtime/src/process/thread_local/mod.rs @@ -26,9 +26,14 @@ mod i686; #[cfg(any(target_arch = "x86", rust_analyzer))] use i686 as imp; -#[cfg(any(target_arch = "aarch64", rust_analyzer))] +#[cfg(any(target_arch = "riscv64", rust_analyzer))] +mod riscv64; +#[cfg(any(target_arch = "riscv64", rust_analyzer))] +use riscv64 as imp; + +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))] mod variant1; -#[cfg(any(target_arch = "aarch64", rust_analyzer))] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))] use variant1 as layout; #[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))] diff --git a/lib/runtime/src/process/thread_local/riscv64.rs b/lib/runtime/src/process/thread_local/riscv64.rs new file mode 100644 index 00000000..4ebaefe4 --- /dev/null +++ b/lib/runtime/src/process/thread_local/riscv64.rs @@ -0,0 +1,12 @@ +#![allow(missing_docs)] + +use abi::error::Error; + +pub fn get_thread_pointer() -> usize { + todo!() +} + +pub unsafe fn set_thread_pointer(value: usize) -> Result<(), Error> { + let _ = value; + todo!() +} diff --git a/lib/runtime/src/sys/mod.rs b/lib/runtime/src/sys/mod.rs index d5ff883a..e1756710 100644 --- a/lib/runtime/src/sys/mod.rs +++ b/lib/runtime/src/sys/mod.rs @@ -9,8 +9,11 @@ mod x86_64; #[cfg(any(target_arch = "x86", rust_analyzer))] #[macro_use] mod i686; +#[cfg(any(target_arch = "riscv64", rust_analyzer))] +#[macro_use] +mod riscv64; -#[allow(missing_docs)] +#[allow(missing_docs, unreachable_code)] mod generated { // Import all the necessary types from generated ABI use abi::{ diff --git a/lib/runtime/src/sys/riscv64.rs b/lib/runtime/src/sys/riscv64.rs new file mode 100644 index 00000000..ad2c6618 --- /dev/null +++ b/lib/runtime/src/sys/riscv64.rs @@ -0,0 +1,53 @@ +/// 64-bit RISC-V implementations of the syscall macro +#[macro_export] +macro_rules! syscall { + ($num:expr $(,)?) => {{ + let _ = $num; + todo!() + }}; + ($num:expr, $a0:expr $(,)?) => {{ + let _ = $num; + let _ = $a0; + todo!() + }}; + ($num:expr, $a0:expr, $a1:expr $(,)?) => {{ + let _ = $num; + let _ = $a0; + let _ = $a1; + todo!() + }}; + ($num:expr, $a0:expr, $a1:expr, $a2:expr $(,)?) => {{ + let _ = $num; + let _ = $a0; + let _ = $a1; + let _ = $a2; + todo!() + }}; + ($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr $(,)?) => {{ + let _ = $num; + let _ = $a0; + let _ = $a1; + let _ = $a2; + let _ = $a3; + todo!() + }}; + ($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr $(,)?) => {{ + let _ = $num; + let _ = $a0; + let _ = $a1; + let _ = $a2; + let _ = $a3; + let _ = $a4; + todo!() + }}; + ($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr $(,)?) => {{ + let _ = $num; + let _ = $a0; + let _ = $a1; + let _ = $a2; + let _ = $a3; + let _ = $a4; + let _ = $a5; + todo!() + }}; +} diff --git a/xtask/src/build/mod.rs b/xtask/src/build/mod.rs index fcf02c55..72451330 100644 --- a/xtask/src/build/mod.rs +++ b/xtask/src/build/mod.rs @@ -42,6 +42,7 @@ pub struct KernelProcessed(pub KernelBuilt); pub struct InitrdGenerated(pub PathBuf); pub struct ImageBuilt(pub PathBuf); pub enum AllBuilt { + Riscv64(KernelProcessed), X86_64(ImageBuilt), AArch64(KernelProcessed, InitrdGenerated), I686(ImageBuilt), @@ -98,12 +99,16 @@ pub fn build_all(env: &BuildEnv) -> Result { // for module in modules { // install_extra.push((module.clone(), module.file_name().unwrap().into())); // } + if env.arch == Arch::riscv64 { + return Ok(AllBuilt::Riscv64(kernel)); + } // Userspace stuff let initrd = userspace::build_initrd(env, install_extra, check)?; // Build target-specific image let image = match env.arch { + Arch::riscv64 => AllBuilt::Riscv64(kernel), Arch::aarch64 => AllBuilt::AArch64(kernel, initrd), Arch::x86_64 => AllBuilt::X86_64(x86_64::build_image(env, kernel, initrd)?), Arch::i686 => AllBuilt::I686(i686::build_image(env, kernel, initrd)?), diff --git a/xtask/src/check.rs b/xtask/src/check.rs index 196668e5..c9991746 100644 --- a/xtask/src/check.rs +++ b/xtask/src/check.rs @@ -56,11 +56,16 @@ fn check_commands_aarch64() -> Result { ]) } +fn check_commands_riscv64() -> Result { + check_command_list([("ld64.lld", "Install LLVM")]) +} + pub fn check_build_env(arch: Arch) -> Result { let user_toolchain = check_user_toolchain()?; let commands = match arch { Arch::x86_64 => check_commands_x86_64()?, Arch::aarch64 => check_commands_aarch64()?, + Arch::riscv64 => check_commands_riscv64()?, Arch::i686 => check_commands_i686()?, }; Ok(AllOk(commands, user_toolchain)) diff --git a/xtask/src/env.rs b/xtask/src/env.rs index 6b404689..d3b5d1fe 100644 --- a/xtask/src/env.rs +++ b/xtask/src/env.rs @@ -62,6 +62,7 @@ pub enum Profile { pub enum Arch { #[default] aarch64, + riscv64, x86_64, i686, } @@ -127,6 +128,7 @@ impl BuildEnv { ) -> Self { let kernel_triple = match (arch, board) { (Arch::aarch64, Board::virt | Board::default) => "aarch64-unknown-qemu", + (Arch::riscv64, Board::virt | Board::default) => "riscv64-unknown-qemu", (Arch::aarch64, Board::raspi4b) => "aarch64-unknown-raspi4b", (Arch::x86_64, Board::default) => "x86_64-unknown-none", (Arch::i686, Board::default) => "i686-unknown-none", @@ -137,6 +139,7 @@ impl BuildEnv { }; let kernel_linker_script = match arch { Arch::aarch64 => format!("arm/{kernel_triple}.ld"), + Arch::riscv64 => format!("riscv/{kernel_triple}.ld"), Arch::i686 | Arch::x86_64 => format!("x86/{kernel_triple}.ld"), }; let kernel_output_dir = @@ -190,6 +193,7 @@ impl BuildEnv { impl XTaskConfig { pub fn components(&self, env: &BuildEnv) -> &BuildComponents { match env.arch { + Arch::riscv64 => todo!(), Arch::aarch64 => &self.target.aarch64.components, Arch::x86_64 => &self.target.x86_64.components, Arch::i686 => &self.target.i686.components, @@ -217,6 +221,7 @@ impl Arch { pub fn user_triple(&self) -> &str { match self { + Self::riscv64 => "riscv64-unknown-yggdrasil", Self::aarch64 => "aarch64-unknown-yggdrasil", Self::x86_64 => "x86_64-unknown-yggdrasil", Self::i686 => "i686-unknown-yggdrasil", @@ -225,6 +230,7 @@ impl Arch { pub fn name(&self) -> &str { match self { + Self::riscv64 => "riscv64", Self::aarch64 => "aarch64", Self::x86_64 => "x86_64", Self::i686 => "i686", diff --git a/xtask/src/qemu.rs b/xtask/src/qemu.rs index f3e6186c..2e739cf3 100644 --- a/xtask/src/qemu.rs +++ b/xtask/src/qemu.rs @@ -7,7 +7,7 @@ use std::{ use qemu::{ aarch64, device::{QemuDevice, QemuDrive, QemuNic, QemuSerialTarget}, - i386, x86_64, Qemu, + i386, riscv64, x86_64, Qemu, }; use crate::{ @@ -254,6 +254,27 @@ fn run_i686( Ok(qemu.into_command()) } +fn run_riscv64( + config: &QemuConfig, + qemu_bin: Option, + devices: Vec, + kernel: PathBuf, +) -> Result { + let _ = config; + let _ = devices; + + let mut qemu = Qemu::new_riscv64(); + if let Some(qemu_bin) = qemu_bin { + qemu.override_qemu(qemu_bin); + } + qemu.with_serial(QemuSerialTarget::MonStdio) + .with_machine(riscv64::Machine::Virt) + .with_cpu(riscv64::Cpu::Rv64) + .with_boot_image(riscv64::Image { kernel }); + + Ok(qemu.into_command()) +} + fn load_qemu_config>(path: P) -> Result { let path = path.as_ref(); @@ -326,6 +347,9 @@ pub fn run( add_devices_from_config(&mut devices, disk.as_ref(), &config)?; let mut command = match built { + AllBuilt::Riscv64(KernelProcessed(KernelBuilt(kernel))) => { + run_riscv64(&config, qemu, devices, kernel)? + } AllBuilt::AArch64(KernelProcessed(KernelBuilt(kernel)), InitrdGenerated(initrd)) => { make_kernel_bin(kernel, &kernel_bin)?; run_aarch64(&config, &env, qemu, devices, kernel_bin, initrd)?