Initial RISC-V implementation
Has no IRQ handling yet
This commit is contained in:
@@ -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
@@ -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
@@ -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" }
|
||||
|
||||
|
||||
@@ -88,6 +88,9 @@ fn main() {
|
||||
panic!("nasm failed with exit status {}", status);
|
||||
}
|
||||
}
|
||||
"riscv64" => {
|
||||
println!("cargo:rustc-cfg=dtb");
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
||||
@@ -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*)
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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);
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use core::fmt::{Result, Write};
|
||||
|
||||
pub fn cpu_info<W: Write>(_w: &mut W) -> Result {
|
||||
unimplemented!()
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub use super::handler::enter_usermode;
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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) {}
|
||||
@@ -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)*));
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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| ())
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
pub unsafe fn kreset() -> ! {
|
||||
println!("kreset");
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub unsafe fn emergency_reset() -> ! {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub unsafe fn kstop() -> ! {
|
||||
println!("kstop");
|
||||
unimplemented!()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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 = ®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<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 })
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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,
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user