diff options
author | Nathan Ringo <nathan@remexre.com> | 2024-09-14 17:37:48 -0500 |
---|---|---|
committer | Nathan Ringo <nathan@remexre.com> | 2024-09-14 17:37:48 -0500 |
commit | 5a7617e4d524a74a4fb21f956fead71e789c454c (patch) | |
tree | 84832f1826f156d4ec54222c238fa247e4b09f34 /crates/kernel/src/lib.rs | |
parent | ec991590e4e3b92e407060410ff33525dc740988 (diff) |
Start of a platform-independent paging interface.
Diffstat (limited to 'crates/kernel/src/lib.rs')
-rw-r--r-- | crates/kernel/src/lib.rs | 204 |
1 files changed, 157 insertions, 47 deletions
diff --git a/crates/kernel/src/lib.rs b/crates/kernel/src/lib.rs index 517ace9..8c964af 100644 --- a/crates/kernel/src/lib.rs +++ b/crates/kernel/src/lib.rs @@ -1,9 +1,13 @@ //! The static library that forms the core of the kernel. #![no_std] -use crate::arch::{sleep_forever, PAGE_SIZE, PAGE_SIZE_BITS}; +use crate::{ + alloc::{alloc_page, init_kernel_page_table, kernel_log_page_table, kernel_map}, + constants::STACK_SIZE, + paging::{MappingFlags, PAGE_SIZE, PAGE_SIZE_BITS}, +}; use core::ptr::NonNull; -use log::{debug, info}; +use log::debug; use vernos_alloc_buddy::BuddyAllocator; use vernos_alloc_physmem_free_list::FreeListAllocator; use vernos_device_tree::FlattenedDeviceTree; @@ -13,34 +17,87 @@ mod panic; pub mod alloc; pub mod arch; +pub mod constants; pub mod logger; +pub mod paging; + +/// Some addresses passed from the entrypoint to hart0. +#[derive(Debug)] +#[repr(C)] +pub struct EarlyBootAddrs { + device_tree: *const u8, + kernel_start: *const [u8; PAGE_SIZE], + kernel_end: *const [u8; PAGE_SIZE], + kernel_rx_end: *const [u8; PAGE_SIZE], + kernel_ro_end: *const [u8; PAGE_SIZE], + kernel_rw_end: *const [u8; PAGE_SIZE], + initial_stack_start: *const [u8; PAGE_SIZE], + stack_end: *const [u8; PAGE_SIZE], + trampoline_start: *const [u8; PAGE_SIZE], +} + +impl EarlyBootAddrs { + /// Looks for a DeviceTree at address in the `EarlyBootAddrs`, and returns it if it looks + /// valid. Panics if the DeviceTree is invalid. + /// + /// ## Safety + /// + /// - The `EarlyBootAddrs` must be accurate. + /// - The `device_tree` pointer must be a valid pointer into physical memory. See + /// `device_tree::FlattenedDeviceTree::from_ptr` for the precise requirements. + unsafe fn flattened_device_tree(&self) -> FlattenedDeviceTree { + FlattenedDeviceTree::from_ptr( + self.device_tree, + self.kernel_start as usize..self.kernel_end as usize, + ) + .expect("invalid DeviceTree") + } +} /// The first stage of booting the kernel. This should be executed by hart0 alone. It runs with /// paging disabled, and: /// /// - sets up a physical memory allocator /// - sets up paging -/// - allocates some global structures in the higher half of memory: -/// - a logger's buffer -/// - a stack for this kernel thread -/// - the DeviceTree in tree form +/// - sets up a virtual memory allocator +/// - sets up an allocator +/// - maps the trampoline page +/// - maps the kernel to higher-half memory +/// - allocates and maps a kernel stack for hart0 /// -/// It returns the top address of the stack for this kernel thread. +/// It updates several fields in `early_boot_addrs` to point to the appropriate himem addresses: +/// +/// - `kernel_start` +/// - `stack_end` /// /// # Safety /// /// - The `device_tree` pointer must be a valid pointer into physical memory. See /// `device_tree::FlattenedDeviceTree::from_ptr` for the precise requirements. +/// - The `kernel_start`, `kernel_rx_end`, `kernel_ro_end`, `kernel_rw_end`, and `kernel_end` +/// addresses must be accurate and page-aligned. +/// - The `stack_start` and `stack_end` addresses must be accurate and page-aligned. +/// - The `trampoline_start` pointer must be accurate and page-aligned. /// - This must be called in supervisor mode with paging disabled. /// - Any other harts must not be running concurrently with us. #[no_mangle] -pub unsafe extern "C" fn hart0_early_boot(device_tree: *const u8) -> ! { - // Set up the logger. - logger::init(); +pub unsafe extern "C" fn hart0_early_boot(early_boot_addrs: &mut EarlyBootAddrs) { + // Set up the early-boot logger. + logger::init_early(); + vernos_utils::dbg!(&*early_boot_addrs); + + // Assert that stuff is aligned properly. + assert!(early_boot_addrs.kernel_start.is_aligned()); + assert!(early_boot_addrs.kernel_end.is_aligned()); + assert!(early_boot_addrs.kernel_rx_end.is_aligned()); + assert!(early_boot_addrs.kernel_ro_end.is_aligned()); + assert!(early_boot_addrs.kernel_rw_end.is_aligned()); + assert!(early_boot_addrs.initial_stack_start.is_aligned()); + assert!(early_boot_addrs.stack_end.is_aligned()); + assert!(early_boot_addrs.trampoline_start.is_aligned()); // Parse the DeviceTree. - let flattened_device_tree = - unsafe { FlattenedDeviceTree::from_ptr(device_tree) }.expect("invalid DeviceTree"); + let flattened_device_tree = unsafe { early_boot_addrs.flattened_device_tree() }; // Find the available physical memory areas and initialize the physical memory // free-list. @@ -84,16 +141,91 @@ pub unsafe extern "C" fn hart0_early_boot(device_tree: *const u8) -> ! { let alloc_buddy = BuddyAllocator::new(physical_memory_free_list) .expect("failed to configure the buddy allocator"); - // Set up the allocators. - alloc::init(alloc_buddy); + // Set up the kernel page table. + init_kernel_page_table(alloc_buddy); + + // Map the trampoline page. + let mut vaddr_bump = usize::MAX - PAGE_SIZE + 1; + kernel_map( + vaddr_bump, + early_boot_addrs.trampoline_start as usize, + PAGE_SIZE, + MappingFlags::R | MappingFlags::X, + ) + .expect("failed to map the trampoline page to himem"); + + // Skip a page down for a guard page, then map the kernel. + let total_kernel_pages = early_boot_addrs + .kernel_rw_end + .offset_from(early_boot_addrs.kernel_start) as usize; + vaddr_bump -= PAGE_SIZE * (total_kernel_pages + 1); + let new_kernel_start = vaddr_bump; + for i in 0..total_kernel_pages { + let vaddr = vaddr_bump + (i * PAGE_SIZE); + let paddr = early_boot_addrs.kernel_start.add(i); + let flags = if paddr < early_boot_addrs.kernel_rx_end { + MappingFlags::R | MappingFlags::X + } else if paddr < early_boot_addrs.kernel_ro_end { + MappingFlags::R + } else { + MappingFlags::R | MappingFlags::W + }; + + kernel_map(vaddr, paddr as usize, PAGE_SIZE, flags) + .expect("failed to map the kernel to himem"); + } + + // Skip a page down for a guard page, then map the top page of the stack. + vaddr_bump -= PAGE_SIZE; + let new_stack_end = vaddr_bump; + vaddr_bump -= PAGE_SIZE; + kernel_map( + vaddr_bump, + early_boot_addrs.initial_stack_start as usize, + PAGE_SIZE, + MappingFlags::R | MappingFlags::W, + ) + .expect("failed to map the initial stack to himem"); + + // Allocate and map more pages for the stack. + let new_stack_start = new_stack_end - STACK_SIZE; + vaddr_bump = new_stack_start; + for i in 0..((STACK_SIZE >> PAGE_SIZE_BITS) - 1) { + let vaddr = new_kernel_start + i << PAGE_SIZE_BITS; + let paddr = + alloc_page(PAGE_SIZE).expect("failed to allocate memory for a hart0 stack page"); + kernel_map( + vaddr, + paddr.into(), + PAGE_SIZE, + MappingFlags::R | MappingFlags::W, + ) + .expect("failed to map a hart0 stack page"); + } + + // Skip another page down for a guard page. + vaddr_bump -= PAGE_SIZE; - todo!() + // Set up the kernel virtual memory allocator (and general allocator). + init_kernel_virtual_memory_allocator(vaddr_bump); + + // Set the fields in `early_boot_addrs` that we promise to. + early_boot_addrs.kernel_start = new_kernel_start as *const [u8; PAGE_SIZE]; + early_boot_addrs.stack_end = new_stack_end as *const [u8; PAGE_SIZE]; + + // Log and return. + kernel_log_page_table(); } -/// The entrypoint to the kernel, to be run after paging is set up. This should be executed by -/// hart0 alone. It performs some early boot tasks, then wakes up any other harts. +/// The entrypoint to the kernel, to be run after paging and the allocator have been set up, and +/// the stack has been switched to be in himem. This should be executed by hart0 alone. It performs +/// some early boot tasks, then wakes up any other harts. +/// +/// The tasks it performs are: /// -/// It receives the stack canary from the initial stack, and validates it. +/// - converts the DeviceTree into a global key-value mapping +/// - upgrades the logger to one that can dynamically grow +/// - TODO /// /// # Safety /// @@ -102,34 +234,12 @@ pub unsafe extern "C" fn hart0_early_boot(device_tree: *const u8) -> ! { /// 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(stack_canary: u64) -> ! { - assert_eq!(stack_canary, 0xdead0bad0defaced); - - /* - // 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(); - } - */ +pub unsafe extern "C" fn hart0_boot(early_boot_addrs: &mut EarlyBootAddrs) -> ! { + // Check that the stack canary was present. + assert_eq!( + *(early_boot_addrs.initial_stack_start as *const u64), + 0xdead0bad0defaced + ); - if true { - todo!(); - } - info!("sleeping forever..."); - sleep_forever(); + todo!("hart0_boot"); } |