Merge branch 'migrate_kernel_to_rmm' into 'master'

Add necessary functionality for migrating kernel paging code to RMM

See merge request redox-os/rmm!6
This commit is contained in:
4lDO2
2022-07-27 15:10:52 +00:00
7 changed files with 176 additions and 61 deletions
+19
View File
@@ -20,6 +20,7 @@ impl FrameCount {
}
}
#[derive(Debug)]
pub struct FrameUsage {
used: FrameCount,
total: FrameCount,
@@ -58,3 +59,21 @@ pub trait FrameAllocator {
unsafe fn usage(&self) -> FrameUsage;
}
impl<T> FrameAllocator for &mut T where T: FrameAllocator {
unsafe fn allocate(&mut self, count: FrameCount) -> Option<PhysicalAddress> {
T::allocate(self, count)
}
unsafe fn free(&mut self, address: PhysicalAddress, count: FrameCount) {
T::free(self, address, count)
}
unsafe fn allocate_one(&mut self) -> Option<PhysicalAddress> {
T::allocate_one(self)
}
unsafe fn free_one(&mut self, address: PhysicalAddress) {
T::free_one(self, address)
}
unsafe fn usage(&self) -> FrameUsage {
T::usage(self)
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ use crate::{
VirtualAddress,
};
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Debug)]
pub struct X8664Arch;
impl Arch for X8664Arch {
+16 -4
View File
@@ -2,6 +2,7 @@ use core::marker::PhantomData;
use crate::{
Arch,
PageFlags,
PhysicalAddress,
};
@@ -23,13 +24,24 @@ impl<A: Arch> PageEntry<A> {
}
#[inline(always)]
pub fn address(&self) -> PhysicalAddress {
PhysicalAddress(self.data & A::ENTRY_ADDRESS_MASK)
pub fn address(&self) -> Result<PhysicalAddress, PhysicalAddress> {
let addr = PhysicalAddress(self.data & A::ENTRY_ADDRESS_MASK);
if self.present() {
Ok(addr)
} else {
Err(addr)
}
}
#[inline(always)]
pub fn flags(&self) -> usize {
self.data & A::ENTRY_FLAGS_MASK
pub fn flags(&self) -> PageFlags<A> {
unsafe { PageFlags::from_data(self.data & A::ENTRY_FLAGS_MASK) }
}
#[inline(always)]
pub fn set_flags(&mut self, flags: PageFlags<A>) {
self.data &= !A::ENTRY_FLAGS_MASK;
self.data |= flags.data();
}
#[inline(always)]
+6 -2
View File
@@ -111,7 +111,11 @@ impl<A: Arch> PageFlags<A> {
impl<A: Arch> fmt::Debug for PageFlags<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PageFlags")
.field("data", &self.data)
.field("present", &self.has_present())
.field("write", &self.has_write())
.field("executable", &self.has_execute())
.field("user", &self.has_user())
.field("bits", &format_args!("{:#0x}", self.data))
.finish()
}
}
}
+27 -10
View File
@@ -8,6 +8,10 @@ use crate::{
VirtualAddress,
};
pub trait Flusher<A> {
fn consume(&mut self, flush: PageFlush<A>);
}
#[must_use = "The page table must be flushed, or the changes unsafely ignored"]
pub struct PageFlush<A> {
virt: VirtualAddress,
@@ -31,9 +35,10 @@ impl<A: Arch> PageFlush<A> {
}
}
#[must_use = "The page table must be flushed, or the changes unsafely ignored"]
pub struct PageFlushAll<A> {
phantom: PhantomData<A>,
// TODO: Might remove Drop and add #[must_use] again, but ergonomically I prefer being able to pass
// a flusher, and have it dropped by the end of the function it is passed to, in order to flush.
pub struct PageFlushAll<A: Arch> {
phantom: PhantomData<fn() -> A>,
}
impl <A: Arch> PageFlushAll<A> {
@@ -43,15 +48,27 @@ impl <A: Arch> PageFlushAll<A> {
}
}
pub fn consume(&self, flush: PageFlush<A>) {
unsafe { flush.ignore(); }
}
pub fn flush(self) {
unsafe { A::invalidate_all(); }
}
pub fn flush(self) {}
pub unsafe fn ignore(self) {
mem::forget(self);
}
}
impl<A: Arch> Drop for PageFlushAll<A> {
fn drop(&mut self) {
unsafe { A::invalidate_all(); }
}
}
impl<A: Arch> Flusher<A> for PageFlushAll<A> {
fn consume(&mut self, flush: PageFlush<A>) {
unsafe { flush.ignore(); }
}
}
impl<A: Arch, T: Flusher<A> + ?Sized> Flusher<A> for &mut T {
fn consume(&mut self, flush: PageFlush<A>) {
<T as Flusher<A>>::consume(self, flush)
}
}
impl<A: Arch> Flusher<A> for () {
fn consume(&mut self, _: PageFlush<A>) {}
}
+99 -34
View File
@@ -8,44 +8,61 @@ use crate::{
PageFlush,
PageTable,
PhysicalAddress,
TableKind,
VirtualAddress,
};
pub struct PageMapper<'f, A, F> {
pub struct PageMapper<A, F> {
table_addr: PhysicalAddress,
allocator: &'f mut F,
phantom: PhantomData<A>,
allocator: F,
_phantom: PhantomData<fn() -> A>,
}
impl<'f, A: Arch, F: FrameAllocator> PageMapper<'f, A, F> {
pub unsafe fn new(table_addr: PhysicalAddress, allocator: &'f mut F) -> Self {
impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
pub unsafe fn new(table_addr: PhysicalAddress, allocator: F) -> Self {
Self {
table_addr,
allocator,
phantom: PhantomData,
_phantom: PhantomData,
}
}
pub unsafe fn create(allocator: &'f mut F) -> Option<Self> {
pub unsafe fn create(mut allocator: F) -> Option<Self> {
let table_addr = allocator.allocate_one()?;
Some(Self::new(table_addr, allocator))
}
pub unsafe fn current(allocator: &'f mut F) -> Self {
pub unsafe fn current(allocator: F) -> Self {
let table_addr = A::table();
Self::new(table_addr, allocator)
}
pub fn is_current(&self) -> bool {
unsafe { self.table().phys() == A::table() }
}
pub unsafe fn make_current(&mut self) {
pub unsafe fn make_current(&self) {
A::set_table(self.table_addr);
}
pub unsafe fn table(&self) -> PageTable<A> {
PageTable::new(
VirtualAddress::new(0),
self.table_addr,
A::PAGE_LEVELS - 1
)
pub fn table(&self) -> PageTable<A> {
// SAFETY: The only way to initialize a PageMapper is via new(), and we assume it upholds
// all necessary invariants for this to be safe.
unsafe {
PageTable::new(
VirtualAddress::new(0),
self.table_addr,
A::PAGE_LEVELS - 1
)
}
}
pub unsafe fn remap(&mut self, virt: VirtualAddress, flags: PageFlags<A>) -> Option<PageFlush<A>> {
self.visit(virt, |p1, i| {
let mut entry = p1.entry(i)?;
entry.set_flags(flags);
p1.set_entry(i, entry);
Some(PageFlush::new(virt))
}).flatten()
}
pub unsafe fn map(&mut self, virt: VirtualAddress, flags: PageFlags<A>) -> Option<PageFlush<A>> {
@@ -71,7 +88,8 @@ impl<'f, A: Arch, F: FrameAllocator> PageMapper<'f, A, F> {
None => {
let next_phys = self.allocator.allocate_one()?;
//TODO: correct flags?
table.set_entry(i, PageEntry::new(next_phys.data() | A::ENTRY_FLAG_READWRITE | A::ENTRY_FLAG_DEFAULT_TABLE));
let flags = A::ENTRY_FLAG_READWRITE | A::ENTRY_FLAG_DEFAULT_TABLE | if virt.kind() == TableKind::User { A::ENTRY_FLAG_USER } else { 0 };
table.set_entry(i, PageEntry::new(next_phys.data() | flags));
table.next(i)?
}
};
@@ -79,27 +97,74 @@ impl<'f, A: Arch, F: FrameAllocator> PageMapper<'f, A, F> {
}
}
}
pub unsafe fn unmap(&mut self, virt: VirtualAddress) -> Option<PageFlush<A>> {
let (old, flush) = self.unmap_phys(virt)?;
self.allocator.free_one(old.address());
Some(flush)
pub unsafe fn map_linearly(&mut self, phys: PhysicalAddress, flags: PageFlags<A>) -> Option<(VirtualAddress, PageFlush<A>)> {
let virt = A::phys_to_virt(phys);
self.map_phys(virt, phys, flags).map(|flush| (virt, flush))
}
pub unsafe fn unmap_phys(&mut self, virt: VirtualAddress) -> Option<(PageEntry<A>, PageFlush<A>)> {
//TODO: verify virt is aligned
fn visit<T>(&self, virt: VirtualAddress, f: impl FnOnce(&mut PageTable<A>, usize) -> T) -> Option<T> {
let mut table = self.table();
//TODO: unmap parents
loop {
let i = table.index_of(virt)?;
if table.level() == 0 {
let entry_opt = table.entry(i);
table.set_entry(i, PageEntry::new(0));
let entry = entry_opt?;
return Some((entry, PageFlush::new(virt)));
} else {
table = table.next(i)?;
unsafe {
loop {
let i = table.index_of(virt)?;
if table.level() == 0 {
return Some(f(&mut table, i));
} else {
table = table.next(i)?;
}
}
}
}
pub fn translate(&self, virt: VirtualAddress) -> Option<(PhysicalAddress, PageFlags<A>)> {
let entry = self.visit(virt, |p1, i| unsafe { p1.entry(i) })??;
Some((entry.address().ok()?, entry.flags()))
}
pub unsafe fn unmap(&mut self, virt: VirtualAddress, unmap_parents: bool) -> Option<PageFlush<A>> {
let (old, _, flush) = self.unmap_phys(virt, unmap_parents)?;
self.allocator.free_one(old);
Some(flush)
}
pub unsafe fn unmap_phys(&mut self, virt: VirtualAddress, unmap_parents: bool) -> Option<(PhysicalAddress, PageFlags<A>, PageFlush<A>)> {
//TODO: verify virt is aligned
let mut table = self.table();
let level = table.level();
unmap_phys_inner(virt, &mut table, level, false, &mut self.allocator).map(|(pa, pf)| (pa, pf, PageFlush::new(virt)))
}
}
unsafe fn unmap_phys_inner<A: Arch>(virt: VirtualAddress, table: &mut PageTable<A>, initial_level: usize, unmap_parents: bool, allocator: &mut impl FrameAllocator) -> Option<(PhysicalAddress, PageFlags<A>)> {
let i = table.index_of(virt)?;
if table.level() == 0 {
let entry_opt = table.entry(i);
table.set_entry(i, PageEntry::new(0));
let entry = entry_opt?;
Some((entry.address().ok()?, entry.flags()))
} else {
let mut subtable = table.next(i)?;
let res = unmap_phys_inner(virt, &mut subtable, initial_level, unmap_parents, allocator)?;
if unmap_parents {
// TODO: Use a counter? This would reduce the remaining number of available bits, but could be
// faster (benchmark is needed).
let is_still_populated = (0..A::PAGE_ENTRIES).map(|j| subtable.entry(j).expect("must be within bounds")).any(|e| e.present());
if !is_still_populated {
allocator.free_one(table.phys());
table.set_entry(i, PageEntry::new(0));
}
}
Some(res)
}
}
impl<A, F: core::fmt::Debug> core::fmt::Debug for PageMapper<A, F> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PageMapper")
.field("frame", &self.table_addr)
.field("allocator", &self.allocator)
.finish()
}
}
+8 -10
View File
@@ -93,16 +93,14 @@ impl<A: Arch> PageTable<A> {
}
pub unsafe fn next(&self, i: usize) -> Option<Self> {
if self.level > 0 {
let entry = self.entry(i)?;
if entry.present() {
return Some(PageTable::new(
self.entry_base(i)?,
entry.address(),
self.level - 1
));
}
if self.level == 0 {
return None;
}
None
Some(PageTable::new(
self.entry_base(i)?,
self.entry(i)?.address().ok()?,
self.level - 1,
))
}
}