diff --git a/libkernel/Cargo.toml b/libkernel/Cargo.toml index 542d532..3c27571 100644 --- a/libkernel/Cargo.toml +++ b/libkernel/Cargo.toml @@ -2,6 +2,12 @@ name = "libkernel" version = "0.1.0" edition = "2024" +description = "Architecture-independent kernel building blocks for operating systems" +license = "MIT" +repository = "https://github.com/hexagonal-sun/moss-kernel/" +readme = "README.md" +keywords = ["kernel", "os", "no-std", "operating-system", "embedded"] +categories = ["no-std", "os", "embedded"] [features] default = [] @@ -43,3 +49,6 @@ tokio = { version = "1.47.1", features = ["full"] } [lints] workspace = true + +[package.metadata.docs.rs] +all-features = true diff --git a/libkernel/README.md b/libkernel/README.md new file mode 100644 index 0000000..036c805 --- /dev/null +++ b/libkernel/README.md @@ -0,0 +1,41 @@ +# libkernel + +Architecture-independent kernel building blocks for operating systems. + +`libkernel` provides the core abstractions that a kernel needs to manage memory, +processes, filesystems, and synchronisation, agnostic of the an underlying CPU +architecture. It is designed to run in a `no_std` environment and uses feature +gates to keep the dependency footprint minimal. + +## Feature gates + +| Feature | Enables | Implies | +|-----------|-------------------------------------------------------|------------------| +| `sync` | Synchronisation primitives (spinlock, mutex, rwlock…) | — | +| `alloc` | Memory allocators (buddy, slab) and collection types | `sync` | +| `paging` | Page tables, address-space management, PTE helpers | `alloc` | +| `proc` | Process identity types (UID/GID, capabilities) | — | +| `fs` | VFS traits, path manipulation, block I/O | `proc`, `sync` | +| `proc_vm` | Process virtual-memory management (mmap, brk, CoW) | `paging`, `fs` | +| `kbuf` | Async-aware circular kernel buffers | `sync` | +| `all` | Everything above | all of the above | + +## Quick start + +Add `libkernel` to your `Cargo.toml` with only the features you need: + +```toml +[dependencies] +libkernel = { version = "0.1", features = ["sync", "proc"] } +``` + +## The `CpuOps` trait + +Most synchronisation and memory primitives are generic over a +[`CpuOps`](https://docs.rs/libkernel/latest/libkernel/trait.CpuOps.html) +implementation. This trait abstracts the handful of arch-specific operations +(core ID, interrupt masking, halt) that the portable code depends on. + +## License + +Licensed under the MIT license. See [LICENSE](../LICENSE) for details. diff --git a/libkernel/src/arch/arm64/memory/mod.rs b/libkernel/src/arch/arm64/memory/mod.rs index def9b1e..0c34472 100644 --- a/libkernel/src/arch/arm64/memory/mod.rs +++ b/libkernel/src/arch/arm64/memory/mod.rs @@ -1,3 +1,5 @@ +//! AArch64 memory management types and page table support. + pub mod pg_descriptors; pub mod pg_tables; pub mod pg_tear_down; diff --git a/libkernel/src/arch/arm64/memory/pg_descriptors.rs b/libkernel/src/arch/arm64/memory/pg_descriptors.rs index 956a3a0..2ab1413 100644 --- a/libkernel/src/arch/arm64/memory/pg_descriptors.rs +++ b/libkernel/src/arch/arm64/memory/pg_descriptors.rs @@ -1,3 +1,5 @@ +//! AArch64 page table entry (PTE) descriptor types and traits. + use paste::paste; use tock_registers::interfaces::{ReadWriteable, Readable}; use tock_registers::{register_bitfields, registers::InMemoryRegister}; @@ -61,9 +63,12 @@ impl TableAddr { } } +/// The memory type attribute applied to a page table mapping. #[derive(Debug, Clone, Copy)] pub enum MemoryType { + /// Device (non-cacheable, non-reorderable) memory. Device, + /// Normal (cacheable) memory. Normal, } @@ -163,6 +168,7 @@ macro_rules! define_descriptor { )) } + /// Returns a new descriptor with the given permissions applied. pub fn set_permissions(self, perms: PtePermissions) -> Self { let reg = InMemoryRegister::new(self.0); use [<$name Fields>]::BlockPageFields; @@ -287,9 +293,13 @@ define_descriptor!( }, ); +/// The decoded state of an L3 page descriptor. pub enum L3DescriptorState { + /// The entry is not present. Invalid, + /// The entry has been swapped out but retains address information. Swapped, + /// The entry is a valid page mapping. Valid, } diff --git a/libkernel/src/arch/arm64/memory/pg_tables.rs b/libkernel/src/arch/arm64/memory/pg_tables.rs index 80c3ba6..92a1da6 100644 --- a/libkernel/src/arch/arm64/memory/pg_tables.rs +++ b/libkernel/src/arch/arm64/memory/pg_tables.rs @@ -1,3 +1,5 @@ +//! AArch64 page table structures, levels, and mapping logic. + use core::marker::PhantomData; use super::{ @@ -17,7 +19,9 @@ use crate::{ }, }; +/// Number of page table descriptors that fit in a single 4 KiB page. pub const DESCRIPTORS_PER_PAGE: usize = PAGE_SIZE / core::mem::size_of::(); +/// Bitmask used to extract the page table index from a shifted virtual address. pub const LEVEL_MASK: usize = DESCRIPTORS_PER_PAGE - 1; /// Trait representing a single level of the page table hierarchy. @@ -47,8 +51,10 @@ pub trait PgTable: Clone + Copy { /// The descriptor (page table entry) type for this level. type Descriptor: PageTableEntry; + /// Constructs this table handle from a typed virtual pointer to its backing array. fn from_ptr(ptr: TVA>) -> Self; + /// Returns the raw mutable pointer to the underlying descriptor array. fn to_raw_ptr(self) -> *mut u64; /// Compute the index into this page table from a virtual address. @@ -81,6 +87,7 @@ pub(super) trait TableMapperTable: PgTable + Clone + Co } } +/// A page-aligned array of raw page table entries for a given table level. #[derive(Clone)] #[repr(C, align(4096))] pub struct PgTableArray { @@ -89,6 +96,7 @@ pub struct PgTableArray { } impl PgTableArray { + /// Creates a zeroed page table array (all entries invalid). pub const fn new() -> Self { Self { pages: [0; DESCRIPTORS_PER_PAGE], @@ -104,8 +112,9 @@ impl Default for PgTableArray { } macro_rules! impl_pgtable { - ($table:ident, $shift:expr, $desc_type:ident) => { + ($(#[$outer:meta])* $table:ident, $shift:expr, $desc_type:ident) => { #[derive(Clone, Copy)] + $(#[$outer])* pub struct $table { base: *mut u64, } @@ -145,22 +154,26 @@ macro_rules! impl_pgtable { }; } -impl_pgtable!(L0Table, 39, L0Descriptor); +impl_pgtable!(/// Level 0 page table (512 GiB per entry). + L0Table, 39, L0Descriptor); impl TableMapperTable for L0Table { type NextLevel = L1Table; } -impl_pgtable!(L1Table, 30, L1Descriptor); +impl_pgtable!(/// Level 1 page table (1 GiB per entry). + L1Table, 30, L1Descriptor); impl TableMapperTable for L1Table { type NextLevel = L2Table; } -impl_pgtable!(L2Table, 21, L2Descriptor); +impl_pgtable!(/// Level 2 page table (2 MiB per entry). + L2Table, 21, L2Descriptor); impl TableMapperTable for L2Table { type NextLevel = L3Table; } -impl_pgtable!(L3Table, 12, L3Descriptor); +impl_pgtable!(/// Level 3 page table (4 KiB per entry). + L3Table, 12, L3Descriptor); /// Trait for temporarily mapping and modifying a page table located at a /// physical address. @@ -459,6 +472,7 @@ where } #[cfg(test)] +#[allow(missing_docs)] pub mod tests { use super::*; use crate::{ diff --git a/libkernel/src/arch/arm64/memory/pg_tear_down.rs b/libkernel/src/arch/arm64/memory/pg_tear_down.rs index 1192b8e..8a7af7d 100644 --- a/libkernel/src/arch/arm64/memory/pg_tear_down.rs +++ b/libkernel/src/arch/arm64/memory/pg_tear_down.rs @@ -1,3 +1,5 @@ +//! Utilities for tearing down and freeing page table hierarchies. + use super::pg_descriptors::{PaMapper, TableMapper}; use super::pg_tables::L0Table; use super::{ diff --git a/libkernel/src/arch/arm64/memory/pg_walk.rs b/libkernel/src/arch/arm64/memory/pg_walk.rs index a948a25..a1c57a4 100644 --- a/libkernel/src/arch/arm64/memory/pg_walk.rs +++ b/libkernel/src/arch/arm64/memory/pg_walk.rs @@ -1,3 +1,5 @@ +//! Page table walking and per-entry modification. + use super::{ pg_descriptors::{L3Descriptor, PageTableEntry, TableMapper}, pg_tables::{L0Table, L3Table, PageTableMapper, PgTable, PgTableArray, TableMapperTable}, @@ -17,7 +19,9 @@ pub struct WalkContext<'a, PM> where PM: PageTableMapper + 'a, { + /// The mapper used to temporarily access page tables by physical address. pub mapper: &'a mut PM, + /// The TLB invalidator invoked after modifying page table entries. pub invalidator: &'a dyn TLBInvalidator, } diff --git a/libkernel/src/arch/arm64/memory/tlb.rs b/libkernel/src/arch/arm64/memory/tlb.rs index a714e86..915f94e 100644 --- a/libkernel/src/arch/arm64/memory/tlb.rs +++ b/libkernel/src/arch/arm64/memory/tlb.rs @@ -1,5 +1,9 @@ +//! TLB invalidation helpers. + +/// Trait for invalidating TLB entries after page table modifications. pub trait TLBInvalidator {} +/// A no-op TLB invalidator used when invalidation is unnecessary. pub struct NullTlbInvalidator {} impl TLBInvalidator for NullTlbInvalidator {} diff --git a/libkernel/src/arch/arm64/mod.rs b/libkernel/src/arch/arm64/mod.rs index eb29191..861afd9 100644 --- a/libkernel/src/arch/arm64/mod.rs +++ b/libkernel/src/arch/arm64/mod.rs @@ -1 +1,3 @@ +//! AArch64 (ARM64) specific implementations. + pub mod memory; diff --git a/libkernel/src/arch/mod.rs b/libkernel/src/arch/mod.rs index a0fff07..5a4f22f 100644 --- a/libkernel/src/arch/mod.rs +++ b/libkernel/src/arch/mod.rs @@ -1 +1,3 @@ +//! Architecture-specific support code. + pub mod arm64; diff --git a/libkernel/src/driver.rs b/libkernel/src/driver.rs index 36598e5..04cb017 100644 --- a/libkernel/src/driver.rs +++ b/libkernel/src/driver.rs @@ -1,5 +1,10 @@ +//! Device descriptor types. + +/// A major/minor pair identifying a character or block device. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct CharDevDescriptor { + /// The major device number (identifies the driver). pub major: u64, + /// The minor device number (identifies the device instance). pub minor: u64, } diff --git a/libkernel/src/error.rs b/libkernel/src/error.rs index 4a9df9b..35cc685 100644 --- a/libkernel/src/error.rs +++ b/libkernel/src/error.rs @@ -1,217 +1,295 @@ +//! Unified kernel error types. +//! +//! This module defines the error hierarchy used throughout the kernel. +//! [`KernelError`] is the top-level enum and wraps domain-specific errors such +//! as [`MapError`], [`FsError`], [`IoError`], and others. A [`Result`] type +//! alias is provided for convenience. +//! +//! The [`syscall_error`] submodule maps [`KernelError`] variants to their POSIX +//! `errno` equivalents for returning values to user space. + use core::convert::Infallible; use thiserror::Error; pub mod syscall_error; +/// Errors that can occur during device probing. #[derive(Debug, Error, PartialEq, Eq, Clone)] pub enum ProbeError { + /// No registers present in the Flattened Device Tree. #[error("No registers present in FDT")] NoReg, + /// No register bank size found in the Flattened Device Tree. #[error("No register bank size in FDT")] NoRegSize, + /// No interrupts declared in the Flattened Device Tree. #[error("No interrupts in FDT")] NoInterrupts, + /// No parent interrupt controller found in the Flattened Device Tree. #[error("No parent interrupt controller in FDT")] NoParentInterrupt, + /// The specified interrupt parent is not an interrupt controller. #[error("The specified interrupt parent isn't an interrupt controller")] NotInterruptController, - // Driver probing should be tried again after other probes have succeeded. + /// Driver probing should be tried again after other probes have succeeded. #[error("Driver probing deferred for other dependencies")] Deferred, - // Device inspected but not a match for this driver; skip silently. + /// Device inspected but not a match for this driver; skip silently. #[error("Device not matched by driver")] NoMatch, } +/// Errors that can occur during page table mapping. #[derive(Debug, Error, PartialEq, Eq, Clone)] pub enum MapError { + /// Physical address is not page aligned. #[error("Physical address not page aligned")] PhysNotAligned, - #[error("Physical address not page aligned")] + /// Virtual address is not page aligned. + #[error("Virtual address not page aligned")] VirtNotAligned, + /// Physical and virtual range sizes do not match. #[error("Physical and virtual range sizes do not match")] SizeMismatch, + /// Failed to walk to the next level page table. #[error("Failed to walk to the next level page table")] WalkFailed, + /// Invalid page table descriptor encountered. #[error("Invalid page table descriptor encountered")] InvalidDescriptor, + /// The region to be mapped is smaller than `PAGE_SIZE`. #[error("The region to be mapped is smaller than PAGE_SIZE")] TooSmall, + /// The virtual address range has already been mapped. #[error("The VA range is has already been mapped")] AlreadyMapped, + /// Page table does not contain an L3 mapping. #[error("Page table does not contain an L3 mapping")] NotL3Mapped, } +/// Errors from block-level I/O operations. #[derive(Error, Debug, PartialEq, Eq, Clone)] pub enum IoError { + /// The requested I/O operation was out of bounds for the block device. #[error("The requested I/O operation was out of bounds for the block device")] OutOfBounds, + /// Corruption found in the filesystem metadata. #[error("Corruption found in the filesystem metadata")] MetadataCorruption, } +/// Errors from filesystem operations. #[derive(Error, Debug, PartialEq, Eq, Clone)] pub enum FsError { + /// The path or file was not found. #[error("The path or file was not found")] NotFound, + /// The path component is not a directory. #[error("The path component is not a directory.")] NotADirectory, + /// The path component is a directory. #[error("The path component is a directory.")] IsADirectory, + /// The file or directory already exists. #[error("The file or directory already exists.")] AlreadyExists, + /// The directory is not empty. #[error("The directory is not empty.")] DirectoryNotEmpty, + /// Invalid input parameters. #[error("Invalid input parameters.")] InvalidInput, + /// The filesystem is corrupted or has an invalid format. #[error("The filesystem is corrupted or has an invalid format.")] InvalidFs, + /// Attempted to access data out of bounds. #[error("Attempted to access data out of bounds.")] OutOfBounds, + /// The operation is not permitted. #[error("The operation is not permitted.")] PermissionDenied, + /// Could not find the specified filesystem driver. #[error("Could not find the specified FS driver")] DriverNotFound, + /// Too many open files. #[error("Too many open files")] TooManyFiles, + /// The device could not be found. #[error("The device could not be found")] NoDevice, + /// Too many symbolic links encountered. #[error("Too many symbolic links encountered")] Loop, + /// Attempted to rename across devices. #[error("Attempted to rename from cross device")] CrossDevice, } +/// Errors that occur when loading or parsing an executable. #[derive(Error, Debug, PartialEq, Eq, Clone)] pub enum ExecError { + /// Invalid ELF format. #[error("Invalid ELF Format")] InvalidElfFormat, + /// Invalid script format. #[error("Invalid Script Format")] InvalidScriptFormat, + /// Invalid program header format. #[error("Invalid Program Header Format")] InvalidPHdrFormat, } +/// Top-level kernel error type wrapping all domain-specific errors. #[derive(Error, Debug, PartialEq, Eq, Clone)] pub enum KernelError { + /// Cannot allocate memory. #[error("Cannot allocate memory")] NoMemory, + /// Memory region not found. #[error("Memory region not found")] NoMemRegion, + /// Invalid value. #[error("Invalid value")] InvalidValue, + /// The current resource is already in use. #[error("The current resource is already in use")] InUse, + /// Page table mapping failed. #[error("Page table mapping failed: {0}")] MappingError(#[from] MapError), + /// Provided object is too large. #[error("Provided object is too large")] TooLarge, + /// Operation not supported. #[error("Operation not supported")] NotSupported, + /// Address family not supported. #[error("Address family not supported")] AddressFamilyNotSupported, + /// Device probe failed. #[error("Device probe failed: {0}")] Probe(#[from] ProbeError), + /// I/O operation failed. #[error("I/O operation failed: {0}")] Io(#[from] IoError), + /// Filesystem operation failed. #[error("Filesystem operation failed: {0}")] Fs(#[from] FsError), + /// Exec error during executable loading. #[error("Exec error: {0}")] Exec(#[from] ExecError), + /// Not a tty. #[error("Not a tty")] NotATty, + /// Fault error during syscall. #[error("Fault error during syscall")] Fault, + /// Not an open file descriptor. #[error("Not an open file descriptor")] BadFd, + /// Cannot seek on a pipe. #[error("Cannot seek on a pipe")] SeekPipe, + /// Broken pipe. #[error("Broken pipe")] BrokenPipe, + /// Operation not permitted. #[error("Operation not permitted")] NotPermitted, + /// Buffer is full. #[error("Buffer is full")] BufferFull, + /// Operation would block. #[error("Operation would block")] TryAgain, + /// No such process. #[error("No such process")] NoProcess, + /// No child process. #[error("No child process")] NoChildProcess, + /// Operation timed out. #[error("Operation timed out")] TimedOut, + /// Value out of range. #[error("Value out of range")] RangeError, + /// Operation not supported on transport endpoint. #[error("Operation not supported on transport endpoint")] OpNotSupported, + /// Interrupted system call. #[error("Interrupted system call")] Interrupted, + /// Name too long. #[error("Name too long")] NameTooLong, + /// Not a socket. #[error("Not a socket")] NotASocket, + /// Other error with a static description. #[error("{0}")] Other(&'static str), } +/// Convenience alias for a [`core::result::Result`] with [`KernelError`]. pub type Result = core::result::Result; impl From for KernelError { diff --git a/libkernel/src/error/syscall_error.rs b/libkernel/src/error/syscall_error.rs index 3d0ad61..a60d80c 100644 --- a/libkernel/src/error/syscall_error.rs +++ b/libkernel/src/error/syscall_error.rs @@ -1,3 +1,8 @@ +//! Maps [`KernelError`] variants to POSIX `errno` values +//! for returning results to user-space system calls. + +#![allow(missing_docs)] + use crate::error::FsError; use super::KernelError; diff --git a/libkernel/src/fs/attr.rs b/libkernel/src/fs/attr.rs index 37d1e6a..e77b4cc 100644 --- a/libkernel/src/fs/attr.rs +++ b/libkernel/src/fs/attr.rs @@ -1,3 +1,5 @@ +//! File attribute types (permissions, modes, and metadata). + use crate::{ error::{KernelError, Result}, proc::{ @@ -7,10 +9,10 @@ use crate::{ }; use super::{FileType, InodeId}; -use bitflags::bitflags; use core::time::Duration; bitflags::bitflags! { + /// POSIX access-mode flags for permission checks (`R_OK`, `W_OK`, `X_OK`). #[derive(Debug, Clone, Copy)] pub struct AccessMode: i32 { /// Execution is permitted @@ -22,58 +24,70 @@ bitflags::bitflags! { } } -bitflags! { - #[derive(Clone, Copy, Debug)] - pub struct FilePermissions: u16 { - const S_IXOTH = 0x0001; - const S_IWOTH = 0x0002; - const S_IROTH = 0x0004; +mod _file_permissions { + #![allow(missing_docs)] + use bitflags::bitflags; + bitflags! { + /// POSIX file permission bits (owner/group/other read/write/execute and setuid/setgid/sticky). + #[derive(Clone, Copy, Debug)] + pub struct FilePermissions: u16 { + const S_IXOTH = 0x0001; + const S_IWOTH = 0x0002; + const S_IROTH = 0x0004; - const S_IXGRP = 0x0008; - const S_IWGRP = 0x0010; - const S_IRGRP = 0x0020; + const S_IXGRP = 0x0008; + const S_IWGRP = 0x0010; + const S_IRGRP = 0x0020; - const S_IXUSR = 0x0040; - const S_IWUSR = 0x0080; - const S_IRUSR = 0x0100; + const S_IXUSR = 0x0040; + const S_IWUSR = 0x0080; + const S_IRUSR = 0x0100; - const S_ISVTX = 0x0200; + const S_ISVTX = 0x0200; - const S_ISGID = 0x0400; - const S_ISUID = 0x0800; + const S_ISGID = 0x0400; + const S_ISUID = 0x0800; + } } } +pub use _file_permissions::FilePermissions; -bitflags! { - #[derive(Clone, Copy, Debug)] - pub struct FileMode: u16 { - const S_IXOTH = 0x0001; - const S_IWOTH = 0x0002; - const S_IROTH = 0x0004; +mod _file_mode { + #![allow(missing_docs)] + use bitflags::bitflags; + bitflags! { + /// Combined file type and permission bits, as returned by `stat`. + #[derive(Clone, Copy, Debug)] + pub struct FileMode: u16 { + const S_IXOTH = 0x0001; + const S_IWOTH = 0x0002; + const S_IROTH = 0x0004; - const S_IXGRP = 0x0008; - const S_IWGRP = 0x0010; - const S_IRGRP = 0x0020; + const S_IXGRP = 0x0008; + const S_IWGRP = 0x0010; + const S_IRGRP = 0x0020; - const S_IXUSR = 0x0040; - const S_IWUSR = 0x0080; - const S_IRUSR = 0x0100; + const S_IXUSR = 0x0040; + const S_IWUSR = 0x0080; + const S_IRUSR = 0x0100; - const S_ISVTX = 0x0200; + const S_ISVTX = 0x0200; - const S_ISGID = 0x0400; - const S_ISUID = 0x0800; + const S_ISGID = 0x0400; + const S_ISUID = 0x0800; - // Mutually-exclusive file types: - const S_IFIFO = 0x1000; - const S_IFCHR = 0x2000; - const S_IFDIR = 0x4000; - const S_IFBLK = 0x6000; - const S_IFREG = 0x8000; - const S_IFLNK = 0xA000; - const S_IFSOCK = 0xC000; + // Mutually-exclusive file types: + const S_IFIFO = 0x1000; + const S_IFCHR = 0x2000; + const S_IFDIR = 0x4000; + const S_IFBLK = 0x6000; + const S_IFREG = 0x8000; + const S_IFLNK = 0xA000; + const S_IFSOCK = 0xC000; + } } } +pub use _file_mode::FileMode; impl From for FilePermissions { fn from(mode: FileMode) -> Self { @@ -82,6 +96,7 @@ impl From for FilePermissions { } impl FileMode { + /// Constructs a `FileMode` from a file type and permission bits. pub fn new(file_type: FileType, permissions: FilePermissions) -> Self { let mut mode = FileMode::from_bits_truncate(permissions.bits()); mode |= match file_type { @@ -98,6 +113,7 @@ impl FileMode { } /// Represents file metadata, similar to `stat`. +#[allow(missing_docs)] #[derive(Debug, Clone)] pub struct FileAttr { pub id: InodeId, @@ -116,6 +132,7 @@ pub struct FileAttr { } impl FileAttr { + /// Returns the combined file type and permission bits as a `FileMode`. pub fn mode(&self) -> FileMode { FileMode::new(self.file_type, self.permissions) } diff --git a/libkernel/src/fs/blk/buffer.rs b/libkernel/src/fs/blk/buffer.rs index c1893e9..e79bcc1 100644 --- a/libkernel/src/fs/blk/buffer.rs +++ b/libkernel/src/fs/blk/buffer.rs @@ -1,3 +1,5 @@ +//! Block I/O buffer management. + use core::{mem, slice}; use crate::{error::Result, fs::BlockDevice, pod::Pod}; diff --git a/libkernel/src/fs/blk/mod.rs b/libkernel/src/fs/blk/mod.rs index 1b5fd21..501839d 100644 --- a/libkernel/src/fs/blk/mod.rs +++ b/libkernel/src/fs/blk/mod.rs @@ -1,3 +1,5 @@ +//! Block device layer. + pub mod buffer; #[cfg(feature = "paging")] pub mod ramdisk; diff --git a/libkernel/src/fs/blk/ramdisk.rs b/libkernel/src/fs/blk/ramdisk.rs index 722a1cc..aa30082 100644 --- a/libkernel/src/fs/blk/ramdisk.rs +++ b/libkernel/src/fs/blk/ramdisk.rs @@ -1,3 +1,5 @@ +//! RAM-backed block device implementation. + use crate::{ KernAddressSpace, error::{IoError, KernelError, Result}, @@ -13,6 +15,7 @@ use alloc::boxed::Box; use async_trait::async_trait; use core::ptr; +/// A block device backed by a region of RAM. pub struct RamdiskBlkDev { base: TVA, num_blocks: u64, diff --git a/libkernel/src/fs/filesystems/ext4/mod.rs b/libkernel/src/fs/filesystems/ext4/mod.rs index 853964b..45d40ab 100644 --- a/libkernel/src/fs/filesystems/ext4/mod.rs +++ b/libkernel/src/fs/filesystems/ext4/mod.rs @@ -110,6 +110,7 @@ impl From for FileAttr { } } +/// Wraps an ext4 directory iterator to produce VFS [`Dirent`] entries. pub struct ReadDirWrapper { inner: AsyncSkip, fs_id: u64, @@ -117,6 +118,7 @@ pub struct ReadDirWrapper { } impl ReadDirWrapper { + /// Creates a new `ReadDirWrapper` starting at the given offset. pub fn new(inner: ReadDir, fs_id: u64, start_offset: u64) -> Self { Self { inner: inner.skip(start_offset as usize), @@ -187,6 +189,7 @@ impl DerefMut for InodeInner { } } +/// An inode within an ext4 filesystem. pub struct Ext4Inode { fs_ref: Weak>, id: NonZeroU32, diff --git a/libkernel/src/fs/filesystems/fat32/mod.rs b/libkernel/src/fs/filesystems/fat32/mod.rs index 8e524ff..6b7a711 100644 --- a/libkernel/src/fs/filesystems/fat32/mod.rs +++ b/libkernel/src/fs/filesystems/fat32/mod.rs @@ -1,3 +1,5 @@ +//! FAT32 filesystem driver. + use crate::{ error::{FsError, Result}, fs::{FileType, Filesystem, Inode, InodeId, attr::FileAttr, blk::buffer::BlockBuffer}, @@ -23,6 +25,7 @@ mod fat; mod file; mod reader; +/// A logical sector number on a FAT32 volume. #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct Sector(u32); @@ -43,23 +46,28 @@ impl Add for Sector { } impl Sector { + /// Returns an iterator over sectors from `self` (inclusive) to `other` (exclusive). pub fn sectors_until(self, other: Self) -> impl Iterator { (self.0..other.0).map(Sector) } } +/// A FAT32 cluster number. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct Cluster(u32); impl Cluster { + /// Returns the raw cluster number as a `usize`. pub fn value(self) -> usize { self.0 as _ } + /// Constructs a cluster number from its high and low 16-bit halves. pub fn from_high_low(clust_high: u16, clust_low: u16) -> Cluster { Cluster((clust_high as u32) << 16 | clust_low as u32) } + /// Returns `true` if this is a valid data cluster (number >= 2). pub fn is_valid(self) -> bool { self.0 >= 2 } @@ -71,6 +79,7 @@ impl Display for Cluster { } } +/// A mounted FAT32 filesystem instance. pub struct Fat32Filesystem { dev: BlockBuffer, bpb: BiosParameterBlock, @@ -80,6 +89,7 @@ pub struct Fat32Filesystem { } impl Fat32Filesystem { + /// Creates a new FAT32 filesystem from the given block device buffer. pub async fn new(dev: BlockBuffer, id: u64) -> Result> { let bpb = BiosParameterBlock::new(&dev).await?; let fat = Fat::read_fat(&dev, &bpb, 0).await?; diff --git a/libkernel/src/fs/filesystems/mod.rs b/libkernel/src/fs/filesystems/mod.rs index 43656b1..fd24cc5 100644 --- a/libkernel/src/fs/filesystems/mod.rs +++ b/libkernel/src/fs/filesystems/mod.rs @@ -1,3 +1,5 @@ +//! Concrete filesystem implementations. + pub mod ext4; pub mod fat32; #[cfg(feature = "alloc")] diff --git a/libkernel/src/fs/filesystems/tmpfs.rs b/libkernel/src/fs/filesystems/tmpfs.rs index 39ac937..5a48787 100644 --- a/libkernel/src/fs/filesystems/tmpfs.rs +++ b/libkernel/src/fs/filesystems/tmpfs.rs @@ -1,3 +1,5 @@ +//! In-memory temporary filesystem (tmpfs). + use crate::{ CpuOps, error::{FsError, KernelError, Result}, @@ -783,6 +785,7 @@ impl TmpFsSymlinkInode { } } +/// An in-memory temporary filesystem backed by page allocations. pub struct TmpFs where C: CpuOps, @@ -802,6 +805,7 @@ where G: PageAllocGetter, T: AddressTranslator<()>, { + /// Creates a new tmpfs instance with the given filesystem ID. pub fn new(fs_id: u64) -> Arc { Arc::new_cyclic(|weak_fs| { let root = @@ -817,6 +821,7 @@ where }) } + /// Allocates the next unique inode ID for this filesystem. pub fn alloc_inode_id(&self) -> u64 { self.next_inode_id.fetch_add(1, Ordering::Relaxed) } diff --git a/libkernel/src/fs/mod.rs b/libkernel/src/fs/mod.rs index 6e5385f..c66cdf0 100644 --- a/libkernel/src/fs/mod.rs +++ b/libkernel/src/fs/mod.rs @@ -32,28 +32,38 @@ use async_trait::async_trait; use attr::{FileAttr, FilePermissions}; use core::time::Duration; -bitflags::bitflags! { - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - pub struct OpenFlags: u32 { - const O_RDONLY = 0b000; - const O_WRONLY = 0b001; - const O_RDWR = 0b010; - const O_ACCMODE = 0b011; - const O_CREAT = 0o100; - const O_EXCL = 0o200; - const O_TRUNC = 0o1000; - const O_DIRECTORY = 0o200000; - const O_APPEND = 0o2000; - const O_NONBLOCK = 0o4000; - const O_CLOEXEC = 0o2000000; +mod _open_flags { + #![allow(missing_docs)] + bitflags::bitflags! { + /// Flags used when opening a file, corresponding to POSIX `O_*` constants. + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub struct OpenFlags: u32 { + const O_RDONLY = 0b000; + const O_WRONLY = 0b001; + const O_RDWR = 0b010; + const O_ACCMODE = 0b011; + const O_CREAT = 0o100; + const O_EXCL = 0o200; + const O_TRUNC = 0o1000; + const O_DIRECTORY = 0o200000; + const O_APPEND = 0o2000; + const O_NONBLOCK = 0o4000; + const O_CLOEXEC = 0o2000000; + } } } +pub use _open_flags::OpenFlags; -// Reserved psuedo filesystem instances created internally in the kernel. +// Reserved pseudo filesystem instances created internally in the kernel. +/// Filesystem instance ID for the device filesystem. pub const DEVFS_ID: u64 = 1; +/// Filesystem instance ID for the proc filesystem. pub const PROCFS_ID: u64 = 2; +/// Filesystem instance ID for the sys filesystem. pub const SYSFS_ID: u64 = 3; +/// Filesystem instance ID for the cgroup filesystem. pub const CGROUPFS_ID: u64 = 4; +/// Starting ID for user-mounted filesystem instances. pub const FS_ID_START: u64 = 10; /// Trait for a mounted filesystem instance. Its main role is to act as a @@ -78,30 +88,34 @@ pub trait Filesystem: Send + Sync { } } -// A unique identifier for an inode across the entire VFS. A tuple of -// (filesystem_id, inode_number). +/// A unique identifier for an inode across the entire VFS, combining a filesystem ID and inode number. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct InodeId(u64, u64); impl InodeId { + /// Creates an `InodeId` from a filesystem ID and an inode number. pub fn from_fsid_and_inodeid(fs_id: u64, inode_id: u64) -> Self { Self(fs_id, inode_id) } + /// Returns a sentinel `InodeId` used as a placeholder. pub fn dummy() -> Self { Self(u64::MAX, u64::MAX) } + /// Returns the filesystem ID component. pub fn fs_id(self) -> u64 { self.0 } + /// Returns the inode number component. pub fn inode_id(self) -> u64 { self.1 } } /// Standard POSIX file types. +#[allow(missing_docs)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum FileType { File, @@ -138,13 +152,18 @@ pub trait DirStream: Send + Sync { /// Represents a single directory entry. #[derive(Debug, Clone)] pub struct Dirent { + /// The inode identifier of this entry. pub id: InodeId, + /// The name of this directory entry. pub name: String, + /// The type of file this entry represents. pub file_type: FileType, + /// The byte offset of this entry within the directory. pub offset: u64, } impl Dirent { + /// Creates a new directory entry. pub fn new(name: String, id: InodeId, file_type: FileType, offset: u64) -> Self { Self { id, @@ -158,8 +177,11 @@ impl Dirent { /// Specifies how to seek within a file, mirroring `std::io::SeekFrom`. #[derive(Debug, Copy, Clone)] pub enum SeekFrom { + /// Seek from the beginning of the file. Start(u64), + /// Seek from the end of the file. End(i64), + /// Seek relative to the current position. Current(i64), } @@ -329,6 +351,7 @@ pub trait Inode: Send + Sync + Any { Ok(()) } + /// Return this inode as an `Any` object, suitable for downcasting. fn as_any(&self) -> &dyn Any; } @@ -336,14 +359,19 @@ pub trait Inode: Send + Sync + Any { /// for common inode operations. #[async_trait] pub trait SimpleFile { + /// Returns the inode ID of this file. fn id(&self) -> InodeId; + /// Returns the file metadata. async fn getattr(&self) -> Result; + /// Reads the entire file contents into a byte vector. async fn read(&self) -> Result>; + /// Reads the target of a symbolic link, if applicable. async fn readlink(&self) -> Result { Err(KernelError::NotSupported) } } +#[allow(missing_docs)] #[async_trait] impl Inode for T where @@ -385,12 +413,14 @@ where } } +/// A simple in-memory directory stream backed by a `Vec` of entries. pub struct SimpleDirStream { entries: Vec, idx: usize, } impl SimpleDirStream { + /// Creates a new `SimpleDirStream` starting at the given offset. pub fn new(entries: Vec, start_offset: u64) -> Self { Self { entries, diff --git a/libkernel/src/lib.rs b/libkernel/src/lib.rs index 24d4e48..66455b6 100644 --- a/libkernel/src/lib.rs +++ b/libkernel/src/lib.rs @@ -1,10 +1,59 @@ +//! # libkernel +//! +//! Architecture-independent kernel building blocks for operating systems. +//! +//! +//! `libkernel` provides the core abstractions that a kernel needs to manage +//! memory, processes, filesystems, and synchronisation — all without tying the +//! implementation to a particular CPU architecture. It is designed to run in a +//! `no_std` environment and relies on feature gates to keep the dependency +//! footprint minimal. +//! +//! ## Feature gates +//! +//! Most of the crate is hidden behind Cargo features so that consumers only pay +//! for the subsystems they need: +//! +//! | Feature | Enables | Implies | +//! |-----------|-------------------------------------------------------|------------------| +//! | `sync` | Synchronisation primitives (spinlock, mutex, rwlock…) | — | +//! | `alloc` | Memory allocators (buddy, slab) and collection types | `sync` | +//! | `paging` | Page tables, address-space management, PTE helpers | `alloc` | +//! | `proc` | Process identity types (UID/GID, capabilities) | — | +//! | `fs` | VFS traits, path manipulation, block I/O | `proc`, `sync` | +//! | `proc_vm` | Process virtual-memory management (mmap, brk, CoW) | `paging`, `fs` | +//! | `kbuf` | Async-aware circular kernel buffers | `sync` | +//! | `all` | Everything above | all of the above | +//! +//! ## The `CpuOps` trait +//! +//! Nearly every synchronisation and memory primitive in the crate is generic +//! over a [`CpuOps`] implementation. This trait abstracts the handful of +//! arch-specific operations (core ID, interrupt masking, halt) that the +//! arch-independent code depends on, making the library portable while still +//! fully testable on the host. +//! +//! ## Crate layout +//! +//! - [`error`] — Unified kernel error types and POSIX errno mapping. +//! - [`memory`] — Typed addresses, memory regions, page allocators, and +//! address-space management. +//! - [`sync`] — Async-aware spinlocks, mutexes, rwlocks, condvars, channels, +//! and per-CPU storage *(feature `sync`)*. +//! - [`fs`] — VFS traits (`Filesystem`, `Inode`, `BlockDevice`), path +//! manipulation, and filesystem driver scaffolding *(feature `fs`)*. +//! - [`proc`] — Process identity types and Linux-compatible capabilities +//! *(feature `proc`)*. +//! - [`arch`] — Architecture-specific support code *(feature `paging`)*. + #![cfg_attr(not(test), no_std)] +#![warn(missing_docs)] #[cfg(feature = "paging")] pub mod arch; -pub mod error; #[cfg(feature = "fs")] pub mod driver; +pub mod error; #[cfg(feature = "fs")] pub mod fs; pub mod memory; @@ -18,10 +67,32 @@ pub mod sync; extern crate alloc; #[cfg(feature = "paging")] -pub use memory::address_space::{ - KernAddressSpace, PageInfo, UserAddressSpace, VirtualMemory, -}; +pub use memory::address_space::{KernAddressSpace, PageInfo, UserAddressSpace, VirtualMemory}; +/// Trait abstracting the small set of CPU operations that the +/// architecture-independent kernel code requires. +/// +/// Every concrete kernel target must provide an implementation of this trait. +/// The synchronisation primitives in [`sync`] and the memory subsystem in +/// [`memory`] are generic over `CpuOps`, which keeps this crate portable while +/// allowing the real kernel — and unit tests — to supply their own +/// implementations. +/// +/// # Example (test mock) +/// +/// ``` +/// use libkernel::CpuOps; +/// +/// struct MockCpu; +/// +/// impl CpuOps for MockCpu { +/// fn id() -> usize { 0 } +/// fn halt() -> ! { loop { core::hint::spin_loop() } } +/// fn disable_interrupts() -> usize { 0 } +/// fn restore_interrupt_state(_flags: usize) {} +/// fn enable_interrupts() {} +/// } +/// ``` pub trait CpuOps: 'static { /// Returns the ID of the currently executing core. fn id() -> usize; @@ -41,6 +112,7 @@ pub trait CpuOps: 'static { } #[cfg(test)] +#[allow(missing_docs)] pub mod test { use core::hint::spin_loop; diff --git a/libkernel/src/memory/address.rs b/libkernel/src/memory/address.rs index c18261b..e0d1442 100644 --- a/libkernel/src/memory/address.rs +++ b/libkernel/src/memory/address.rs @@ -1,4 +1,4 @@ -//! `address` module: Type-safe handling of virtual and physical addresses. +//! Type-safe handling of virtual and physical addresses. //! //! This module defines strongly-typed address representations for both physical //! and virtual memory. It provides abstractions to ensure correct usage and @@ -147,20 +147,24 @@ impl Address { self.inner & PAGE_MASK } + /// Returns the null (zero) address. pub const fn null() -> Self { Self::from_value(0) } + /// Returns a new address offset forward by `n` bytes. #[must_use] pub const fn add_bytes(self, n: usize) -> Self { Self::from_value(self.value() + n) } + /// Returns a new address offset backward by `n` bytes. #[must_use] pub fn sub_bytes(self, n: usize) -> Self { Self::from_value(self.value() - n) } + /// Returns `true` if this is the null (zero) address. pub fn is_null(self) -> bool { self.inner == 0 } @@ -291,6 +295,7 @@ impl PA { TPA::from_value(self.value()) } + /// Converts this physical address to a page frame number. pub fn to_pfn(&self) -> PageFrame { PageFrame::from_pfn(self.inner >> PAGE_SHIFT) } @@ -298,7 +303,9 @@ impl PA { /// Trait for translating between physical and virtual addresses. pub trait AddressTranslator: 'static + Send + Sync { + /// Translates a virtual address to its corresponding physical address. fn virt_to_phys(va: TVA) -> TPA; + /// Translates a physical address to its corresponding virtual address. fn phys_to_virt(pa: TPA) -> TVA; } diff --git a/libkernel/src/memory/address_space.rs b/libkernel/src/memory/address_space.rs index 098612b..52c9d8b 100644 --- a/libkernel/src/memory/address_space.rs +++ b/libkernel/src/memory/address_space.rs @@ -1,3 +1,5 @@ +//! Kernel and user address-space management. + use alloc::vec::Vec; use crate::{ @@ -14,7 +16,9 @@ use crate::{ /// An architecture-independent representation of a page table entry (PTE). pub struct PageInfo { + /// The page frame number identifying the physical page. pub pfn: PageFrame, + /// The permission bits for this page table entry. pub perms: PtePermissions, } diff --git a/libkernel/src/memory/allocators/mod.rs b/libkernel/src/memory/allocators/mod.rs index eafb440..3392826 100644 --- a/libkernel/src/memory/allocators/mod.rs +++ b/libkernel/src/memory/allocators/mod.rs @@ -1,3 +1,5 @@ +//! Memory allocators. + mod frame; pub mod phys; pub mod slab; diff --git a/libkernel/src/memory/allocators/phys.rs b/libkernel/src/memory/allocators/phys.rs index 24ec8b6..54ae9a4 100644 --- a/libkernel/src/memory/allocators/phys.rs +++ b/libkernel/src/memory/allocators/phys.rs @@ -1,3 +1,5 @@ +//! Physical page-frame allocator (buddy allocator). + use crate::{ CpuOps, error::{KernelError, Result}, @@ -155,22 +157,28 @@ impl FrameAllocatorInner { } } +/// Thread-safe wrapper around the buddy frame allocator. pub struct FrameAllocator { pub(super) inner: SpinLockIrq, } +/// An RAII guard for a contiguous allocation of physical page frames. +/// +/// When dropped, the pages are automatically returned to the allocator. pub struct PageAllocation<'a, CPU: CpuOps> { region: PhysMemoryRegion, inner: &'a SpinLockIrq, } impl PageAllocation<'_, CPU> { + /// Consumes the allocation without freeing it, returning the underlying region. pub fn leak(self) -> PhysMemoryRegion { let region = self.region; core::mem::forget(self); region } + /// Returns a reference to the physical memory region backing this allocation. pub fn region(&self) -> &PhysMemoryRegion { &self.region } @@ -434,11 +442,14 @@ impl FrameAllocator { } } +/// Provides access to the global page-frame allocator. pub trait PageAllocGetter: Send + Sync + 'static { + /// Returns a reference to the global [`FrameAllocator`]. fn global_page_alloc() -> &'static FrameAllocator; } #[cfg(test)] +#[allow(missing_docs)] pub mod tests { use super::*; use crate::{ diff --git a/libkernel/src/memory/allocators/slab/allocator.rs b/libkernel/src/memory/allocators/slab/allocator.rs index 008cf7a..8511f73 100644 --- a/libkernel/src/memory/allocators/slab/allocator.rs +++ b/libkernel/src/memory/allocators/slab/allocator.rs @@ -1,4 +1,4 @@ -/// A slab allocator for Moss. +//! A slab memory allocator. use super::{ SLAB_FRAME_ALLOC_ORDER, SLAB_MAX_OBJ_SHIFT, alloc_order, slab::{Slab, SlabState}, @@ -36,18 +36,18 @@ const MAX_FREE_SLABS: usize = 32; /// /// There is no 'full' list. Full slabs are unlinked from both the 'partial' and /// 'free' lists. They are allowed to float "in the ether" (referenced only by -/// the global [FrameList]). When freeing an object from a 'full' slab, the +/// the global `FrameList`). When freeing an object from a 'full' slab, the /// allocator detects the state transition and re-links the frame into the /// 'partial'/'free' list. /// /// # Safety and Ownership /// -/// The FA and the `SlabManager` share a list of frame metadata via [FrameList]. To +/// The FA and the `SlabManager` share a list of frame metadata via `FrameList`. To /// share this list safely, we implement an implicit ownership model: /// /// 1. When the FA allocates a frame, it initializes the metadata. /// 2. We convert this frame into a slab allocation via -/// [PageAllocation::as_slab]. +/// `PageAllocation::as_slab`. /// 3. Once that function returns, this `SlabManager` is considered the /// exclusive owner of that frame's metadata. /// @@ -55,7 +55,7 @@ const MAX_FREE_SLABS: usize = 32; /// the metadata of any frame in its possession, as the `SpinLock` protecting /// this struct guarantees exclusive access to the specific size class that /// "owns" the frame. Ownership is eventually returned to the FA via -/// [FrameAllocatorInner::free_slab]. +/// `FrameAllocatorInner::free_slab`. pub struct SlabManager, T: AddressTranslator<()>> { pub(super) free: LinkedList, pub(super) partial: LinkedList, @@ -253,6 +253,7 @@ impl, T: AddressTranslator<()>> SlabManager } } +/// The top-level slab allocator, dispatching allocations to size-appropriate [`SlabManager`]s. pub struct SlabAllocator, T: AddressTranslator<()>> { pub(super) managers: [SpinLockIrq, CPU>; SLAB_MAX_OBJ_SHIFT as usize + 1], @@ -268,6 +269,7 @@ unsafe impl, T: AddressTranslator<()>> Sync } impl, T: AddressTranslator<()>> SlabAllocator { + /// Creates a new slab allocator backed by the given frame list. pub fn new(frame_list: FrameList) -> Self { Self { managers: core::array::from_fn(|n| { @@ -276,6 +278,7 @@ impl, T: AddressTranslator<()>> SlabAllocat } } + /// Returns a reference to the slab manager responsible for the given layout, if one exists. pub fn allocator_for_layout( &self, layout: core::alloc::Layout, diff --git a/libkernel/src/memory/allocators/slab/cache.rs b/libkernel/src/memory/allocators/slab/cache.rs index 3f2e88a..06cd701 100644 --- a/libkernel/src/memory/allocators/slab/cache.rs +++ b/libkernel/src/memory/allocators/slab/cache.rs @@ -1,3 +1,5 @@ +//! Per-object-size slab cache management. + use super::{ alloc_order, allocator::{SlabAllocator, SlabManager}, @@ -20,6 +22,7 @@ const NUM_PTR_CACHES: usize = SLAB_MAX_OBJ_SHIFT as usize + 1; // Ensure that our cache fits in a single page. const _: () = assert!(core::mem::size_of::() <= PAGE_SIZE); +/// A fixed-size cache of recently freed pointers for a single size class. #[repr(C)] pub struct PtrCache { next_free: usize, @@ -27,6 +30,7 @@ pub struct PtrCache { } impl PtrCache { + /// Creates an empty pointer cache. pub fn new() -> Self { Self { next_free: 0, @@ -34,10 +38,12 @@ impl PtrCache { } } + /// Returns `true` if no pointers are cached. pub fn is_empty(&self) -> bool { self.next_free == 0 } + /// Returns `true` if the cache has no remaining capacity. pub fn is_full(&self) -> bool { self.next_free == PTRS_PER_SZ_CLASS } @@ -66,6 +72,7 @@ impl PtrCache { } } + /// Returns a cached pointer, or `None` if the cache is empty. pub fn alloc(&mut self) -> Option<*mut u8> { if self.is_empty() { return None; diff --git a/libkernel/src/memory/allocators/slab/heap.rs b/libkernel/src/memory/allocators/slab/heap.rs index 3fc50af..451b101 100644 --- a/libkernel/src/memory/allocators/slab/heap.rs +++ b/libkernel/src/memory/allocators/slab/heap.rs @@ -1,3 +1,5 @@ +//! Kernel heap built on top of the slab allocator. + use super::{allocator::SlabAllocator, cache::SlabCache}; use crate::{ CpuOps, @@ -11,15 +13,21 @@ use crate::{ }; use core::{alloc::GlobalAlloc, marker::PhantomData, ops::DerefMut}; +/// Provides access to the global slab allocator instance. pub trait SlabGetter, T: AddressTranslator<()>> { + /// Returns a reference to the global slab allocator. fn global_slab_alloc() -> &'static SlabAllocator; } +/// Per-CPU storage backend for a [`SlabCache`] pointer. pub trait SlabCacheStorage { + /// Stores the slab cache pointer (e.g. into per-CPU data). fn store(ptr: *mut SlabCache); + /// Retrieves the slab cache for the current CPU. fn get() -> impl DerefMut; } +/// The kernel heap allocator backed by the slab allocator and frame allocator. pub struct KHeap where CPU: CpuOps, @@ -56,6 +64,7 @@ where T: AddressTranslator<()>, SG: SlabGetter, { + /// Creates a new kernel heap instance. pub const fn new() -> Self { Self { phantom1: PhantomData, @@ -74,6 +83,7 @@ where pages_needed.next_power_of_two().ilog2() as usize } + /// Initializes the per-CPU slab cache for the current CPU. pub fn init_for_this_cpu() { let page: ClaimedPage = ClaimedPage::alloc_zeroed().expect("Cannot allocate heap page"); diff --git a/libkernel/src/memory/allocators/slab/mod.rs b/libkernel/src/memory/allocators/slab/mod.rs index ea76b71..5d5206d 100644 --- a/libkernel/src/memory/allocators/slab/mod.rs +++ b/libkernel/src/memory/allocators/slab/mod.rs @@ -1,3 +1,5 @@ +//! Slab allocator for small, fixed-size kernel objects. + use crate::memory::PAGE_SIZE; // Allocations of order 2 (4 pages) from the FA for slabs. diff --git a/libkernel/src/memory/allocators/smalloc.rs b/libkernel/src/memory/allocators/smalloc.rs index 4c1b6a0..2ae3362 100644 --- a/libkernel/src/memory/allocators/smalloc.rs +++ b/libkernel/src/memory/allocators/smalloc.rs @@ -1,5 +1,4 @@ -//! `smalloc` module: A simple physical memory allocator for early boot and -//! kernel use. +//! A simple physical memory allocator for early boot and kernel use. //! //! This allocator manages a fixed number of memory and reservation regions and //! supports basic allocation and freeing of physical memory blocks. @@ -34,6 +33,7 @@ use core::{ ops::{Index, IndexMut}, }; +/// A fixed-capacity list of non-overlapping physical memory regions. #[derive(Clone)] pub struct RegionList { count: usize, @@ -42,10 +42,12 @@ pub struct RegionList { } impl RegionList { + /// Returns `true` if the list contains no regions. pub fn is_empty(&self) -> bool { self.count == 0 } + /// Creates a new region list backed by a pre-allocated buffer at `region_ptr`. pub const fn new(max: usize, region_ptr: *mut PhysMemoryRegion) -> Self { Self { count: 0, @@ -68,6 +70,7 @@ impl RegionList { self.count -= 1; } + /// Inserts a region, merging with adjacent neighbours when possible. pub fn insert_region(&mut self, mut new_region: PhysMemoryRegion) { if self.count == self.max { panic!("Cannot insert into full region list"); @@ -108,6 +111,7 @@ impl RegionList { self[insert_idx] = new_region; } + /// Returns an iterator over the regions in this list. pub fn iter(&self) -> impl Iterator { (0..self.count).map(|x| self[x]) } @@ -156,8 +160,11 @@ impl IndexMut for RegionList { } } +/// A simple physical memory allocator that tracks free and reserved regions. pub struct Smalloc> { + /// Available memory regions. pub memory: RegionList, + /// Reserved memory regions (e.g. ACPI tables, device memory). pub res: RegionList, permit_region_realloc: bool, _phantom: PhantomData, @@ -171,6 +178,7 @@ enum RegionListType { unsafe impl> Send for Smalloc {} impl> Smalloc { + /// Creates a new allocator with the given memory and reservation lists. pub const fn new(memory: RegionList, reserved_list: RegionList) -> Self { Self { memory, @@ -230,6 +238,7 @@ impl> Smalloc { None } + /// Allocates a region of the given `size` and alignment. Returns the base physical address. pub fn alloc(&mut self, size: usize, align: usize) -> Result { if self.res.requires_reallocation() { self.grow_region_list(RegionListType::Res)?; @@ -245,6 +254,7 @@ impl> Smalloc { Ok(address) } + /// Frees a previously allocated region at `addr` with the given `size`. pub fn free(&mut self, addr: PA, size: usize) -> Result<()> { let region_to_remove = PhysMemoryRegion::new(addr, size); @@ -344,6 +354,7 @@ impl> Smalloc { } } + /// Marks a physical region as reserved without allocating from free memory. pub fn add_reservation(&mut self, region: PhysMemoryRegion) -> Result<()> { if self.res.requires_reallocation() { self.grow_region_list(RegionListType::Res)?; @@ -354,6 +365,7 @@ impl> Smalloc { Ok(()) } + /// Returns the base address of the first memory region, if any. pub fn base_ram_base_address(&self) -> Option { if self.memory.is_empty() { None @@ -362,6 +374,7 @@ impl> Smalloc { } } + /// Adds a new physical memory region to the allocator. pub fn add_memory(&mut self, region: PhysMemoryRegion) -> Result<()> { if self.memory.requires_reallocation() { self.grow_region_list(RegionListType::Mem)?; @@ -372,16 +385,19 @@ impl> Smalloc { Ok(()) } + /// Allocates a single page-aligned page and returns its frame number. pub fn alloc_page(&mut self) -> Result { let pa = self.alloc(PAGE_SIZE, PAGE_SIZE)?; Ok(PageFrame::from_pfn(pa.value() >> PAGE_SHIFT)) } + /// Returns an iterator over all memory regions known to the allocator. pub fn iter_memory(&self) -> impl Iterator { self.memory.iter() } + /// Returns a copy of the memory region list. pub fn get_memory_list(&self) -> RegionList { self.memory.clone() } @@ -399,6 +415,7 @@ impl> Smalloc { } } +/// Iterator over free (unreserved) physical memory regions. pub struct FreeRegionsIter where M: Iterator, diff --git a/libkernel/src/memory/claimed_page.rs b/libkernel/src/memory/claimed_page.rs index bf8d887..2086266 100644 --- a/libkernel/src/memory/claimed_page.rs +++ b/libkernel/src/memory/claimed_page.rs @@ -1,3 +1,5 @@ +//! RAII wrapper for a page frame claimed from the physical page allocator. + use super::{ PAGE_SIZE, address::AddressTranslator, @@ -55,6 +57,7 @@ impl, T: AddressTranslator<()>> ClaimedPage PA { self.0.region().start_address() @@ -98,6 +101,7 @@ impl, T: AddressTranslator<()>> ClaimedPage PageFrame { self.0.leak().start_address().to_pfn() } diff --git a/libkernel/src/memory/kbuf.rs b/libkernel/src/memory/kbuf.rs index 48e036c..170d28d 100644 --- a/libkernel/src/memory/kbuf.rs +++ b/libkernel/src/memory/kbuf.rs @@ -22,6 +22,7 @@ struct KBufInner> { write_waiters: WakerSet, } +/// A page-backed, async-aware circular kernel buffer. pub struct KBufCore, C: CpuOps> { inner: Arc, C>>, } @@ -35,6 +36,7 @@ impl, C: CpuOps> Clone for KBufCore { } impl, C: CpuOps> KBufCore { + /// Creates a new kernel buffer backed by the given storage. pub fn new(storage: S) -> Self { let rb = unsafe { SharedRb::from_raw_parts(storage, 0, 0) }; @@ -47,6 +49,7 @@ impl, C: CpuOps> KBufCore { } } + /// Returns a future that resolves when data is available for reading. pub fn read_ready(&self) -> impl Future + use { let lock = self.inner.clone(); @@ -59,6 +62,7 @@ impl, C: CpuOps> KBufCore { ) } + /// Waits until at least one slot is available for writing. pub async fn write_ready(&self) { wait_until( self.inner.clone(), @@ -81,6 +85,7 @@ impl, C: CpuOps> KBufCore { } } + /// Attempts to push `obj` into the buffer without waiting. Returns `Err(obj)` if full. pub fn try_push(&self, obj: T) -> core::result::Result<(), T> { let mut inner = self.inner.lock_save_irq(); @@ -93,6 +98,7 @@ impl, C: CpuOps> KBufCore { res } + /// Pops a value from the buffer, waiting asynchronously if it is empty. pub async fn pop(&self) -> T { loop { self.read_ready().await; @@ -103,6 +109,7 @@ impl, C: CpuOps> KBufCore { } } + /// Attempts to pop a value without blocking, returning `None` if the buffer is empty. pub fn try_pop(&self) -> Option { let mut inner = self.inner.lock_save_irq(); @@ -115,12 +122,14 @@ impl, C: CpuOps> KBufCore { res } + /// Returns the capacity of the underlying ring buffer. pub fn capacity(&self) -> NonZeroUsize { self.inner.lock_save_irq().buf.capacity() } } impl, C: CpuOps> KBufCore { + /// Asynchronously pops up to `buf.len()` items into `buf`, blocking until at least one is available. pub async fn pop_slice(&self, buf: &mut [T]) -> usize { wait_until( self.inner.clone(), @@ -141,6 +150,7 @@ impl, C: CpuOps> KBufCore { .await } + /// Attempts to pop up to `buf.len()` items without blocking. pub fn try_pop_slice(&self, buf: &mut [T]) -> usize { let mut guard = self.inner.lock_save_irq(); let size = guard.buf.pop_slice(buf); @@ -150,6 +160,7 @@ impl, C: CpuOps> KBufCore { size } + /// Asynchronously pushes items from `buf`, blocking until space is available. pub async fn push_slice(&self, buf: &[T]) -> usize { wait_until( self.inner.clone(), @@ -176,6 +187,7 @@ impl, C: CpuOps> KBufCore { .await } + /// Attempts to push items from `buf` without blocking. pub fn try_push_slice(&self, buf: &[T]) -> usize { let mut guard = self.inner.lock_save_irq(); let size = guard.buf.push_slice(buf); diff --git a/libkernel/src/memory/mod.rs b/libkernel/src/memory/mod.rs index 6561307..d149dc2 100644 --- a/libkernel/src/memory/mod.rs +++ b/libkernel/src/memory/mod.rs @@ -1,3 +1,13 @@ +//! Memory management primitives. +//! +//! This module contains the core building blocks for kernel memory management: +//! typed addresses, memory regions, page frame tracking, page allocators, +//! address-space abstractions, and kernel buffers. +//! +//! The always-available submodules ([`address`], [`page`], [`region`]) require +//! no features. Higher-level subsystems are gated behind their respective +//! feature flags. + pub mod address; #[cfg(feature = "paging")] pub mod address_space; @@ -16,6 +26,9 @@ pub mod pg_offset; pub mod proc_vm; pub mod region; +/// The system page size in bytes (4 KiB). pub const PAGE_SIZE: usize = 4096; +/// The number of bits to shift to convert between byte offsets and page numbers. pub const PAGE_SHIFT: usize = PAGE_SIZE.trailing_zeros() as usize; +/// Bitmask for extracting the within-page offset from an address. pub const PAGE_MASK: usize = PAGE_SIZE - 1; diff --git a/libkernel/src/memory/page.rs b/libkernel/src/memory/page.rs index e6c2b2b..cb038cb 100644 --- a/libkernel/src/memory/page.rs +++ b/libkernel/src/memory/page.rs @@ -1,11 +1,14 @@ -use super::{ - PAGE_SHIFT, - address::PA, - region::PhysMemoryRegion, -}; +//! Page frame numbers. +//! +//! A [`PageFrame`] is a lightweight handle for a physical page, identified by +//! its page frame number (PFN). + +use super::{PAGE_SHIFT, address::PA, region::PhysMemoryRegion}; use crate::memory::PAGE_SIZE; use core::fmt::Display; +/// A page frame number (PFN) — an index into physical memory in units of +/// [`PAGE_SIZE`]. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct PageFrame { n: usize, @@ -18,18 +21,22 @@ impl Display for PageFrame { } impl PageFrame { + /// Creates a `PageFrame` from a raw page frame number. pub fn from_pfn(n: usize) -> Self { Self { n } } + /// Returns the physical address of the start of this page frame. pub fn pa(&self) -> PA { PA::from_value(self.n << PAGE_SHIFT) } + /// Returns this page frame as a single-page physical memory region. pub fn as_phys_range(&self) -> PhysMemoryRegion { PhysMemoryRegion::new(self.pa(), PAGE_SIZE) } + /// Returns the raw page frame number. pub fn value(&self) -> usize { self.n } @@ -41,6 +48,7 @@ impl PageFrame { } } + /// Returns a new `PageFrame` offset by `n` pages. #[must_use] pub fn add_pages(self, n: usize) -> Self { Self { n: self.n + n } diff --git a/libkernel/src/memory/permissions.rs b/libkernel/src/memory/permissions.rs index 037624a..fe4fdaf 100644 --- a/libkernel/src/memory/permissions.rs +++ b/libkernel/src/memory/permissions.rs @@ -1,3 +1,5 @@ +//! Page-table entry permission flags. + use core::fmt; #[cfg(feature = "proc_vm")] diff --git a/libkernel/src/memory/pg_offset.rs b/libkernel/src/memory/pg_offset.rs index 486f8c0..b8fca33 100644 --- a/libkernel/src/memory/pg_offset.rs +++ b/libkernel/src/memory/pg_offset.rs @@ -1,7 +1,10 @@ +//! Page-offset arithmetic helpers. + use super::address::{AddressTranslator, TPA, TVA}; use crate::VirtualMemory; use core::marker::PhantomData; +/// Translates between physical and virtual addresses using a fixed page-offset mapping. pub struct PageOffsetTranslator { _phantom: PhantomData, } diff --git a/libkernel/src/memory/proc_vm/memory_map/mod.rs b/libkernel/src/memory/proc_vm/memory_map/mod.rs index fc45fc1..1461a1d 100644 --- a/libkernel/src/memory/proc_vm/memory_map/mod.rs +++ b/libkernel/src/memory/proc_vm/memory_map/mod.rs @@ -1,3 +1,5 @@ +//! Memory map management for a process address space. + use super::vmarea::{VMAPermissions, VMArea, VMAreaKind}; use crate::{ UserAddressSpace, @@ -17,11 +19,20 @@ pub struct MemoryMap { address_space: AS, } +/// Specifies how the kernel should choose the virtual address for a mapping. #[derive(Debug, PartialEq, Eq)] pub enum AddressRequest { + /// Let the kernel pick any suitable address. Any, + /// Prefer the given address but fall back to any free region. Hint(VA), - Fixed { address: VA, permit_overlap: bool }, + /// Map at exactly the given address. + Fixed { + /// The exact virtual address to map at. + address: VA, + /// If `true`, existing mappings in the range may be replaced. + permit_overlap: bool, + }, } impl MemoryMap { @@ -168,6 +179,7 @@ impl MemoryMap { self.unmap_region(range.align_to_page_boundary(), None) } + /// Changes the memory protection flags for a page-aligned region. pub fn mprotect( &mut self, protect_region: VirtMemoryRegion, @@ -498,18 +510,22 @@ impl MemoryMap { }) } + /// Returns a mutable reference to the underlying address space. pub fn address_space_mut(&mut self) -> &mut AS { &mut self.address_space } + /// Returns the number of VMAs in this memory map. pub fn vma_count(&self) -> usize { self.vmas.len() } + /// Returns an iterator over all VMAs in address order. pub fn iter_vmas(&self) -> impl Iterator { self.vmas.values() } } #[cfg(test)] +#[allow(missing_docs)] pub mod tests; diff --git a/libkernel/src/memory/proc_vm/mod.rs b/libkernel/src/memory/proc_vm/mod.rs index 624d10f..cf890b0 100644 --- a/libkernel/src/memory/proc_vm/mod.rs +++ b/libkernel/src/memory/proc_vm/mod.rs @@ -15,6 +15,7 @@ pub mod vmarea; const BRK_PERMISSIONS: VMAPermissions = VMAPermissions::rw(); +/// The virtual memory state of a user-space process. pub struct ProcessVM { mm: MemoryMap, brk: VirtMemoryRegion, @@ -49,6 +50,7 @@ impl ProcessVM { Ok(Self { mm, brk }) } + /// Constructs a `ProcessVM` from an existing memory map. pub fn from_map(map: MemoryMap) -> Self { // Last entry will be the VMA with the highest address. let brk = map @@ -67,6 +69,7 @@ impl ProcessVM { } } + /// Creates an empty `ProcessVM` with no mappings and a zero-sized heap. pub fn empty() -> Result { Ok(Self { mm: MemoryMap::new()?, @@ -74,6 +77,7 @@ impl ProcessVM { }) } + /// Finds the VMA covering `addr` if the given access type is permitted. pub fn find_vma_for_fault(&self, addr: VA, access_type: AccessKind) -> Option<&VMArea> { let vma = self.mm.find_vma(addr)?; @@ -84,18 +88,22 @@ impl ProcessVM { } } + /// Returns a non-mutable reference to the underlying memory map. pub fn mm(&self) -> &MemoryMap { &self.mm } + /// Returns a mutable reference to the underlying memory map. pub fn mm_mut(&mut self) -> &mut MemoryMap { &mut self.mm } + /// Returns the current start address of the program break (heap). pub fn start_brk(&self) -> VA { self.brk.start_address() } + /// Returns the current end address of the program break (heap). pub fn current_brk(&self) -> VA { self.brk.end_address() } @@ -165,6 +173,7 @@ impl ProcessVM { Ok(new_end_addr) } + /// Clones this process VM, marking all writable pages as copy-on-write. pub fn clone_as_cow(&mut self) -> Result { Ok(Self { mm: self.mm.clone_as_cow()?, diff --git a/libkernel/src/memory/proc_vm/vmarea.rs b/libkernel/src/memory/proc_vm/vmarea.rs index 6f29d1e..322cd72 100644 --- a/libkernel/src/memory/proc_vm/vmarea.rs +++ b/libkernel/src/memory/proc_vm/vmarea.rs @@ -26,12 +26,16 @@ use object::{ /// Describes the permissions assigned for this VMA. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct VMAPermissions { + /// Whether reads are allowed. pub read: bool, + /// Whether writes are allowed. pub write: bool, + /// Whether execution is allowed. pub execute: bool, } impl VMAPermissions { + /// Read-write permissions. pub const fn rw() -> Self { Self { read: true, @@ -40,6 +44,7 @@ impl VMAPermissions { } } + /// Read-execute permissions. pub const fn rx() -> Self { Self { read: true, @@ -48,6 +53,7 @@ impl VMAPermissions { } } + /// Read-only permissions. pub const fn ro() -> Self { Self { read: true, @@ -156,10 +162,12 @@ pub enum VMAreaKind { } impl VMAreaKind { + /// Creates a new anonymous VMA kind. pub fn new_anon() -> Self { Self::Anon } + /// Creates a new file-backed VMA kind. pub fn new_file(file: Arc, offset: u64, len: u64) -> Self { Self::File(VMFileMapping { file, offset, len }) } @@ -173,6 +181,7 @@ impl VMAreaKind { /// managing a process's memory layout. #[derive(Clone, PartialEq)] pub struct VMArea { + /// The virtual address range of this VMA. pub region: VirtMemoryRegion, pub(super) name: String, pub(super) kind: VMAreaKind, @@ -195,6 +204,7 @@ impl VMArea { } } + /// Sets a human-readable name for this VMA (e.g. `[stack]`, `[heap]`). pub fn set_name>(&mut self, s: S) { self.name = s.as_ref().to_string(); } @@ -266,9 +276,9 @@ impl VMArea { /// /// # Returns /// - /// - [`AccessValidation::Valid`]: If the address and permissions are valid. - /// - [`AccessValidation::NotPresent`]: If the address is outside this VMA. - /// - [`AccessValidation::PermissionDenied`]: If the address is inside this + /// - [`FaultValidation::Valid`]: If the address and permissions are valid. + /// - [`FaultValidation::NotPresent`]: If the address is outside this VMA. + /// - [`FaultValidation::PermissionDenied`]: If the address is inside this /// VMA but the access is not allowed. This allows the caller to immediately /// identify a segmentation fault without checking other VMAs. pub fn validate_fault(&self, addr: VA, kind: AccessKind) -> FaultValidation { @@ -371,10 +381,12 @@ impl VMArea { }) } + /// Returns the memory permissions for this VMA. pub fn permissions(&self) -> VMAPermissions { self.permissions } + /// Returns `true` if the given virtual address falls within this VMA. pub fn contains_address(&self, addr: VA) -> bool { self.region.contains_address(addr) } @@ -464,6 +476,7 @@ impl VMArea { self.region } + /// Returns the file offset for a file-backed VMA, or `None` for anonymous mappings. pub fn file_offset(&self) -> Option { match self.kind { VMAreaKind::File(ref vmfile_mapping) => Some(vmfile_mapping.offset()), @@ -471,6 +484,7 @@ impl VMArea { } } + /// Returns the inode ID of the backing file, or `None` for anonymous mappings. pub fn inode_id(&self) -> Option { match self.kind { VMAreaKind::File(ref vmfile_mapping) => Some(vmfile_mapping.file().id()), @@ -478,12 +492,14 @@ impl VMArea { } } + /// Returns the human-readable name of this VMA. pub fn name(&self) -> &str { &self.name } } #[cfg(test)] +#[allow(missing_docs)] pub mod tests { use crate::fs::InodeId; use core::any::Any; diff --git a/libkernel/src/memory/region.rs b/libkernel/src/memory/region.rs index 5cee2b0..e40abd1 100644 --- a/libkernel/src/memory/region.rs +++ b/libkernel/src/memory/region.rs @@ -1,4 +1,4 @@ -//! `region` module: Contiguous memory regions. +//! Contiguous memory regions. //! //! This module defines `MemoryRegion`, a generic abstraction for handling //! ranges of memory in both physical and virtual address spaces. @@ -382,6 +382,7 @@ impl PhysMemoryRegion { VirtMemoryRegion::new(self.address.to_va::(), self.size) } + /// Returns an iterator over the page frame numbers in this region. pub fn iter_pfns(self) -> impl Iterator { let mut count = 0; let pages_count = self.size >> PAGE_SHIFT; diff --git a/libkernel/src/pod.rs b/libkernel/src/pod.rs index 78177f9..86b634f 100644 --- a/libkernel/src/pod.rs +++ b/libkernel/src/pod.rs @@ -1,3 +1,9 @@ +//! Plain Old Data trait and blanket implementations. +//! +//! Types that implement [`Pod`] can be safely created by copying their raw byte +//! representation. This is useful for reading on-disk structures from block +//! devices. + /// An unsafe trait indicating that a type is "Plain Old Data". /// /// A type is `Pod` if it is a simple collection of bytes with no invalid bit diff --git a/libkernel/src/proc/caps.rs b/libkernel/src/proc/caps.rs index 7a37449..1ca6d6f 100644 --- a/libkernel/src/proc/caps.rs +++ b/libkernel/src/proc/caps.rs @@ -1,6 +1,22 @@ +//! Linux-compatible process capabilities. +//! +//! This module implements the five capability sets (`effective`, `permitted`, +//! `inheritable`, `ambient`, `bounding`) used by the kernel for +//! fine-grained privilege checks. + +#![allow(missing_docs)] + use crate::error::{KernelError, Result}; +// Linux-compatible capability flags. +// +// Each flag corresponds to a single Linux capability as defined in +// `include/uapi/linux/capability.h`. bitflags::bitflags! { + /// Linux-compatible capability flags. + /// + /// Each flag corresponds to a single Linux capability as defined in + /// `include/uapi/linux/capability.h`. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CapabilitiesFlags: u64 { const CAP_CHOWN = 1 << 0; @@ -47,6 +63,9 @@ bitflags::bitflags! { } } +/// The five capability sets associated with a process. +/// +/// See `capabilities(7)` for the full semantics of each set. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Capabilities { effective: CapabilitiesFlags, @@ -57,6 +76,7 @@ pub struct Capabilities { } impl Capabilities { + /// Creates a new `Capabilities` with the given sets. pub fn new( effective: CapabilitiesFlags, permitted: CapabilitiesFlags, @@ -73,6 +93,7 @@ impl Capabilities { } } + /// Creates a root (all-capable) set. pub fn new_root() -> Self { Self { effective: CapabilitiesFlags::all(), @@ -83,6 +104,7 @@ impl Capabilities { } } + /// Creates an empty (no capabilities) set. pub fn new_empty() -> Self { Self { effective: CapabilitiesFlags::empty(), @@ -129,30 +151,37 @@ impl Capabilities { Ok(()) } + /// Returns the effective capability flags. pub fn effective(&self) -> CapabilitiesFlags { self.effective } + /// Returns the permitted capability flags. pub fn permitted(&self) -> CapabilitiesFlags { self.permitted } + /// Returns the inheritable capability flags. pub fn inheritable(&self) -> CapabilitiesFlags { self.inheritable } + /// Returns the ambient capability flags. pub fn ambient(&self) -> CapabilitiesFlags { self.ambient } + /// Returns a mutable reference to the ambient capability flags. pub fn ambient_mut(&mut self) -> &mut CapabilitiesFlags { &mut self.ambient } + /// Returns the bounding capability flags. pub fn bounding(&self) -> CapabilitiesFlags { self.bounding } + /// Returns a mutable reference to the bounding capability flags. pub fn bounding_mut(&mut self) -> &mut CapabilitiesFlags { &mut self.bounding } diff --git a/libkernel/src/proc/ids.rs b/libkernel/src/proc/ids.rs index 31ac874..1cddc62 100644 --- a/libkernel/src/proc/ids.rs +++ b/libkernel/src/proc/ids.rs @@ -1,16 +1,25 @@ +//! User and group identity types. +//! +//! [`Uid`] and [`Gid`] are thin wrappers around `u32` that prevent accidental +//! mixing of user IDs and group IDs at the type level. + +/// A user identity (UID). #[repr(C)] #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct Uid(u32); impl Uid { + /// Creates a new `Uid` with the given numeric value. pub const fn new(id: u32) -> Self { Self(id) } + /// Returns `true` if this is the root (UID 0) user. pub fn is_root(self) -> bool { self.0 == 0 } + /// Returns the root UID (0). pub fn new_root() -> Self { Self(0) } @@ -29,15 +38,18 @@ impl From for u32 { } } +/// A group identity (GID). #[repr(C)] #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct Gid(u32); impl Gid { + /// Creates a new `Gid` with the given numeric value. pub const fn new(id: u32) -> Self { Self(id) } + /// Returns the root group GID (0). pub fn new_root_group() -> Self { Self(0) } diff --git a/libkernel/src/proc/mod.rs b/libkernel/src/proc/mod.rs index d11c71f..30a8d8c 100644 --- a/libkernel/src/proc/mod.rs +++ b/libkernel/src/proc/mod.rs @@ -1,2 +1,8 @@ +//! Process identity and capability types. +//! +//! This module provides the fundamental types for process credentials: +//! user/group identifiers ([`ids`]) and Linux-compatible capabilities +//! ([`caps`]). + pub mod caps; pub mod ids; diff --git a/libkernel/src/sync/condvar.rs b/libkernel/src/sync/condvar.rs index 9281892..b3cc360 100644 --- a/libkernel/src/sync/condvar.rs +++ b/libkernel/src/sync/condvar.rs @@ -1,3 +1,5 @@ +//! Async-aware condition variable. + use super::spinlock::SpinLockIrq; use super::waker_set::WakerSet; use crate::CpuOps; @@ -5,8 +7,11 @@ use alloc::sync::Arc; /// The type of wakeup that should occur after a state update. pub enum WakeupType { + /// Do not wake any waiting task. None, + /// Wake exactly one waiting task. One, + /// Wake all waiting tasks. All, } diff --git a/libkernel/src/sync/mod.rs b/libkernel/src/sync/mod.rs index e0a0655..079e9c1 100644 --- a/libkernel/src/sync/mod.rs +++ b/libkernel/src/sync/mod.rs @@ -1,3 +1,8 @@ +//! Synchronisation primitives for `no_std` kernel environments. +//! +//! All primitives are generic over [`CpuOps`](crate::CpuOps) so they can +//! disable/restore interrupts on the local core. + pub mod condvar; pub mod mpsc; pub mod mutex; diff --git a/libkernel/src/sync/mutex.rs b/libkernel/src/sync/mutex.rs index 23a47e9..68e4be6 100644 --- a/libkernel/src/sync/mutex.rs +++ b/libkernel/src/sync/mutex.rs @@ -1,3 +1,5 @@ +//! Async-aware mutual-exclusion lock. + use alloc::collections::VecDeque; use core::cell::UnsafeCell; use core::future::Future; diff --git a/libkernel/src/sync/once_lock.rs b/libkernel/src/sync/once_lock.rs index f934fcb..8d22ee5 100644 --- a/libkernel/src/sync/once_lock.rs +++ b/libkernel/src/sync/once_lock.rs @@ -1,3 +1,5 @@ +//! A thread-safe cell that is initialized exactly once. + use core::fmt; use crate::CpuOps; diff --git a/libkernel/src/sync/per_cpu.rs b/libkernel/src/sync/per_cpu.rs index 19c5567..13630fc 100644 --- a/libkernel/src/sync/per_cpu.rs +++ b/libkernel/src/sync/per_cpu.rs @@ -65,6 +65,7 @@ where } } +/// A mutable borrow of per-CPU data that restores interrupts on drop. pub struct IrqSafeRefMut<'a, T, CPU: CpuOps> { borrow: ManuallyDrop>, flags: usize, @@ -198,6 +199,7 @@ impl PerCpu, CPU> { } } + /// Attempts to mutably borrow the per-CPU data, returning `None` if already borrowed. #[track_caller] pub fn try_borrow_mut(&self) -> Option, CPU>> { let flags = CPU::disable_interrupts(); @@ -225,6 +227,7 @@ impl PerCpu, CPU> { } impl PerCpu { + /// Returns a reference to the data for the given CPU. pub fn get_by_cpu(&self, cpu_id: usize) -> &T { unsafe { self.get_for_cpu(cpu_id) } } diff --git a/libkernel/src/sync/rwlock.rs b/libkernel/src/sync/rwlock.rs index daa85f6..2b6759a 100644 --- a/libkernel/src/sync/rwlock.rs +++ b/libkernel/src/sync/rwlock.rs @@ -1,3 +1,5 @@ +//! Async-aware readers–writer lock. + use super::spinlock::SpinLockIrq; use crate::CpuOps; use crate::sync::mutex::Mutex; diff --git a/libkernel/src/sync/spinlock.rs b/libkernel/src/sync/spinlock.rs index a913df0..0ba14b0 100644 --- a/libkernel/src/sync/spinlock.rs +++ b/libkernel/src/sync/spinlock.rs @@ -1,3 +1,5 @@ +//! Low-level spin lock primitives. + use core::cell::UnsafeCell; use core::hint::spin_loop; use core::marker::PhantomData; diff --git a/libkernel/src/sync/waker_set.rs b/libkernel/src/sync/waker_set.rs index 3b9cd1f..8e164a2 100644 --- a/libkernel/src/sync/waker_set.rs +++ b/libkernel/src/sync/waker_set.rs @@ -1,3 +1,5 @@ +//! Waker registration and notification set. + use alloc::collections::BTreeMap; use alloc::sync::Arc; use core::{ @@ -9,6 +11,7 @@ use crate::CpuOps; use super::spinlock::SpinLockIrq; +/// A set of registered [`Waker`]s that can be selectively or collectively woken. pub struct WakerSet { waiters: BTreeMap, next_id: u64, @@ -21,6 +24,7 @@ impl Default for WakerSet { } impl WakerSet { + /// Creates a new, empty waker set. pub fn new() -> Self { Self { waiters: BTreeMap::new(), @@ -38,6 +42,7 @@ impl WakerSet { id } + /// Returns `true` if the given token is still registered in the set. pub fn contains_token(&self, token: u64) -> bool { self.waiters.contains_key(&token) } @@ -84,6 +89,7 @@ impl WakerSet { } } + /// Registers a waker together with associated data, returning its token. pub fn register_with_data(&mut self, waker: &Waker, data: T) -> u64 { let id = self.allocate_id();