doc: update docs

This commit is contained in:
2021-10-18 15:06:08 +03:00
parent 0d827c2820
commit 76b4a78d32
19 changed files with 218 additions and 53 deletions
+6
View File
@@ -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)
+1 -1
View File
@@ -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,
+17 -2
View File
@@ -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);
}
+1 -1
View File
@@ -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! {
@@ -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;
+4
View File
@@ -0,0 +1,4 @@
//! AArch64 architectural registers
pub mod cpacr_el1;
pub use cpacr_el1::CPACR_EL1;
+8 -1
View File
@@ -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;
+2 -2
View File
@@ -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
+5 -5
View File
@@ -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 {
+7
View File
@@ -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<IrqSafeNullLock<Heap>> = 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 };
+9 -6
View File
@@ -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
+22
View File
@@ -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<MemoryRegion>,
}
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<usize, Errno> {
MANAGER
.lock()
@@ -58,6 +74,7 @@ pub fn alloc_contiguous_pages(pu: PageUsage, count: usize) -> Result<usize, Errn
.alloc_contiguous_pages(pu, count)
}
/// Allocates a single physical memory page.
pub fn alloc_page(pu: PageUsage) -> Result<usize, Errno> {
MANAGER.lock().as_mut().unwrap().alloc_page(pu)
}
@@ -84,6 +101,8 @@ fn find_contiguous<T: Iterator<Item = MemoryRegion>>(iter: T, count: usize) -> O
None
}
/// Initializes physical memory manager using an iterator of available
/// physical memory ranges
pub unsafe fn init_from_iter<T: Iterator<Item = MemoryRegion> + Clone>(iter: T) {
let mut mem_base = usize::MAX;
for reg in iter.clone() {
@@ -122,6 +141,9 @@ pub unsafe fn init_from_iter<T: Iterator<Item = MemoryRegion> + 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,
+16 -1
View File
@@ -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<ReservedRegion> = MaybeUninit::uninit();
static mut RESERVED_REGION_PAGES: MaybeUninit<ReservedRegion> = 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;
+10 -14
View File
@@ -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<usize, Errno> {
// 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;
+19 -11
View File
@@ -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<T>` for convenient MMIO register access.
///
/// See [DeviceMemory].
pub struct DeviceMemoryIo<T> {
mmio: DeviceMemory,
_0: PhantomData<T>,
}
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<Self, Errno> {
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<T> DeviceMemoryIo<T> {
/// Constructs a new [DeviceMemoryIo<T>] from existing `mmio` region
pub const fn new(mmio: DeviceMemory) -> Self {
Self {
mmio,
@@ -63,6 +66,9 @@ impl<T> DeviceMemoryIo<T> {
}
}
/// Allocates and maps device MMIO memory.
///
/// See [DeviceMemory::map]
pub unsafe fn map(name: &'static str, phys: usize, count: usize) -> Result<Self, Errno> {
DeviceMemory::map(name, phys, count).map(Self::new)
}
@@ -77,6 +83,8 @@ impl<T> Deref for DeviceMemoryIo<T> {
}
}
/// Sets up device mapping tables and disable lower-half
/// identity-mapped translation
pub fn enable() -> Result<(), Errno> {
unsafe {
KERNEL_TTBR1.init_device_map();
+36
View File
@@ -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;
+31 -1
View File
@@ -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<UnsafeCell<Process>>;
/// 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<u32, ProcessRef>,
queue: VecDeque<u32>,
idle: u32,
current: Option<u32>,
}
/// Process scheduler state and queues
pub struct Scheduler {
inner: InitOnce<IrqSafeNullLock<SchedulerInner>>,
}
@@ -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 {
+3 -4
View File
@@ -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<T: ?Sized> {
value: UnsafeCell<T>,
}
/// 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,
+10 -2
View File
@@ -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<T> {
state: AtomicBool,
inner: UnsafeCell<MaybeUninit<T>>,
}
impl<T> InitOnce<T> {
/// Constructs a new instance of [InitOnce<T>]
pub const fn new() -> Self {
Self {
state: AtomicBool::new(false),
@@ -17,21 +20,26 @@ impl<T> InitOnce<T> {
}
}
/// Returns `true` if this [InitOnce<T>] 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<T>");
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<T>"
);