diff options
author | Nathan Ringo <nathan@remexre.com> | 2024-09-01 19:59:44 -0500 |
---|---|---|
committer | Nathan Ringo <nathan@remexre.com> | 2024-09-01 19:59:44 -0500 |
commit | 386df39c9866a4d945de46ef0dcab2363c674e0e (patch) | |
tree | c0572ce6a2c81c93546210f599dff553783c5760 /crates/kernel | |
parent | 6b98b6afea6e790abe738a67aa28bab54c91afe0 (diff) |
Move almost all the kernel into crates/.
Diffstat (limited to 'crates/kernel')
-rw-r--r-- | crates/kernel/Cargo.toml | 17 | ||||
-rw-r--r-- | crates/kernel/src/arch/hosted.rs | 29 | ||||
-rw-r--r-- | crates/kernel/src/arch/mod.rs | 11 | ||||
-rw-r--r-- | crates/kernel/src/arch/riscv64/interrupts.rs | 80 | ||||
-rw-r--r-- | crates/kernel/src/arch/riscv64/mod.rs | 14 | ||||
-rw-r--r-- | crates/kernel/src/lib.rs | 101 | ||||
-rw-r--r-- | crates/kernel/src/panic.rs | 12 |
7 files changed, 264 insertions, 0 deletions
diff --git a/crates/kernel/Cargo.toml b/crates/kernel/Cargo.toml new file mode 100644 index 0000000..3d7b61b --- /dev/null +++ b/crates/kernel/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "vernos_kernel" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +cfg-if = { version = "1.0.0", default-features = false } +log = { version = "0.4.20", default-features = false } +vernos_alloc_buddy = { path = "../alloc_buddy" } +vernos_alloc_physmem_free_list = { path = "../alloc_physmem_free_list" } +vernos_device_tree = { path = "../device_tree" } +vernos_driver_riscv_timer = { path = "../driver_riscv_timer" } +vernos_utils = { path = "../utils" } +void = { version = "1.0.2", default-features = false } diff --git a/crates/kernel/src/arch/hosted.rs b/crates/kernel/src/arch/hosted.rs new file mode 100644 index 0000000..df62bab --- /dev/null +++ b/crates/kernel/src/arch/hosted.rs @@ -0,0 +1,29 @@ +//! Support for running under an operating system that provides libstd, for testing. + +extern crate std; + +use std::{thread::sleep, time::Duration}; + +/// The size of a page of memory. +/// +/// Obviously, this value is unrealistic, but for now we just need the hosted arch to compile. +pub const PAGE_SIZE: usize = 64; + +/// The number of bits in the size of a page of memory. +/// +/// Obviously, this value is unrealistic, but for now we just need the hosted arch to compile. +pub const PAGE_SIZE_BITS: usize = 6; + +/// No-opped interrupt support. +/// +/// TODO: Should this use Unix signals? +pub mod interrupts { + pub fn disable_interrupts() {} +} + +/// Sleeps forever, in one-second chunks. +pub fn sleep_forever() -> ! { + loop { + sleep(Duration::from_secs(1)); + } +} diff --git a/crates/kernel/src/arch/mod.rs b/crates/kernel/src/arch/mod.rs new file mode 100644 index 0000000..bfdfcc7 --- /dev/null +++ b/crates/kernel/src/arch/mod.rs @@ -0,0 +1,11 @@ +cfg_if::cfg_if! { + if #[cfg(not(target_os = "none"))] { + mod hosted; + pub use self::hosted::*; + } else if #[cfg(target_arch = "riscv64")] { + mod riscv64; + pub use self::riscv64::*; + } else { + compile_error!("unsupported platform"); + } +} diff --git a/crates/kernel/src/arch/riscv64/interrupts.rs b/crates/kernel/src/arch/riscv64/interrupts.rs new file mode 100644 index 0000000..84f2258 --- /dev/null +++ b/crates/kernel/src/arch/riscv64/interrupts.rs @@ -0,0 +1,80 @@ +use core::{ + arch::{asm, global_asm}, + time::Duration, +}; +use log::info; +use vernos_driver_riscv_timer::{set_timer, Instant}; + +/// Sets up the timer interrupt. +#[inline(never)] +pub(crate) fn example_timer() { + let now = Instant::now(); + info!("now = {now:?}"); + + let in_a_sec = now + Duration::from_secs(1); + info!("in_a_sec = {in_a_sec:?}"); + info!("setting a timer for 1s..."); + set_timer(in_a_sec); + + enable_interrupts(); +} + +/// Disables interrupts. +pub fn disable_interrupts() { + // Set SSTATUS.SIE to 0, which disables interrupts. + // + // SAFETY: Not running interrupts shouldn't be able to compromise safety. + unsafe { + asm!( + "csrc sstatus, {sie}", + sie = in(reg) (1 << 1), + options(nomem, nostack) + ); + } +} + +/// Enables interrupts. +pub fn enable_interrupts() { + // Set STVEC.BASE to the handler function, and STVEC.MODE to Direct. Since the trap_handler_asm + // has a `.align 4` before it, the lower two bits of its address should already be zero. + // + // SAFETY: Even if interrupts were already enabled, this is a valid handler. + unsafe { + asm!( + "csrw stvec, {stvec}", + stvec = in(reg) trap_handler_asm, + options(nomem, nostack) + ); + } + + // Set SSTATUS.SIE to 1, which enables interrupts. + // + // SAFETY: We just initialized STVEC, so it should be able to handle interrupts. + unsafe { + asm!( + "csrs sstatus, {sie}", + sie = in(reg) (1 << 1), + options(nomem, nostack) + ); + } +} + +fn trap_handler() { + todo!("trap_handler") +} + +// The assembly code that calls the Rust trap handler, after saving all caller-save registers +// to the stack. +global_asm! { + // Declare the handler's symbol. + ".align 4", + "trap_handler_asm:", + // TODO + "nop", + "call {trap_handler}", + trap_handler = sym trap_handler +} + +extern "C" { + fn trap_handler_asm(); +} diff --git a/crates/kernel/src/arch/riscv64/mod.rs b/crates/kernel/src/arch/riscv64/mod.rs new file mode 100644 index 0000000..216a90c --- /dev/null +++ b/crates/kernel/src/arch/riscv64/mod.rs @@ -0,0 +1,14 @@ +pub mod interrupts; + +/// The size of a page of memory. +pub const PAGE_SIZE: usize = 4096; + +/// The number of bits in the size of a page of memory. +pub const PAGE_SIZE_BITS: usize = 12; + +/// Halts the hart. +pub fn sleep_forever() -> ! { + loop { + unsafe { core::arch::asm!("wfi") } + } +} diff --git a/crates/kernel/src/lib.rs b/crates/kernel/src/lib.rs new file mode 100644 index 0000000..eff6d36 --- /dev/null +++ b/crates/kernel/src/lib.rs @@ -0,0 +1,101 @@ +//! The static library that forms the core of the kernel. +#![no_std] + +use crate::arch::{sleep_forever, PAGE_SIZE}; +use log::{debug, info, warn}; +use vernos_alloc_physmem_free_list::FreeListAllocator; +use vernos_device_tree::FlattenedDeviceTree; +use vernos_utils::dbg; + +#[cfg(target_os = "none")] +mod panic; + +pub mod arch; + +/// The entrypoint to the kernel. This should be executed by hart0 alone. It performs some early +/// boot tasks, then wakes up any other harts. +/// +/// # Safety +/// +/// - The `device_tree` pointer must be a valid pointer into physical memory. See +/// `device_tree::FlattenedDeviceTree::from_ptr` for the precise requirements. +/// - This must be called in supervisor mode with paging and traps disabled, but with all traps +/// delegated to supervisor mode. +/// - Any other harts must not be running concurrently with us. TODO: Define their state. +#[no_mangle] +pub unsafe extern "C" fn hart0_boot(device_tree: *const u8) -> ! { + // Set up the logger. + // + // TODO: This should really be named something better than console. + // console::init(); + + // Parse the DeviceTree. + let flattened_device_tree = + unsafe { FlattenedDeviceTree::from_ptr(device_tree) }.expect("invalid DeviceTree"); + + // Find the available physical memory areas and initialize the physical memory + // free-list. + let mut physical_memory_free_list = FreeListAllocator::<PAGE_SIZE>::new(); + dbg!(physical_memory_free_list); + + /* + flattened_device_tree + .for_each_node(|node| { + if node.is_unit(&["", "memory"]) { + // Get the memory ranges. + let Some(reg) = node.get_reg_usize() else { + warn!("{}reg was not valid", node.name()); + return Ok(()); + }; + + for (addr, size) in reg { + physical_memory_free_list.add_range(addr..addr + size); + } + } + Ok(()) + }) + .unwrap_or_else(|err| void::unreachable(err)); + + // Log the physical memory we found. + debug!( + "found {} usable regions of physical memory{}", + physical_memory_free_list.len(), + if physical_memory_free_list.is_empty() { + "" + } else { + ":" + } + ); + for region in physical_memory_free_list.drain() { + debug!( + "{:p}..{:p} ({} byte{})", + region.start as *const u8, + region.end as *const u8, + region.len(), + if region.len() == 1 { "" } else { "s" } + ) + } + */ + + // After this point, everything else is for debugging. + #[cfg(target_arch = "riscv64")] + { + flattened_device_tree + .for_each_node(|node| { + if node.is_unit(&["", "cpus"]) { + if let Some(timebase_frequency) = node.get_prop_u32("timebase-frequency") { + // SAFETY: Other harts are not concurrently running, so they can't be + // concurrently accessing or modifying this. + unsafe { + vernos_driver_riscv_timer::TIMEBASE_FREQUENCY = timebase_frequency; + } + } + } + Ok(()) + }) + .unwrap_or_else(|err| void::unreachable(err)); + arch::interrupts::example_timer(); + } + info!("sleeping forever..."); + sleep_forever(); +} diff --git a/crates/kernel/src/panic.rs b/crates/kernel/src/panic.rs new file mode 100644 index 0000000..dcb8d31 --- /dev/null +++ b/crates/kernel/src/panic.rs @@ -0,0 +1,12 @@ +//! The kernel panic handler. + +use crate::arch::{interrupts::disable_interrupts, sleep_forever}; +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + log::error!("{info}"); + + disable_interrupts(); + sleep_forever(); +} |