diff --git a/Makefile b/Makefile index f2268a8..2029474 100644 --- a/Makefile +++ b/Makefile @@ -76,6 +76,12 @@ endif clean: cargo clean +doc: + cd kernel && cargo doc --all-features --target=../etc/$(ARCH)-$(MACH).json + +doc-open: + cd kernel && cargo doc --open --all-features --target=../etc/$(ARCH)-$(MACH).json + clippy: cd kernel && cargo clippy $(CARGO_BUILD_OPTS) diff --git a/kernel/src/arch/aarch64/boot/mod.rs b/kernel/src/arch/aarch64/boot/mod.rs index dec33bd..f6c3759 100644 --- a/kernel/src/arch/aarch64/boot/mod.rs +++ b/kernel/src/arch/aarch64/boot/mod.rs @@ -1,6 +1,6 @@ //! aarch64 common boot logic -use crate::arch::{aarch64::asm::CPACR_EL1, machine}; +use crate::arch::{aarch64::reg::CPACR_EL1, machine}; use crate::dev::{fdt::DeviceTree, irq::IntSource, Device}; use crate::mem::{ self, heap, diff --git a/kernel/src/arch/aarch64/context.rs b/kernel/src/arch/aarch64/context.rs index 43986dc..c5d9532 100644 --- a/kernel/src/arch/aarch64/context.rs +++ b/kernel/src/arch/aarch64/context.rs @@ -1,4 +1,4 @@ -#![allow(missing_docs)] +//! Thread context use crate::mem::{ self, @@ -11,9 +11,12 @@ struct Stack { sp: usize, } +/// Structure representing thread context #[repr(C)] pub struct Context { - pub k_sp: usize, // 0x00 + /// Thread's kernel stack pointer + pub k_sp: usize, // 0x00 + /// Thread's translation table physical address with ASID pub ttbr0: usize, // 0x08 stack_base_phys: usize, @@ -21,6 +24,7 @@ pub struct Context { } impl Context { + /// Constructs a new kernel-space thread context pub fn kernel(entry: usize, arg: usize, ttbr0: usize, ustack: usize) -> Self { let mut stack = Stack::new(8); @@ -51,11 +55,22 @@ impl Context { } } + /// Performs initial thread entry + /// + /// # Safety + /// + /// Unsafe: does not check if any context has already been activated + /// before, so must only be called once. pub unsafe extern "C" fn enter(&mut self) -> ! { __aa64_ctx_switch_to(self); panic!("This code should not run"); } + /// Performs context switch from `self` to `to`. + /// + /// # Safety + /// + /// Unsafe: does not check if `self` is actually an active context. pub unsafe extern "C" fn switch(&mut self, to: &mut Context) { __aa64_ctx_switch(to, self); } diff --git a/kernel/src/arch/aarch64/mod.rs b/kernel/src/arch/aarch64/mod.rs index 611b704..3eafcc8 100644 --- a/kernel/src/arch/aarch64/mod.rs +++ b/kernel/src/arch/aarch64/mod.rs @@ -3,11 +3,11 @@ use cortex_a::registers::DAIF; use tock_registers::interfaces::{Readable, Writeable}; -pub mod asm; pub mod boot; pub mod context; pub mod exception; pub mod irq; +pub mod reg; pub mod timer; cfg_if! { diff --git a/kernel/src/arch/aarch64/asm.rs b/kernel/src/arch/aarch64/reg/cpacr_el1.rs similarity index 68% rename from kernel/src/arch/aarch64/asm.rs rename to kernel/src/arch/aarch64/reg/cpacr_el1.rs index 0075ab8..3c7077b 100644 --- a/kernel/src/arch/aarch64/asm.rs +++ b/kernel/src/arch/aarch64/reg/cpacr_el1.rs @@ -1,5 +1,4 @@ -//! Assembly intrinsics for AArch64 platform -#![allow(missing_docs)] +//! CPACR_EL1 register use tock_registers::{ interfaces::{Readable, Writeable}, @@ -8,15 +7,24 @@ use tock_registers::{ register_bitfields! { u64, + #[allow(missing_docs)] + /// EL1 Architectural Feature Access Control Register pub CPACR_EL1 [ + /// Enable EL0 and EL1 SIMD/FP accesses to EL1 FPEN OFFSET(20) NUMBITS(2) [ + /// Trap both EL0 and EL1 TrapAll = 0, + /// Trap EL0 TrapEl0 = 1, + /// Trap EL1 + TrapEl1 = 2, + /// Do not trap any SIMD/FP instructions TrapNone = 3 ] ] } +/// CPACR_EL1 register pub struct Reg; impl Readable for Reg { @@ -45,4 +53,5 @@ impl Writeable for Reg { } } +/// CPACR_EL1 register pub const CPACR_EL1: Reg = Reg; diff --git a/kernel/src/arch/aarch64/reg/mod.rs b/kernel/src/arch/aarch64/reg/mod.rs new file mode 100644 index 0000000..a85fe8e --- /dev/null +++ b/kernel/src/arch/aarch64/reg/mod.rs @@ -0,0 +1,4 @@ +//! AArch64 architectural registers + +pub mod cpacr_el1; +pub use cpacr_el1::CPACR_EL1; diff --git a/kernel/src/debug.rs b/kernel/src/debug.rs index 0dc1e5e..067eefe 100644 --- a/kernel/src/debug.rs +++ b/kernel/src/debug.rs @@ -1,8 +1,15 @@ //! Debug output module. //! -//! The module provides [debug!] and [debugln!] macros +//! The module provides [print!] and [println!] macros //! which can be used in similar way to print! and //! println! from std. +//! +//! Level-specific debugging macros are provided as well: +//! +//! * [debugln!] +//! * [infoln!] +//! * [warnln!] +//! * [errorln!] use crate::dev::serial::SerialDevice; use core::fmt; diff --git a/kernel/src/dev/irq.rs b/kernel/src/dev/irq.rs index 65246b9..c05e3ab 100644 --- a/kernel/src/dev/irq.rs +++ b/kernel/src/dev/irq.rs @@ -13,14 +13,14 @@ pub trait IntController: Device { /// Implementation-specific definition for "IRQ line" type IrqNumber; - /// Binds a handler [IntSource] to a specific [irq] line + /// Binds a handler [IntSource] to a specific `irq` line fn register_handler( &self, irq: Self::IrqNumber, handler: &'static (dyn IntSource + Sync), ) -> Result<(), Errno>; - /// Enables/unmasks [irq] line + /// Enables/unmasks `irq` line fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Errno>; /// Handles all pending IRQs for this interrupt controller diff --git a/kernel/src/dev/pci/mod.rs b/kernel/src/dev/pci/mod.rs index 3469bd8..eb23cd9 100644 --- a/kernel/src/dev/pci/mod.rs +++ b/kernel/src/dev/pci/mod.rs @@ -87,7 +87,7 @@ pub trait PciHostDevice: Device { } impl PciAddress { - /// Constructs a [PCIAddress] instance from its components + /// Constructs a [PciAddress] instance from its components #[inline(always)] pub const fn new(bus: u8, dev: u8, func: u8) -> Self { Self { @@ -95,25 +95,25 @@ impl PciAddress { } } - /// Returns `bus` field of [PCIAddress] + /// Returns `bus` field of [PciAddress] #[inline(always)] pub const fn bus(self) -> u8 { (self.value >> 8) as u8 } - /// Returns `dev` field of [PCIAddress] + /// Returns `dev` field of [PciAddress] #[inline(always)] pub const fn dev(self) -> u8 { ((self.value >> 3) as u8) & 0x1F } - /// Returns `func` field of [PCIAddress] + /// Returns `func` field of [PciAddress] #[inline(always)] pub const fn func(self) -> u8 { (self.value as u8) & 0x7 } - /// Returns a new [PCIAddress], constructed from `self`, but with + /// Returns a new [PciAddress], constructed from `self`, but with /// specified `func` number #[inline(always)] pub const fn with_func(self, func: u8) -> Self { diff --git a/kernel/src/mem/heap.rs b/kernel/src/mem/heap.rs index 31a6aa8..36011c6 100644 --- a/kernel/src/mem/heap.rs +++ b/kernel/src/mem/heap.rs @@ -1,3 +1,5 @@ +//! Kernel heap allocation facilities + use crate::sync::IrqSafeNullLock; use crate::util::InitOnce; use core::alloc::{GlobalAlloc, Layout}; @@ -49,6 +51,11 @@ static SYSTEM_ALLOC: SystemAlloc = SystemAlloc; static HEAP: InitOnce> = InitOnce::new(); +/// Initializes kernel heap with virtual `base` address and `size`. +/// +/// # Safety +/// +/// Unsafe: accepts arbitrary `base` and `size` parameters. pub unsafe fn init(base: usize, size: usize) { let heap = Heap { base, size, ptr: 0 }; diff --git a/kernel/src/mem/mod.rs b/kernel/src/mem/mod.rs index fc27d75..04267aa 100644 --- a/kernel/src/mem/mod.rs +++ b/kernel/src/mem/mod.rs @@ -1,5 +1,4 @@ //! Memory management and functions module -#![allow(missing_docs)] pub mod heap; pub mod phys; @@ -7,13 +6,20 @@ pub mod virt; /// Virtual offset applied to kernel address space pub const KERNEL_OFFSET: usize = 0xFFFFFF8000000000; + +/// Default page size used by the kernel +pub const PAGE_SIZE: usize = 4096; + +/// Returns input `addr` with [KERNEL_OFFSET] applied. /// +/// Will panic if `addr` is not mapped by kernel's +/// direct translation tables. pub fn virtualize(addr: usize) -> usize { - // TODO remove this function + assert!(addr < (256 << 30)); addr + KERNEL_OFFSET } -/// +/// Returns the physical address of kernel's end in memory. pub fn kernel_end_phys() -> usize { extern "C" { static __kernel_end: u8; @@ -21,9 +27,6 @@ pub fn kernel_end_phys() -> usize { unsafe { &__kernel_end as *const _ as usize - KERNEL_OFFSET } } -/// -pub const PAGE_SIZE: usize = 4096; - /// See memcpy(3p). /// /// # Safety diff --git a/kernel/src/mem/phys/mod.rs b/kernel/src/mem/phys/mod.rs index bd4391d..06bc637 100644 --- a/kernel/src/mem/phys/mod.rs +++ b/kernel/src/mem/phys/mod.rs @@ -1,3 +1,5 @@ +//! Physical memory management facilities + use crate::mem::PAGE_SIZE; use core::mem::size_of; use error::Errno; @@ -12,33 +14,46 @@ type ManagerImpl = SimpleManager; const MAX_PAGES: usize = 1024 * 1024; +/// These describe what a memory page is used for #[derive(PartialEq, Debug, Clone, Copy)] pub enum PageUsage { + /// The page cannot be allocated/used Reserved, + /// The page can be allocated and is unused at the moment Available, + /// Kernel data page Kernel, + /// Kernel heap page KernelHeap, + /// Translation tables Paging, + /// Userspace page UserStack, } +/// Data structure representing a single physical memory page pub struct PageInfo { refcount: usize, usage: PageUsage, } +/// Page-aligned physical memory region #[derive(Clone)] pub struct MemoryRegion { + /// Start address (page-aligned) pub start: usize, + /// End address (page-aligned) pub end: usize, } +/// Wrapper for single-region physical memory initialization #[repr(transparent)] #[derive(Clone)] pub struct SimpleMemoryIterator { inner: Option, } impl SimpleMemoryIterator { + /// Constructs a new instance of [Self] pub const fn new(reg: MemoryRegion) -> Self { Self { inner: Some(reg) } } @@ -50,6 +65,7 @@ impl Iterator for SimpleMemoryIterator { } } +/// Allocates a contiguous range of `count` physical memory pages. pub fn alloc_contiguous_pages(pu: PageUsage, count: usize) -> Result { MANAGER .lock() @@ -58,6 +74,7 @@ pub fn alloc_contiguous_pages(pu: PageUsage, count: usize) -> Result Result { MANAGER.lock().as_mut().unwrap().alloc_page(pu) } @@ -84,6 +101,8 @@ fn find_contiguous>(iter: T, count: usize) -> O None } +/// Initializes physical memory manager using an iterator of available +/// physical memory ranges pub unsafe fn init_from_iter + Clone>(iter: T) { let mut mem_base = usize::MAX; for reg in iter.clone() { @@ -122,6 +141,9 @@ pub unsafe fn init_from_iter + Clone>(iter: T) *MANAGER.lock() = Some(manager); } +/// Initializes physical memory manager using a single memory region. +/// +/// See [init_from_iter]. pub unsafe fn init_from_region(base: usize, size: usize) { let iter = SimpleMemoryIterator::new(MemoryRegion { start: base, diff --git a/kernel/src/mem/phys/reserved.rs b/kernel/src/mem/phys/reserved.rs index b5f1e73..48e3f57 100644 --- a/kernel/src/mem/phys/reserved.rs +++ b/kernel/src/mem/phys/reserved.rs @@ -2,11 +2,15 @@ use crate::mem::{kernel_end_phys, PAGE_SIZE}; use core::mem::MaybeUninit; use core::ptr::null_mut; +/// Data structure representing a region of unusable memory pub struct ReservedRegion { + /// Start address (page aligned) pub start: usize, + /// End address (page aligned) pub end: usize, next: *mut ReservedRegion, } +/// Struct for iterating over reserved memory regions pub struct ReservedRegionIterator { ptr: *mut ReservedRegion, } @@ -22,8 +26,9 @@ impl Iterator for ReservedRegionIterator { } } impl ReservedRegion { + /// Constructs a new instance of [Self] pub const fn new(start: usize, end: usize) -> ReservedRegion { - //assert!(start.is_paligned() && end.is_paligned()); + assert!(start & 0xFFF == 0 && end & 0xFFF == 0); ReservedRegion { start, end, @@ -34,6 +39,12 @@ impl ReservedRegion { static mut RESERVED_REGIONS_HEAD: *mut ReservedRegion = null_mut(); static mut RESERVED_REGION_KERNEL: MaybeUninit = MaybeUninit::uninit(); static mut RESERVED_REGION_PAGES: MaybeUninit = MaybeUninit::uninit(); + +/// Adds a `region` to reserved memory region list. +/// +/// # Safety +/// +/// Unsafe: `region` is passed as a raw pointer. pub unsafe fn reserve(usage: &str, region: *mut ReservedRegion) { infoln!( "Reserving {:?} region: {:#x}..{:#x}", @@ -44,6 +55,7 @@ pub unsafe fn reserve(usage: &str, region: *mut ReservedRegion) { (*region).next = RESERVED_REGIONS_HEAD; RESERVED_REGIONS_HEAD = region; } + pub(super) unsafe fn reserve_kernel() { RESERVED_REGION_KERNEL.write(ReservedRegion::new(0, kernel_end_phys())); reserve("kernel", RESERVED_REGION_KERNEL.as_mut_ptr()); @@ -52,6 +64,9 @@ pub(super) unsafe fn reserve_pages(base: usize, count: usize) { RESERVED_REGION_PAGES.write(ReservedRegion::new(base, base + count * PAGE_SIZE)); reserve("pages", RESERVED_REGION_PAGES.as_mut_ptr()); } + +/// Returns `true` if physical memory referred to by `page` cannot be +/// used and/or allocated pub fn is_reserved(page: usize) -> bool { unsafe { let mut iter = RESERVED_REGIONS_HEAD; diff --git a/kernel/src/mem/virt/fixed.rs b/kernel/src/mem/virt/fixed.rs index 28e32c2..1327828 100644 --- a/kernel/src/mem/virt/fixed.rs +++ b/kernel/src/mem/virt/fixed.rs @@ -1,17 +1,4 @@ -// -//#[repr(C, align(0x1000))] -//pub struct OldTable([u64; 512]); -// -//#[no_mangle] -//static mut KERNEL_TTBR1: OldTable = OldTable([0; 512]); -//// 1GiB -//static mut KERNEL_L1: OldTable = OldTable([0; 512]); -//// 2MiB -//static mut KERNEL_L2: OldTable = OldTable([0; 512]); -//static mut COUNT: usize = 0; -//static mut BIG_COUNT: usize = 1; -//static mut HUGE_COUNT: usize = 1; -// +//! Fixed-size table group for device MMIO mappings use crate::mem::{ self, @@ -22,6 +9,8 @@ use error::Errno; const DEVICE_MAP_OFFSET: usize = mem::KERNEL_OFFSET + (256usize << 30); +/// Fixed-layout group of tables describing device MMIO and kernel identity +/// mappings #[repr(C, align(0x1000))] pub struct FixedTableGroup { l0: Table, @@ -34,6 +23,8 @@ pub struct FixedTableGroup { } impl FixedTableGroup { + /// Constructs a new instance of [Self], initialized with non-present mapping + /// entries pub const fn empty() -> Self { Self { l0: Table::empty(), @@ -46,6 +37,10 @@ impl FixedTableGroup { } } + /// Allocates a virtual memory range from this table group for requested + /// `phys`..`phys + count * PAGE_SIZE` physical memory region and maps it. + /// + /// TODO: only allows 4K, 2M and 1G mappings. pub fn map_region(&mut self, phys: usize, count: usize) -> Result { // TODO generalize region allocation let phys_page = phys & !0xFFF; @@ -101,6 +96,7 @@ impl FixedTableGroup { } } + /// Sets up initial mappings for 4K, 2M and 1G device memory page translation pub fn init_device_map(&mut self) { let l1_phys = (&self.l1 as *const _) as usize - mem::KERNEL_OFFSET; let l2_phys = (&self.l2 as *const _) as usize - mem::KERNEL_OFFSET; diff --git a/kernel/src/mem/virt/mod.rs b/kernel/src/mem/virt/mod.rs index 688fd69..5d4a9ec 100644 --- a/kernel/src/mem/virt/mod.rs +++ b/kernel/src/mem/virt/mod.rs @@ -1,4 +1,4 @@ -#![allow(missing_docs)] +//! Virtual memory and translation table management use core::marker::PhantomData; use core::ops::Deref; @@ -15,7 +15,10 @@ pub use fixed::FixedTableGroup; #[no_mangle] static mut KERNEL_TTBR1: FixedTableGroup = FixedTableGroup::empty(); -#[derive(Debug)] +/// Structure representing a region of memory used for MMIO/device access +// TODO: this shouldn't be trivially-cloneable and should instead incorporate +// refcount and properly implement Drop trait +#[derive(Debug, Clone)] #[allow(dead_code)] pub struct DeviceMemory { name: &'static str, @@ -23,16 +26,24 @@ pub struct DeviceMemory { count: usize, } +/// Structure implementing `Deref` for convenient MMIO register access. +/// +/// See [DeviceMemory]. pub struct DeviceMemoryIo { mmio: DeviceMemory, _0: PhantomData, } impl DeviceMemory { + /// Returns base address of this MMIO region #[inline(always)] pub const fn base(&self) -> usize { self.base } + /// Allocates a virtual memory region and maps it to contiguous region + /// `phys`..`phys + count * PAGE_SIZE` for MMIO use. + /// + /// See [FixedTableGroup::map_region] pub fn map(name: &'static str, phys: usize, count: usize) -> Result { let base = unsafe { KERNEL_TTBR1.map_region(phys, count) }?; debugln!( @@ -44,18 +55,10 @@ impl DeviceMemory { ); Ok(Self { name, base, count }) } - - pub unsafe fn clone(&self) -> Self { - // TODO maybe add refcount and remove "unsafe"? - Self { - name: self.name, - base: self.base, - count: self.count, - } - } } impl DeviceMemoryIo { + /// Constructs a new [DeviceMemoryIo] from existing `mmio` region pub const fn new(mmio: DeviceMemory) -> Self { Self { mmio, @@ -63,6 +66,9 @@ impl DeviceMemoryIo { } } + /// Allocates and maps device MMIO memory. + /// + /// See [DeviceMemory::map] pub unsafe fn map(name: &'static str, phys: usize, count: usize) -> Result { DeviceMemory::map(name, phys, count).map(Self::new) } @@ -77,6 +83,8 @@ impl Deref for DeviceMemoryIo { } } +/// Sets up device mapping tables and disable lower-half +/// identity-mapped translation pub fn enable() -> Result<(), Errno> { unsafe { KERNEL_TTBR1.init_device_map(); diff --git a/kernel/src/mem/virt/table.rs b/kernel/src/mem/virt/table.rs index 8457fdc..67f3391 100644 --- a/kernel/src/mem/virt/table.rs +++ b/kernel/src/mem/virt/table.rs @@ -1,3 +1,5 @@ +//! Translation table manipulation facilities + use crate::mem::{ self, phys::{self, PageUsage}, @@ -5,32 +7,49 @@ use crate::mem::{ use core::ops::{Index, IndexMut}; use error::Errno; +/// Transparent wrapper structure representing a single +/// translation table entry #[derive(Clone, Copy)] #[repr(transparent)] pub struct Entry(u64); +/// Structure describing a single level of translation mappings #[repr(C, align(0x1000))] pub struct Table { entries: [Entry; 512], } +/// Wrapper for top-most level of address translation tables #[repr(transparent)] pub struct Space(Table); bitflags! { + /// Attributes attached to each translation [Entry] pub struct MapAttributes: u64 { // TODO use 2 lower bits to determine mapping size? + /// nG bit -- determines whether a TLB entry associated with this mapping + /// applies only to current ASID or all ASIDs. const NOT_GLOBAL = 1 << 11; + /// AF bit -- must be set by software, otherwise Access Error exception is + /// generated when the page is accessed const ACCESS = 1 << 10; + /// The memory region is outer-shareable const SH_OUTER = 2 << 8; + /// This page is used for device-MMIO mapping and uses MAIR attribute #1 const DEVICE = 1 << 2; + /// UXN bit -- if set, page may not be used for instruction fetching from EL0 const UXN = 1 << 54; + /// PXN bit -- if set, page may not be used for instruction fetching from EL1 const PXN = 1 << 53; } } impl Table { + /// Returns next-level translation table reference for `index`, if one is present. + /// If `index` represents a `Block`-type mapping, will return an error. + /// If `index` does not map to any translation table, will try to allocate, init and + /// map a new one, returning it after doing so. pub fn next_level_table_or_alloc(&mut self, index: usize) -> Result<&'static mut Table, Errno> { let entry = self[index]; if entry.is_present() { @@ -48,6 +67,7 @@ impl Table { } } + /// Constructs and fills a [Table] with non-present mappings pub const fn empty() -> Table { Table { entries: [Entry::invalid(); 512], @@ -74,32 +94,45 @@ impl Entry { const TABLE: u64 = 1 << 1; const PHYS_MASK: u64 = 0x0000FFFFFFFFF000; + /// Constructs a single non-present mapping pub const fn invalid() -> Self { Self(0) } + /// Constructs a `Block`-type memory mapping pub const fn block(phys: usize, attrs: MapAttributes) -> Self { Self((phys as u64 & Self::PHYS_MASK) | attrs.bits() | Self::PRESENT) } + /// Constructs a `Table` or `Page`-type mapping depending on translation level + /// this entry is used at pub const fn table(phys: usize, attrs: MapAttributes) -> Self { Self((phys as u64 & Self::PHYS_MASK) | attrs.bits() | Self::PRESENT | Self::TABLE) } + /// Returns `true` if this entry is not invalid pub const fn is_present(self) -> bool { self.0 & Self::PRESENT != 0 } + /// Returns `true` if this entry is a `Table` or `Page`-type mapping pub const fn is_table(self) -> bool { self.0 & Self::TABLE != 0 } + /// Returns the target address of this translation entry. + /// + /// # Safety + /// + /// Does not check if the entry is actually valid. pub const unsafe fn address_unchecked(self) -> usize { (self.0 & Self::PHYS_MASK) as usize } } impl Space { + /// Creates a new virtual address space and fills it with [Entry::invalid()] + /// mappings. Does physical memory page allocation. pub fn alloc_empty() -> Result<&'static mut Self, Errno> { let phys = phys::alloc_page(PageUsage::Paging)?; let res = unsafe { &mut *(mem::virtualize(phys) as *mut Self) }; @@ -107,6 +140,9 @@ impl Space { Ok(res) } + /// Inserts a single `virt` -> `phys` translation entry to this address space. + /// + /// TODO: only works with 4K-sized pages at this moment. pub fn map(&mut self, virt: usize, phys: usize, flags: MapAttributes) -> Result<(), Errno> { let l0i = virt >> 30; let l1i = (virt >> 21) & 0x1FF; diff --git a/kernel/src/proc/mod.rs b/kernel/src/proc/mod.rs index 4f249ee..7fd66bb 100644 --- a/kernel/src/proc/mod.rs +++ b/kernel/src/proc/mod.rs @@ -1,4 +1,4 @@ -#![allow(missing_docs)] +//! Process and thread manipulation facilities use crate::mem::{ self, @@ -14,8 +14,10 @@ use core::sync::atomic::{AtomicU32, Ordering}; pub use crate::arch::platform::context::{self, Context}; +/// Wrapper type for a process struct reference pub type ProcessRef = Rc>; +/// Structure describing an operating system process #[allow(dead_code)] pub struct Process { ctx: Context, @@ -24,12 +26,15 @@ pub struct Process { } struct SchedulerInner { + // TODO the process list itself is not a scheduler-related thing so maybe + // move it outside? processes: BTreeMap, queue: VecDeque, idle: u32, current: Option, } +/// Process scheduler state and queues pub struct Scheduler { inner: InitOnce>, } @@ -96,18 +101,31 @@ impl SchedulerInner { } impl Scheduler { + /// Constructs a new kernel-space process with `entry` and `arg`. + /// Returns resulting process ID + // TODO see the first TODO here pub fn new_kernel(&self, entry: usize, arg: usize) -> u32 { self.inner.get().lock().new_kernel(entry, arg) } + /// Initializes inner data structure: + /// + /// * idle thread + /// * process list/queue data structs pub fn init(&self) { self.inner.init(IrqSafeNullLock::new(SchedulerInner::new())); } + /// Schedules a thread for execution pub fn enqueue(&self, pid: u32) { self.inner.get().lock().queue.push_back(pid); } + /// Performs initial process entry. + /// + /// # Safety + /// + /// Unsafe: may only be called once, repeated calls will cause UB. pub unsafe fn enter(&self) -> ! { let thread = { let mut inner = self.inner.get().lock(); @@ -124,6 +142,8 @@ impl Scheduler { (*thread.get()).ctx.enter(); } + /// Switches to the next task scheduled for execution. If there're + /// none present in the queue, switches to the idle task. pub fn switch(&self) { let (from, to) = { let mut inner = self.inner.get().lock(); @@ -175,6 +195,9 @@ extern "C" fn f0(a: usize) -> ! { } } +/// Performs a task switch. +/// +/// See [Scheduler::switch] pub fn switch() { SCHED.switch(); } @@ -183,6 +206,13 @@ static SCHED: Scheduler = Scheduler { inner: InitOnce::new(), }; +/// Sets up initial process and enters it. +/// +/// See [Scheduler::enter] +/// +/// # Safety +/// +/// Unsafe: May only be called once. pub unsafe fn enter() -> ! { SCHED.init(); for i in 0..4 { diff --git a/kernel/src/sync.rs b/kernel/src/sync.rs index a701a93..5022ad0 100644 --- a/kernel/src/sync.rs +++ b/kernel/src/sync.rs @@ -4,14 +4,13 @@ use crate::arch::platform::{irq_mask_save, irq_restore}; use core::cell::UnsafeCell; use core::ops::{Deref, DerefMut}; -/// Same as [NullLock], but ensures IRQs are disabled while -/// the lock is held +/// Lock structure ensuring IRQs are disabled when inner value is accessed pub struct IrqSafeNullLock { value: UnsafeCell, } -/// Same as [NullLockGuard], but reverts IRQ mask back to normal -/// when dropped +/// Guard-structure wrapping a reference to value owned by [IrqSafeNullLock]. +/// Restores saved IRQ state when dropped. pub struct IrqSafeNullLockGuard<'a, T: ?Sized> { value: &'a mut T, irq_state: u64, diff --git a/kernel/src/util.rs b/kernel/src/util.rs index 8596b87..948c31c 100644 --- a/kernel/src/util.rs +++ b/kernel/src/util.rs @@ -1,15 +1,18 @@ -#![allow(missing_docs)] +//! Various utilities used by the kernel use core::cell::UnsafeCell; use core::mem::MaybeUninit; use core::sync::atomic::{AtomicBool, Ordering}; +/// Wrapper structure to guarantee single initialization +/// of a value pub struct InitOnce { state: AtomicBool, inner: UnsafeCell>, } impl InitOnce { + /// Constructs a new instance of [InitOnce] pub const fn new() -> Self { Self { state: AtomicBool::new(false), @@ -17,21 +20,26 @@ impl InitOnce { } } + /// Returns `true` if this [InitOnce] can be used #[inline(always)] pub fn is_initialized(&self) -> bool { self.state.load(Ordering::Acquire) } + /// Returns the initialized value. Will panic if the value has not + /// yet been initialized. #[allow(clippy::mut_from_ref)] pub fn get(&self) -> &mut T { assert!(self.is_initialized(), "Access to uninitialized InitOnce"); unsafe { (*self.inner.get()).assume_init_mut() } } + /// Initializes the storage with `value`. Will panic if the storage has + /// already been initialized. pub fn init(&self, value: T) { assert!( self.state - .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed) + .compare_exchange_weak(false, true, Ordering::Release, Ordering::Relaxed) .is_ok(), "Double-initialization of InitOnce" );