Merge branch 'fix' into 'master'

Revived tests

See merge request redox-os/rmm!12
This commit is contained in:
Jeremy Soller
2024-09-02 21:16:38 +00:00
18 changed files with 306 additions and 293 deletions
+16 -21
View File
@@ -1,16 +1,7 @@
use core::{
marker::PhantomData,
mem,
};
use core::{marker::PhantomData, mem};
use crate::{
Arch,
BumpAllocator,
FrameAllocator,
FrameCount,
FrameUsage,
PhysicalAddress,
VirtualAddress,
Arch, BumpAllocator, FrameAllocator, FrameCount, FrameUsage, PhysicalAddress, VirtualAddress,
};
#[repr(transparent)]
@@ -94,7 +85,7 @@ impl<A: Arch> BuddyAllocator<A> {
// Allocate buddy table
let table_phys = bump_allocator.allocate_one()?;
let table_virt = A::phys_to_virt(table_phys);
for i in 0 .. (A::PAGE_SIZE / mem::size_of::<BuddyEntry<A>>()) {
for i in 0..(A::PAGE_SIZE / mem::size_of::<BuddyEntry<A>>()) {
let virt = table_virt.add(i * mem::size_of::<BuddyEntry<A>>());
A::write(virt, BuddyEntry::<A>::empty());
}
@@ -117,7 +108,7 @@ impl<A: Arch> BuddyAllocator<A> {
area.size -= offset;
offset = 0;
}
for i in 0 .. (A::PAGE_SIZE / mem::size_of::<BuddyEntry<A>>()) {
for i in 0..(A::PAGE_SIZE / mem::size_of::<BuddyEntry<A>>()) {
let virt = table_virt.add(i * mem::size_of::<BuddyEntry<A>>());
let mut entry = A::read::<BuddyEntry<A>>(virt);
let inserted = if area.base.add(area.size) == { entry.base } {
@@ -147,7 +138,7 @@ impl<A: Arch> BuddyAllocator<A> {
//TODO: sort areas?
// Allocate buddy maps
for i in 0 .. Self::BUDDY_ENTRIES {
for i in 0..Self::BUDDY_ENTRIES {
let virt = table_virt.add(i * mem::size_of::<BuddyEntry<A>>());
let mut entry = A::read::<BuddyEntry<A>>(virt);
@@ -186,13 +177,15 @@ impl<A: Arch> FrameAllocator for BuddyAllocator<A> {
return None;
}
for entry_i in 0 .. Self::BUDDY_ENTRIES {
let virt = self.table_virt.add(entry_i * mem::size_of::<BuddyEntry<A>>());
for entry_i in 0..Self::BUDDY_ENTRIES {
let virt = self
.table_virt
.add(entry_i * mem::size_of::<BuddyEntry<A>>());
let mut entry = A::read::<BuddyEntry<A>>(virt);
let mut free_page = entry.skip;
let mut free_count = 0;
for page in entry.skip .. entry.pages() {
for page in entry.skip..entry.pages() {
let usage = entry.usage(page)?;
if usage.0 == 0 {
free_count += 1;
@@ -207,7 +200,7 @@ impl<A: Arch> FrameAllocator for BuddyAllocator<A> {
}
if free_count == count.data() {
for page in free_page .. free_page + free_count {
for page in free_page..free_page + free_count {
// Update usage
let mut usage = entry.usage(page)?;
usage.0 += 1;
@@ -243,7 +236,7 @@ impl<A: Arch> FrameAllocator for BuddyAllocator<A> {
}
let size = count.data() * A::PAGE_SIZE;
for i in 0 .. Self::BUDDY_ENTRIES {
for i in 0..Self::BUDDY_ENTRIES {
let virt = self.table_virt.add(i * mem::size_of::<BuddyEntry<A>>());
let mut entry = A::read::<BuddyEntry<A>>(virt);
@@ -269,7 +262,9 @@ impl<A: Arch> FrameAllocator for BuddyAllocator<A> {
entry.used -= 1;
}
entry.set_usage(page, usage).expect("failed to set usage during free");
entry
.set_usage(page, usage)
.expect("failed to set usage during free");
}
// Write updated entry
@@ -283,7 +278,7 @@ impl<A: Arch> FrameAllocator for BuddyAllocator<A> {
unsafe fn usage(&self) -> FrameUsage {
let mut total = 0;
let mut used = 0;
for i in 0 .. Self::BUDDY_ENTRIES {
for i in 0..Self::BUDDY_ENTRIES {
let virt = self.table_virt.add(i * mem::size_of::<BuddyEntry<A>>());
let entry = A::read::<BuddyEntry<A>>(virt);
total += entry.size >> A::PAGE_SHIFT;
+2 -9
View File
@@ -1,13 +1,6 @@
use core::marker::PhantomData;
use crate::{
Arch,
FrameAllocator,
FrameCount,
FrameUsage,
MemoryArea,
PhysicalAddress,
};
use crate::{Arch, FrameAllocator, FrameCount, FrameUsage, MemoryArea, PhysicalAddress};
pub struct BumpAllocator<A> {
areas: &'static [MemoryArea],
@@ -17,7 +10,7 @@ pub struct BumpAllocator<A> {
}
impl<A: Arch> BumpAllocator<A> {
pub fn new(areas: &'static [MemoryArea], offset: usize) -> Self {
pub fn new(areas: &'static [MemoryArea], _offset: usize) -> Self {
Self {
areas,
offset: 0,
+6 -4
View File
@@ -1,7 +1,6 @@
use crate::PhysicalAddress;
pub use self::buddy::*;
pub use self::bump::*;
pub use self::{buddy::*, bump::*};
mod buddy;
mod bump;
@@ -30,7 +29,7 @@ impl FrameUsage {
pub fn new(used: FrameCount, total: FrameCount) -> Self {
Self { used, total }
}
pub fn used(&self) -> FrameCount {
self.used
}
@@ -60,7 +59,10 @@ pub trait FrameAllocator {
unsafe fn usage(&self) -> FrameUsage;
}
impl<T> FrameAllocator for &mut T where T: FrameAllocator {
impl<T> FrameAllocator for &mut T
where
T: FrameAllocator,
{
unsafe fn allocate(&mut self, count: FrameCount) -> Option<PhysicalAddress> {
T::allocate(self, count)
}
+10 -16
View File
@@ -1,12 +1,6 @@
use core::arch::asm;
use crate::{
Arch,
MemoryArea,
PhysicalAddress,
TableKind,
VirtualAddress,
};
use crate::{Arch, MemoryArea, PhysicalAddress, TableKind, VirtualAddress};
#[derive(Clone, Copy)]
pub struct AArch64Arch;
@@ -18,12 +12,10 @@ impl Arch for AArch64Arch {
//TODO
const ENTRY_ADDRESS_SHIFT: usize = 52;
const ENTRY_FLAG_DEFAULT_PAGE: usize
= Self::ENTRY_FLAG_PRESENT
const ENTRY_FLAG_DEFAULT_PAGE: usize = Self::ENTRY_FLAG_PRESENT
| 1 << 1 // Page flag
| 1 << 10 // Access flag
| Self::ENTRY_FLAG_NO_GLOBAL
;
| Self::ENTRY_FLAG_NO_GLOBAL;
const ENTRY_FLAG_DEFAULT_TABLE: usize
= Self::ENTRY_FLAG_PRESENT
| 1 << 1 // Table flag
@@ -58,12 +50,14 @@ impl Arch for AArch64Arch {
#[inline(always)]
unsafe fn invalidate_all() {
asm!("
asm!(
"
dsb ishst
tlbi vmalle1is
dsb ish
isb
");
"
);
}
#[inline(always)]
@@ -72,7 +66,7 @@ impl Arch for AArch64Arch {
match table_kind {
TableKind::User => {
asm!("mrs {0}, ttbr0_el1", out(reg) address);
},
}
TableKind::Kernel => {
asm!("mrs {0}, ttbr1_el1", out(reg) address);
}
@@ -85,7 +79,7 @@ impl Arch for AArch64Arch {
match table_kind {
TableKind::User => {
asm!("msr ttbr0_el1, {0}", in(reg) address.data());
},
}
TableKind::Kernel => {
asm!("msr ttbr1_el1, {0}", in(reg) address.data());
}
@@ -101,8 +95,8 @@ impl Arch for AArch64Arch {
#[cfg(test)]
mod tests {
use crate::Arch;
use super::AArch64Arch;
use crate::Arch;
#[test]
fn constants() {
+84 -53
View File
@@ -1,18 +1,9 @@
use core::{
marker::PhantomData,
mem,
ptr,
};
use core::{marker::PhantomData, mem, ptr};
use std::collections::BTreeMap;
use crate::{
MEGABYTE,
Arch,
MemoryArea,
PageEntry,
PhysicalAddress,
VirtualAddress,
arch::x86_64::X8664Arch,
arch::x86_64::X8664Arch, page::PageFlags, Arch, MemoryArea, PageEntry, PhysicalAddress,
TableKind, VirtualAddress, MEGABYTE,
};
#[derive(Clone, Copy)]
@@ -35,6 +26,9 @@ impl Arch for EmulateArch {
const PHYS_OFFSET: usize = X8664Arch::PHYS_OFFSET;
const ENTRY_FLAG_GLOBAL: usize = X8664Arch::ENTRY_FLAG_GLOBAL;
const ENTRY_FLAG_NO_GLOBAL: usize = X8664Arch::ENTRY_FLAG_NO_GLOBAL;
unsafe fn init() -> &'static [MemoryArea] {
// Create machine with PAGE_ENTRIES pages offset mapped (2 MiB on x86_64)
let mut machine = Machine::new(MEMORY_SIZE);
@@ -43,7 +37,10 @@ impl Arch for EmulateArch {
let pml4 = 0;
let pdp = pml4 + Self::PAGE_SIZE;
let flags = Self::ENTRY_FLAG_READWRITE | Self::ENTRY_FLAG_PRESENT;
machine.write_phys::<usize>(PhysicalAddress::new(pml4 + 256 * Self::PAGE_ENTRY_SIZE), pdp | flags);
machine.write_phys::<usize>(
PhysicalAddress::new(pml4 + 256 * Self::PAGE_ENTRY_SIZE),
pdp | flags,
);
// PDP link to PD
let pd = pdp + Self::PAGE_SIZE;
@@ -56,13 +53,16 @@ impl Arch for EmulateArch {
// PT links to frames
for i in 0..Self::PAGE_ENTRIES {
let page = i * Self::PAGE_SIZE;
machine.write_phys::<usize>(PhysicalAddress::new(pt + i * Self::PAGE_ENTRY_SIZE), page | flags);
machine.write_phys::<usize>(
PhysicalAddress::new(pt + i * Self::PAGE_ENTRY_SIZE),
page | flags,
);
}
MACHINE = Some(machine);
// Set table to pml4
EmulateArch::set_table(PhysicalAddress::new(pml4));
EmulateArch::set_table(TableKind::Kernel, PhysicalAddress::new(pml4));
&MEMORY_AREAS
}
@@ -93,12 +93,12 @@ impl Arch for EmulateArch {
}
#[inline(always)]
unsafe fn table() -> PhysicalAddress {
unsafe fn table(_table_kind: TableKind) -> PhysicalAddress {
MACHINE.as_mut().unwrap().get_table()
}
#[inline(always)]
unsafe fn set_table(address: PhysicalAddress) {
unsafe fn set_table(_table_kind: TableKind, address: PhysicalAddress) {
MACHINE.as_mut().unwrap().set_table(address);
}
fn virt_is_valid(_address: VirtualAddress) -> bool {
@@ -111,12 +111,12 @@ const MEMORY_SIZE: usize = 64 * MEGABYTE;
static MEMORY_AREAS: [MemoryArea; 2] = [
MemoryArea {
base: PhysicalAddress::new(EmulateArch::PAGE_SIZE * 4), // Initial PML4, PDP, PD, and PT wasted
size: MEMORY_SIZE/2 - EmulateArch::PAGE_SIZE * 4,
size: MEMORY_SIZE / 2 - EmulateArch::PAGE_SIZE * 4,
},
// Second area for debugging
MemoryArea {
base: PhysicalAddress::new(MEMORY_SIZE/2),
size: MEMORY_SIZE/2,
base: PhysicalAddress::new(MEMORY_SIZE / 2),
size: MEMORY_SIZE / 2,
},
];
@@ -142,11 +142,13 @@ impl<A: Arch> Machine<A> {
fn read_phys<T>(&self, phys: PhysicalAddress) -> T {
let size = mem::size_of::<T>();
if phys.add(size).data() <= self.memory.len() {
unsafe {
ptr::read(self.memory.as_ptr().add(phys.data()) as *const T)
}
unsafe { ptr::read(self.memory.as_ptr().add(phys.data()) as *const T) }
} else {
panic!("read_phys: 0x{:X} size 0x{:X} outside of memory", phys.data(), size);
panic!(
"read_phys: 0x{:X} size 0x{:X} outside of memory",
phys.data(),
size
);
}
}
@@ -157,29 +159,34 @@ impl<A: Arch> Machine<A> {
ptr::write(self.memory.as_mut_ptr().add(phys.data()) as *mut T, value);
}
} else {
panic!("write_phys: 0x{:X} size 0x{:X} outside of memory", phys.data(), size);
panic!(
"write_phys: 0x{:X} size 0x{:X} outside of memory",
phys.data(),
size
);
}
}
fn write_phys_bytes(&mut self, phys: PhysicalAddress, value: u8, count: usize) {
if phys.add(count).data() <= self.memory.len() {
unsafe {
ptr::write_bytes(self.memory.as_mut_ptr().add(phys.data()) as *mut u8, value, count);
ptr::write_bytes(self.memory.as_mut_ptr().add(phys.data()), value, count);
}
} else {
panic!("write_phys_bytes: 0x{:X} count 0x{:X} outside of memory", phys.data(), count);
panic!(
"write_phys_bytes: 0x{:X} count 0x{:X} outside of memory",
phys.data(),
count
);
}
}
fn translate(&self, virt: VirtualAddress) -> Option<(PhysicalAddress, usize)> {
fn translate(&self, virt: VirtualAddress) -> Option<(PhysicalAddress, PageFlags<A>)> {
let virt_data = virt.data();
let page = virt_data & A::PAGE_ADDRESS_MASK;
let offset = virt_data & A::PAGE_OFFSET_MASK;
let entry = self.map.get(&VirtualAddress::new(page))?;
Some((
entry.address().add(offset),
entry.flags(),
))
Some((entry.address().ok()?.add(offset), entry.flags()))
}
fn read<T>(&self, virt: VirtualAddress) -> T {
@@ -187,7 +194,10 @@ impl<A: Arch> Machine<A> {
let virt_data = virt.data();
let size = mem::size_of::<T>();
if (virt_data & A::PAGE_ADDRESS_MASK) != ((virt_data + (size - 1)) & A::PAGE_ADDRESS_MASK) {
panic!("read: 0x{:X} size 0x{:X} passes page boundary", virt_data, size);
panic!(
"read: 0x{:X} size 0x{:X} passes page boundary",
virt_data, size
);
}
if let Some((phys, _flags)) = self.translate(virt) {
@@ -202,11 +212,14 @@ impl<A: Arch> Machine<A> {
let virt_data = virt.data();
let size = mem::size_of::<T>();
if (virt_data & A::PAGE_ADDRESS_MASK) != ((virt_data + (size - 1)) & A::PAGE_ADDRESS_MASK) {
panic!("write: 0x{:X} size 0x{:X} passes page boundary", virt_data, size);
panic!(
"write: 0x{:X} size 0x{:X} passes page boundary",
virt_data, size
);
}
if let Some((phys, flags)) = self.translate(virt) {
if flags & A::ENTRY_FLAG_READWRITE != 0 {
if flags.has_write() {
self.write_phys(phys, value);
} else {
panic!("write: 0x{:X} size 0x{:X} not writable", virt_data, size);
@@ -219,18 +232,28 @@ impl<A: Arch> Machine<A> {
fn write_bytes(&mut self, virt: VirtualAddress, value: u8, count: usize) {
//TODO: allow writing past page boundaries
let virt_data = virt.data();
if (virt_data & A::PAGE_ADDRESS_MASK) != ((virt_data + (count - 1)) & A::PAGE_ADDRESS_MASK) {
panic!("write_bytes: 0x{:X} count 0x{:X} passes page boundary", virt_data, count);
if (virt_data & A::PAGE_ADDRESS_MASK) != ((virt_data + (count - 1)) & A::PAGE_ADDRESS_MASK)
{
panic!(
"write_bytes: 0x{:X} count 0x{:X} passes page boundary",
virt_data, count
);
}
if let Some((phys, flags)) = self.translate(virt) {
if flags & A::ENTRY_FLAG_READWRITE != 0 {
if flags.has_write() {
self.write_phys_bytes(phys, value, count);
} else {
panic!("write_bytes: 0x{:X} count 0x{:X} not writable", virt_data, count);
panic!(
"write_bytes: 0x{:X} count 0x{:X} not writable",
virt_data, count
);
}
} else {
panic!("write_bytes: 0x{:X} count 0x{:X} not present", virt_data, count);
panic!(
"write_bytes: 0x{:X} count 0x{:X} not present",
virt_data, count
);
}
}
@@ -247,37 +270,45 @@ impl<A: Arch> Machine<A> {
for i4 in 0..A::PAGE_ENTRIES {
let e3 = self.read_phys::<usize>(PhysicalAddress::new(a4 + i4 * A::PAGE_ENTRY_SIZE));
let f3 = e3 & A::ENTRY_FLAGS_MASK;
if f3 & A::ENTRY_FLAG_PRESENT == 0 { continue; }
if f3 & A::ENTRY_FLAG_PRESENT == 0 {
continue;
}
// Page directory pointer
let a3 = e3 & A::ENTRY_ADDRESS_MASK;
for i3 in 0..A::PAGE_ENTRIES {
let e2 = self.read_phys::<usize>(PhysicalAddress::new(a3 + i3 * A::PAGE_ENTRY_SIZE));
let e2 =
self.read_phys::<usize>(PhysicalAddress::new(a3 + i3 * A::PAGE_ENTRY_SIZE));
let f2 = e2 & A::ENTRY_FLAGS_MASK;
if f2 & A::ENTRY_FLAG_PRESENT == 0 { continue; }
if f2 & A::ENTRY_FLAG_PRESENT == 0 {
continue;
}
// Page directory
let a2 = e2 & A::ENTRY_ADDRESS_MASK;
for i2 in 0..A::PAGE_ENTRIES {
let e1 = self.read_phys::<usize>(PhysicalAddress::new(a2 + i2 * A::PAGE_ENTRY_SIZE));
let e1 =
self.read_phys::<usize>(PhysicalAddress::new(a2 + i2 * A::PAGE_ENTRY_SIZE));
let f1 = e1 & A::ENTRY_FLAGS_MASK;
if f1 & A::ENTRY_FLAG_PRESENT == 0 { continue; }
if f1 & A::ENTRY_FLAG_PRESENT == 0 {
continue;
}
// Page table
let a1 = e1 & A::ENTRY_ADDRESS_MASK;
for i1 in 0..A::PAGE_ENTRIES {
let e = self.read_phys::<usize>(PhysicalAddress::new(a1 + i1 * A::PAGE_ENTRY_SIZE));
let e = self
.read_phys::<usize>(PhysicalAddress::new(a1 + i1 * A::PAGE_ENTRY_SIZE));
let f = e & A::ENTRY_FLAGS_MASK;
if f & A::ENTRY_FLAG_PRESENT == 0 { continue; }
if f & A::ENTRY_FLAG_PRESENT == 0 {
continue;
}
// Page
let page =
(i4 << 39) |
(i3 << 30) |
(i2 << 21) |
(i1 << 12);
let page = (i4 << 39) | (i3 << 30) | (i2 << 21) | (i1 << 12);
//println!("map 0x{:X} to 0x{:X}, 0x{:X}", page, a, f);
self.map.insert(VirtualAddress::new(page), PageEntry::new(e));
self.map
.insert(VirtualAddress::new(page), PageEntry::new(e));
}
}
}
+11 -20
View File
@@ -1,35 +1,25 @@
use core::ptr;
use crate::{
MemoryArea,
PhysicalAddress,
TableKind,
VirtualAddress,
};
use crate::{MemoryArea, PhysicalAddress, TableKind, VirtualAddress};
//TODO: Support having all page tables compile on all architectures
#[cfg(all(feature = "std", target_pointer_width = "64"))]
pub use self::emulate::EmulateArch;
#[cfg(target_pointer_width = "32")]
pub use self::{
x86::X86Arch,
};
pub use self::x86::X86Arch;
#[cfg(target_pointer_width = "64")]
pub use self::{
aarch64::AArch64Arch,
riscv64::{
RiscV64Sv39Arch,
RiscV64Sv48Arch
},
riscv64::{RiscV64Sv39Arch, RiscV64Sv48Arch},
x86_64::X8664Arch,
};
#[cfg(all(feature = "std", target_pointer_width = "64"))]
pub use self::emulate::EmulateArch;
#[cfg(target_pointer_width = "64")]
mod aarch64;
#[cfg(target_pointer_width = "64")]
mod riscv64;
#[cfg(all(feature = "std", target_pointer_width = "64"))]
mod emulate;
#[cfg(target_pointer_width = "64")]
mod riscv64;
#[cfg(target_pointer_width = "32")]
mod x86;
#[cfg(target_pointer_width = "64")]
@@ -62,11 +52,12 @@ pub trait Arch: Clone + Copy {
const PAGE_ENTRY_SIZE: usize = 1 << (Self::PAGE_SHIFT - Self::PAGE_ENTRY_SHIFT);
const PAGE_ENTRIES: usize = 1 << Self::PAGE_ENTRY_SHIFT;
const PAGE_ENTRY_MASK: usize = Self::PAGE_ENTRIES - 1;
const PAGE_NEGATIVE_MASK: usize = !((Self::PAGE_ADDRESS_SIZE as u64) - 1) as usize;
const PAGE_NEGATIVE_MASK: usize = !(Self::PAGE_ADDRESS_SIZE - 1) as usize;
const ENTRY_ADDRESS_SIZE: u64 = 1 << Self::ENTRY_ADDRESS_SHIFT;
const ENTRY_ADDRESS_MASK: usize = (Self::ENTRY_ADDRESS_SIZE - (Self::PAGE_SIZE as u64)) as usize;
const ENTRY_FLAGS_MASK: usize = !Self::ENTRY_ADDRESS_MASK as usize;
const ENTRY_ADDRESS_MASK: usize =
(Self::ENTRY_ADDRESS_SIZE - (Self::PAGE_SIZE as u64)) as usize;
const ENTRY_FLAGS_MASK: usize = !Self::ENTRY_ADDRESS_MASK;
unsafe fn init() -> &'static [MemoryArea];
+5 -14
View File
@@ -1,12 +1,6 @@
use core::arch::asm;
use crate::{
Arch,
MemoryArea,
PhysicalAddress,
TableKind,
VirtualAddress,
};
use crate::{Arch, MemoryArea, PhysicalAddress, TableKind, VirtualAddress};
#[derive(Clone, Copy)]
pub struct RiscV64Sv39Arch;
@@ -22,9 +16,7 @@ impl Arch for RiscV64Sv39Arch {
= Self::ENTRY_FLAG_PRESENT
| 1 << 1 // Read flag
;
const ENTRY_FLAG_DEFAULT_TABLE: usize
= Self::ENTRY_FLAG_PRESENT
;
const ENTRY_FLAG_DEFAULT_TABLE: usize = Self::ENTRY_FLAG_PRESENT;
const ENTRY_FLAG_PRESENT: usize = 1 << 0;
const ENTRY_FLAG_READONLY: usize = 0;
const ENTRY_FLAG_READWRITE: usize = 1 << 2;
@@ -51,14 +43,13 @@ impl Arch for RiscV64Sv39Arch {
let satp: usize;
asm!("csrr {0}, satp", out(reg) satp);
PhysicalAddress::new(
(satp & 0x0000_0FFF_FFFF_FFFF) << Self::PAGE_SHIFT // Convert from PPN
(satp & 0x0000_0FFF_FFFF_FFFF) << Self::PAGE_SHIFT, // Convert from PPN
)
}
#[inline(always)]
unsafe fn set_table(_table_kind: TableKind, address: PhysicalAddress) {
let satp =
(8 << 60) | // Sv39 MODE
let satp = (8 << 60) | // Sv39 MODE
(address.data() >> Self::PAGE_SHIFT); // Convert to PPN (TODO: ensure alignment)
asm!("csrw satp, {0}", in(reg) satp);
}
@@ -73,8 +64,8 @@ impl Arch for RiscV64Sv39Arch {
#[cfg(test)]
mod tests {
use crate::Arch;
use super::RiscV64Sv39Arch;
use crate::Arch;
#[test]
fn constants() {
+9 -17
View File
@@ -1,12 +1,6 @@
use core::arch::asm;
use crate::{
Arch,
MemoryArea,
PhysicalAddress,
TableKind,
VirtualAddress,
};
use crate::{Arch, MemoryArea, PhysicalAddress, TableKind, VirtualAddress};
#[derive(Clone, Copy)]
pub struct RiscV64Sv48Arch;
@@ -22,9 +16,7 @@ impl Arch for RiscV64Sv48Arch {
= Self::ENTRY_FLAG_PRESENT
| 1 << 1 // Read flag
;
const ENTRY_FLAG_DEFAULT_TABLE: usize
= Self::ENTRY_FLAG_PRESENT
;
const ENTRY_FLAG_DEFAULT_TABLE: usize = Self::ENTRY_FLAG_PRESENT;
const ENTRY_FLAG_PRESENT: usize = 1 << 0;
const ENTRY_FLAG_READONLY: usize = 0;
const ENTRY_FLAG_READWRITE: usize = 1 << 2;
@@ -51,14 +43,13 @@ impl Arch for RiscV64Sv48Arch {
let satp: usize;
asm!("csrr {0}, satp", out(reg) satp);
PhysicalAddress::new(
(satp & 0x0000_0FFF_FFFF_FFFF) << Self::PAGE_SHIFT // Convert from PPN
(satp & 0x0000_0FFF_FFFF_FFFF) << Self::PAGE_SHIFT, // Convert from PPN
)
}
#[inline(always)]
unsafe fn set_table(_table_kind: TableKind, address: PhysicalAddress) {
let satp =
(9 << 60) | // Sv48 MODE
let satp = (9 << 60) | // Sv48 MODE
(address.data() >> Self::PAGE_SHIFT); // Convert to PPN (TODO: ensure alignment)
asm!("csrw satp, {0}", in(reg) satp);
}
@@ -67,15 +58,14 @@ impl Arch for RiscV64Sv48Arch {
let mask = 0xFFFF_8000_0000_0000;
let masked = address.data() & mask;
masked == mask
|| masked == 0
masked == mask || masked == 0
}
}
#[cfg(test)]
mod tests {
use crate::Arch;
use super::RiscV64Sv48Arch;
use crate::Arch;
#[test]
fn constants() {
@@ -104,7 +94,9 @@ mod tests {
assert!(RiscV64Sv48Arch::virt_is_valid(VirtualAddress::new(address)));
}
fn no(address: usize) {
assert!(!RiscV64Sv48Arch::virt_is_valid(VirtualAddress::new(address)));
assert!(!RiscV64Sv48Arch::virt_is_valid(VirtualAddress::new(
address
)));
}
yes(0xFFFF_8000_1337_1337);
+2 -8
View File
@@ -1,13 +1,7 @@
//TODO: USE PAE
use core::arch::asm;
use crate::{
Arch,
MemoryArea,
PhysicalAddress,
TableKind,
VirtualAddress,
};
use crate::{Arch, MemoryArea, PhysicalAddress, TableKind, VirtualAddress};
#[derive(Clone, Copy)]
pub struct X86Arch;
@@ -61,8 +55,8 @@ impl Arch for X86Arch {
#[cfg(test)]
mod tests {
use crate::Arch;
use super::{VirtualAddress, X86Arch};
use crate::Arch;
#[test]
fn constants() {
+3 -10
View File
@@ -1,12 +1,6 @@
use core::arch::asm;
use crate::{
Arch,
MemoryArea,
PhysicalAddress,
TableKind,
VirtualAddress,
};
use crate::{Arch, MemoryArea, PhysicalAddress, TableKind, VirtualAddress};
#[derive(Clone, Copy, Debug)]
pub struct X8664Arch;
@@ -65,15 +59,14 @@ impl VirtualAddress {
pub fn is_canonical(self) -> bool {
let masked = self.data() & 0xFFFF_8000_0000_0000;
// TODO: 5-level paging
masked == 0xFFFF_8000_0000_0000
|| masked == 0
masked == 0xFFFF_8000_0000_0000 || masked == 0
}
}
#[cfg(test)]
mod tests {
use crate::Arch;
use super::{VirtualAddress, X8664Arch};
use crate::Arch;
#[test]
fn constants() {
+1 -5
View File
@@ -1,11 +1,7 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![feature(doc_cfg)]
pub use crate::{
allocator::*,
arch::*,
page::*,
};
pub use crate::{allocator::*, arch::*, page::*};
mod allocator;
mod arch;
+46 -44
View File
@@ -1,24 +1,11 @@
#![cfg(target_pointer_width = "64")]
use rmm::{
KILOBYTE,
MEGABYTE,
GIGABYTE,
TERABYTE,
Arch,
BuddyAllocator,
BumpAllocator,
EmulateArch,
FrameAllocator,
FrameCount,
MemoryArea,
PageFlags,
PageFlushAll,
PageMapper,
PageTable,
PhysicalAddress,
VirtualAddress,
Arch, BuddyAllocator, BumpAllocator, EmulateArch, Flusher, FrameAllocator, FrameCount,
MemoryArea, PageFlags, PageFlushAll, PageMapper, PageTable, PhysicalAddress, TableKind,
VirtualAddress, GIGABYTE, KILOBYTE, MEGABYTE, TERABYTE,
};
use std::marker::PhantomData;
pub fn format_size(size: usize) -> String {
if size >= 2 * TERABYTE {
@@ -34,6 +21,7 @@ pub fn format_size(size: usize) -> String {
}
}
#[allow(dead_code)]
unsafe fn dump_tables<A: Arch>(table: PageTable<A>) {
let level = table.level();
for i in 0..A::PAGE_ENTRIES {
@@ -41,7 +29,11 @@ unsafe fn dump_tables<A: Arch>(table: PageTable<A>) {
if let Some(entry) = table.entry(i) {
if entry.present() {
let base = table.entry_base(i).unwrap();
println!("0x{:X}: 0x{:X}", base.data(), entry.address().data());
println!(
"0x{:X}: 0x{:X}",
base.data(),
entry.address().unwrap().data()
);
}
}
} else {
@@ -179,18 +171,15 @@ unsafe fn new_tables<A: Arch>(areas: &'static [MemoryArea]) {
{
// Map all physical areas at PHYS_OFFSET
let mut mapper = PageMapper::<A, _>::create(
&mut bump_allocator
).expect("failed to create Mapper");
let mut mapper = PageMapper::<A, _>::create(TableKind::Kernel, &mut bump_allocator)
.expect("failed to create Mapper");
for area in areas.iter() {
for i in 0..area.size / A::PAGE_SIZE {
let phys = area.base.add(i * A::PAGE_SIZE);
let virt = A::phys_to_virt(phys);
let flush = mapper.map_phys(
virt,
phys,
PageFlags::<A>::new().write(true)
).expect("failed to map page to frame");
let flush = mapper
.map_phys(virt, phys, PageFlags::<A>::new().write(true))
.expect("failed to map page to frame");
flush.ignore(); // Not the active table
}
}
@@ -229,35 +218,39 @@ unsafe fn new_tables<A: Arch>(areas: &'static [MemoryArea]) {
}
}
let mut mapper = PageMapper::<A, _>::current(
&mut allocator
);
let flush_all = PageFlushAll::new();
let mut mapper = PageMapper::<A, _>::current(TableKind::Kernel, &mut allocator);
let mut flush_all = PageFlushAll::new();
for i in 0..16 {
let virt = VirtualAddress::new(MEGABYTE + i * A::PAGE_SIZE);
let flush = mapper.map(
virt,
PageFlags::<A>::new().user(true).write(true)
).expect("failed to map page");
let flush = mapper
.map(virt, PageFlags::<A>::new().user(true).write(true))
.expect("failed to map page");
flush_all.consume(flush);
}
flush_all.flush();
let flush_all = PageFlushAll::new();
let mut flush_all = PageFlushAll::new();
for i in 0..16 {
let virt = VirtualAddress::new(MEGABYTE + i * A::PAGE_SIZE);
let flush = mapper.unmap(
virt,
).expect("failed to unmap page");
let flush = mapper.unmap(virt, false).expect("failed to unmap page");
flush_all.consume(flush);
}
flush_all.flush();
let usage = allocator.usage();
println!("Allocator usage:");
println!(" Used: {}", format_size(usage.used().data() * A::PAGE_SIZE));
println!(" Free: {}", format_size(usage.free().data() * A::PAGE_SIZE));
println!(" Total: {}", format_size(usage.total().data() * A::PAGE_SIZE));
println!(
" Used: {}",
format_size(usage.used().data() * A::PAGE_SIZE)
);
println!(
" Free: {}",
format_size(usage.free().data() * A::PAGE_SIZE)
);
println!(
" Total: {}",
format_size(usage.total().data() * A::PAGE_SIZE)
);
}
unsafe fn inner<A: Arch>() {
@@ -270,19 +263,28 @@ unsafe fn inner<A: Arch>() {
//dump_tables(PageTable::<A>::top());
for i in &[1, 2, 4, 8, 16, 32] {
let phys = PhysicalAddress::new(i * MEGABYTE);
let virt = A::phys_to_virt(phys);
// Test read
println!("0x{:X} (0x{:X}) = 0x{:X}", virt.data(), phys.data(), A::read::<u8>(virt));
println!(
"0x{:X} (0x{:X}) = 0x{:X}",
virt.data(),
phys.data(),
A::read::<u8>(virt)
);
// Test write
A::write::<u8>(virt, 0x5A);
// Test read
println!("0x{:X} (0x{:X}) = 0x{:X}", virt.data(), phys.data(), A::read::<u8>(virt));
println!(
"0x{:X} (0x{:X}) = 0x{:X}",
virt.data(),
phys.data(),
A::read::<u8>(virt)
);
}
}
+5 -6
View File
@@ -1,10 +1,6 @@
use core::marker::PhantomData;
use crate::{
Arch,
PageFlags,
PhysicalAddress,
};
use crate::{Arch, PageFlags, PhysicalAddress};
#[derive(Clone, Copy, Debug)]
pub struct PageEntry<A> {
@@ -15,7 +11,10 @@ pub struct PageEntry<A> {
impl<A: Arch> PageEntry<A> {
#[inline(always)]
pub fn new(data: usize) -> Self {
Self { data, phantom: PhantomData }
Self {
data,
phantom: PhantomData,
}
}
#[inline(always)]
+8 -8
View File
@@ -14,10 +14,10 @@ impl<A: Arch> PageFlags<A> {
unsafe {
Self::from_data(
// Flags set to present, kernel space, read-only, no-execute by default
A::ENTRY_FLAG_DEFAULT_PAGE |
A::ENTRY_FLAG_READONLY |
A::ENTRY_FLAG_NO_EXEC |
A::ENTRY_FLAG_NO_GLOBAL,
A::ENTRY_FLAG_DEFAULT_PAGE
| A::ENTRY_FLAG_READONLY
| A::ENTRY_FLAG_NO_EXEC
| A::ENTRY_FLAG_NO_GLOBAL,
)
}
}
@@ -27,10 +27,10 @@ impl<A: Arch> PageFlags<A> {
unsafe {
Self::from_data(
// Flags set to present, kernel space, read-only, no-execute by default
A::ENTRY_FLAG_DEFAULT_TABLE |
A::ENTRY_FLAG_READONLY |
A::ENTRY_FLAG_NO_EXEC |
A::ENTRY_FLAG_NO_GLOBAL,
A::ENTRY_FLAG_DEFAULT_TABLE
| A::ENTRY_FLAG_READONLY
| A::ENTRY_FLAG_NO_EXEC
| A::ENTRY_FLAG_NO_GLOBAL,
)
}
}
+12 -12
View File
@@ -1,12 +1,6 @@
use core::{
marker::PhantomData,
mem,
};
use core::{marker::PhantomData, mem};
use crate::{
Arch,
VirtualAddress,
};
use crate::{Arch, VirtualAddress};
pub trait Flusher<A> {
fn consume(&mut self, flush: PageFlush<A>);
@@ -27,7 +21,9 @@ impl<A: Arch> PageFlush<A> {
}
pub fn flush(self) {
unsafe { A::invalidate(self.virt); }
unsafe {
A::invalidate(self.virt);
}
}
pub unsafe fn ignore(self) {
@@ -41,7 +37,7 @@ pub struct PageFlushAll<A: Arch> {
phantom: PhantomData<fn() -> A>,
}
impl <A: Arch> PageFlushAll<A> {
impl<A: Arch> PageFlushAll<A> {
pub fn new() -> Self {
Self {
phantom: PhantomData,
@@ -56,12 +52,16 @@ impl <A: Arch> PageFlushAll<A> {
}
impl<A: Arch> Drop for PageFlushAll<A> {
fn drop(&mut self) {
unsafe { A::invalidate_all(); }
unsafe {
A::invalidate_all();
}
}
}
impl<A: Arch> Flusher<A> for PageFlushAll<A> {
fn consume(&mut self, flush: PageFlush<A>) {
unsafe { flush.ignore(); }
unsafe {
flush.ignore();
}
}
}
impl<A: Arch, T: Flusher<A> + ?Sized> Flusher<A> for &mut T {
+72 -30
View File
@@ -1,14 +1,7 @@
use core::marker::PhantomData;
use crate::{
Arch,
FrameAllocator,
PageEntry,
PageFlags,
PageFlush,
PageTable,
PhysicalAddress,
TableKind,
Arch, FrameAllocator, PageEntry, PageFlags, PageFlush, PageTable, PhysicalAddress, TableKind,
VirtualAddress,
};
@@ -50,13 +43,7 @@ impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
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
)
}
unsafe { PageTable::new(VirtualAddress::new(0), self.table_addr, A::PAGE_LEVELS - 1) }
}
pub fn allocator(&self) -> &F {
@@ -67,7 +54,11 @@ impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
&mut self.allocator
}
pub unsafe fn remap_with_full(&mut self, virt: VirtualAddress, f: impl FnOnce(PhysicalAddress, PageFlags<A>) -> (PhysicalAddress, PageFlags<A>)) -> Option<(PageFlags<A>, PhysicalAddress, PageFlush<A>)> {
pub unsafe fn remap_with_full(
&mut self,
virt: VirtualAddress,
f: impl FnOnce(PhysicalAddress, PageFlags<A>) -> (PhysicalAddress, PageFlags<A>),
) -> Option<(PageFlags<A>, PhysicalAddress, PageFlush<A>)> {
self.visit(virt, |p1, i| {
let old_entry = p1.entry(i)?;
let old_phys = old_entry.address().ok()?;
@@ -77,21 +68,41 @@ impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
let new_entry = PageEntry::new(new_phys.data() | new_flags.data());
p1.set_entry(i, new_entry);
Some((old_flags, old_phys, PageFlush::new(virt)))
}).flatten()
})
.flatten()
}
pub unsafe fn remap_with(&mut self, virt: VirtualAddress, map_flags: impl FnOnce(PageFlags<A>) -> PageFlags<A>) -> Option<(PageFlags<A>, PhysicalAddress, PageFlush<A>)> {
self.remap_with_full(virt, |same_phys, old_flags| (same_phys, map_flags(old_flags)))
pub unsafe fn remap_with(
&mut self,
virt: VirtualAddress,
map_flags: impl FnOnce(PageFlags<A>) -> PageFlags<A>,
) -> Option<(PageFlags<A>, PhysicalAddress, PageFlush<A>)> {
self.remap_with_full(virt, |same_phys, old_flags| {
(same_phys, map_flags(old_flags))
})
}
pub unsafe fn remap(&mut self, virt: VirtualAddress, flags: PageFlags<A>) -> Option<PageFlush<A>> {
pub unsafe fn remap(
&mut self,
virt: VirtualAddress,
flags: PageFlags<A>,
) -> Option<PageFlush<A>> {
self.remap_with(virt, |_| flags).map(|(_, _, flush)| flush)
}
pub unsafe fn map(&mut self, virt: VirtualAddress, flags: PageFlags<A>) -> Option<PageFlush<A>> {
pub unsafe fn map(
&mut self,
virt: VirtualAddress,
flags: PageFlags<A>,
) -> Option<PageFlush<A>> {
let phys = self.allocator.allocate_one()?;
self.map_phys(virt, phys, flags)
}
pub unsafe fn map_phys(&mut self, virt: VirtualAddress, phys: PhysicalAddress, flags: PageFlags<A>) -> Option<PageFlush<A>> {
pub unsafe fn map_phys(
&mut self,
virt: VirtualAddress,
phys: PhysicalAddress,
flags: PageFlags<A>,
) -> Option<PageFlush<A>> {
//TODO: verify virt and phys are aligned
//TODO: verify flags have correct bits
let entry = PageEntry::new(phys.data() | flags.data());
@@ -109,7 +120,13 @@ impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
None => {
let next_phys = self.allocator.allocate_one()?;
//TODO: correct flags?
let flags = A::ENTRY_FLAG_READWRITE | A::ENTRY_FLAG_DEFAULT_TABLE | if virt.kind() == TableKind::User { A::ENTRY_FLAG_USER } else { 0 };
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)?
}
@@ -118,11 +135,19 @@ impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
}
}
}
pub unsafe fn map_linearly(&mut self, phys: PhysicalAddress, flags: PageFlags<A>) -> Option<(VirtualAddress, PageFlush<A>)> {
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))
}
fn visit<T>(&self, virt: VirtualAddress, f: impl FnOnce(&mut PageTable<A>, usize) -> T) -> Option<T> {
fn visit<T>(
&self,
virt: VirtualAddress,
f: impl FnOnce(&mut PageTable<A>, usize) -> T,
) -> Option<T> {
let mut table = self.table();
unsafe {
loop {
@@ -140,20 +165,35 @@ impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
Some((entry.address().ok()?, entry.flags()))
}
pub unsafe fn unmap(&mut self, virt: VirtualAddress, unmap_parents: bool) -> Option<PageFlush<A>> {
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>)> {
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, unmap_parents, &mut self.allocator).map(|(pa, pf)| (pa, pf, PageFlush::new(virt)))
unmap_phys_inner(virt, &mut table, level, unmap_parents, &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>)> {
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 {
@@ -172,7 +212,9 @@ unsafe fn unmap_phys_inner<A: Arch>(virt: VirtualAddress, table: &mut PageTable<
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());
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(subtable.phys());
+1 -7
View File
@@ -1,10 +1,4 @@
pub use self::{
entry::*,
flags::*,
flush::*,
mapper::*,
table::*,
};
pub use self::{entry::*, flags::*, flush::*, mapper::*, table::*};
mod entry;
mod flags;
+13 -9
View File
@@ -1,12 +1,7 @@
use core::marker::PhantomData;
use crate::{
Arch,
PhysicalAddress,
TableKind,
VirtualAddress,
};
use super::PageEntry;
use crate::{Arch, PhysicalAddress, TableKind, VirtualAddress};
pub struct PageTable<A> {
base: VirtualAddress,
@@ -17,14 +12,19 @@ pub struct PageTable<A> {
impl<A: Arch> PageTable<A> {
pub unsafe fn new(base: VirtualAddress, phys: PhysicalAddress, level: usize) -> Self {
Self { base, phys, level, phantom: PhantomData }
Self {
base,
phys,
level,
phantom: PhantomData,
}
}
pub unsafe fn top(table_kind: TableKind) -> Self {
Self::new(
VirtualAddress::new(0),
A::table(table_kind),
A::PAGE_LEVELS - 1
A::PAGE_LEVELS - 1,
)
}
@@ -85,7 +85,11 @@ impl<A: Arch> PageTable<A> {
// Canonicalize address first
let address = VirtualAddress::new(address.data() & A::PAGE_ADDRESS_MASK);
let level_shift = self.level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT;
let level_mask = (A::PAGE_ENTRIES << level_shift) - 1;
// Intentionally wraps around at last-level table to get all-ones mask on architectures
// where addressable physical address space covers entire usized space (e.g. x86)
let level_mask = A::PAGE_ENTRIES
.wrapping_shl(level_shift as u32)
.wrapping_sub(1);
if address >= self.base && address <= self.base.add(level_mask) {
Some((address.data() >> level_shift) & A::PAGE_ENTRY_MASK)
} else {