summaryrefslogtreecommitdiff
path: root/crates/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'crates/kernel')
-rw-r--r--crates/kernel/Cargo.toml17
-rw-r--r--crates/kernel/src/arch/hosted.rs29
-rw-r--r--crates/kernel/src/arch/mod.rs11
-rw-r--r--crates/kernel/src/arch/riscv64/interrupts.rs80
-rw-r--r--crates/kernel/src/arch/riscv64/mod.rs14
-rw-r--r--crates/kernel/src/lib.rs101
-rw-r--r--crates/kernel/src/panic.rs12
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();
+}