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:
@@ -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
@@ -7,7 +7,7 @@ use crate::{
|
||||
VirtualAddress,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct X8664Arch;
|
||||
|
||||
impl Arch for X8664Arch {
|
||||
|
||||
+16
-4
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user