Skip to content

Commit 39eaded

Browse files
authored
Merge pull request #301 from hexagonal-sun/libkernel-yet-more-fixes
Libkernel yet more fixes
2 parents 971462e + fee153d commit 39eaded

7 files changed

Lines changed: 239 additions & 8 deletions

File tree

libkernel/src/arch/arm64/memory/pg_descriptors.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ macro_rules! define_descriptor {
219219
let addr = reg.read(BlockPageFields::OUTPUT_ADDR);
220220
Some(PA::from_value((addr << Self::MAP_SHIFT) as usize))
221221
}
222+
223+
fn permissions(self) -> Option<PtePermissions> {
224+
self.permissions()
225+
}
222226
}
223227
}
224228
)?

libkernel/src/arch/x86_64/memory/pg_descriptors.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,14 @@ macro_rules! impl_pa_mapper {
195195

196196
Some(self.address())
197197
}
198+
199+
fn permissions(self) -> Option<PtePermissions> {
200+
if (self.0 & $marker) != $marker {
201+
return None;
202+
}
203+
204+
Some(self.permissions())
205+
}
198206
}
199207
}
200208
)+

libkernel/src/arch/x86_64/memory/pg_walk.rs

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,20 @@ use crate::{
44
error::{MapError, Result},
55
memory::{
66
PAGE_SIZE,
7-
address::{TPA, VA},
7+
address::{PA, TPA, VA},
88
paging::{
9-
NullTlbInvalidator, PageTableEntry, PageTableMapper, PgTable, PgTableArray,
10-
walk::{RecursiveWalker, WalkContext},
9+
NullTlbInvalidator, PaMapper, PageTableEntry, PageTableMapper, PgTable, PgTableArray,
10+
TableMapper,
11+
permissions::PtePermissions,
12+
walk::{RecursiveWalker, Translator, WalkContext},
1113
},
12-
region::VirtMemoryRegion,
14+
region::{PhysMemoryRegion, VirtMemoryRegion},
1315
},
1416
};
1517

1618
use super::{
1719
pg_descriptors::PTE,
18-
pg_tables::{PML4Table, PTable},
20+
pg_tables::{PDPTable, PML4Table, PTable},
1921
};
2022

2123
impl RecursiveWalker<PTE> for PTable {
@@ -112,6 +114,70 @@ pub fn get_pte<PM: PageTableMapper>(
112114
Ok(descriptor)
113115
}
114116

117+
impl Translator for PML4Table {
118+
fn translate<PM: PageTableMapper>(
119+
table_pa: TPA<PgTableArray<Self>>,
120+
va: VA,
121+
ctx: &mut WalkContext<PM>,
122+
) -> Result<Option<(PA, usize, PtePermissions)>> {
123+
let desc = unsafe {
124+
ctx.mapper
125+
.with_page_table(table_pa, |pgtable| Self::from_ptr(pgtable).get_desc(va))?
126+
};
127+
match desc.next_table_address() {
128+
Some(next_pa) => PDPTable::translate(next_pa, va, ctx),
129+
None if desc.is_valid() => Err(MapError::InvalidDescriptor.into()),
130+
None => Ok(None),
131+
}
132+
}
133+
}
134+
135+
impl Translator for PTable {
136+
fn translate<PM: PageTableMapper>(
137+
table_pa: TPA<PgTableArray<Self>>,
138+
va: VA,
139+
ctx: &mut WalkContext<PM>,
140+
) -> Result<Option<(PA, usize, PtePermissions)>> {
141+
let desc = unsafe {
142+
ctx.mapper
143+
.with_page_table(table_pa, |pgtable| Self::from_ptr(pgtable).get_desc(va))?
144+
};
145+
146+
match desc.mapped_address() {
147+
Some(pa) => Ok(Some((
148+
pa,
149+
1 << Self::Descriptor::MAP_SHIFT,
150+
desc.permissions(),
151+
))),
152+
None if desc.is_valid() => Err(MapError::InvalidDescriptor.into()),
153+
None => Ok(None),
154+
}
155+
}
156+
}
157+
158+
/// Translates the VA into a physical region plus an offset and permissions.
159+
pub fn translate<PM: PageTableMapper>(
160+
pml4_table: TPA<PgTableArray<PML4Table>>,
161+
va: VA,
162+
mapper: &mut PM,
163+
) -> Result<Option<(PhysMemoryRegion, usize, PtePermissions)>> {
164+
let mut walk_ctx = WalkContext {
165+
mapper,
166+
// Safe to not invalidate the TLB, as we are not modifying any PTEs.
167+
invalidator: &NullTlbInvalidator {},
168+
};
169+
170+
if let Some((pa, blk_sz, perms)) = PML4Table::translate(pml4_table, va, &mut walk_ctx)? {
171+
debug_assert!(blk_sz.is_power_of_two());
172+
173+
let offset = va.value() & (blk_sz - 1);
174+
175+
Ok(Some((PhysMemoryRegion::new(pa, blk_sz), offset, perms)))
176+
} else {
177+
Ok(None)
178+
}
179+
}
180+
115181
#[cfg(test)]
116182
mod tests {
117183
use super::*;

libkernel/src/memory/allocators/smalloc.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ pub struct RegionList {
4141
regions: *mut PhysMemoryRegion,
4242
}
4343

44+
const NULL_PAGE_RGN: PhysMemoryRegion = PhysMemoryRegion::new(PA::null(), PAGE_SIZE);
45+
4446
impl RegionList {
4547
/// Returns `true` if the list contains no regions.
4648
pub fn is_empty(&self) -> bool {
@@ -248,6 +250,14 @@ impl<T: AddressTranslator<()>> Smalloc<T> {
248250
.find_allocation_location(size, align)
249251
.ok_or(KernelError::NoMemory)?;
250252

253+
if address.is_null() {
254+
// Reserve the zero page and try again. We never want to return
255+
// an allocation of NULL since it trips up UB checks.
256+
self.res.insert_region(NULL_PAGE_RGN);
257+
258+
return self.alloc(size, align);
259+
}
260+
251261
// Allocation fits and doesn't overlap any reservation
252262
self.res.insert_region(PhysMemoryRegion::new(address, size));
253263

libkernel/src/memory/paging/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ pub trait PaMapper: PageTableEntry {
7070

7171
/// Return the mapped physical address.
7272
fn mapped_address(self) -> Option<PA>;
73+
74+
/// Return the permissions set on the PTE.
75+
fn permissions(self) -> Option<PtePermissions>;
7376
}
7477

7578
/// Trait representing a single level of the page table hierarchy.

libkernel/src/memory/paging/walk.rs

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
use crate::{
44
error::MapError,
55
memory::{
6-
address::{TPA, VA},
6+
address::{PA, TPA, VA},
77
region::VirtMemoryRegion,
88
},
99
};
1010

1111
use super::{
12-
PageTableEntry, PageTableMapper, PgTable, PgTableArray, TLBInvalidator, TableMapper,
13-
TableMapperTable,
12+
PaMapper, PageTableEntry, PageTableMapper, PgTable, PgTableArray, TLBInvalidator, TableMapper,
13+
TableMapperTable, permissions::PtePermissions,
1414
};
1515

1616
/// A collection of context required to modify page tables.
@@ -96,3 +96,40 @@ where
9696
Ok(())
9797
}
9898
}
99+
100+
pub(crate) trait Translator: PgTable + Sized {
101+
fn translate<PM: PageTableMapper>(
102+
table_pa: TPA<PgTableArray<Self>>,
103+
va: VA,
104+
ctx: &mut WalkContext<PM>,
105+
) -> crate::error::Result<Option<(PA, usize, PtePermissions)>>;
106+
}
107+
108+
impl<T> Translator for T
109+
where
110+
T: TableMapperTable,
111+
T::Descriptor: PaMapper,
112+
<T::Descriptor as TableMapper>::NextLevel: Translator,
113+
{
114+
fn translate<PM: PageTableMapper>(
115+
table_pa: TPA<PgTableArray<Self>>,
116+
va: VA,
117+
ctx: &mut WalkContext<PM>,
118+
) -> crate::error::Result<Option<(PA, usize, PtePermissions)>> {
119+
let desc = unsafe {
120+
ctx.mapper
121+
.with_page_table(table_pa, |pgtable| T::from_ptr(pgtable).get_desc(va))?
122+
};
123+
124+
if let Some(next_pa) = desc.next_table_address() {
125+
<T::Descriptor as TableMapper>::NextLevel::translate(next_pa, va, ctx)
126+
} else if let Some(block_pa) = desc.mapped_address() {
127+
let block_size = 1usize << T::Descriptor::MAP_SHIFT;
128+
Ok(Some((block_pa, block_size, desc.permissions().unwrap())))
129+
} else if desc.is_valid() {
130+
Err(MapError::InvalidDescriptor)?
131+
} else {
132+
Ok(None)
133+
}
134+
}
135+
}

libkernel/src/memory/region.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ impl<T: MemKind> MemoryRegion<T> {
5959
}
6060
}
6161

62+
/// Returns `true` if this memory region is empty.
63+
pub fn is_empty(self) -> bool {
64+
self.size == 0
65+
}
66+
6267
/// Create a memory region from a start and end address.
6368
///
6469
/// The size is calculated as `end - start`. No alignment is enforced.
@@ -71,6 +76,27 @@ impl<T: MemKind> MemoryRegion<T> {
7176
}
7277
}
7378

79+
/// Cap the size of the region. If `max_size < self.size`, the region is
80+
/// shrunk.
81+
pub fn cap_size(self, max_size: usize) -> Self {
82+
if max_size < self.size {
83+
Self::new(self.start_address(), max_size)
84+
} else {
85+
self
86+
}
87+
}
88+
89+
/// Shrink this region by moving the start address 'forward' by `x` bytes.
90+
///
91+
/// If `x` moves the start address beyond the end of the region, it
92+
/// saturates on the boundary and becomes empty.
93+
pub fn shrink_start(self, x: usize) -> Self {
94+
Self {
95+
address: self.start_address().add_bytes(x),
96+
size: self.size.saturating_sub(x),
97+
}
98+
}
99+
74100
/// Return a new region with the same size but a different start address.
75101
pub fn with_start_address(mut self, new_start: Address<T, ()>) -> Self {
76102
self.address = new_start;
@@ -676,6 +702,83 @@ mod tests {
676702
assert_eq!(main.punch_hole(hole), (Some(main), None));
677703
}
678704

705+
#[test]
706+
fn is_empty_zero_size() {
707+
let r = region(0x1000, 0);
708+
assert!(r.is_empty());
709+
}
710+
711+
#[test]
712+
fn is_empty_nonzero_size() {
713+
let r = region(0x1000, 0x10);
714+
assert!(!r.is_empty());
715+
}
716+
717+
#[test]
718+
fn is_empty_empty_constructor() {
719+
assert!(PhysMemoryRegion::empty().is_empty());
720+
}
721+
722+
#[test]
723+
fn cap_size_below_current() {
724+
let r = region(0x1000, 0x100);
725+
let capped = r.cap_size(0x50);
726+
assert_eq!(capped.start_address().value(), 0x1000);
727+
assert_eq!(capped.size(), 0x50);
728+
}
729+
730+
#[test]
731+
fn cap_size_equal_to_current() {
732+
let r = region(0x1000, 0x100);
733+
let capped = r.cap_size(0x100);
734+
assert_eq!(capped, r);
735+
}
736+
737+
#[test]
738+
fn cap_size_above_current() {
739+
let r = region(0x1000, 0x100);
740+
let capped = r.cap_size(0x200);
741+
assert_eq!(capped, r);
742+
}
743+
744+
#[test]
745+
fn cap_size_to_zero() {
746+
let r = region(0x1000, 0x100);
747+
let capped = r.cap_size(0);
748+
assert_eq!(capped.start_address().value(), 0x1000);
749+
assert!(capped.is_empty());
750+
}
751+
752+
#[test]
753+
fn shrink_start_within_bounds() {
754+
let r = region(0x1000, 0x100);
755+
let shrunk = r.shrink_start(0x40);
756+
assert_eq!(shrunk.start_address().value(), 0x1040);
757+
assert_eq!(shrunk.size(), 0xC0);
758+
}
759+
760+
#[test]
761+
fn shrink_start_exact_size() {
762+
let r = region(0x1000, 0x100);
763+
let shrunk = r.shrink_start(0x100);
764+
assert_eq!(shrunk.start_address().value(), 0x1100);
765+
assert!(shrunk.is_empty());
766+
}
767+
768+
#[test]
769+
fn shrink_start_beyond_end_saturates() {
770+
let r = region(0x1000, 0x100);
771+
let shrunk = r.shrink_start(0x200);
772+
assert_eq!(shrunk.start_address().value(), 0x1200);
773+
assert!(shrunk.is_empty());
774+
}
775+
776+
#[test]
777+
fn shrink_start_zero() {
778+
let r = region(0x1000, 0x100);
779+
assert_eq!(r.shrink_start(0), r);
780+
}
781+
679782
#[test]
680783
fn test_punch_hole_disjoint() {
681784
// Hole is completely far away (after)

0 commit comments

Comments
 (0)