Initial RISC-V implementation

Has no IRQ handling yet
This commit is contained in:
Andrey Turkin
2024-10-20 12:15:09 +03:00
parent db32f5f7a3
commit 1921c6814b
36 changed files with 1999 additions and 57 deletions
-4
View File
@@ -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
Generated
+50 -43
View File
@@ -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",
+2 -2
View File
@@ -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" }
+3
View File
@@ -88,6 +88,9 @@ fn main() {
panic!("nasm failed with exit status {}", status);
}
}
"riscv64" => {
println!("cargo:rustc-cfg=dtb");
}
_ => (),
}
+68
View File
@@ -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*)
}
}
+1 -1
View File
@@ -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);
+6
View File
@@ -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::*;
+42
View File
@@ -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);
+65
View File
@@ -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<Log>>,
#[cfg(feature = "serial_debug")]
serial: MutexGuard<'a, Option<SerialPort>>,
#[cfg(feature = "graphical_debug")]
display: MutexGuard<'a, Option<DebugDisplay>>,
}
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(())
}
}
+5
View File
@@ -0,0 +1,5 @@
use core::fmt::{Result, Write};
pub fn cpu_info<W: Write>(_w: &mut W) -> Result {
unimplemented!()
}
+90
View File
@@ -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;
+78
View File
@@ -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<Mmio<u8>>,
}
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<Option<SerialPort>> = 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::<Mmio<u8>>::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(())
}
+201
View File
@@ -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()
}
+319
View File
@@ -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::<InterruptStack>());
}
}
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)
)
}
+79
View File
@@ -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<u32> {
Some(0)
}
pub fn available_irqs_iter(_cpu_id: LogicalCpuId) -> impl Iterator<Item = u8> + '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"
);
}
+1
View File
@@ -0,0 +1 @@
pub use super::handler::enter_usermode;
+31
View File
@@ -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<Self> {
let fp: usize;
asm!("mv {}, fp", out(reg) fp);
let pc_ptr = fp.checked_sub(mem::size_of::<usize>())?;
let fp = pc_ptr.checked_sub(mem::size_of::<usize>())?;
Some(StackTrace {
fp,
pc_ptr: pc_ptr as *const usize,
})
}
pub unsafe fn next(self) -> Option<Self> {
let fp = *(self.fp as *const usize);
let pc_ptr = fp.checked_sub(mem::size_of::<usize>())?;
let fp = pc_ptr.checked_sub(mem::size_of::<usize>())?;
Some(StackTrace {
fp: fp,
pc_ptr: pc_ptr as *const usize,
})
}
}
+32
View File
@@ -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) {}
+16
View File
@@ -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)*));
}
+45
View File
@@ -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
);
}
+72
View File
@@ -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
}
+11
View File
@@ -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;
}
}
+29
View File
@@ -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<RmmA> for InactiveFlusher {
fn consume(&mut self, flush: PageFlush<RmmA>) {
// 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);
}
}
+80
View File
@@ -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<RmmA, crate::memory::TheFrameAllocator>;
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<Page> {
if self.start < self.end {
let page = self.start;
self.start = self.start.next();
Some(page)
} else {
None
}
}
}
+31
View File
@@ -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<A: Arch>(virt: VirtualAddress) -> PageFlags<A> {
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)
}
}
+69
View File
@@ -0,0 +1,69 @@
use core::result;
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub struct Error {
pub errno: i32,
}
pub type Result<T, E = Error> = result::Result<T, E>;
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<usize> {
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<usize> {
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| ())
}
}
+235
View File
@@ -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<usize> {
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);
}
+13
View File
@@ -0,0 +1,13 @@
pub unsafe fn kreset() -> ! {
println!("kreset");
unimplemented!()
}
pub unsafe fn emergency_reset() -> ! {
unimplemented!()
}
pub unsafe fn kstop() -> ! {
println!("kstop");
unimplemented!()
}
+26
View File
@@ -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
}
}
+248
View File
@@ -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::<InterruptStack>();
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::<InterruptStack>()).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 = &regs.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<EnvRegisters> {
self.read_env_regs()
}
pub(crate) fn read_env_regs(&self) -> Result<EnvRegisters> {
match self.regs() {
Some(stack) => Ok(EnvRegisters {
tp: stack.registers.x4,
}),
None => Err(Error::new(ESRCH)),
}
}
}
pub static EMPTY_CR3: Once<rmm::PhysicalAddress> = 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<Table> {
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 })
}
+4
View File
@@ -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;
+8
View File
@@ -65,11 +65,19 @@ impl SerialPort<Pio<u8>> {
}
impl SerialPort<Mmio<u32>> {
#[allow(dead_code)]
pub unsafe fn new(base: usize) -> &'static mut SerialPort<Mmio<u32>> {
&mut *(base as *mut Self)
}
}
impl SerialPort<Mmio<u8>> {
#[allow(dead_code)]
pub unsafe fn new(base: usize) -> &'static mut SerialPort<Mmio<u8>> {
&mut *(base as *mut Self)
}
}
impl<T: Io> SerialPort<T>
where
T::Value: From<u8> + TryInto<u8>,
+6 -2
View File
@@ -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<Vec<u8>> = 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,
))
}
+3
View File
@@ -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;
+7 -5
View File
@@ -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);
}
+23
View File
@@ -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
}