mirror of
https://github.com/hexagonal-sun/moss-kernel.git
synced 2026-05-24 17:05:41 -04:00
Merge pull request #301 from hexagonal-sun/libkernel-yet-more-fixes
Libkernel yet more fixes
This commit is contained in:
@@ -219,6 +219,10 @@ macro_rules! define_descriptor {
|
||||
let addr = reg.read(BlockPageFields::OUTPUT_ADDR);
|
||||
Some(PA::from_value((addr << Self::MAP_SHIFT) as usize))
|
||||
}
|
||||
|
||||
fn permissions(self) -> Option<PtePermissions> {
|
||||
self.permissions()
|
||||
}
|
||||
}
|
||||
}
|
||||
)?
|
||||
|
||||
@@ -195,6 +195,14 @@ macro_rules! impl_pa_mapper {
|
||||
|
||||
Some(self.address())
|
||||
}
|
||||
|
||||
fn permissions(self) -> Option<PtePermissions> {
|
||||
if (self.0 & $marker) != $marker {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(self.permissions())
|
||||
}
|
||||
}
|
||||
}
|
||||
)+
|
||||
|
||||
@@ -4,18 +4,20 @@ use crate::{
|
||||
error::{MapError, Result},
|
||||
memory::{
|
||||
PAGE_SIZE,
|
||||
address::{TPA, VA},
|
||||
address::{PA, TPA, VA},
|
||||
paging::{
|
||||
NullTlbInvalidator, PageTableEntry, PageTableMapper, PgTable, PgTableArray,
|
||||
walk::{RecursiveWalker, WalkContext},
|
||||
NullTlbInvalidator, PaMapper, PageTableEntry, PageTableMapper, PgTable, PgTableArray,
|
||||
TableMapper,
|
||||
permissions::PtePermissions,
|
||||
walk::{RecursiveWalker, Translator, WalkContext},
|
||||
},
|
||||
region::VirtMemoryRegion,
|
||||
region::{PhysMemoryRegion, VirtMemoryRegion},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
pg_descriptors::PTE,
|
||||
pg_tables::{PML4Table, PTable},
|
||||
pg_tables::{PDPTable, PML4Table, PTable},
|
||||
};
|
||||
|
||||
impl RecursiveWalker<PTE> for PTable {
|
||||
@@ -112,6 +114,70 @@ pub fn get_pte<PM: PageTableMapper>(
|
||||
Ok(descriptor)
|
||||
}
|
||||
|
||||
impl Translator for PML4Table {
|
||||
fn translate<PM: PageTableMapper>(
|
||||
table_pa: TPA<PgTableArray<Self>>,
|
||||
va: VA,
|
||||
ctx: &mut WalkContext<PM>,
|
||||
) -> Result<Option<(PA, usize, PtePermissions)>> {
|
||||
let desc = unsafe {
|
||||
ctx.mapper
|
||||
.with_page_table(table_pa, |pgtable| Self::from_ptr(pgtable).get_desc(va))?
|
||||
};
|
||||
match desc.next_table_address() {
|
||||
Some(next_pa) => PDPTable::translate(next_pa, va, ctx),
|
||||
None if desc.is_valid() => Err(MapError::InvalidDescriptor.into()),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Translator for PTable {
|
||||
fn translate<PM: PageTableMapper>(
|
||||
table_pa: TPA<PgTableArray<Self>>,
|
||||
va: VA,
|
||||
ctx: &mut WalkContext<PM>,
|
||||
) -> Result<Option<(PA, usize, PtePermissions)>> {
|
||||
let desc = unsafe {
|
||||
ctx.mapper
|
||||
.with_page_table(table_pa, |pgtable| Self::from_ptr(pgtable).get_desc(va))?
|
||||
};
|
||||
|
||||
match desc.mapped_address() {
|
||||
Some(pa) => Ok(Some((
|
||||
pa,
|
||||
1 << Self::Descriptor::MAP_SHIFT,
|
||||
desc.permissions(),
|
||||
))),
|
||||
None if desc.is_valid() => Err(MapError::InvalidDescriptor.into()),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Translates the VA into a physical region plus an offset and permissions.
|
||||
pub fn translate<PM: PageTableMapper>(
|
||||
pml4_table: TPA<PgTableArray<PML4Table>>,
|
||||
va: VA,
|
||||
mapper: &mut PM,
|
||||
) -> Result<Option<(PhysMemoryRegion, usize, PtePermissions)>> {
|
||||
let mut walk_ctx = WalkContext {
|
||||
mapper,
|
||||
// Safe to not invalidate the TLB, as we are not modifying any PTEs.
|
||||
invalidator: &NullTlbInvalidator {},
|
||||
};
|
||||
|
||||
if let Some((pa, blk_sz, perms)) = PML4Table::translate(pml4_table, va, &mut walk_ctx)? {
|
||||
debug_assert!(blk_sz.is_power_of_two());
|
||||
|
||||
let offset = va.value() & (blk_sz - 1);
|
||||
|
||||
Ok(Some((PhysMemoryRegion::new(pa, blk_sz), offset, perms)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -41,6 +41,8 @@ pub struct RegionList {
|
||||
regions: *mut PhysMemoryRegion,
|
||||
}
|
||||
|
||||
const NULL_PAGE_RGN: PhysMemoryRegion = PhysMemoryRegion::new(PA::null(), PAGE_SIZE);
|
||||
|
||||
impl RegionList {
|
||||
/// Returns `true` if the list contains no regions.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
@@ -248,6 +250,14 @@ impl<T: AddressTranslator<()>> Smalloc<T> {
|
||||
.find_allocation_location(size, align)
|
||||
.ok_or(KernelError::NoMemory)?;
|
||||
|
||||
if address.is_null() {
|
||||
// Reserve the zero page and try again. We never want to return
|
||||
// an allocation of NULL since it trips up UB checks.
|
||||
self.res.insert_region(NULL_PAGE_RGN);
|
||||
|
||||
return self.alloc(size, align);
|
||||
}
|
||||
|
||||
// Allocation fits and doesn't overlap any reservation
|
||||
self.res.insert_region(PhysMemoryRegion::new(address, size));
|
||||
|
||||
|
||||
@@ -70,6 +70,9 @@ pub trait PaMapper: PageTableEntry {
|
||||
|
||||
/// Return the mapped physical address.
|
||||
fn mapped_address(self) -> Option<PA>;
|
||||
|
||||
/// Return the permissions set on the PTE.
|
||||
fn permissions(self) -> Option<PtePermissions>;
|
||||
}
|
||||
|
||||
/// Trait representing a single level of the page table hierarchy.
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
use crate::{
|
||||
error::MapError,
|
||||
memory::{
|
||||
address::{TPA, VA},
|
||||
address::{PA, TPA, VA},
|
||||
region::VirtMemoryRegion,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
PageTableEntry, PageTableMapper, PgTable, PgTableArray, TLBInvalidator, TableMapper,
|
||||
TableMapperTable,
|
||||
PaMapper, PageTableEntry, PageTableMapper, PgTable, PgTableArray, TLBInvalidator, TableMapper,
|
||||
TableMapperTable, permissions::PtePermissions,
|
||||
};
|
||||
|
||||
/// A collection of context required to modify page tables.
|
||||
@@ -96,3 +96,40 @@ where
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait Translator: PgTable + Sized {
|
||||
fn translate<PM: PageTableMapper>(
|
||||
table_pa: TPA<PgTableArray<Self>>,
|
||||
va: VA,
|
||||
ctx: &mut WalkContext<PM>,
|
||||
) -> crate::error::Result<Option<(PA, usize, PtePermissions)>>;
|
||||
}
|
||||
|
||||
impl<T> Translator for T
|
||||
where
|
||||
T: TableMapperTable,
|
||||
T::Descriptor: PaMapper,
|
||||
<T::Descriptor as TableMapper>::NextLevel: Translator,
|
||||
{
|
||||
fn translate<PM: PageTableMapper>(
|
||||
table_pa: TPA<PgTableArray<Self>>,
|
||||
va: VA,
|
||||
ctx: &mut WalkContext<PM>,
|
||||
) -> crate::error::Result<Option<(PA, usize, PtePermissions)>> {
|
||||
let desc = unsafe {
|
||||
ctx.mapper
|
||||
.with_page_table(table_pa, |pgtable| T::from_ptr(pgtable).get_desc(va))?
|
||||
};
|
||||
|
||||
if let Some(next_pa) = desc.next_table_address() {
|
||||
<T::Descriptor as TableMapper>::NextLevel::translate(next_pa, va, ctx)
|
||||
} else if let Some(block_pa) = desc.mapped_address() {
|
||||
let block_size = 1usize << T::Descriptor::MAP_SHIFT;
|
||||
Ok(Some((block_pa, block_size, desc.permissions().unwrap())))
|
||||
} else if desc.is_valid() {
|
||||
Err(MapError::InvalidDescriptor)?
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,11 @@ impl<T: MemKind> MemoryRegion<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this memory region is empty.
|
||||
pub fn is_empty(self) -> bool {
|
||||
self.size == 0
|
||||
}
|
||||
|
||||
/// Create a memory region from a start and end address.
|
||||
///
|
||||
/// The size is calculated as `end - start`. No alignment is enforced.
|
||||
@@ -71,6 +76,27 @@ impl<T: MemKind> MemoryRegion<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Cap the size of the region. If `max_size < self.size`, the region is
|
||||
/// shrunk.
|
||||
pub fn cap_size(self, max_size: usize) -> Self {
|
||||
if max_size < self.size {
|
||||
Self::new(self.start_address(), max_size)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Shrink this region by moving the start address 'forward' by `x` bytes.
|
||||
///
|
||||
/// If `x` moves the start address beyond the end of the region, it
|
||||
/// saturates on the boundary and becomes empty.
|
||||
pub fn shrink_start(self, x: usize) -> Self {
|
||||
Self {
|
||||
address: self.start_address().add_bytes(x),
|
||||
size: self.size.saturating_sub(x),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a new region with the same size but a different start address.
|
||||
pub fn with_start_address(mut self, new_start: Address<T, ()>) -> Self {
|
||||
self.address = new_start;
|
||||
@@ -676,6 +702,83 @@ mod tests {
|
||||
assert_eq!(main.punch_hole(hole), (Some(main), None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_empty_zero_size() {
|
||||
let r = region(0x1000, 0);
|
||||
assert!(r.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_empty_nonzero_size() {
|
||||
let r = region(0x1000, 0x10);
|
||||
assert!(!r.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_empty_empty_constructor() {
|
||||
assert!(PhysMemoryRegion::empty().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cap_size_below_current() {
|
||||
let r = region(0x1000, 0x100);
|
||||
let capped = r.cap_size(0x50);
|
||||
assert_eq!(capped.start_address().value(), 0x1000);
|
||||
assert_eq!(capped.size(), 0x50);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cap_size_equal_to_current() {
|
||||
let r = region(0x1000, 0x100);
|
||||
let capped = r.cap_size(0x100);
|
||||
assert_eq!(capped, r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cap_size_above_current() {
|
||||
let r = region(0x1000, 0x100);
|
||||
let capped = r.cap_size(0x200);
|
||||
assert_eq!(capped, r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cap_size_to_zero() {
|
||||
let r = region(0x1000, 0x100);
|
||||
let capped = r.cap_size(0);
|
||||
assert_eq!(capped.start_address().value(), 0x1000);
|
||||
assert!(capped.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shrink_start_within_bounds() {
|
||||
let r = region(0x1000, 0x100);
|
||||
let shrunk = r.shrink_start(0x40);
|
||||
assert_eq!(shrunk.start_address().value(), 0x1040);
|
||||
assert_eq!(shrunk.size(), 0xC0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shrink_start_exact_size() {
|
||||
let r = region(0x1000, 0x100);
|
||||
let shrunk = r.shrink_start(0x100);
|
||||
assert_eq!(shrunk.start_address().value(), 0x1100);
|
||||
assert!(shrunk.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shrink_start_beyond_end_saturates() {
|
||||
let r = region(0x1000, 0x100);
|
||||
let shrunk = r.shrink_start(0x200);
|
||||
assert_eq!(shrunk.start_address().value(), 0x1200);
|
||||
assert!(shrunk.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shrink_start_zero() {
|
||||
let r = region(0x1000, 0x100);
|
||||
assert_eq!(r.shrink_start(0), r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_punch_hole_disjoint() {
|
||||
// Hole is completely far away (after)
|
||||
|
||||
Reference in New Issue
Block a user