Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,34 @@ jobs:
- name: Run clippy
run: cargo clippy -- -D warnings

- name: Run unit tests for libkernel
run: cargo test -p libkernel --target x86_64-unknown-linux-gnu --all-features
- name: Run clippy for libkernel (all features, including tests)
run: >
cargo clippy -p libkernel
--target x86_64-unknown-linux-gnu
--all-features --all-targets -- -D warnings

libkernel-features:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v6

# Use the toolchain from rust-toolchain.toml
- name: Install Rust
uses: actions-rust-lang/setup-rust-toolchain@v1

- name: Install cargo-hack
uses: taiki-e/install-action@cargo-hack

- name: Check libkernel for every feature combination
run: >
cargo hack check -p libkernel
--target x86_64-unknown-linux-gnu
--feature-powerset --no-dev-deps --keep-going

- name: Test libkernel for every feature combination
run: >
cargo hack test -p libkernel
--target x86_64-unknown-linux-gnu
--feature-powerset --keep-going
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,3 @@ codegen-units = 1

[lints]
workspace = true

17 changes: 11 additions & 6 deletions libkernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,26 @@ categories = ["no-std", "os", "embedded"]

[features]
default = []
sync = []
sync = ["dep:log"]
alloc = ["sync", "dep:intrusive-collections"]
paging = ["alloc", "dep:tock-registers", "dep:paste"]
proc = []
proc = ["dep:bitflags"]
address_space = ["paging"]
fs = ["proc", "sync", "dep:async-trait", "dep:ext4plus"]
proc_vm = ["paging", "fs", "dep:object"]
proc_vm = ["address_space", "fs", "dep:object"]
kbuf = ["sync", "dep:ringbuf"]
all = ["paging", "fs", "proc_vm", "kbuf"]
all = ["paging", "address_space", "fs", "proc_vm", "kbuf"]

[dependencies]
# Always-on dependencies
bitflags = { workspace = true }
log = { workspace = true }
thiserror = { version = "2.0.12", default-features = false }

# sync
log = { workspace = true, optional = true }

# proc
bitflags = { workspace = true, optional = true }

# alloc
intrusive-collections = { version = "0.10.0", default-features = false, optional = true }

Expand Down
21 changes: 11 additions & 10 deletions libkernel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@ 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, 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 |
| Feature | Enables | Implies |
|-----------------|-------------------------------------------------------|-----------------------|
| `sync` | Synchronisation primitives (spinlock, mutex, rwlock…) | — |
| `alloc` | Memory allocators (buddy, slab) and collection types | `sync` |
| `paging` | Page tables, PTE helpers | `alloc` |
| `address_space` | Kernel/user address-space traits and access kinds | `paging` |
| `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) | `address_space`, `fs` |
| `kbuf` | Async-aware circular kernel buffers | `sync` |
| `all` | Everything above | all of the above |

## Quick start

Expand Down
8 changes: 2 additions & 6 deletions libkernel/src/arch/arm64/memory/pg_tear_down.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,7 @@ mod tests {
.insert(region.start_address().value(), region.size())
.is_some()
{
panic!(
"Double free detected! Physical Address {:?} was freed twice.",
region
);
panic!("Double free detected! Physical Address {region:?} was freed twice.");
}
})
.expect("Teardown failed");
Expand Down Expand Up @@ -479,8 +476,7 @@ mod tests {
let slot = unsafe { (table_pa as *const u64).add(idx).read_volatile() };
assert_eq!(
slot, 0,
"table frame {:#x} slot {idx} should be zeroed after FreeAndClear",
table_pa
"table frame {table_pa:#x} slot {idx} should be zeroed after FreeAndClear"
);
}
}
Expand Down
3 changes: 1 addition & 2 deletions libkernel/src/arch/x86_64/memory/pg_descriptors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,8 +535,7 @@ mod tests {
assert_eq!(
d.as_raw() & (1 << 7),
0,
"PAT bit (7) must be clear on PTE for {:?}",
mt
"PAT bit (7) must be clear on PTE for {mt:?}"
);
}
}
Expand Down
16 changes: 6 additions & 10 deletions libkernel/src/arch/x86_64/memory/pg_tear_down.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,7 @@ mod tests {
.insert(region.start_address().value(), region.size())
.is_some()
{
panic!(
"Double free detected! Physical Address {:?} was freed twice.",
region
);
panic!("Double free detected! Physical Address {region:?} was freed twice.");
}
})
.expect("Teardown failed");
Expand Down Expand Up @@ -392,10 +389,10 @@ mod tests {
harness.inner.root_table,
&mut harness.inner.create_walk_ctx(),
|entry| {
if let EntryKind::Mapping(virt) = &entry.kind {
if virt.start_address().value() >= (1usize << 47) {
return TeardownAction::Skip;
}
if let EntryKind::Mapping(virt) = &entry.kind
&& virt.start_address().value() >= (1usize << 47)
{
return TeardownAction::Skip;
}
TeardownAction::Free
},
Expand Down Expand Up @@ -534,8 +531,7 @@ mod tests {
let slot = unsafe { (table_pa as *const u64).add(idx).read_volatile() };
assert_eq!(
slot, 0,
"table frame {:#x} slot {idx} should be zeroed after FreeAndClear",
table_pa
"table frame {table_pa:#x} slot {idx} should be zeroed after FreeAndClear"
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion libkernel/src/fs/blk/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Block device layer.

pub mod buffer;
#[cfg(feature = "paging")]
#[cfg(feature = "address_space")]
pub mod ramdisk;
2 changes: 1 addition & 1 deletion libkernel/src/fs/blk/ramdisk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use crate::{
memory::{
PAGE_SIZE,
address::{TVA, VA},
address_space::KernAddressSpace,
paging::permissions::PtePermissions,
proc_vm::address_space::KernAddressSpace,
region::{PhysMemoryRegion, VirtMemoryRegion},
},
};
Expand Down
4 changes: 2 additions & 2 deletions libkernel/src/fs/filesystems/fat32/bpb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ pub mod test {
let bpb = create_test_bpb();

// Clusters 0 and 1 are reserved and should not be converted to sectors.
assert!(matches!(bpb.cluster_to_sectors(Cluster(0)), Err(_)));
assert!(matches!(bpb.cluster_to_sectors(Cluster(1)), Err(_)));
assert!(bpb.cluster_to_sectors(Cluster(0)).is_err());
assert!(bpb.cluster_to_sectors(Cluster(1)).is_err());
}
}
4 changes: 2 additions & 2 deletions libkernel/src/fs/filesystems/fat32/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ mod test {
utf16_chars.push(0x0000); // Null terminator

// Pad to a multiple of 13 characters
while utf16_chars.len() % 13 != 0 {
while !utf16_chars.len().is_multiple_of(13) {
utf16_chars.push(0xFFFF);
}

Expand Down Expand Up @@ -526,7 +526,7 @@ mod test {
ptr::write_unaligned(&raw mut lfn.name3, chunk[11..13].try_into().unwrap());
}

lfn_entries.push(unsafe { core::mem::transmute(lfn) });
lfn_entries.push(unsafe { core::mem::transmute::<LfnEntry, [u8; 32]>(lfn) });
}
lfn_entries
}
Expand Down
3 changes: 1 addition & 2 deletions libkernel/src/fs/filesystems/fat32/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,7 @@ pub mod test {

fn iter_clusters(&self, root: Cluster) -> impl Iterator<Item = Result<Cluster>> {
// Assume a simple contiguous chain for testing.
let num_clusters =
(self.file_data.len() + self.sectors_per_cluster - 1) / self.sectors_per_cluster;
let num_clusters = self.file_data.len().div_ceil(self.sectors_per_cluster);
(0..num_clusters).map(move |i| Ok(Cluster((root.value() + i) as u32)))
}
}
Expand Down
13 changes: 8 additions & 5 deletions libkernel/src/fs/filesystems/tmpfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,12 @@ mod tests {
}
}

/// The `TmpFs` instantiation used throughout these tests.
type TestTmpFs = TmpFs<MockCpuOps, TmpFsPgAllocGetter, IdentityTranslator>;

/// The regular-file inode type matching [`TestTmpFs`].
type TestTmpFsReg = TmpFsReg<MockCpuOps, TmpFsPgAllocGetter, IdentityTranslator>;

/// Initializes the global allocator for the test suite.
fn init_allocator() {
PG_ALLOC.get_or_init(|| {
Expand All @@ -878,10 +884,7 @@ mod tests {
}

/// Creates a fresh Filesystem and a detached regular file for isolated file testing.
fn setup_env() -> (
Arc<TmpFs<MockCpuOps, TmpFsPgAllocGetter, IdentityTranslator>>,
TmpFsReg<MockCpuOps, TmpFsPgAllocGetter, IdentityTranslator>,
) {
fn setup_env() -> (Arc<TestTmpFs>, TestTmpFsReg) {
init_allocator();
let fs = TmpFs::new(0);
let reg = TmpFsReg::new(
Expand All @@ -893,7 +896,7 @@ mod tests {
}

/// Creates just the Filesystem to test directory hierarchies.
fn setup_fs() -> Arc<TmpFs<MockCpuOps, TmpFsPgAllocGetter, IdentityTranslator>> {
fn setup_fs() -> Arc<TestTmpFs> {
init_allocator();
TmpFs::new(1)
}
Expand Down
21 changes: 11 additions & 10 deletions libkernel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@
//! 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, 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 |
//! | Feature | Enables | Implies |
//! |-----------------|-------------------------------------------------------|-----------------------|
//! | `sync` | Synchronisation primitives (spinlock, mutex, rwlock…) | — |
//! | `alloc` | Memory allocators (buddy, slab) and collection types | `sync` |
//! | `paging` | Page tables, PTE helpers | `alloc` |
//! | `address_space` | Kernel/user address-space traits and access kinds | `paging` |
//! | `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) | `address_space`, `fs` |
//! | `kbuf` | Async-aware circular kernel buffers | `sync` |
//! | `all` | Everything above | all of the above |
//!
//! ## The `CpuOps` trait
//!
Expand Down
25 changes: 25 additions & 0 deletions libkernel/src/memory/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,20 @@ impl<T> AddressTranslator<T> for IdentityTranslator {
}
}

/// Translates between physical and virtual addresses using a fixed page-offset
/// mapping.
pub struct PageOffsetTranslator<const OFFSET: usize>;

impl<T, const OFFSET: usize> AddressTranslator<T> for PageOffsetTranslator<OFFSET> {
fn virt_to_phys(va: TVA<T>) -> TPA<T> {
TPA::from_value(va.value() - OFFSET)
}

fn phys_to_virt(pa: TPA<T>) -> TVA<T> {
TVA::from_value(pa.value() + OFFSET)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -435,6 +449,17 @@ mod tests {
assert_eq!(va.value(), TEST_ADDR);
}

#[test]
fn test_page_offset_translation() {
const OFFSET: usize = usize::MAX & !0xffff;
for value in [0, NON_ALIGNED_ADDR, 0xffff] {
let pa = TPA::<u8>::from_value(value);
let va: TVA<u8> = pa.to_va::<PageOffsetTranslator<OFFSET>>();
assert_eq!(va.value(), OFFSET + value);
assert_eq!(va.to_pa::<PageOffsetTranslator<OFFSET>>(), pa);
}
}

#[test]
fn test_va_pointer_conversion() {
let va = VA::from_value(0x1000);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
//! Kernel and user address-space management.
//!
//! Available with the `address_space` feature, independently of process VMAs
//! and filesystems.

use alloc::vec::Vec;

Expand All @@ -14,6 +17,17 @@ use crate::{
sync::spinlock::SpinLockIrq,
};

/// Describes the kind of access that occurred during a page fault.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum AccessKind {
/// The CPU attempted to read the faulting address.
Read,
/// The CPU attempted to write to the faulting address.
Write,
/// The CPU attempted to execute the instruction at the faulting address.
Execute,
}

/// An architecture-independent representation of a page table entry (PTE).
pub struct PageInfo {
/// The page frame number identifying the physical page.
Expand Down
Loading
Loading