//! The static library that forms the core of the kernel. #![no_std] use crate::arch::{sleep_forever, PAGE_SIZE, PAGE_SIZE_BITS}; use core::ptr::NonNull; use log::{debug, info}; use vernos_alloc_buddy::BuddyAllocator; use vernos_alloc_physmem_free_list::FreeListAllocator; use vernos_device_tree::FlattenedDeviceTree; #[cfg(target_os = "none")] mod panic; pub mod alloc; pub mod arch; pub mod logger; /// 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 /// /// It returns the top address of the stack for this kernel thread. /// /// # 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 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(); // 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::::new(); let mut physical_memory_region_count = 0; flattened_device_tree .for_each_memory_range::<_, PAGE_SIZE>(|addrs| { let len_bytes = addrs.end - addrs.start; assert!(addrs.start.trailing_zeros() as usize >= PAGE_SIZE_BITS); assert!(len_bytes.trailing_zeros() as usize >= PAGE_SIZE_BITS); // UNWRAP: for_each_memory_range avoids returning the zero address. let addr = NonNull::new(addrs.start as *mut [u8; PAGE_SIZE]).unwrap(); let len_pages = len_bytes >> PAGE_SIZE_BITS; physical_memory_free_list.add(addr, len_pages); physical_memory_region_count += 1; Ok(()) }) .unwrap_or_else(|err| void::unreachable(err)); // Log the physical memory we found. debug!( "found {} usable regions of physical memory{}", physical_memory_region_count, if physical_memory_region_count == 0 { "" } else { ":" } ); for (addr, len_pages) in physical_memory_free_list.iter() { debug!( "{:p}..{:p} ({} bytes)", addr.as_ptr(), addr.as_ptr().wrapping_add(len_pages), len_pages << PAGE_SIZE_BITS, ) } // Initialize the buddy allocator. let alloc_buddy = BuddyAllocator::new(physical_memory_free_list) .expect("failed to configure the buddy allocator"); // Set up the allocators. alloc::init(alloc_buddy); todo!() } /// 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. /// /// It receives the stack canary from the initial stack, and validates it. /// /// # Safety /// /// - `hart0_early_boot` must have been called. /// - This must be called in supervisor mode with 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(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(); } */ if true { todo!(); } info!("sleeping forever..."); sleep_forever(); }