diff --git a/.gitmodules b/.gitmodules index 43b05611a4..0a0f6ac822 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,3 @@ -[submodule "syscall"] - path = syscall - url = https://gitlab.redox-os.org/redox-os/syscall.git - branch = master [submodule "slab_allocator"] path = slab_allocator url = https://gitlab.redox-os.org/redox-os/slab_allocator diff --git a/Cargo.lock b/Cargo.lock index 0130238673..32ff40ab75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,15 +16,15 @@ dependencies = [ [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bit_field" @@ -46,9 +46,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "byteorder" @@ -94,21 +94,27 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", ] [[package]] -name = "indexmap" -version = "2.5.0" +name = "hashbrown" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.0", ] [[package]] @@ -117,12 +123,12 @@ version = "0.4.1" dependencies = [ "arrayvec", "bitfield", - "bitflags 2.4.2", + "bitflags 2.6.0", "byteorder", "cc", "fdt", "goblin", - "hashbrown", + "hashbrown 0.14.5", "indexmap", "linked_list_allocator 0.9.1", "log", @@ -160,9 +166,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -170,21 +176,21 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "plain" @@ -194,18 +200,18 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -228,8 +234,9 @@ checksum = "64072665120942deff5fd5425d6c1811b854f4939e7f1c01ce755f64432bbea7" [[package]] name = "redox_syscall" version = "0.5.7" +source = "git+https://gitlab.redox-os.org/redox-os/syscall.git?branch=master#f3fc45a1b74debc4a188276640fdfb8be47ccc25" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", ] [[package]] @@ -247,9 +254,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "scopeguard" @@ -265,18 +272,18 @@ checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -356,9 +363,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -421,15 +428,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "winnow" @@ -453,18 +460,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 47f89ae271..ab110d02ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ hashbrown = { version = "0.14.3", default-features = false, features = ["ahash", linked_list_allocator = "0.9.0" log = "0.4" redox-path = "0.2.0" -redox_syscall = { path = "syscall", default-features = false } +redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git", branch = "master", default-features = false } slab_allocator = { path = "slab_allocator", optional = true } spin = "0.9.8" spinning_top = { version = "0.3", features = ["arc_lock"] } @@ -35,7 +35,7 @@ features = ["elf32", "elf64"] version = "0.1.16" default-features = false -[target.'cfg(target_arch = "aarch64")'.dependencies] +[target.'cfg(any(target_arch = "aarch64", target_arch = "riscv64"))'.dependencies] byteorder = { version = "1", default-features = false } fdt = { git = "https://github.com/repnop/fdt.git", rev = "2fb1409edd1877c714a0aa36b6a7c5351004be54" } diff --git a/build.rs b/build.rs index 14005a8662..6efcc57184 100644 --- a/build.rs +++ b/build.rs @@ -88,6 +88,9 @@ fn main() { panic!("nasm failed with exit status {}", status); } } + "riscv64" => { + println!("cargo:rustc-cfg=dtb"); + } _ => (), } diff --git a/linkers/riscv64.ld b/linkers/riscv64.ld new file mode 100644 index 0000000000..634b5c2cab --- /dev/null +++ b/linkers/riscv64.ld @@ -0,0 +1,68 @@ +ENTRY(kstart) +OUTPUT_FORMAT("elf64-littleriscv", "elf64-littleriscv", "elf64-littleriscv" ) + +KERNEL_OFFSET = 0xFFFFFF0000000000; + +SECTIONS { + . = KERNEL_OFFSET; + + . += SIZEOF_HEADERS; + + /* Force the zero page to be part of a segment by creating a + * dummy section in the zero page. + * Linker will map the segment with the lowest vaddr value at + * 0xFFFFFF0000000000 even if the segment has a higher vaddr. + * As such without the zero page being part of a segment, the + * kernel would be loaded at an offset from the expected + * location. As the redox kernel is not currently relocatable, + * this would result in a crash. A similar issue likely exists + * with multiboot/multiboot2 and the paddr of the segment. + */ + .dummy ALIGN(8) : AT(ADDR(.dummy) - KERNEL_OFFSET) {} + + . = ALIGN(4096); + + .text : AT(ADDR(.text) - KERNEL_OFFSET) { + __text_start = .; + *(.early_init.text*) + . = ALIGN(4096); + *(.text*) + __usercopy_start = .; + *(.usercopy-fns) + __usercopy_end = .; + . = ALIGN(4096); + __text_end = .; + } + + .rodata : AT(ADDR(.rodata) - KERNEL_OFFSET) { + __rodata_start = .; + *(.rodata*) + . = ALIGN(4096); + __rodata_end = .; + } + + .data : AT(ADDR(.data) - KERNEL_OFFSET) { + __data_start = .; + *(.data*) + *(.sdata*) + . = ALIGN(4096); + __data_end = .; + *(.got*) + . = ALIGN(4096); + __bss_start = .; + *(.bss*) + *(.sbss*) + . = ALIGN(4096); + __bss_end = .; + } + + __end = .; + + /DISCARD/ : { + *(.comment*) + *(.eh_frame*) + *(.gcc_except_table*) + *(.note*) + *(.rel.eh_frame*) + } +} diff --git a/src/arch/aarch64/device/serial.rs b/src/arch/aarch64/device/serial.rs index 0543fa54a6..db51de9d30 100644 --- a/src/arch/aarch64/device/serial.rs +++ b/src/arch/aarch64/device/serial.rs @@ -31,7 +31,7 @@ pub unsafe fn init_early(dtb: &Fdt) { return; } - if let Some((phys, _size, skip_init, cts)) = diag_uart_range(dtb) { + if let Some((phys, _size, skip_init, cts, _)) = diag_uart_range(dtb) { let virt = crate::PHYS_OFFSET + phys; { let mut serial_port = SerialPort::new(virt, skip_init, cts); diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 018fd3ba13..2aae6399b1 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -19,3 +19,9 @@ pub use self::x86_64::*; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] #[macro_use] mod x86_shared; + +#[cfg(target_arch = "riscv64")] +#[macro_use] +pub mod riscv64; +#[cfg(target_arch = "riscv64")] +pub use self::riscv64::*; diff --git a/src/arch/riscv64/consts.rs b/src/arch/riscv64/consts.rs new file mode 100644 index 0000000000..95b3d9a4de --- /dev/null +++ b/src/arch/riscv64/consts.rs @@ -0,0 +1,42 @@ +#![allow(unused)] + +use super::CurrentRmmArch; +use rmm::Arch; + +const PML4_SHIFT: usize = (CurrentRmmArch::PAGE_LEVELS - 1) * CurrentRmmArch::PAGE_ENTRY_SHIFT + + CurrentRmmArch::PAGE_SHIFT; +/// The size of a single PML4 +pub const PML4_SIZE: usize = 1_usize << PML4_SHIFT; +pub const PML4_MASK: usize = CurrentRmmArch::PAGE_ENTRY_MASK << PML4_SHIFT; + +/// Offset of recursive paging (deprecated, but still reserved) +pub const RECURSIVE_PAGE_OFFSET: usize = (-(PML4_SIZE as isize)) as usize; +pub const RECURSIVE_PAGE_PTE3: usize = (RECURSIVE_PAGE_OFFSET & PML4_MASK) / PML4_SIZE; + +/// Offset of kernel +pub const KERNEL_OFFSET: usize = RECURSIVE_PAGE_OFFSET - PML4_SIZE; +pub const KERNEL_PTE3: usize = (KERNEL_OFFSET & PML4_MASK) / PML4_SIZE; + +/// Offset to kernel heap +pub const KERNEL_HEAP_OFFSET: usize = KERNEL_OFFSET - PML4_SIZE; +pub const KERNEL_HEAP_PTE3: usize = (KERNEL_HEAP_OFFSET & PML4_MASK) / PML4_SIZE; +/// Size of kernel heap +pub const KERNEL_HEAP_SIZE: usize = 1 * 1024 * 1024; // 1 MB + +/// Offset of temporary mapping for misc kernel bring-up actions +pub const KERNEL_TMP_MISC_OFFSET: usize = KERNEL_HEAP_OFFSET - PML4_SIZE; + +/// Offset to kernel percpu variables +pub const KERNEL_PERCPU_OFFSET: usize = KERNEL_TMP_MISC_OFFSET - PML4_SIZE; +pub const KERNEL_PERCPU_PML4: usize = (KERNEL_PERCPU_OFFSET & PML4_MASK) / PML4_SIZE; +/// Size of kernel percpu variables +pub const KERNEL_PERCPU_SHIFT: u8 = 16; // 2^16 = 64 KiB +pub const KERNEL_PERCPU_SIZE: usize = 1_usize << KERNEL_PERCPU_SHIFT; + +/// Offset of physmap +// This needs to match RMM's PHYS_OFFSET +pub const PHYS_OFFSET: usize = (-1_isize << (CurrentRmmArch::PAGE_ADDRESS_SHIFT - 1)) as usize; +pub const PHYS_PML4: usize = (PHYS_OFFSET & PML4_MASK) / PML4_SIZE; + +/// End offset of the user image, i.e. kernel start +pub const USER_END_OFFSET: usize = 1_usize << (CurrentRmmArch::PAGE_ADDRESS_SHIFT - 1); diff --git a/src/arch/riscv64/debug.rs b/src/arch/riscv64/debug.rs new file mode 100644 index 0000000000..628bea6dcf --- /dev/null +++ b/src/arch/riscv64/debug.rs @@ -0,0 +1,65 @@ +use crate::{ + arch::riscv64::sbi::SBI, + log::{Log, LOG}, +}; +use core::fmt; +use spin::MutexGuard; + +#[cfg(feature = "serial_debug")] +use super::device::serial::{SerialPort, COM1}; + +#[cfg(feature = "graphical_debug")] +use crate::devices::graphical_debug::{DebugDisplay, DEBUG_DISPLAY}; + +pub struct Writer<'a> { + log: MutexGuard<'a, Option>, + #[cfg(feature = "serial_debug")] + serial: MutexGuard<'a, Option>, + #[cfg(feature = "graphical_debug")] + display: MutexGuard<'a, Option>, +} + +impl<'a> Writer<'a> { + pub fn new() -> Writer<'a> { + Writer { + log: LOG.lock(), + #[cfg(feature = "graphical_debug")] + display: DEBUG_DISPLAY.lock(), + #[cfg(feature = "serial_debug")] + serial: COM1.lock(), + } + } + + pub fn write(&mut self, buf: &[u8], preserve: bool) { + if preserve { + if let Some(ref mut log) = *self.log { + log.write(buf); + } + } + + #[cfg(feature = "graphical_debug")] + { + if let Some(ref mut display) = *self.display { + let _ = display.write(buf); + } + } + + #[cfg(feature = "serial_debug")] + { + if let Some(ref mut serial) = *self.serial { + serial.write(buf); + } + } + + { + let _ = SBI.debug_console_write(buf); + } + } +} + +impl<'a> fmt::Write for Writer<'a> { + fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { + self.write(s.as_bytes(), true); + Ok(()) + } +} diff --git a/src/arch/riscv64/device/cpu/mod.rs b/src/arch/riscv64/device/cpu/mod.rs new file mode 100644 index 0000000000..23103f1b88 --- /dev/null +++ b/src/arch/riscv64/device/cpu/mod.rs @@ -0,0 +1,5 @@ +use core::fmt::{Result, Write}; + +pub fn cpu_info(_w: &mut W) -> Result { + unimplemented!() +} diff --git a/src/arch/riscv64/device/mod.rs b/src/arch/riscv64/device/mod.rs new file mode 100644 index 0000000000..e86c4c00e5 --- /dev/null +++ b/src/arch/riscv64/device/mod.rs @@ -0,0 +1,90 @@ +use crate::{arch::time, dtb::DTB_BINARY}; +use fdt::{ + node::{FdtNode, NodeProperty}, + Fdt, +}; + +pub mod cpu; +pub mod serial; + +fn string_property(name: &str) -> bool { + name == "compatible" + || name == "model" + || name == "device_type" + || name == "status" + || name == "riscv,isa-base" + || name == "riscv,isa" + || name == "mmu-type" + || name == "stdout-path" +} + +fn print_property(prop: &NodeProperty, n_spaces: usize) { + (0..n_spaces).for_each(|_| print!(" ")); + print!("{} =", prop.name); + if string_property(prop.name) + && let Some(str) = prop.as_str() + { + println!(" \"{}\"", str); + } else if let Some(value) = prop.as_usize() { + println!(" 0x{:08x}", value); + } else { + for v in prop.value { + print!(" {:02x}", v); + } + println!(); + } +} +fn print_node(node: &FdtNode<'_, '_>, n_spaces: usize) { + (0..n_spaces).for_each(|_| print!(" ")); + println!("{}/", node.name); + for prop in node.properties() { + print_property(&prop, n_spaces + 4); + } + + for child in node.children() { + print_node(&child, n_spaces + 4); + } +} + +pub(crate) fn dump_fdt(fdt: &Fdt) { + if let Some(root) = fdt.find_node("/") { + print_node(&root, 0); + } +} + +unsafe fn init_intc(cpu: &FdtNode) { + let intc_node = cpu + .children() + .find(|x| x.name == "interrupt-controller") + .unwrap(); + assert_eq!(intc_node.compatible().unwrap().first(), "riscv,cpu-intc"); +} + +pub unsafe fn init() { + let data = DTB_BINARY.get().unwrap(); + let fdt = Fdt::new(data).unwrap(); + + let cpu = fdt.find_node(format!("/cpus/cpu@{}", 0).as_str()).unwrap(); + init_intc(&cpu); + init_time(&fdt); +} + +fn init_time(fdt: &Fdt) { + let cpus = fdt.find_node("/cpus").unwrap(); + let clock_freq = cpus + .property("timebase-frequency") + .unwrap() + .as_usize() + .unwrap(); + time::init(clock_freq); +} + +pub unsafe fn init_noncore() { + let data = DTB_BINARY.get().unwrap(); + let fdt = Fdt::new(data).unwrap(); + + serial::init(&fdt); +} + +#[derive(Default)] +pub struct ArchPercpuMisc; diff --git a/src/arch/riscv64/device/serial.rs b/src/arch/riscv64/device/serial.rs new file mode 100644 index 0000000000..041b0f2e22 --- /dev/null +++ b/src/arch/riscv64/device/serial.rs @@ -0,0 +1,78 @@ +use alloc::{boxed::Box, vec::Vec}; +use byteorder::{ByteOrder, BE}; +use fdt::Fdt; +use log::info; +use spin::Mutex; +use syscall::Mmio; + +use crate::{ + devices::uart_16550, + dtb::{diag_uart_range, DTB_BINARY}, + scheme::{ + debug::{debug_input, debug_notify}, + irq::irq_trigger, + }, +}; + +pub struct SerialPort { + inner: &'static mut uart_16550::SerialPort>, +} +impl SerialPort { + pub fn write(&mut self, buf: &[u8]) { + self.inner.write(buf) + } + pub fn receive(&mut self) { + while let Some(c) = self.inner.receive() { + debug_input(c); + } + debug_notify(); + } +} + +pub static COM1: Mutex> = Mutex::new(None); + +pub struct Com1Irq {} + +pub unsafe fn init_early(dtb: &Fdt) { + if COM1.lock().is_some() { + // Hardcoded UART + return; + } + + if let Some((phys, _, _, _, compatible)) = diag_uart_range(dtb) { + let virt = crate::PHYS_OFFSET + phys; + let port = if compatible == "ns16550a" { + let serial_port = uart_16550::SerialPort::>::new(virt); + serial_port.init(); + Some(SerialPort { inner: serial_port }) + } else { + None + }; + match port { + Some(port) => { + *COM1.lock() = Some(port); + } + None => {} + } + } +} + +pub unsafe fn init(fdt: &Fdt) -> Option<()> { + let data = DTB_BINARY.get().unwrap(); + let fdt = Fdt::new(data).unwrap(); + if let Some(node) = fdt.find_compatible(&["ns16550a"]) { + let interrupts = node.property("interrupts").unwrap(); + let mut intr_data: [u32; 3] = [0, 0, 0]; + for (idx, chunk) in interrupts.value.chunks(4).enumerate() { + if idx >= intr_data.len() { + break; + } + let val = BE::read_u32(chunk); + intr_data[idx] = val; + } + } + if let Some(ref mut serial_port) = *COM1.lock() { + // serial_port.enable_irq(); // FIXME receive int is enabled by default in 16550. Disable by default? + } + Some(()) +} diff --git a/src/arch/riscv64/interrupt/exception.rs b/src/arch/riscv64/interrupt/exception.rs new file mode 100644 index 0000000000..85ce556223 --- /dev/null +++ b/src/arch/riscv64/interrupt/exception.rs @@ -0,0 +1,201 @@ +use core::{ + arch::global_asm, + sync::atomic::{AtomicUsize, Ordering}, +}; +use log::{error, info}; +use rmm::VirtualAddress; + +use crate::{memory::GenericPfFlags, panic::stack_trace, ptrace, syscall, syscall::flag::*}; + +const BREAKPOINT: usize = 3; +const USERMODE_ECALL: usize = 8; +const INSTRUCTION_PAGE_FAULT: usize = 12; +const LOAD_PAGE_FAULT: usize = 13; +const STORE_PAGE_FAULT: usize = 15; + +use super::InterruptStack; + +global_asm!(concat!( + ".global exception_handler\n", + ".p2align 3\n", +"exception_handler:\n", + "csrrw tp, sscratch, tp\n", + "beq tp, x0, 3f\n", // exception before percpu data is available; got to be S mode + + "sd t0, 0(tp)\n", + "csrr t0, sstatus\n", + "andi t0, t0, 1<<8\n",// SPP bit + "bne t0, x0, 2f\n", + + // trap/interrupt from U mode, switch stacks + "ld t0, 0(tp)\n", + "sd sp, 0(tp)\n", + "ld sp, 8(tp)\n", + + push_registers!(), + "ld t0, 0(tp)\n", + "sd t0, (1 * 8)(sp)\n", // save original SP + "csrrw t0, sscratch, tp\n", + "sd t0, (3 * 8)(sp)\n", // save original TP, and restore sscratch to handle double faults + + "mv a0, sp\n", + "jal {0}\n", + + // save S mode stack to percpu + "addi t0, sp, 32 * 8\n", + "sd t0, 8(tp)\n", + "li t0, 1 << 8\n", // return to U mode (sstatus might've been modified by nested trap or context switch) + "csrc sstatus, t0\n", + "j 4f\n", + +"2: ld t0, 0(tp)\n", // S-mode +"3:\n", // S mode early + + "addi sp, sp, -2 * 8\n", // fake stack frame for the stack tracer + + push_registers!(), + + "addi t1, sp, 34 * 8\n", + "sd t1, (1 * 8)(sp)\n", // save original SP + "csrrw t1, sscratch, tp\n", + "sd t1, (3 * 8)(sp)\n", // save original TP, and restore sscratch to handle double faults + + "sd t0, (33 * 8)(sp)\n", // fill the stack frame. t0 holds original pc after push_registers + "sd fp, (32 * 8)(sp)\n", + "addi fp, sp, 34 * 8\n", + + "mv a0, sp\n", + "jal {0}\n", + // return to S mode with interrupts disabled + // (sstatus might've been modified by nested trap or context switch) + "li t0, 1 << 8\n", + "csrs sstatus, t0\n", + "li t0, 1 << 5\n", + "csrc sstatus, t0\n", + +"4:", + pop_registers!(), + "sret", +), + sym exception_handler_inner +); + +unsafe fn exception_handler_inner(regs: &mut InterruptStack) { + let scause: usize; + let sstatus: usize; + core::arch::asm!( + "csrr t0, scause", + "csrr t1, sstatus", + lateout("t0") scause, + lateout("t1") sstatus, + options(nostack) + ); + + //log::info!("Exception handler incoming: sepc={:x} scause={:x} sstatus={:x}", regs.iret.sepc, scause, sstatus); + + let user_mode = sstatus & (1 << 8) == 0; + + if (scause as isize) < 0 { + handle_interrupt(scause & 0xF); + } else if page_fault(scause, regs, user_mode) { + } else if user_mode { + handle_user_exception(scause, regs); + } else { + handle_system_exception(scause, regs); + } + //log::info!("Exception handler outgoing"); +} + +unsafe fn handle_system_exception(scause: usize, regs: &InterruptStack) { + let stval: usize; + let tp: usize; + core::arch::asm!( + "csrr t0, stval", + "mv t1, tp", + lateout("t0") stval, + lateout("t1") tp, + options(nostack) + ); + + error!( + "S-mode exception! scause={:#016x}, stval={:#016x}", + scause, stval + ); + regs.dump(); + + if tp == 0 { + // Early failure - before misc::init and potentially before RMM init + // Do not attempt to trace stack because it would probably trap again + loop {} + } + + stack_trace(); + loop {} +} + +unsafe fn handle_interrupt(interrupt: usize) {} + +unsafe fn handle_user_exception(scause: usize, regs: &mut InterruptStack) { + if scause == USERMODE_ECALL { + let r = &mut regs.registers; + regs.iret.sepc += 4; // skip ecall + let ret = syscall::syscall(r.x17, r.x10, r.x11, r.x12, r.x13, r.x14); + r.x10 = ret; + return; + } + + if scause == BREAKPOINT { + if ptrace::breakpoint_callback(PTRACE_STOP_BREAKPOINT, None).is_some() { + return; + } + } + + let stval: usize; + core::arch::asm!( + "csrr t0, stval", + lateout("t0") stval, + options(nostack) + ); + + info!( + "U-mode exception! scause={:#016x}, stval={:#016x}", + scause, stval + ); + regs.dump(); + + let signal = match scause { + 0 | 4 | 6 | 18 | 19 => SIGBUS, // misaligned / machine check + 2 | 8 | 9 => SIGILL, // Illegal instruction / breakpoint / ecall + BREAKPOINT => SIGTRAP, + _ => SIGSEGV, + }; + crate::ksignal(signal); +} + +unsafe fn page_fault(scause: usize, regs: &mut InterruptStack, user_mode: bool) -> bool { + if scause != INSTRUCTION_PAGE_FAULT && scause != LOAD_PAGE_FAULT && scause != STORE_PAGE_FAULT { + return false; + } + + let stval: usize; + core::arch::asm!( + "csrr t0, stval", + lateout("t0") stval, + options(nostack) + ); + + let address = VirtualAddress::new(stval); + let mut generic_flags = GenericPfFlags::empty(); + + generic_flags.set(GenericPfFlags::INVOLVED_WRITE, scause == STORE_PAGE_FAULT); + generic_flags.set(GenericPfFlags::USER_NOT_SUPERVISOR, user_mode); + generic_flags.set( + GenericPfFlags::INSTR_NOT_DATA, + scause == INSTRUCTION_PAGE_FAULT, + ); + // FIXME can these conditions be distinguished? Should they be? + generic_flags.set(GenericPfFlags::INVL, false); + generic_flags.set(GenericPfFlags::PRESENT, false); + + crate::memory::page_fault_handler(regs, generic_flags, address).is_ok() +} diff --git a/src/arch/riscv64/interrupt/handler.rs b/src/arch/riscv64/interrupt/handler.rs new file mode 100644 index 0000000000..f495f6676d --- /dev/null +++ b/src/arch/riscv64/interrupt/handler.rs @@ -0,0 +1,319 @@ +use crate::{memory::ArchIntCtx, syscall::IntRegisters}; +use core::mem::size_of; + +#[derive(Default)] +#[repr(C)] +pub struct Registers { + pub x1: usize, // ra + pub x2: usize, // sp + pub x3: usize, // gp + pub x4: usize, // tp + pub x5: usize, // t0 + pub x6: usize, // t1 + pub x7: usize, // t2 + pub x8: usize, // s0/fp + pub x9: usize, // s1 + pub x10: usize, // a0... + pub x11: usize, + pub x12: usize, + pub x13: usize, + pub x14: usize, + pub x15: usize, + pub x16: usize, + pub x17: usize, // a7 + pub x18: usize, // s2... + pub x19: usize, + pub x20: usize, + pub x21: usize, + pub x22: usize, + pub x23: usize, + pub x24: usize, + pub x25: usize, + pub x26: usize, + pub x27: usize, // s11 + pub x28: usize, // t3... + pub x29: usize, + pub x30: usize, + pub x31: usize, // t6 +} + +impl Registers { + pub fn dump(&self) { + println!("X1: {:>016X}", { self.x1 }); + println!("X2: {:>016X}", { self.x2 }); + println!("X3: {:>016X}", { self.x3 }); + println!("X4: {:>016X}", { self.x4 }); + println!("X5: {:>016X}", { self.x5 }); + println!("X6: {:>016X}", { self.x6 }); + println!("X7: {:>016X}", { self.x7 }); + println!("X8: {:>016X}", { self.x8 }); + println!("X9: {:>016X}", { self.x9 }); + println!("X10: {:>016X}", { self.x10 }); + println!("X11: {:>016X}", { self.x11 }); + println!("X12: {:>016X}", { self.x12 }); + println!("X13: {:>016X}", { self.x13 }); + println!("X14: {:>016X}", { self.x14 }); + println!("X15: {:>016X}", { self.x15 }); + println!("X16: {:>016X}", { self.x16 }); + println!("X17: {:>016X}", { self.x17 }); + println!("X18: {:>016X}", { self.x18 }); + println!("X19: {:>016X}", { self.x19 }); + println!("X20: {:>016X}", { self.x20 }); + println!("X21: {:>016X}", { self.x21 }); + println!("X22: {:>016X}", { self.x22 }); + println!("X23: {:>016X}", { self.x23 }); + println!("X24: {:>016X}", { self.x24 }); + println!("X25: {:>016X}", { self.x25 }); + println!("X26: {:>016X}", { self.x26 }); + println!("X27: {:>016X}", { self.x27 }); + println!("X28: {:>016X}", { self.x28 }); + println!("X29: {:>016X}", { self.x29 }); + println!("X30: {:>016X}", { self.x30 }); + println!("X31: {:>016X}", { self.x31 }); + } +} + +#[derive(Default)] +#[repr(C)] +pub struct IretRegisters { + pub sepc: usize, +} + +impl IretRegisters { + pub fn dump(&self) { + println!("SEPC: {:>016X}", { self.sepc }); + } +} + +// NOTE: Layout of this structure must be synced with assembly code in exception.rs +#[derive(Default)] +#[repr(C)] +pub struct InterruptStack { + pub registers: Registers, + pub iret: IretRegisters, +} + +impl InterruptStack { + pub fn init(&mut self) { + const { + assert!(32 * 8 == size_of::()); + } + } + pub fn set_stack_pointer(&mut self, sp: usize) { + self.registers.x2 = sp; + } + pub fn stack_pointer(&self) -> usize { + self.registers.x2 + } + pub fn set_instr_pointer(&mut self, ip: usize) { + self.iret.sepc = ip; + } + pub fn instr_pointer(&self) -> usize { + self.iret.sepc + } + pub fn sig_archdep_reg(&self) -> usize { + self.registers.x5 + } + + pub fn set_syscall_ret_reg(&mut self, ret: usize) { + self.registers.x10 = ret; + } + + pub fn dump(&self) { + self.iret.dump(); + self.registers.dump(); + } + + /// Saves all registers to a struct used by the proc: + /// scheme to read/write registers. + pub fn save(&self, all: &mut IntRegisters) { + all.pc = self.iret.sepc; + all.x31 = self.registers.x31; + all.x30 = self.registers.x30; + all.x29 = self.registers.x29; + all.x28 = self.registers.x28; + all.x27 = self.registers.x27; + all.x26 = self.registers.x26; + all.x25 = self.registers.x25; + all.x24 = self.registers.x24; + all.x23 = self.registers.x23; + all.x22 = self.registers.x22; + all.x21 = self.registers.x21; + all.x20 = self.registers.x20; + all.x19 = self.registers.x19; + all.x18 = self.registers.x18; + all.x17 = self.registers.x17; + all.x16 = self.registers.x16; + all.x15 = self.registers.x15; + all.x14 = self.registers.x14; + all.x13 = self.registers.x13; + all.x12 = self.registers.x12; + all.x11 = self.registers.x11; + all.x10 = self.registers.x10; + all.x9 = self.registers.x9; + all.x8 = self.registers.x8; + all.x7 = self.registers.x7; + all.x6 = self.registers.x6; + all.x5 = self.registers.x5; + all.x2 = self.registers.x2; + all.x1 = self.registers.x1; + } + + /// Loads all registers from a struct used by the proc: + /// scheme to read/write registers. + pub fn load(&mut self, all: &IntRegisters) { + self.iret.sepc = all.pc; + self.registers.x31 = all.x31; + self.registers.x30 = all.x30; + self.registers.x29 = all.x29; + self.registers.x28 = all.x28; + self.registers.x27 = all.x27; + self.registers.x26 = all.x26; + self.registers.x25 = all.x25; + self.registers.x24 = all.x24; + self.registers.x23 = all.x23; + self.registers.x22 = all.x22; + self.registers.x21 = all.x21; + self.registers.x20 = all.x20; + self.registers.x19 = all.x19; + self.registers.x18 = all.x18; + self.registers.x17 = all.x17; + self.registers.x16 = all.x16; + self.registers.x15 = all.x15; + self.registers.x14 = all.x14; + self.registers.x13 = all.x13; + self.registers.x12 = all.x12; + self.registers.x11 = all.x11; + self.registers.x10 = all.x10; + self.registers.x9 = all.x9; + self.registers.x8 = all.x8; + self.registers.x7 = all.x7; + self.registers.x6 = all.x6; + self.registers.x5 = all.x5; + self.registers.x2 = all.x2; + self.registers.x1 = all.x1; + } + + //TODO + pub fn is_singlestep(&self) -> bool { + false + } + pub fn set_singlestep(&mut self, _singlestep: bool) {} +} + +impl ArchIntCtx for InterruptStack { + fn ip(&self) -> usize { + self.iret.sepc + } + fn recover_and_efault(&mut self) { + // Set the return value to nonzero to indicate usercopy failure (EFAULT), and emulate the + // return instruction by setting the return pointer to the saved LR value. + self.iret.sepc = self.registers.x1; // ra + self.registers.x10 = 1; // a0 + } +} + +/// Except for sp and tp +#[macro_export] +macro_rules! push_registers { + () => { + " + addi sp, sp, -32 * 8 + sd x1, (0 * 8)(sp) + // skip sp + sd x3, (2 * 8)(sp) + // skip tp + sd x5, (4 * 8)(sp) + sd x6, (5 * 8)(sp) + sd x7, (6 * 8)(sp) + sd x8, (7 * 8)(sp) + sd x9, (8 * 8)(sp) + sd x10, (9 * 8)(sp) + sd x11, (10 * 8)(sp) + sd x12, (11 * 8)(sp) + sd x13, (12 * 8)(sp) + sd x14, (13 * 8)(sp) + sd x15, (14 * 8)(sp) + sd x16, (15 * 8)(sp) + sd x17, (16 * 8)(sp) + sd x18, (17 * 8)(sp) + sd x19, (18 * 8)(sp) + sd x20, (19 * 8)(sp) + sd x21, (20 * 8)(sp) + sd x22, (21 * 8)(sp) + sd x23, (22 * 8)(sp) + sd x24, (23 * 8)(sp) + sd x25, (24 * 8)(sp) + sd x26, (25 * 8)(sp) + sd x27, (26 * 8)(sp) + sd x28, (27 * 8)(sp) + sd x29, (28 * 8)(sp) + sd x30, (29 * 8)(sp) + sd x31, (30 * 8)(sp) + + csrr t0, sepc + sd t0, (31 * 8)(sp) + " + }; // keep sepc value in t0 on exit +} + +#[macro_export] +macro_rules! pop_registers { + () => { + " + ld t0, (31 * 8)(sp) + csrw sepc, t0 + + ld x1, (0 * 8)(sp) + // skip sp, it'll be restored later + ld x3, (2 * 8)(sp) + ld x4, (3 * 8)(sp) + ld x5, (4 * 8)(sp) + ld x6, (5 * 8)(sp) + ld x7, (6 * 8)(sp) + ld x8, (7 * 8)(sp) + ld x9, (8 * 8)(sp) + ld x10, (9 * 8)(sp) + ld x11, (10 * 8)(sp) + ld x12, (11 * 8)(sp) + ld x13, (12 * 8)(sp) + ld x14, (13 * 8)(sp) + ld x15, (14 * 8)(sp) + ld x16, (15 * 8)(sp) + ld x17, (16 * 8)(sp) + ld x18, (17 * 8)(sp) + ld x19, (18 * 8)(sp) + ld x20, (19 * 8)(sp) + ld x21, (20 * 8)(sp) + ld x22, (21 * 8)(sp) + ld x23, (22 * 8)(sp) + ld x24, (23 * 8)(sp) + ld x25, (24 * 8)(sp) + ld x26, (25 * 8)(sp) + ld x27, (26 * 8)(sp) + ld x28, (27 * 8)(sp) + ld x29, (28 * 8)(sp) + ld x30, (29 * 8)(sp) + ld x31, (30 * 8)(sp) + ld sp, (1 * 8)(sp) + " + }; +} + +#[naked] +pub unsafe extern "C" fn enter_usermode() -> ! { + core::arch::asm!( + concat!( + "jalr s11\n", + "li t0, 1 << 8\n", // force U mode on sret + "csrc sstatus, t0\n", + "li t0, 0x6000\n", // set FS to dirty (enable FPU in U mode) + "csrs sstatus, t0\n", + "addi t0, sp, 32 * 8\n", // save S mode stack to percpu + "sd t0, 8(tp)\n", + pop_registers!(), + "sret\n", + ), + options(noreturn) + ) +} diff --git a/src/arch/riscv64/interrupt/mod.rs b/src/arch/riscv64/interrupt/mod.rs new file mode 100644 index 0000000000..8a8af53c3b --- /dev/null +++ b/src/arch/riscv64/interrupt/mod.rs @@ -0,0 +1,79 @@ +use core::arch::asm; + +#[macro_use] +mod handler; + +mod exception; +pub mod syscall; +pub mod trace; + +pub use self::trace::StackTrace; + +use crate::cpu_set::LogicalCpuId; +pub use handler::InterruptStack; + +pub fn bsp_apic_id() -> Option { + Some(0) +} +pub fn available_irqs_iter(_cpu_id: LogicalCpuId) -> impl Iterator + 'static { + unimplemented!(); + 0..0 +} +pub fn is_reserved(_cpu_id: LogicalCpuId, _index: u8) -> ! { + unimplemented!() +} +pub fn set_reserved(_cpu_id: LogicalCpuId, _index: u8, _reserved: bool) -> ! { + unimplemented!() +} + +/// Clear interrupts +#[inline(always)] +pub unsafe fn disable() { + asm!("csrci sstatus, 1 << 1") +} + +/// Set interrupts +#[inline(always)] +pub unsafe fn enable() { + asm!("csrsi sstatus, 1 << 1") +} + +/// Set interrupts and halt +/// This will atomically wait for the next interrupt +/// Performing enable followed by halt is not guaranteed to be atomic, use this instead! +#[inline(always)] +pub unsafe fn enable_and_halt() { + asm!("csrsi sstatus, 1 << 1", "wfi") +} + +/// Set interrupts and nop +/// This will enable interrupts and allow the IF flag to be processed +/// Simply enabling interrupts does not gurantee that they will trigger, use this instead! +#[inline(always)] +pub unsafe fn enable_and_nop() { + asm!("csrsi sstatus, 1 << 1", "nop") +} + +/// Halt instruction +#[inline(always)] +pub unsafe fn halt() { + asm!("wfi", options(nomem, nostack)) +} + +/// Pause instruction +/// Safe because it is similar to a NOP, and has no memory effects +#[inline(always)] +pub fn pause() { + unsafe { + // It's a hint instruction, safe to execute without Zihintpause extension + asm!("pause", options(nomem, nostack)); + } +} + +pub unsafe fn init() { + // Setup interrupt handlers + asm!( + "la t0, exception_handler", // WARL=0 - direct mode combined handler + "csrw stvec, t0" + ); +} diff --git a/src/arch/riscv64/interrupt/syscall.rs b/src/arch/riscv64/interrupt/syscall.rs new file mode 100644 index 0000000000..b7c675146c --- /dev/null +++ b/src/arch/riscv64/interrupt/syscall.rs @@ -0,0 +1 @@ +pub use super::handler::enter_usermode; diff --git a/src/arch/riscv64/interrupt/trace.rs b/src/arch/riscv64/interrupt/trace.rs new file mode 100644 index 0000000000..88d754a2b9 --- /dev/null +++ b/src/arch/riscv64/interrupt/trace.rs @@ -0,0 +1,31 @@ +use core::{arch::asm, mem}; + +pub struct StackTrace { + pub fp: usize, + pub pc_ptr: *const usize, +} + +impl StackTrace { + #[inline(always)] + pub unsafe fn start() -> Option { + let fp: usize; + asm!("mv {}, fp", out(reg) fp); + + let pc_ptr = fp.checked_sub(mem::size_of::())?; + let fp = pc_ptr.checked_sub(mem::size_of::())?; + Some(StackTrace { + fp, + pc_ptr: pc_ptr as *const usize, + }) + } + + pub unsafe fn next(self) -> Option { + let fp = *(self.fp as *const usize); + let pc_ptr = fp.checked_sub(mem::size_of::())?; + let fp = pc_ptr.checked_sub(mem::size_of::())?; + Some(StackTrace { + fp: fp, + pc_ptr: pc_ptr as *const usize, + }) + } +} diff --git a/src/arch/riscv64/ipi.rs b/src/arch/riscv64/ipi.rs new file mode 100644 index 0000000000..cc4df34237 --- /dev/null +++ b/src/arch/riscv64/ipi.rs @@ -0,0 +1,32 @@ +#[derive(Clone, Copy, Debug)] +#[repr(u8)] +pub enum IpiKind { + Wakeup = 0x40, + Tlb = 0x41, + Switch = 0x42, + Pit = 0x43, +} + +#[derive(Clone, Copy, Debug)] +#[repr(u8)] +pub enum IpiTarget { + Current = 1, + All = 2, + Other = 3, +} + +#[cfg(not(feature = "multi_core"))] +#[inline(always)] +pub fn ipi(_kind: IpiKind, _target: IpiTarget) {} + +#[cfg(feature = "multi_core")] +#[inline(always)] +pub fn ipi(_kind: IpiKind, _target: IpiTarget) {} + +#[cfg(not(feature = "multi_core"))] +#[inline(always)] +pub fn ipi_single(_kind: IpiKind, _target: crate::cpu_set::LogicalCpuId) {} + +#[cfg(feature = "multi_core")] +#[inline(always)] +pub fn ipi_single(_kind: IpiKind, _target: crate::cpu_set::LogicalCpuId) {} diff --git a/src/arch/riscv64/macros.rs b/src/arch/riscv64/macros.rs new file mode 100644 index 0000000000..4e3566fc55 --- /dev/null +++ b/src/arch/riscv64/macros.rs @@ -0,0 +1,16 @@ +/// Print to console +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ({ + use core::fmt::Write; + let _ = write!($crate::arch::debug::Writer::new(), $($arg)*); + }); +} + +/// Print with new line to console +#[macro_export] +macro_rules! println { + () => (print!("\n")); + ($fmt:expr) => (print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); +} diff --git a/src/arch/riscv64/misc.rs b/src/arch/riscv64/misc.rs new file mode 100644 index 0000000000..60bde118ac --- /dev/null +++ b/src/arch/riscv64/misc.rs @@ -0,0 +1,45 @@ +use core::arch::asm; + +use crate::{ + cpu_set::LogicalCpuId, + paging::{RmmA, RmmArch}, + percpu::PercpuBlock, +}; + +#[repr(C)] +pub struct ArchPercpu { + // These fields must be kept first and in this order. Assembly in exception.rs depends on it + pub tmp: usize, + pub s_sp: usize, + + pub percpu: PercpuBlock, +} + +impl PercpuBlock { + pub fn current() -> &'static Self { + unsafe { + let tp: *const ArchPercpu; + asm!( "mv t0, tp", out("t0") tp ); + let arch_percpu = &*tp; + &arch_percpu.percpu + } + } +} + +#[cold] +pub unsafe fn init(cpu_id: LogicalCpuId) { + let frame = crate::memory::allocate_frame().expect("failed to allocate percpu memory"); + let virt = RmmA::phys_to_virt(frame.base()).data() as *mut ArchPercpu; + + virt.write(ArchPercpu { + tmp: 0, + s_sp: 0, + percpu: PercpuBlock::init(cpu_id), + }); + + asm!( + "mv tp, {}", + "csrw sscratch, tp", + in(reg) virt as usize + ); +} diff --git a/src/arch/riscv64/mod.rs b/src/arch/riscv64/mod.rs new file mode 100644 index 0000000000..de5fc417c5 --- /dev/null +++ b/src/arch/riscv64/mod.rs @@ -0,0 +1,72 @@ +#[macro_use] +pub mod macros; + +pub mod consts; +pub mod debug; +pub mod device; +pub mod interrupt; +pub mod ipi; +pub mod misc; +pub mod paging; +pub mod rmm; +mod sbi; +pub mod start; +pub mod stop; +pub mod time; + +pub use ::rmm::RiscV64Sv48Arch as CurrentRmmArch; +use core::arch::asm; + +pub use arch_copy_to_user as arch_copy_from_user; + +#[link_section = ".usercopy-fns"] +#[naked] +pub unsafe extern "C" fn arch_copy_to_user(dst: usize, src: usize, len: usize) -> u8 { + asm!( + " + addi sp, sp, -16 + sd fp, 0(sp) + sd ra, 8(sp) + addi fp, sp, 16 + li t1, 1 << 18 // SUM + csrs sstatus, t1 + jal 2f + csrc sstatus, t1 + ld ra, -8(fp) + ld fp, -16(fp) + addi sp, sp, 16 + ret + + 2: or t0, a0, a1 + andi t0, t0, 7 + bne t0, x0, 4f + srli t2, a2, 3 + andi a2, a2, 7 + beq t2, x0, 4f + 3: ld t0, 0(a1) + sd t0, 0(a0) + addi a0, a0, 8 + addi a1, a1, 8 + addi t2, t2, -1 + bne t2, x0, 3b + + 4: beq a2, x0, 5f + lb t0, 0(a1) + sb t0, 0(a0) + addi a0, a0, 1 + addi a1, a1, 1 + addi a2, a2, -1 + bne a2, x0, 4b + 5: mv a0, x0 + ret + ", + options(noreturn) + ) +} + +pub const KFX_SIZE: usize = 1024; + +// This function exists as the KFX size is dynamic on x86_64. +pub fn kfx_size() -> usize { + KFX_SIZE +} diff --git a/src/arch/riscv64/paging/entry.rs b/src/arch/riscv64/paging/entry.rs new file mode 100644 index 0000000000..affe00f6d8 --- /dev/null +++ b/src/arch/riscv64/paging/entry.rs @@ -0,0 +1,11 @@ +/// A page table entry +#[repr(packed(8))] +pub struct Entry(u64); + +bitflags! { + pub struct EntryFlags: usize { + const NO_CACHE = 1 << 4; + const DEV_MEM = 0; + const WRITE_COMBINING = 0; + } +} diff --git a/src/arch/riscv64/paging/mapper.rs b/src/arch/riscv64/paging/mapper.rs new file mode 100644 index 0000000000..1543c6ff80 --- /dev/null +++ b/src/arch/riscv64/paging/mapper.rs @@ -0,0 +1,29 @@ +use crate::ipi::{ipi, IpiKind, IpiTarget}; + +use super::RmmA; + +pub use rmm::{Flusher, PageFlush, PageFlushAll}; + +pub struct InactiveFlusher { + _inner: (), +} +impl InactiveFlusher { + // TODO: cpu id + pub fn new() -> Self { + Self { _inner: () } + } +} + +impl Flusher for InactiveFlusher { + fn consume(&mut self, flush: PageFlush) { + // TODO: Push to TLB "mailbox" or tell it to reload CR3 if there are too many entries. + unsafe { + flush.ignore(); + } + } +} +impl Drop for InactiveFlusher { + fn drop(&mut self) { + ipi(IpiKind::Tlb, IpiTarget::Other); + } +} diff --git a/src/arch/riscv64/paging/mod.rs b/src/arch/riscv64/paging/mod.rs new file mode 100644 index 0000000000..a23903bcfe --- /dev/null +++ b/src/arch/riscv64/paging/mod.rs @@ -0,0 +1,80 @@ +#![allow(unused)] + +pub use super::CurrentRmmArch as RmmA; +pub use rmm::{Arch as RmmArch, PageFlags, PhysicalAddress, TableKind, VirtualAddress}; + +pub type PageMapper = rmm::PageMapper; +pub use crate::rmm::KernelMapper; + +pub mod entry; +pub mod mapper; + +/// Number of entries per page table +pub const ENTRY_COUNT: usize = RmmA::PAGE_ENTRIES; + +/// Size of pages +pub const PAGE_SIZE: usize = RmmA::PAGE_SIZE; +pub const PAGE_MASK: usize = RmmA::PAGE_OFFSET_MASK; + +#[cold] +pub unsafe fn init() { + // Assuming SBI already set up PMAs correctly for us + // TODO: detect Svpbmt present/enabled and override device memory with PBMT=IO +} + +/// Page +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Page { + number: usize, +} + +impl Page { + pub fn start_address(self) -> VirtualAddress { + VirtualAddress::new(self.number * PAGE_SIZE) + } + + pub fn containing_address(address: VirtualAddress) -> Page { + Page { + number: address.data() / PAGE_SIZE, + } + } + + pub fn range_inclusive(start: Page, r#final: Page) -> PageIter { + PageIter { + start, + end: r#final.next(), + } + } + + pub fn next(self) -> Page { + self.next_by(1) + } + pub fn next_by(self, n: usize) -> Page { + Self { + number: self.number + n, + } + } + + pub fn offset_from(self, other: Self) -> usize { + self.number - other.number + } +} + +pub struct PageIter { + start: Page, + end: Page, +} + +impl Iterator for PageIter { + type Item = Page; + + fn next(&mut self) -> Option { + if self.start < self.end { + let page = self.start; + self.start = self.start.next(); + Some(page) + } else { + None + } + } +} diff --git a/src/arch/riscv64/rmm.rs b/src/arch/riscv64/rmm.rs new file mode 100644 index 0000000000..2c1388e5b2 --- /dev/null +++ b/src/arch/riscv64/rmm.rs @@ -0,0 +1,31 @@ +use super::CurrentRmmArch as RmmA; +use rmm::{Arch, PageFlags, VirtualAddress}; + +pub struct KernelMapper { + mapper: crate::paging::PageMapper, + ro: bool, +} +impl KernelMapper { + pub fn lock() -> Self { + unimplemented!() + } + pub fn get_mut(&mut self) -> Option<&mut crate::paging::PageMapper> { + unimplemented!() + } +} + +pub unsafe fn page_flags(virt: VirtualAddress) -> PageFlags { + use crate::kernel_executable_offsets::*; + let virt_addr = virt.data(); + + if virt_addr >= __text_start() && virt_addr < __text_end() { + // Remap text read-only, execute + PageFlags::new().execute(true) + } else if virt_addr >= __rodata_start() && virt_addr < __rodata_end() { + // Remap rodata read-only, no execute + PageFlags::new() + } else { + // Remap everything else read-write, no execute + PageFlags::new().write(true) + } +} diff --git a/src/arch/riscv64/sbi.rs b/src/arch/riscv64/sbi.rs new file mode 100644 index 0000000000..a519754124 --- /dev/null +++ b/src/arch/riscv64/sbi.rs @@ -0,0 +1,69 @@ +use core::result; + +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub struct Error { + pub errno: i32, +} + +pub type Result = result::Result; + +impl Error { + pub fn new(errno: i32) -> Error { + Error { errno } + } +} + +pub unsafe fn sbi_svc(eid: u32, fid: u32, arg0: u32, arg1: u32, arg2: u32) -> Result { + let error: usize; + let value: usize; + core::arch::asm!( + "ecall", + in("a7") eid as usize, + in("a6") fid as usize, + in("a0") arg0 as usize, + in("a1") arg1 as usize, + in("a2") arg2 as usize, + lateout("a0") error, + lateout("a1") value, + options(nostack), + ); + if error == 0 { + Ok(value) + } else { + Err(Error::new(error as i32)) + } +} + +pub struct Sbi {} +pub static SBI: Sbi = Sbi {}; + +const DEBUG_CONSOLE_EXTENSION: u32 = 0x4442434E; +const TIMER_EXTENSION: u32 = 0x54494D45; + +impl Sbi { + pub fn debug_console_write(self: &Self, buf: &[u8]) -> Result { + let addr = buf.as_ptr() as u64; + unsafe { + sbi_svc( + DEBUG_CONSOLE_EXTENSION, + 0, + buf.len() as u32, + addr as u32, + (addr >> 32) as u32, + ) + } + } + + pub fn set_timer(self: &Self, stime_value: u64) -> Result<()> { + unsafe { + sbi_svc( + TIMER_EXTENSION, + 0, + stime_value as u32, + (stime_value >> 32) as u32, + 0, + ) + } + .map(|_x| ()) + } +} diff --git a/src/arch/riscv64/start.rs b/src/arch/riscv64/start.rs new file mode 100644 index 0000000000..8014c1b86d --- /dev/null +++ b/src/arch/riscv64/start.rs @@ -0,0 +1,235 @@ +use core::{ + arch::asm, + slice, + sync::atomic::{AtomicU32, AtomicUsize, Ordering}, +}; +use fdt::Fdt; +use log::info; + +use crate::{ + allocator, + memory::Frame, + paging::{PhysicalAddress, PAGE_SIZE}, +}; + +use crate::{ + arch::{device::serial::init_early, interrupt, paging}, + device, + startup::memory::{register_bootloader_areas, register_memory_region, BootloaderMemoryKind}, +}; + +#[cfg(feature = "graphical_debug")] +use crate::devices::graphical_debug; +use crate::{ + devices::graphical_debug::{DebugDisplay, DEBUG_DISPLAY, FRAMEBUFFER}, + dtb::register_dev_memory_ranges, +}; + +/// Test of zero values in BSS. +static mut BSS_TEST_ZERO: usize = 0; +/// Test of non-zero values in data. +static mut DATA_TEST_NONZERO: usize = 0xFFFF_FFFF_FFFF_FFFF; + +pub static KERNEL_BASE: AtomicUsize = AtomicUsize::new(0); +pub static KERNEL_SIZE: AtomicUsize = AtomicUsize::new(0); +pub static CPU_COUNT: AtomicU32 = AtomicU32::new(0); +pub static BOOT_HART_ID: AtomicUsize = AtomicUsize::new(0); + +#[repr(packed)] +pub struct KernelArgs { + kernel_base: usize, + kernel_size: usize, + stack_base: usize, + stack_size: usize, + env_base: usize, + env_size: usize, + acpi_base: usize, + acpi_size: usize, + areas_base: usize, + areas_size: usize, + + /// The physical base 64-bit pointer to the contiguous bootstrap/initfs. + bootstrap_base: usize, + /// Size of contiguous bootstrap/initfs physical region, not necessarily page aligned. + bootstrap_size: usize, +} + +fn get_boot_hart_id(env: &[u8]) -> Option { + for line in core::str::from_utf8(env).unwrap_or("").lines() { + let mut parts = line.splitn(2, '='); + let name = parts.next().unwrap_or(""); + let value = parts.next().unwrap_or(""); + + if name == "BOOT_HART_ID" { + return usize::from_str_radix(value, 16).ok(); + } + } + None +} + +/// The entry to Rust, all things must be initialized +#[no_mangle] +pub unsafe extern "C" fn kstart(args_ptr: *const KernelArgs) -> ! { + asm!( + "mv tp, x0", // reset percpu until it is initialized + "csrw sscratch, tp", + "sd x0, -16(fp)", // and stop frame walker here + "sd x0, -8(fp)", + ); + + let bootstrap = { + let args = &*args_ptr; + + // BSS should already be zero + { + assert_eq!(BSS_TEST_ZERO, 0); + assert_eq!(DATA_TEST_NONZERO, 0xFFFF_FFFF_FFFF_FFFF); + } + + KERNEL_BASE.store(args.kernel_base, Ordering::SeqCst); + KERNEL_SIZE.store(args.kernel_size, Ordering::SeqCst); + + let env = slice::from_raw_parts( + (crate::PHYS_OFFSET + args.env_base) as *const u8, + args.env_size, + ); + + let dtb_data = if args.acpi_base != 0 { + Some((crate::PHYS_OFFSET + args.acpi_base, args.acpi_size)) + } else { + None + }; + let dtb = dtb_data + .map(|(base, size)| unsafe { slice::from_raw_parts(base as *const u8, size) }) + .and_then(|data| Fdt::new(data).ok()); + + #[cfg(feature = "graphical_debug")] + graphical_debug::init(env); + + #[cfg(feature = "serial_debug")] + if let Some(dtb) = &dtb { + init_early(dtb); + } + + // Initialize logger + crate::log::init_logger(|r| { + use core::fmt::Write; + let _ = write!( + crate::debug::Writer::new(), + "{}:{} -- {}\n", + r.target(), + r.level(), + r.args() + ); + }); + ::log::set_max_level(::log::LevelFilter::Debug); + + info!("Redox OS starting..."); + info!( + "Kernel: {:X}:{:X}", + { args.kernel_base }, + args.kernel_base + args.kernel_size + ); + info!( + "Stack: {:X}:{:X}", + { args.stack_base }, + args.stack_base + args.stack_size + ); + info!( + "Env: {:X}:{:X}", + { args.env_base }, + args.env_base + args.env_size + ); + info!( + "RSDPs: {:X}:{:X}", + { args.acpi_size }, + args.acpi_size + args.acpi_size + ); + info!( + "Areas: {:X}:{:X}", + { args.areas_base }, + args.areas_base + args.areas_size + ); + info!( + "Bootstrap: {:X}:{:X}", + { args.bootstrap_base }, + args.bootstrap_base + args.bootstrap_size + ); + + if let Some(dtb) = &dtb { + device::dump_fdt(&dtb); + } + + interrupt::init(); + + let bootstrap = crate::Bootstrap { + base: Frame::containing(PhysicalAddress::new(args.bootstrap_base)), + page_count: args.bootstrap_size / PAGE_SIZE, + env, + }; + + // Initialize RMM + register_bootloader_areas(args.areas_base, args.areas_size); + if let Some(dt) = &dtb { + register_dev_memory_ranges(dt); + } + + register_memory_region( + args.kernel_base, + args.kernel_size, + BootloaderMemoryKind::Kernel, + ); + register_memory_region( + args.stack_base, + args.stack_size, + BootloaderMemoryKind::IdentityMap, + ); + register_memory_region( + args.env_base, + args.env_size, + BootloaderMemoryKind::IdentityMap, + ); + register_memory_region( + args.acpi_base, + args.acpi_size, + BootloaderMemoryKind::IdentityMap, + ); + register_memory_region( + args.bootstrap_base, + args.bootstrap_size, + BootloaderMemoryKind::IdentityMap, + ); + + crate::startup::memory::init(None, None); + + let boot_hart_id = get_boot_hart_id(env).expect("Didn't get boot HART id from bootloader"); + info!("Booting on HART {}", boot_hart_id); + BOOT_HART_ID.store(boot_hart_id, Ordering::Relaxed); + + paging::init(); + + crate::misc::init(crate::cpu_set::LogicalCpuId::new(0)); + + CPU_COUNT.store(1, Ordering::SeqCst); + + // Setup kernel heap + allocator::init(); + + // Activate memory logging + crate::log::init(); + + crate::dtb::init(dtb_data); + + // Initialize devices + device::init(); + + // Initialize all of the non-core devices not otherwise needed to complete initialization + device::init_noncore(); + + // FIXME bringup AP HARTs + + bootstrap + }; + + crate::kmain(CPU_COUNT.load(Ordering::SeqCst), bootstrap); +} diff --git a/src/arch/riscv64/stop.rs b/src/arch/riscv64/stop.rs new file mode 100644 index 0000000000..b0bd5793fd --- /dev/null +++ b/src/arch/riscv64/stop.rs @@ -0,0 +1,13 @@ +pub unsafe fn kreset() -> ! { + println!("kreset"); + unimplemented!() +} + +pub unsafe fn emergency_reset() -> ! { + unimplemented!() +} + +pub unsafe fn kstop() -> ! { + println!("kstop"); + unimplemented!() +} diff --git a/src/arch/riscv64/time.rs b/src/arch/riscv64/time.rs new file mode 100644 index 0000000000..82c8e7892f --- /dev/null +++ b/src/arch/riscv64/time.rs @@ -0,0 +1,26 @@ +use core::{ + arch::asm, + sync::atomic::{AtomicUsize, Ordering}, +}; + +static MTIME_FREQ_HZ: AtomicUsize = AtomicUsize::new(0); + +pub fn init(freq_hz: usize) { + MTIME_FREQ_HZ.store(freq_hz, Ordering::Relaxed); +} + +pub fn monotonic_absolute() -> u128 { + let freq_hz = MTIME_FREQ_HZ.load(Ordering::Relaxed); + if freq_hz > 0 { + let counter: usize; + unsafe { + asm!( + "rdtime t0", + lateout("t0") counter + ); + }; + counter as u128 * 1_000_000_000u128 / freq_hz as u128 + } else { + 0 + } +} diff --git a/src/context/arch/riscv64.rs b/src/context/arch/riscv64.rs new file mode 100644 index 0000000000..8b761b730f --- /dev/null +++ b/src/context/arch/riscv64.rs @@ -0,0 +1,248 @@ +use crate::{ + arch::{ + interrupt::InterruptStack, + paging::{PageMapper, ENTRY_COUNT}, + }, + context::{context::Kstack, memory::Table}, + memory::{KernelMapper, RmmA}, + percpu::PercpuBlock, + syscall::FloatRegisters, +}; +use alloc::sync::Arc; +use core::{mem::offset_of, sync::atomic::AtomicBool}; +use rmm::{Arch, TableKind, VirtualAddress}; +use spin::Once; +use spinning_top::RwSpinlock; +use syscall::{error::*, EnvRegisters}; + +pub static CONTEXT_SWITCH_LOCK: AtomicBool = AtomicBool::new(false); + +pub const KFX_ALIGN: usize = 16; +pub const KSTACK_SIZE: usize = 65536; +pub const KSTACK_ALIGN: usize = 16; + +#[derive(Clone, Debug, Default)] +pub struct Context { + sp: usize, + ra: usize, + fp: usize, + s1: usize, + s2: usize, + s3: usize, + s4: usize, + s5: usize, + s6: usize, + s7: usize, + s8: usize, + s9: usize, + s10: usize, + s11: usize, + sstatus: usize, +} + +impl Context { + pub fn new() -> Self { + Self::default() + } + + fn set_stack(&mut self, address: usize) { + self.sp = address; + } + + fn set_ra(&mut self, address: usize) { + self.ra = address; + } + + fn set_s11(&mut self, address: usize) { + self.s11 = address; + } + + pub(crate) fn setup_initial_call( + &mut self, + stack: &Kstack, + func: extern "C" fn(), + userspace_allowed: bool, + ) { + let mut stack_top = stack.initial_top(); + + const INT_REGS_SIZE: usize = core::mem::size_of::(); + + if userspace_allowed { + unsafe { + // Zero-initialize InterruptStack registers. + stack_top = stack_top.sub(INT_REGS_SIZE); + stack_top.write_bytes(0_u8, INT_REGS_SIZE); + (&mut *stack_top.cast::()).init(); + } + } + + self.set_ra(crate::interrupt::syscall::enter_usermode as usize); + self.set_s11(func as usize); + + self.set_stack(stack_top as usize); + } +} + +impl super::Context { + pub fn get_fx_regs(&self) -> FloatRegisters { + unimplemented!() + } + + pub fn set_fx_regs(&mut self, mut _new: FloatRegisters) { + unimplemented!() + } + + pub fn current_syscall(&self) -> Option<[usize; 6]> { + if !self.inside_syscall { + return None; + } + let regs = self.regs()?; + let regs = ®s.registers; + Some([regs.x17, regs.x10, regs.x11, regs.x12, regs.x13, regs.x14]) + } + + pub(crate) fn write_current_env_regs(&mut self, regs: EnvRegisters) -> Result<()> { + self.write_env_regs(regs) + } + + pub(crate) fn write_env_regs(&mut self, regs: EnvRegisters) -> Result<()> { + if RmmA::virt_is_valid(VirtualAddress::new(regs.tp)) { + match self.regs_mut() { + Some(stack) => { + stack.registers.x4 = regs.tp; + Ok(()) + } + None => Err(Error::new(ESRCH)), + } + } else { + Err(Error::new(EINVAL)) + } + } + + pub(crate) fn read_current_env_regs(&self) -> Result { + self.read_env_regs() + } + + pub(crate) fn read_env_regs(&self) -> Result { + match self.regs() { + Some(stack) => Ok(EnvRegisters { + tp: stack.registers.x4, + }), + None => Err(Error::new(ESRCH)), + } + } +} + +pub static EMPTY_CR3: Once = Once::new(); + +// SAFETY: EMPTY_CR3 must be initialized. +pub unsafe fn empty_cr3() -> rmm::PhysicalAddress { + debug_assert!(EMPTY_CR3.poll().is_some()); + *EMPTY_CR3.get_unchecked() +} + +/// Switch to the next context by restoring its stack and registers +pub unsafe fn switch_to(prev: &mut super::Context, next: &mut super::Context) { + // FIXME floating point + PercpuBlock::current() + .new_addrsp_tmp + .set(next.addr_space.clone()); + + switch_to_inner(&mut prev.arch, &mut next.arch); +} + +#[naked] +unsafe extern "C" fn switch_to_inner(prev: &mut Context, next: &mut Context) { + core::arch::asm!(r#" + sd s1, {off_s1}(a0) + ld s1, {off_s1}(a1) + + sd s2, {off_s2}(a0) + ld s2, {off_s2}(a1) + + sd s3, {off_s3}(a0) + ld s3, {off_s3}(a1) + + sd s4, {off_s4}(a0) + ld s4, {off_s4}(a1) + + sd s5, {off_s5}(a0) + ld s5, {off_s5}(a1) + + sd s6, {off_s6}(a0) + ld s6, {off_s6}(a1) + + sd s7, {off_s7}(a0) + ld s7, {off_s7}(a1) + + sd s8, {off_s8}(a0) + ld s8, {off_s8}(a1) + + sd s9, {off_s9}(a0) + ld s9, {off_s9}(a1) + + sd s10, {off_s10}(a0) + ld s10, {off_s10}(a1) + + sd s11, {off_s11}(a0) + ld s11, {off_s11}(a1) + + sd s11, {off_s11}(a0) + ld s11, {off_s11}(a1) + + sd sp, {off_sp}(a0) + ld sp, {off_sp}(a1) + + sd ra, {off_ra}(a0) + ld ra, {off_ra}(a1) + + sd fp, {off_fp}(a0) + ld fp, {off_fp}(a1) + + csrr t0, sstatus + sd t0, {off_sstatus}(a0) + ld t0, {off_sstatus}(a1) + csrw sstatus, t0 + + j {switch_hook} + "#, + off_s1 = const(offset_of!(Context, s1)), + off_s2 = const(offset_of!(Context, s2)), + off_s3 = const(offset_of!(Context, s3)), + off_s4 = const(offset_of!(Context, s4)), + off_s5 = const(offset_of!(Context, s5)), + off_s6 = const(offset_of!(Context, s6)), + off_s7 = const(offset_of!(Context, s7)), + off_s8 = const(offset_of!(Context, s8)), + off_s9 = const(offset_of!(Context, s9)), + off_s10 = const(offset_of!(Context, s10)), + off_s11 = const(offset_of!(Context, s11)), + off_sp = const(offset_of!(Context, sp)), + off_ra = const(offset_of!(Context, ra)), + off_fp = const(offset_of!(Context, fp)), + off_sstatus = const(offset_of!(Context, sstatus)), + + switch_hook = sym crate::context::switch_finish_hook, + options(noreturn), + ); +} + +/// Allocates a new empty utable +pub fn setup_new_utable() -> Result { + let utable = unsafe { + PageMapper::create(TableKind::User, crate::memory::TheFrameAllocator) + .ok_or(Error::new(ENOMEM))? + }; + + // Copy higher half (kernel) mappings + unsafe { + let active_ktable = KernelMapper::lock(); + for pde_no in ENTRY_COUNT / 2..ENTRY_COUNT { + if let Some(entry) = active_ktable.table().entry(pde_no) { + utable.table().set_entry(pde_no, entry); + } + } + } + + Ok(Table { utable }) +} diff --git a/src/context/mod.rs b/src/context/mod.rs index 436ea0e7cd..cc40c63094 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -38,6 +38,10 @@ mod arch; #[path = "arch/x86_64.rs"] mod arch; +#[cfg(target_arch = "riscv64")] +#[path = "arch/riscv64.rs"] +mod arch; + /// Context struct pub mod context; diff --git a/src/devices/uart_16550.rs b/src/devices/uart_16550.rs index 6a40573b81..2a30a42a92 100644 --- a/src/devices/uart_16550.rs +++ b/src/devices/uart_16550.rs @@ -65,11 +65,19 @@ impl SerialPort> { } impl SerialPort> { + #[allow(dead_code)] pub unsafe fn new(base: usize) -> &'static mut SerialPort> { &mut *(base as *mut Self) } } +impl SerialPort> { + #[allow(dead_code)] + pub unsafe fn new(base: usize) -> &'static mut SerialPort> { + &mut *(base as *mut Self) + } +} + impl SerialPort where T::Value: From + TryInto, diff --git a/src/dtb/mod.rs b/src/dtb/mod.rs index f46ad9ec3c..49acb3cceb 100644 --- a/src/dtb/mod.rs +++ b/src/dtb/mod.rs @@ -1,7 +1,7 @@ use crate::startup::memory::{register_memory_region, BootloaderMemoryKind}; use alloc::vec::Vec; use core::slice; -use fdt::Fdt; +use fdt::{node::NodeProperty, Fdt}; use spin::once::Once; pub static DTB_BINARY: Once> = Once::new(); @@ -84,11 +84,14 @@ pub fn register_dev_memory_ranges(dt: &Fdt) { } } -pub fn diag_uart_range(dtb: &Fdt) -> Option<(usize, usize, bool, bool)> { +pub fn diag_uart_range<'a>(dtb: &'a Fdt) -> Option<(usize, usize, bool, bool, &'a str)> { let stdout_path = dtb.chosen().stdout().unwrap(); let uart_node = stdout_path.node(); let skip_init = uart_node.property("skip-init").is_some(); let cts_event_walkaround = uart_node.property("cts-event-walkaround").is_some(); + let compatible = uart_node + .property("compatible") + .and_then(NodeProperty::as_str)?; let mut reg = uart_node.reg().unwrap(); let memory = reg.nth(0).unwrap(); @@ -98,6 +101,7 @@ pub fn diag_uart_range(dtb: &Fdt) -> Option<(usize, usize, bool, bool)> { memory.size.unwrap(), skip_init, cts_event_walkaround, + compatible, )) } diff --git a/src/main.rs b/src/main.rs index 2ec6bff955..4970956322 100644 --- a/src/main.rs +++ b/src/main.rs @@ -54,6 +54,9 @@ #![feature(variant_count)] #![cfg_attr(not(test), no_std)] #![cfg_attr(not(test), no_main)] +#![feature(option_get_or_insert_default)] +#![feature(array_chunks)] +#![feature(if_let_guard)] #[macro_use] extern crate alloc; diff --git a/src/scheme/irq.rs b/src/scheme/irq.rs index 1c87bae7e3..7fee52a47e 100644 --- a/src/scheme/irq.rs +++ b/src/scheme/irq.rs @@ -12,15 +12,13 @@ use alloc::{collections::BTreeMap, string::String, vec::Vec}; use spin::{Mutex, Once, RwLock}; use syscall::dirent::{DirEntry, DirentBuf, DirentKind}; -use crate::{ - arch::interrupt::{available_irqs_iter, bsp_apic_id, is_reserved, set_reserved}, - context::file::InternalFlags, -}; +use crate::context::file::InternalFlags; + +use crate::arch::interrupt::{available_irqs_iter, bsp_apic_id, is_reserved, set_reserved}; use crate::{ cpu_set::LogicalCpuId, event, - interrupt::irq::acknowledge, syscall::{ data::Stat, error::*, @@ -29,6 +27,9 @@ use crate::{ }, }; +#[cfg(not(target_arch = "riscv64"))] +use crate::interrupt::irq::acknowledge; + use super::{CallerCtx, GlobalSchemes, OpenResult}; /// IRQ queues @@ -329,6 +330,7 @@ impl crate::scheme::KernelScheme for IrqScheme { return Ok(0); } handle_ack.store(ack, Ordering::SeqCst); + #[cfg(not(target_arch = "riscv64"))] unsafe { acknowledge(handle_irq as usize); } diff --git a/targets/riscv64-unknown-kernel.json b/targets/riscv64-unknown-kernel.json new file mode 100644 index 0000000000..d3fbba508a --- /dev/null +++ b/targets/riscv64-unknown-kernel.json @@ -0,0 +1,23 @@ +{ + "llvm-target": "riscv64-unknown-none", + "target-endian": "little", + "target-pointer-width": "64", + "target-c-int-width": "32", + "data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", + "arch": "riscv64", + "os": "none", + "env": "", + "vendor": "unknown", + "linker": "rust-lld", + "linker-flavor": "gnu-lld", + "features": "+m,+a,+c,+zihintpause", + "dynamic-linking": false, + "executables": true, + "relocation-model": "pic", + "disable-redzone": true, + "frame-pointer": "always", + "exe-suffix": "", + "has-rpath": false, + "no-default-libraries": true, + "position-independent-executables": false +}