Update to the 2024 edition
On newer rustc versions let_chain is only allowed with the 2024 edition.
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@
|
||||
name = "rmm"
|
||||
version = "0.1.0"
|
||||
authors = ["Jeremy Soller <jeremy@system76.com>"]
|
||||
edition = "2018"
|
||||
edition = "2024"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
+186
-172
@@ -54,22 +54,28 @@ impl<A: Arch> BuddyEntry<A> {
|
||||
}
|
||||
|
||||
unsafe fn usage_addr(&self, page: usize) -> Option<VirtualAddress> {
|
||||
if page < self.pages() {
|
||||
let phys = self.base.add(page * mem::size_of::<BuddyUsage>());
|
||||
Some(A::phys_to_virt(phys))
|
||||
} else {
|
||||
None
|
||||
unsafe {
|
||||
if page < self.pages() {
|
||||
let phys = self.base.add(page * mem::size_of::<BuddyUsage>());
|
||||
Some(A::phys_to_virt(phys))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn usage(&self, page: usize) -> Option<BuddyUsage> {
|
||||
let addr = self.usage_addr(page)?;
|
||||
Some(A::read(addr))
|
||||
unsafe {
|
||||
let addr = self.usage_addr(page)?;
|
||||
Some(A::read(addr))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn set_usage(&self, page: usize, usage: BuddyUsage) -> Option<()> {
|
||||
let addr = self.usage_addr(page)?;
|
||||
Some(A::write(addr, usage))
|
||||
unsafe {
|
||||
let addr = self.usage_addr(page)?;
|
||||
Some(A::write(addr, usage))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,208 +88,216 @@ impl<A: Arch> BuddyAllocator<A> {
|
||||
const BUDDY_ENTRIES: usize = A::PAGE_SIZE / mem::size_of::<BuddyEntry<A>>();
|
||||
|
||||
pub unsafe fn new(mut bump_allocator: BumpAllocator<A>) -> Option<Self> {
|
||||
// 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>>()) {
|
||||
let virt = table_virt.add(i * mem::size_of::<BuddyEntry<A>>());
|
||||
A::write(virt, BuddyEntry::<A>::empty());
|
||||
}
|
||||
|
||||
let allocator = Self {
|
||||
table_virt,
|
||||
phantom: PhantomData,
|
||||
};
|
||||
|
||||
// Add areas to buddy table, combining areas when possible, and skipping frames used
|
||||
// by the bump allocator
|
||||
let mut offset = bump_allocator.offset();
|
||||
for old_area in bump_allocator.areas().iter() {
|
||||
let mut area = old_area.clone();
|
||||
if offset >= area.size {
|
||||
offset -= area.size;
|
||||
continue;
|
||||
} else if offset > 0 {
|
||||
area.base = area.base.add(offset);
|
||||
area.size -= offset;
|
||||
offset = 0;
|
||||
}
|
||||
unsafe {
|
||||
// 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>>()) {
|
||||
let virt = table_virt.add(i * mem::size_of::<BuddyEntry<A>>());
|
||||
A::write(virt, BuddyEntry::<A>::empty());
|
||||
}
|
||||
|
||||
let allocator = Self {
|
||||
table_virt,
|
||||
phantom: PhantomData,
|
||||
};
|
||||
|
||||
// Add areas to buddy table, combining areas when possible, and skipping frames used
|
||||
// by the bump allocator
|
||||
let mut offset = bump_allocator.offset();
|
||||
for old_area in bump_allocator.areas().iter() {
|
||||
let mut area = old_area.clone();
|
||||
if offset >= area.size {
|
||||
offset -= area.size;
|
||||
continue;
|
||||
} else if offset > 0 {
|
||||
area.base = area.base.add(offset);
|
||||
area.size -= offset;
|
||||
offset = 0;
|
||||
}
|
||||
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 } {
|
||||
// Combine entry at start
|
||||
entry.base = area.base;
|
||||
entry.size += area.size;
|
||||
true
|
||||
} else if area.base == entry.base.add(entry.size) {
|
||||
// Combine entry at end
|
||||
entry.size += area.size;
|
||||
true
|
||||
} else if entry.size == 0 {
|
||||
// Create new entry
|
||||
entry.base = area.base;
|
||||
entry.size = area.size;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if inserted {
|
||||
A::write(virt, entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: sort areas?
|
||||
|
||||
// Allocate buddy maps
|
||||
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);
|
||||
let inserted = if area.base.add(area.size) == { entry.base } {
|
||||
// Combine entry at start
|
||||
entry.base = area.base;
|
||||
entry.size += area.size;
|
||||
true
|
||||
} else if area.base == entry.base.add(entry.size) {
|
||||
// Combine entry at end
|
||||
entry.size += area.size;
|
||||
true
|
||||
} else if entry.size == 0 {
|
||||
// Create new entry
|
||||
entry.base = area.base;
|
||||
entry.size = area.size;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if inserted {
|
||||
A::write(virt, entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: sort areas?
|
||||
// Only set up entries that have enough space for their own usage map
|
||||
let usage_pages = entry.usage_pages();
|
||||
if entry.pages() > usage_pages {
|
||||
// Mark all usage bytes as unused
|
||||
let usage_start = entry.usage_addr(0)?;
|
||||
for page in 0..usage_pages {
|
||||
A::write_bytes(usage_start.add(page << A::PAGE_SHIFT), 0, A::PAGE_SIZE);
|
||||
}
|
||||
|
||||
// Allocate buddy maps
|
||||
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);
|
||||
|
||||
// Only set up entries that have enough space for their own usage map
|
||||
let usage_pages = entry.usage_pages();
|
||||
if entry.pages() > usage_pages {
|
||||
// Mark all usage bytes as unused
|
||||
let usage_start = entry.usage_addr(0)?;
|
||||
for page in 0..usage_pages {
|
||||
A::write_bytes(usage_start.add(page << A::PAGE_SHIFT), 0, A::PAGE_SIZE);
|
||||
// Mark bytes used for usage as used
|
||||
for page in 0..usage_pages {
|
||||
entry.set_usage(page, BuddyUsage(1))?;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark bytes used for usage as used
|
||||
for page in 0..usage_pages {
|
||||
entry.set_usage(page, BuddyUsage(1))?;
|
||||
}
|
||||
// Skip the pages used for usage
|
||||
entry.skip = usage_pages;
|
||||
|
||||
// Set used pages to pages used for usage
|
||||
entry.used = usage_pages;
|
||||
|
||||
// Write updated entry
|
||||
A::write(virt, entry);
|
||||
}
|
||||
|
||||
// Skip the pages used for usage
|
||||
entry.skip = usage_pages;
|
||||
|
||||
// Set used pages to pages used for usage
|
||||
entry.used = usage_pages;
|
||||
|
||||
// Write updated entry
|
||||
A::write(virt, entry);
|
||||
Some(allocator)
|
||||
}
|
||||
|
||||
Some(allocator)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Arch> FrameAllocator for BuddyAllocator<A> {
|
||||
unsafe fn allocate(&mut self, count: FrameCount) -> Option<PhysicalAddress> {
|
||||
if self.table_virt.data() == 0 {
|
||||
return None;
|
||||
}
|
||||
unsafe {
|
||||
if self.table_virt.data() == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
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);
|
||||
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() {
|
||||
let usage = entry.usage(page)?;
|
||||
if usage.0 == 0 {
|
||||
free_count += 1;
|
||||
let mut free_page = entry.skip;
|
||||
let mut free_count = 0;
|
||||
for page in entry.skip..entry.pages() {
|
||||
let usage = entry.usage(page)?;
|
||||
if usage.0 == 0 {
|
||||
free_count += 1;
|
||||
|
||||
if free_count == count.data() {
|
||||
break;
|
||||
if free_count == count.data() {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
free_page = page + 1;
|
||||
free_count = 0;
|
||||
}
|
||||
} else {
|
||||
free_page = page + 1;
|
||||
free_count = 0;
|
||||
}
|
||||
|
||||
if free_count == count.data() {
|
||||
for page in free_page..free_page + free_count {
|
||||
// Update usage
|
||||
let mut usage = entry.usage(page)?;
|
||||
usage.0 += 1;
|
||||
entry.set_usage(page, usage);
|
||||
|
||||
// Zero page
|
||||
let page_phys = entry.base.add(page << A::PAGE_SHIFT);
|
||||
let page_virt = A::phys_to_virt(page_phys);
|
||||
A::write_bytes(page_virt, 0, A::PAGE_SIZE);
|
||||
}
|
||||
|
||||
// Update skip if necessary
|
||||
if entry.skip == free_page {
|
||||
entry.skip = free_page + free_count;
|
||||
}
|
||||
|
||||
// Update used page count
|
||||
entry.used += free_count;
|
||||
|
||||
// Write updated entry
|
||||
A::write(virt, entry);
|
||||
|
||||
return Some(entry.base.add(free_page << A::PAGE_SHIFT));
|
||||
}
|
||||
}
|
||||
|
||||
if free_count == count.data() {
|
||||
for page in free_page..free_page + free_count {
|
||||
// Update usage
|
||||
let mut usage = entry.usage(page)?;
|
||||
usage.0 += 1;
|
||||
entry.set_usage(page, usage);
|
||||
|
||||
// Zero page
|
||||
let page_phys = entry.base.add(page << A::PAGE_SHIFT);
|
||||
let page_virt = A::phys_to_virt(page_phys);
|
||||
A::write_bytes(page_virt, 0, A::PAGE_SIZE);
|
||||
}
|
||||
|
||||
// Update skip if necessary
|
||||
if entry.skip == free_page {
|
||||
entry.skip = free_page + free_count;
|
||||
}
|
||||
|
||||
// Update used page count
|
||||
entry.used += free_count;
|
||||
|
||||
// Write updated entry
|
||||
A::write(virt, entry);
|
||||
|
||||
return Some(entry.base.add(free_page << A::PAGE_SHIFT));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
unsafe fn free(&mut self, base: PhysicalAddress, count: FrameCount) {
|
||||
if self.table_virt.data() == 0 {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
if self.table_virt.data() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let size = count.data() * A::PAGE_SIZE;
|
||||
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);
|
||||
let size = count.data() * A::PAGE_SIZE;
|
||||
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);
|
||||
|
||||
if base >= { entry.base } && base.add(size) <= entry.base.add(entry.size) {
|
||||
let start_page = (base.data() - { entry.base }.data()) >> A::PAGE_SHIFT;
|
||||
for page in start_page..start_page + count.data() {
|
||||
let mut usage = entry.usage(page).expect("failed to get usage during free");
|
||||
if base >= { entry.base } && base.add(size) <= entry.base.add(entry.size) {
|
||||
let start_page = (base.data() - { entry.base }.data()) >> A::PAGE_SHIFT;
|
||||
for page in start_page..start_page + count.data() {
|
||||
let mut usage = entry.usage(page).expect("failed to get usage during free");
|
||||
|
||||
if usage.0 > 0 {
|
||||
usage.0 -= 1;
|
||||
} else {
|
||||
panic!("tried to free already free frame");
|
||||
}
|
||||
|
||||
// If page was freed
|
||||
if usage.0 == 0 {
|
||||
// Update skip if necessary
|
||||
if page < entry.skip {
|
||||
entry.skip = page;
|
||||
if usage.0 > 0 {
|
||||
usage.0 -= 1;
|
||||
} else {
|
||||
panic!("tried to free already free frame");
|
||||
}
|
||||
|
||||
// Update used page count
|
||||
entry.used -= 1;
|
||||
// If page was freed
|
||||
if usage.0 == 0 {
|
||||
// Update skip if necessary
|
||||
if page < entry.skip {
|
||||
entry.skip = page;
|
||||
}
|
||||
|
||||
// Update used page count
|
||||
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
|
||||
A::write(virt, entry);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Write updated entry
|
||||
A::write(virt, entry);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn usage(&self) -> FrameUsage {
|
||||
let mut total = 0;
|
||||
let mut used = 0;
|
||||
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;
|
||||
used += entry.used;
|
||||
unsafe {
|
||||
let mut total = 0;
|
||||
let mut used = 0;
|
||||
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;
|
||||
used += entry.used;
|
||||
}
|
||||
FrameUsage::new(FrameCount::new(used), FrameCount::new(total))
|
||||
}
|
||||
FrameUsage::new(FrameCount::new(used), FrameCount::new(total))
|
||||
}
|
||||
}
|
||||
|
||||
+25
-16
@@ -11,7 +11,9 @@ pub struct BumpAllocator<A> {
|
||||
|
||||
impl<A: Arch> BumpAllocator<A> {
|
||||
pub fn new(mut areas: &'static [MemoryArea], mut offset: usize) -> Self {
|
||||
while let Some(first) = areas.first() && first.size <= offset {
|
||||
while let Some(first) = areas.first()
|
||||
&& first.size <= offset
|
||||
{
|
||||
offset -= first.size;
|
||||
areas = &areas[1..];
|
||||
}
|
||||
@@ -32,7 +34,9 @@ impl<A: Arch> BumpAllocator<A> {
|
||||
}
|
||||
pub fn abs_offset(&self) -> PhysicalAddress {
|
||||
let (areas, off) = self.cur_areas;
|
||||
areas.first().map_or(PhysicalAddress::new(0), |a| a.base.add(off))
|
||||
areas
|
||||
.first()
|
||||
.map_or(PhysicalAddress::new(0), |a| a.base.add(off))
|
||||
}
|
||||
pub fn offset(&self) -> usize {
|
||||
(unsafe { self.usage().total().data() - self.usage().free().data() }) * A::PAGE_SIZE
|
||||
@@ -41,21 +45,23 @@ impl<A: Arch> BumpAllocator<A> {
|
||||
|
||||
impl<A: Arch> FrameAllocator for BumpAllocator<A> {
|
||||
unsafe fn allocate(&mut self, count: FrameCount) -> Option<PhysicalAddress> {
|
||||
let req_size = count.data() * A::PAGE_SIZE;
|
||||
unsafe {
|
||||
let req_size = count.data() * A::PAGE_SIZE;
|
||||
|
||||
let block = loop {
|
||||
let area = self.cur_areas.0.first()?;
|
||||
let off = self.cur_areas.1;
|
||||
if area.size - off < req_size {
|
||||
self.cur_areas = (&self.cur_areas.0[1..], 0);
|
||||
continue;
|
||||
}
|
||||
self.cur_areas.1 += req_size;
|
||||
let block = loop {
|
||||
let area = self.cur_areas.0.first()?;
|
||||
let off = self.cur_areas.1;
|
||||
if area.size - off < req_size {
|
||||
self.cur_areas = (&self.cur_areas.0[1..], 0);
|
||||
continue;
|
||||
}
|
||||
self.cur_areas.1 += req_size;
|
||||
|
||||
break area.base.add(off);
|
||||
};
|
||||
A::write_bytes(A::phys_to_virt(block), 0, req_size);
|
||||
Some(block)
|
||||
break area.base.add(off);
|
||||
};
|
||||
A::write_bytes(A::phys_to_virt(block), 0, req_size);
|
||||
Some(block)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn free(&mut self, _address: PhysicalAddress, _count: FrameCount) {
|
||||
@@ -65,6 +71,9 @@ impl<A: Arch> FrameAllocator for BumpAllocator<A> {
|
||||
unsafe fn usage(&self) -> FrameUsage {
|
||||
let total = self.orig_areas.0.iter().map(|a| a.size).sum::<usize>() - self.orig_areas.1;
|
||||
let free = self.cur_areas.0.iter().map(|a| a.size).sum::<usize>() - self.cur_areas.1;
|
||||
FrameUsage::new(FrameCount::new((total - free) / A::PAGE_SIZE), FrameCount::new(total / A::PAGE_SIZE))
|
||||
FrameUsage::new(
|
||||
FrameCount::new((total - free) / A::PAGE_SIZE),
|
||||
FrameCount::new(total / A::PAGE_SIZE),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,11 +49,13 @@ pub trait FrameAllocator {
|
||||
unsafe fn free(&mut self, address: PhysicalAddress, count: FrameCount);
|
||||
|
||||
unsafe fn allocate_one(&mut self) -> Option<PhysicalAddress> {
|
||||
self.allocate(FrameCount::new(1))
|
||||
unsafe { self.allocate(FrameCount::new(1)) }
|
||||
}
|
||||
|
||||
unsafe fn free_one(&mut self, address: PhysicalAddress) {
|
||||
self.free(address, FrameCount::new(1));
|
||||
unsafe {
|
||||
self.free(address, FrameCount::new(1));
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn usage(&self) -> FrameUsage;
|
||||
@@ -64,18 +66,18 @@ where
|
||||
T: FrameAllocator,
|
||||
{
|
||||
unsafe fn allocate(&mut self, count: FrameCount) -> Option<PhysicalAddress> {
|
||||
T::allocate(self, count)
|
||||
unsafe { T::allocate(self, count) }
|
||||
}
|
||||
unsafe fn free(&mut self, address: PhysicalAddress, count: FrameCount) {
|
||||
T::free(self, address, count)
|
||||
unsafe { T::free(self, address, count) }
|
||||
}
|
||||
unsafe fn allocate_one(&mut self) -> Option<PhysicalAddress> {
|
||||
T::allocate_one(self)
|
||||
unsafe { T::allocate_one(self) }
|
||||
}
|
||||
unsafe fn free_one(&mut self, address: PhysicalAddress) {
|
||||
T::free_one(self, address)
|
||||
unsafe { T::free_one(self, address) }
|
||||
}
|
||||
unsafe fn usage(&self) -> FrameUsage {
|
||||
T::usage(self)
|
||||
unsafe { T::usage(self) }
|
||||
}
|
||||
}
|
||||
|
||||
+27
-19
@@ -42,51 +42,59 @@ impl Arch for AArch64Arch {
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn invalidate(address: VirtualAddress) {
|
||||
asm!("
|
||||
unsafe {
|
||||
asm!("
|
||||
dsb ishst
|
||||
tlbi vaae1is, {}
|
||||
dsb ish
|
||||
isb
|
||||
", in(reg) (address.data() >> Self::PAGE_SHIFT));
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn invalidate_all() {
|
||||
asm!(
|
||||
"
|
||||
unsafe {
|
||||
asm!(
|
||||
"
|
||||
dsb ishst
|
||||
tlbi vmalle1is
|
||||
dsb ish
|
||||
isb
|
||||
"
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn table(table_kind: TableKind) -> PhysicalAddress {
|
||||
let address: usize;
|
||||
match table_kind {
|
||||
TableKind::User => {
|
||||
asm!("mrs {0}, ttbr0_el1", out(reg) address);
|
||||
}
|
||||
TableKind::Kernel => {
|
||||
asm!("mrs {0}, ttbr1_el1", out(reg) address);
|
||||
unsafe {
|
||||
let address: usize;
|
||||
match table_kind {
|
||||
TableKind::User => {
|
||||
asm!("mrs {0}, ttbr0_el1", out(reg) address);
|
||||
}
|
||||
TableKind::Kernel => {
|
||||
asm!("mrs {0}, ttbr1_el1", out(reg) address);
|
||||
}
|
||||
}
|
||||
PhysicalAddress::new(address)
|
||||
}
|
||||
PhysicalAddress::new(address)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn set_table(table_kind: TableKind, address: PhysicalAddress) {
|
||||
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());
|
||||
unsafe {
|
||||
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());
|
||||
}
|
||||
}
|
||||
Self::invalidate_all();
|
||||
}
|
||||
Self::invalidate_all();
|
||||
}
|
||||
|
||||
fn virt_is_valid(_address: VirtualAddress) -> bool {
|
||||
|
||||
+46
-38
@@ -34,76 +34,84 @@ impl Arch for EmulateArch {
|
||||
const ENTRY_FLAG_WRITE_COMBINING: usize = X8664Arch::ENTRY_FLAG_WRITE_COMBINING;
|
||||
|
||||
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);
|
||||
unsafe {
|
||||
// Create machine with PAGE_ENTRIES pages offset mapped (2 MiB on x86_64)
|
||||
let mut machine = Machine::new(MEMORY_SIZE);
|
||||
|
||||
// PML4 index 256 (PHYS_OFFSET) link to PDP
|
||||
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,
|
||||
);
|
||||
|
||||
// PDP link to PD
|
||||
let pd = pdp + Self::PAGE_SIZE;
|
||||
machine.write_phys::<usize>(PhysicalAddress::new(pdp), pd | flags);
|
||||
|
||||
// PD link to PT
|
||||
let pt = pd + Self::PAGE_SIZE;
|
||||
machine.write_phys::<usize>(PhysicalAddress::new(pd), pt | flags);
|
||||
|
||||
// PT links to frames
|
||||
for i in 0..Self::PAGE_ENTRIES {
|
||||
let page = i * Self::PAGE_SIZE;
|
||||
// PML4 index 256 (PHYS_OFFSET) link to PDP
|
||||
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(pt + i * Self::PAGE_ENTRY_SIZE),
|
||||
page | flags,
|
||||
PhysicalAddress::new(pml4 + 256 * Self::PAGE_ENTRY_SIZE),
|
||||
pdp | flags,
|
||||
);
|
||||
|
||||
// PDP link to PD
|
||||
let pd = pdp + Self::PAGE_SIZE;
|
||||
machine.write_phys::<usize>(PhysicalAddress::new(pdp), pd | flags);
|
||||
|
||||
// PD link to PT
|
||||
let pt = pd + Self::PAGE_SIZE;
|
||||
machine.write_phys::<usize>(PhysicalAddress::new(pd), pt | flags);
|
||||
|
||||
// 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 = Some(machine);
|
||||
|
||||
// Set table to pml4
|
||||
EmulateArch::set_table(TableKind::Kernel, PhysicalAddress::new(pml4));
|
||||
|
||||
&MEMORY_AREAS
|
||||
}
|
||||
|
||||
MACHINE = Some(machine);
|
||||
|
||||
// Set table to pml4
|
||||
EmulateArch::set_table(TableKind::Kernel, PhysicalAddress::new(pml4));
|
||||
|
||||
&MEMORY_AREAS
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn read<T>(address: VirtualAddress) -> T {
|
||||
MACHINE.as_ref().unwrap().read(address)
|
||||
unsafe { MACHINE.as_ref().unwrap().read(address) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn write<T>(address: VirtualAddress, value: T) {
|
||||
MACHINE.as_mut().unwrap().write(address, value)
|
||||
unsafe { MACHINE.as_mut().unwrap().write(address, value) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn write_bytes(address: VirtualAddress, value: u8, count: usize) {
|
||||
MACHINE.as_mut().unwrap().write_bytes(address, value, count)
|
||||
unsafe { MACHINE.as_mut().unwrap().write_bytes(address, value, count) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn invalidate(address: VirtualAddress) {
|
||||
MACHINE.as_mut().unwrap().invalidate(address);
|
||||
unsafe {
|
||||
MACHINE.as_mut().unwrap().invalidate(address);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn invalidate_all() {
|
||||
MACHINE.as_mut().unwrap().invalidate_all();
|
||||
unsafe {
|
||||
MACHINE.as_mut().unwrap().invalidate_all();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn table(_table_kind: TableKind) -> PhysicalAddress {
|
||||
MACHINE.as_mut().unwrap().get_table()
|
||||
unsafe { MACHINE.as_mut().unwrap().get_table() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn set_table(_table_kind: TableKind, address: PhysicalAddress) {
|
||||
MACHINE.as_mut().unwrap().set_table(address);
|
||||
unsafe {
|
||||
MACHINE.as_mut().unwrap().set_table(address);
|
||||
}
|
||||
}
|
||||
fn virt_is_valid(_address: VirtualAddress) -> bool {
|
||||
// TODO: Don't see why an emulated arch would have any problems with canonicalness...
|
||||
|
||||
+7
-5
@@ -65,25 +65,27 @@ pub trait Arch: Clone + Copy {
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn read<T>(address: VirtualAddress) -> T {
|
||||
ptr::read(address.data() as *const T)
|
||||
unsafe { ptr::read(address.data() as *const T) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn write<T>(address: VirtualAddress, value: T) {
|
||||
ptr::write(address.data() as *mut T, value)
|
||||
unsafe { ptr::write(address.data() as *mut T, value) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn write_bytes(address: VirtualAddress, value: u8, count: usize) {
|
||||
ptr::write_bytes(address.data() as *mut u8, value, count)
|
||||
unsafe { ptr::write_bytes(address.data() as *mut u8, value, count) }
|
||||
}
|
||||
|
||||
unsafe fn invalidate(address: VirtualAddress);
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn invalidate_all() {
|
||||
//TODO: this stub only works on x86_64, maybe make the arch implement this?
|
||||
Self::set_table(TableKind::User, Self::table(TableKind::User));
|
||||
unsafe {
|
||||
//TODO: this stub only works on x86_64, maybe make the arch implement this?
|
||||
Self::set_table(TableKind::User, Self::table(TableKind::User));
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn table(table_kind: TableKind) -> PhysicalAddress;
|
||||
|
||||
+18
-10
@@ -34,29 +34,37 @@ impl Arch for RiscV64Sv39Arch {
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn invalidate(address: VirtualAddress) {
|
||||
asm!("sfence.vma {}", in(reg) address.data());
|
||||
unsafe {
|
||||
asm!("sfence.vma {}", in(reg) address.data());
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn invalidate_all() {
|
||||
asm!("sfence.vma");
|
||||
unsafe {
|
||||
asm!("sfence.vma");
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn table(_table_kind: TableKind) -> PhysicalAddress {
|
||||
let satp: usize;
|
||||
asm!("csrr {0}, satp", out(reg) satp);
|
||||
PhysicalAddress::new(
|
||||
(satp & Self::ENTRY_ADDRESS_MASK) << Self::PAGE_SHIFT, // Convert from PPN
|
||||
)
|
||||
unsafe {
|
||||
let satp: usize;
|
||||
asm!("csrr {0}, satp", out(reg) satp);
|
||||
PhysicalAddress::new(
|
||||
(satp & Self::ENTRY_ADDRESS_MASK) << Self::PAGE_SHIFT, // Convert from PPN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn set_table(_table_kind: TableKind, address: PhysicalAddress) {
|
||||
let satp = (8 << 60) | // Sv39 MODE
|
||||
unsafe {
|
||||
let satp = (8 << 60) | // Sv39 MODE
|
||||
(address.data() >> Self::PAGE_SHIFT); // Convert to PPN (TODO: ensure alignment)
|
||||
asm!("csrw satp, {0}", in(reg) satp);
|
||||
Self::invalidate_all();
|
||||
asm!("csrw satp, {0}", in(reg) satp);
|
||||
Self::invalidate_all();
|
||||
}
|
||||
}
|
||||
|
||||
fn virt_is_valid(address: VirtualAddress) -> bool {
|
||||
|
||||
+18
-10
@@ -34,29 +34,37 @@ impl Arch for RiscV64Sv48Arch {
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn invalidate(address: VirtualAddress) {
|
||||
asm!("sfence.vma {}", in(reg) address.data());
|
||||
unsafe {
|
||||
asm!("sfence.vma {}", in(reg) address.data());
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn invalidate_all() {
|
||||
asm!("sfence.vma");
|
||||
unsafe {
|
||||
asm!("sfence.vma");
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn table(_table_kind: TableKind) -> PhysicalAddress {
|
||||
let satp: usize;
|
||||
asm!("csrr {0}, satp", out(reg) satp);
|
||||
PhysicalAddress::new(
|
||||
(satp & Self::ENTRY_ADDRESS_MASK) << Self::PAGE_SHIFT, // Convert from PPN
|
||||
)
|
||||
unsafe {
|
||||
let satp: usize;
|
||||
asm!("csrr {0}, satp", out(reg) satp);
|
||||
PhysicalAddress::new(
|
||||
(satp & Self::ENTRY_ADDRESS_MASK) << Self::PAGE_SHIFT, // Convert from PPN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn set_table(_table_kind: TableKind, address: PhysicalAddress) {
|
||||
let satp = (9 << 60) | // Sv48 MODE
|
||||
unsafe {
|
||||
let satp = (9 << 60) | // Sv48 MODE
|
||||
(address.data() >> Self::PAGE_SHIFT); // Convert to PPN (TODO: ensure alignment)
|
||||
asm!("csrw satp, {0}", in(reg) satp);
|
||||
Self::invalidate_all();
|
||||
asm!("csrw satp, {0}", in(reg) satp);
|
||||
Self::invalidate_all();
|
||||
}
|
||||
}
|
||||
|
||||
fn virt_is_valid(address: VirtualAddress) -> bool {
|
||||
|
||||
+11
-5
@@ -32,19 +32,25 @@ impl Arch for X8664Arch {
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn invalidate(address: VirtualAddress) {
|
||||
asm!("invlpg [{0}]", in(reg) address.data());
|
||||
unsafe {
|
||||
asm!("invlpg [{0}]", in(reg) address.data());
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn table(_table_kind: TableKind) -> PhysicalAddress {
|
||||
let address: usize;
|
||||
asm!("mov {0}, cr3", out(reg) address);
|
||||
PhysicalAddress::new(address)
|
||||
unsafe {
|
||||
let address: usize;
|
||||
asm!("mov {0}, cr3", out(reg) address);
|
||||
PhysicalAddress::new(address)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn set_table(_table_kind: TableKind, address: PhysicalAddress) {
|
||||
asm!("mov cr3, {0}", in(reg) address.data());
|
||||
unsafe {
|
||||
asm!("mov cr3, {0}", in(reg) address.data());
|
||||
}
|
||||
}
|
||||
|
||||
fn virt_is_valid(address: VirtualAddress) -> bool {
|
||||
|
||||
+183
-167
@@ -23,22 +23,24 @@ 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 {
|
||||
if level == 0 {
|
||||
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().unwrap().data()
|
||||
);
|
||||
unsafe {
|
||||
let level = table.level();
|
||||
for i in 0..A::PAGE_ENTRIES {
|
||||
if level == 0 {
|
||||
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().unwrap().data()
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(next) = table.next(i) {
|
||||
dump_tables(next);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(next) = table.next(i) {
|
||||
dump_tables(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,21 +66,25 @@ impl<A: Arch> SlabNode<A> {
|
||||
}
|
||||
|
||||
pub unsafe fn insert(&mut self, phys: PhysicalAddress) {
|
||||
let virt = A::phys_to_virt(phys);
|
||||
A::write(virt, self.next);
|
||||
self.next = phys;
|
||||
self.count += 1;
|
||||
unsafe {
|
||||
let virt = A::phys_to_virt(phys);
|
||||
A::write(virt, self.next);
|
||||
self.next = phys;
|
||||
self.count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn remove(&mut self) -> Option<PhysicalAddress> {
|
||||
if self.count > 0 {
|
||||
let phys = self.next;
|
||||
let virt = A::phys_to_virt(phys);
|
||||
self.next = A::read(virt);
|
||||
self.count -= 1;
|
||||
Some(phys)
|
||||
} else {
|
||||
None
|
||||
unsafe {
|
||||
if self.count > 0 {
|
||||
let phys = self.next;
|
||||
let virt = A::phys_to_virt(phys);
|
||||
self.next = A::read(virt);
|
||||
self.count -= 1;
|
||||
Some(phys)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,57 +97,63 @@ pub struct SlabAllocator<A> {
|
||||
|
||||
impl<A: Arch> SlabAllocator<A> {
|
||||
pub unsafe fn new(areas: &'static [MemoryArea], offset: usize) -> Self {
|
||||
let mut allocator = Self {
|
||||
nodes: [
|
||||
SlabNode::empty(),
|
||||
SlabNode::empty(),
|
||||
SlabNode::empty(),
|
||||
SlabNode::empty(),
|
||||
],
|
||||
phantom: PhantomData,
|
||||
};
|
||||
unsafe {
|
||||
let mut allocator = Self {
|
||||
nodes: [
|
||||
SlabNode::empty(),
|
||||
SlabNode::empty(),
|
||||
SlabNode::empty(),
|
||||
SlabNode::empty(),
|
||||
],
|
||||
phantom: PhantomData,
|
||||
};
|
||||
|
||||
// Add unused areas to free lists
|
||||
let mut area_offset = offset;
|
||||
for area in areas.iter() {
|
||||
if area_offset < area.size {
|
||||
let area_base = area.base.add(area_offset);
|
||||
let area_size = area.size - area_offset;
|
||||
allocator.free(area_base, area_size);
|
||||
area_offset = 0;
|
||||
} else {
|
||||
area_offset -= area.size;
|
||||
// Add unused areas to free lists
|
||||
let mut area_offset = offset;
|
||||
for area in areas.iter() {
|
||||
if area_offset < area.size {
|
||||
let area_base = area.base.add(area_offset);
|
||||
let area_size = area.size - area_offset;
|
||||
allocator.free(area_base, area_size);
|
||||
area_offset = 0;
|
||||
} else {
|
||||
area_offset -= area.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allocator
|
||||
allocator
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn allocate(&mut self, size: usize) -> Option<PhysicalAddress> {
|
||||
for level in 0..A::PAGE_LEVELS - 1 {
|
||||
let level_shift = level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT;
|
||||
let level_size = 1 << level_shift;
|
||||
if size <= level_size {
|
||||
if let Some(base) = self.nodes[level].remove() {
|
||||
self.free(base.add(size), level_size - size);
|
||||
return Some(base);
|
||||
unsafe {
|
||||
for level in 0..A::PAGE_LEVELS - 1 {
|
||||
let level_shift = level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT;
|
||||
let level_size = 1 << level_shift;
|
||||
if size <= level_size {
|
||||
if let Some(base) = self.nodes[level].remove() {
|
||||
self.free(base.add(size), level_size - size);
|
||||
return Some(base);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
//TODO: This causes fragmentation, since neighbors are not identified
|
||||
//TODO: remainders less than PAGE_SIZE will be lost
|
||||
pub unsafe fn free(&mut self, mut base: PhysicalAddress, mut size: usize) {
|
||||
for level in (0..A::PAGE_LEVELS - 1).rev() {
|
||||
let level_shift = level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT;
|
||||
let level_size = 1 << level_shift;
|
||||
while size >= level_size {
|
||||
println!("Add {:X} {}", base.data(), format_size(level_size));
|
||||
self.nodes[level].insert(base);
|
||||
base = base.add(level_size);
|
||||
size -= level_size;
|
||||
unsafe {
|
||||
for level in (0..A::PAGE_LEVELS - 1).rev() {
|
||||
let level_shift = level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT;
|
||||
let level_size = 1 << level_shift;
|
||||
while size >= level_size {
|
||||
println!("Add {:X} {}", base.data(), format_size(level_size));
|
||||
self.nodes[level].insert(base);
|
||||
base = base.add(level_size);
|
||||
size -= level_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,133 +170,137 @@ impl<A: Arch> SlabAllocator<A> {
|
||||
}
|
||||
|
||||
unsafe fn new_tables<A: Arch>(areas: &'static [MemoryArea]) {
|
||||
// First, calculate how much memory we have
|
||||
let mut size = 0;
|
||||
for area in areas.iter() {
|
||||
size += area.size;
|
||||
}
|
||||
|
||||
println!("Memory: {}", format_size(size));
|
||||
|
||||
// Create a basic allocator for the first pages
|
||||
let mut bump_allocator = BumpAllocator::<A>::new(areas, 0);
|
||||
|
||||
{
|
||||
// Map all physical areas at PHYS_OFFSET
|
||||
let mut mapper = PageMapper::<A, _>::create(TableKind::Kernel, &mut bump_allocator)
|
||||
.expect("failed to create Mapper");
|
||||
unsafe {
|
||||
// First, calculate how much memory we have
|
||||
let mut size = 0;
|
||||
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");
|
||||
flush.ignore(); // Not the active table
|
||||
}
|
||||
size += area.size;
|
||||
}
|
||||
|
||||
// Use the new table
|
||||
mapper.make_current();
|
||||
}
|
||||
println!("Memory: {}", format_size(size));
|
||||
|
||||
// Create the physical memory map
|
||||
let offset = bump_allocator.offset();
|
||||
println!("Permanently used: {}", format_size(offset));
|
||||
// Create a basic allocator for the first pages
|
||||
let mut bump_allocator = BumpAllocator::<A>::new(areas, 0);
|
||||
|
||||
let mut allocator = BuddyAllocator::<A>::new(bump_allocator).unwrap();
|
||||
|
||||
for i in 0..16 {
|
||||
{
|
||||
let phys_opt = allocator.allocate_one();
|
||||
println!("page {}: {:X?}", i, phys_opt);
|
||||
if i % 3 == 0 {
|
||||
if let Some(phys) = phys_opt {
|
||||
println!("free {}: {:X?}", i, phys_opt);
|
||||
allocator.free_one(phys);
|
||||
// Map all physical areas at PHYS_OFFSET
|
||||
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");
|
||||
flush.ignore(); // Not the active table
|
||||
}
|
||||
}
|
||||
|
||||
// Use the new table
|
||||
mapper.make_current();
|
||||
}
|
||||
|
||||
// Create the physical memory map
|
||||
let offset = bump_allocator.offset();
|
||||
println!("Permanently used: {}", format_size(offset));
|
||||
|
||||
let mut allocator = BuddyAllocator::<A>::new(bump_allocator).unwrap();
|
||||
|
||||
for i in 0..16 {
|
||||
{
|
||||
let phys_opt = allocator.allocate_one();
|
||||
println!("page {}: {:X?}", i, phys_opt);
|
||||
if i % 3 == 0 {
|
||||
if let Some(phys) = phys_opt {
|
||||
println!("free {}: {:X?}", i, phys_opt);
|
||||
allocator.free_one(phys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let phys_opt = allocator.allocate(FrameCount::new(16));
|
||||
println!("page*16 {}: {:X?}", i, phys_opt);
|
||||
if i % 2 == 0 {
|
||||
if let Some(phys) = phys_opt {
|
||||
println!("free*16 {}: {:X?}", i, phys_opt);
|
||||
allocator.free(phys, FrameCount::new(16));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let phys_opt = allocator.allocate(FrameCount::new(16));
|
||||
println!("page*16 {}: {:X?}", i, phys_opt);
|
||||
if i % 2 == 0 {
|
||||
if let Some(phys) = phys_opt {
|
||||
println!("free*16 {}: {:X?}", i, phys_opt);
|
||||
allocator.free(phys, FrameCount::new(16));
|
||||
}
|
||||
}
|
||||
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");
|
||||
flush_all.consume(flush);
|
||||
}
|
||||
}
|
||||
flush_all.flush();
|
||||
|
||||
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");
|
||||
flush_all.consume(flush);
|
||||
}
|
||||
flush_all.flush();
|
||||
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, false).expect("failed to unmap page");
|
||||
flush_all.consume(flush);
|
||||
}
|
||||
flush_all.flush();
|
||||
|
||||
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, false).expect("failed to unmap page");
|
||||
flush_all.consume(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)
|
||||
);
|
||||
}
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
unsafe fn inner<A: Arch>() {
|
||||
let areas = A::init();
|
||||
unsafe {
|
||||
let areas = A::init();
|
||||
|
||||
// Debug table
|
||||
//dump_tables(PageTable::<A>::top());
|
||||
// Debug table
|
||||
//dump_tables(PageTable::<A>::top());
|
||||
|
||||
new_tables::<A>(areas);
|
||||
new_tables::<A>(areas);
|
||||
|
||||
//dump_tables(PageTable::<A>::top());
|
||||
//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);
|
||||
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)
|
||||
);
|
||||
// Test read
|
||||
println!(
|
||||
"0x{:X} (0x{:X}) = 0x{:X}",
|
||||
virt.data(),
|
||||
phys.data(),
|
||||
A::read::<u8>(virt)
|
||||
);
|
||||
|
||||
// Test write
|
||||
A::write::<u8>(virt, 0x5A);
|
||||
// Test write
|
||||
A::write::<u8>(virt, 0x5A);
|
||||
|
||||
// Test read
|
||||
println!(
|
||||
"0x{:X} (0x{:X}) = 0x{:X}",
|
||||
virt.data(),
|
||||
phys.data(),
|
||||
A::read::<u8>(virt)
|
||||
);
|
||||
// Test read
|
||||
println!(
|
||||
"0x{:X} (0x{:X}) = 0x{:X}",
|
||||
virt.data(),
|
||||
phys.data(),
|
||||
A::read::<u8>(virt)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+105
-82
@@ -23,13 +23,17 @@ impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
|
||||
}
|
||||
|
||||
pub unsafe fn create(table_kind: TableKind, mut allocator: F) -> Option<Self> {
|
||||
let table_addr = allocator.allocate_one()?;
|
||||
Some(Self::new(table_kind, table_addr, allocator))
|
||||
unsafe {
|
||||
let table_addr = allocator.allocate_one()?;
|
||||
Some(Self::new(table_kind, table_addr, allocator))
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn current(table_kind: TableKind, allocator: F) -> Self {
|
||||
let table_addr = A::table(table_kind);
|
||||
Self::new(table_kind, table_addr, allocator)
|
||||
unsafe {
|
||||
let table_addr = A::table(table_kind);
|
||||
Self::new(table_kind, table_addr, allocator)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_current(&self) -> bool {
|
||||
@@ -37,7 +41,9 @@ impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
|
||||
}
|
||||
|
||||
pub unsafe fn make_current(&self) {
|
||||
A::set_table(self.table_kind, self.table_addr);
|
||||
unsafe {
|
||||
A::set_table(self.table_kind, self.table_addr);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn table(&self) -> PageTable<A> {
|
||||
@@ -59,33 +65,37 @@ impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
|
||||
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()?;
|
||||
let old_flags = old_entry.flags();
|
||||
let (new_phys, new_flags) = f(old_phys, old_flags);
|
||||
// TODO: Higher-level PageEntry::new interface?
|
||||
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()
|
||||
unsafe {
|
||||
self.visit(virt, |p1, i| {
|
||||
let old_entry = p1.entry(i)?;
|
||||
let old_phys = old_entry.address().ok()?;
|
||||
let old_flags = old_entry.flags();
|
||||
let (new_phys, new_flags) = f(old_phys, old_flags);
|
||||
// TODO: Higher-level PageEntry::new interface?
|
||||
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()
|
||||
}
|
||||
}
|
||||
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))
|
||||
})
|
||||
unsafe {
|
||||
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>> {
|
||||
self.remap_with(virt, |_| flags).map(|(_, _, flush)| flush)
|
||||
unsafe { self.remap_with(virt, |_| flags).map(|(_, _, flush)| flush) }
|
||||
}
|
||||
|
||||
pub unsafe fn map(
|
||||
@@ -93,8 +103,10 @@ impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
|
||||
virt: VirtualAddress,
|
||||
flags: PageFlags<A>,
|
||||
) -> Option<PageFlush<A>> {
|
||||
let phys = self.allocator.allocate_one()?;
|
||||
self.map_phys(virt, phys, flags)
|
||||
unsafe {
|
||||
let phys = self.allocator.allocate_one()?;
|
||||
self.map_phys(virt, phys, flags)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn map_phys(
|
||||
@@ -103,34 +115,36 @@ impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
|
||||
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());
|
||||
let mut table = self.table();
|
||||
loop {
|
||||
let i = table.index_of(virt)?;
|
||||
if table.level() == 0 {
|
||||
//TODO: check for overwriting entry
|
||||
table.set_entry(i, entry);
|
||||
return Some(PageFlush::new(virt));
|
||||
} else {
|
||||
let next_opt = table.next(i);
|
||||
let next = match next_opt {
|
||||
Some(some) => some,
|
||||
None => {
|
||||
let next_phys = self.allocator.allocate_one()?;
|
||||
//TODO: correct flags?
|
||||
let flags = A::ENTRY_FLAG_DEFAULT_TABLE
|
||||
| if virt.kind() == TableKind::User {
|
||||
A::ENTRY_FLAG_TABLE_USER
|
||||
} else {
|
||||
0
|
||||
};
|
||||
table.set_entry(i, PageEntry::new(next_phys.data(), flags));
|
||||
table.next(i)?
|
||||
}
|
||||
};
|
||||
table = next;
|
||||
unsafe {
|
||||
//TODO: verify virt and phys are aligned
|
||||
//TODO: verify flags have correct bits
|
||||
let entry = PageEntry::new(phys.data(), flags.data());
|
||||
let mut table = self.table();
|
||||
loop {
|
||||
let i = table.index_of(virt)?;
|
||||
if table.level() == 0 {
|
||||
//TODO: check for overwriting entry
|
||||
table.set_entry(i, entry);
|
||||
return Some(PageFlush::new(virt));
|
||||
} else {
|
||||
let next_opt = table.next(i);
|
||||
let next = match next_opt {
|
||||
Some(some) => some,
|
||||
None => {
|
||||
let next_phys = self.allocator.allocate_one()?;
|
||||
//TODO: correct flags?
|
||||
let flags = A::ENTRY_FLAG_DEFAULT_TABLE
|
||||
| if virt.kind() == TableKind::User {
|
||||
A::ENTRY_FLAG_TABLE_USER
|
||||
} else {
|
||||
0
|
||||
};
|
||||
table.set_entry(i, PageEntry::new(next_phys.data(), flags));
|
||||
table.next(i)?
|
||||
}
|
||||
};
|
||||
table = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,8 +153,10 @@ impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
|
||||
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))
|
||||
unsafe {
|
||||
let virt = A::phys_to_virt(phys);
|
||||
self.map_phys(virt, phys, flags).map(|flush| (virt, flush))
|
||||
}
|
||||
}
|
||||
fn visit<T>(
|
||||
&self,
|
||||
@@ -169,9 +185,11 @@ impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
|
||||
virt: VirtualAddress,
|
||||
unmap_parents: bool,
|
||||
) -> Option<PageFlush<A>> {
|
||||
let (old, _, flush) = self.unmap_phys(virt, unmap_parents)?;
|
||||
self.allocator.free_one(old);
|
||||
Some(flush)
|
||||
unsafe {
|
||||
let (old, _, flush) = self.unmap_phys(virt, unmap_parents)?;
|
||||
self.allocator.free_one(old);
|
||||
Some(flush)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn unmap_phys(
|
||||
@@ -179,11 +197,13 @@ impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
|
||||
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)))
|
||||
unsafe {
|
||||
//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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe fn unmap_phys_inner<A: Arch>(
|
||||
@@ -193,35 +213,38 @@ unsafe fn unmap_phys_inner<A: Arch>(
|
||||
unmap_parents: bool,
|
||||
allocator: &mut impl FrameAllocator,
|
||||
) -> Option<(PhysicalAddress, PageFlags<A>)> {
|
||||
let i = table.index_of(virt)?;
|
||||
unsafe {
|
||||
let i = table.index_of(virt)?;
|
||||
|
||||
if table.level() == 0 {
|
||||
let entry_opt = table.entry(i);
|
||||
table.set_entry(i, PageEntry::new(0, 0));
|
||||
let entry = entry_opt?;
|
||||
if table.level() == 0 {
|
||||
let entry_opt = table.entry(i);
|
||||
table.set_entry(i, PageEntry::new(0, 0));
|
||||
let entry = entry_opt?;
|
||||
|
||||
Some((entry.address().ok()?, entry.flags()))
|
||||
} else {
|
||||
let mut subtable = table.next(i)?;
|
||||
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)?;
|
||||
let res =
|
||||
unmap_phys_inner(virt, &mut subtable, initial_level, unmap_parents, allocator)?;
|
||||
|
||||
//TODO: This is a bad idea for architectures where the kernel mappings are done in the process tables,
|
||||
// as these mappings may become out of sync
|
||||
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());
|
||||
//TODO: This is a bad idea for architectures where the kernel mappings are done in the process tables,
|
||||
// as these mappings may become out of sync
|
||||
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(subtable.phys());
|
||||
table.set_entry(i, PageEntry::new(0, 0));
|
||||
if !is_still_populated {
|
||||
allocator.free_one(subtable.phys());
|
||||
table.set_entry(i, PageEntry::new(0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(res)
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<A, F: core::fmt::Debug> core::fmt::Debug for PageMapper<A, F> {
|
||||
|
||||
+43
-31
@@ -21,11 +21,13 @@ impl<A: Arch> PageTable<A> {
|
||||
}
|
||||
|
||||
pub unsafe fn top(table_kind: TableKind) -> Self {
|
||||
Self::new(
|
||||
VirtualAddress::new(0),
|
||||
A::table(table_kind),
|
||||
A::PAGE_LEVELS - 1,
|
||||
)
|
||||
unsafe {
|
||||
Self::new(
|
||||
VirtualAddress::new(0),
|
||||
A::table(table_kind),
|
||||
A::PAGE_LEVELS - 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn base(&self) -> VirtualAddress {
|
||||
@@ -41,16 +43,18 @@ impl<A: Arch> PageTable<A> {
|
||||
}
|
||||
|
||||
pub unsafe fn virt(&self) -> VirtualAddress {
|
||||
A::phys_to_virt(self.phys)
|
||||
unsafe {
|
||||
A::phys_to_virt(self.phys)
|
||||
|
||||
// Recursive mapping
|
||||
// let mut addr = 0xFFFF_FFFF_FFFF_F000;
|
||||
// for level in (self.level + 1 .. A::PAGE_LEVELS).rev() {
|
||||
// let index = (self.base.0 >> (level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT)) & A::PAGE_ENTRY_MASK;
|
||||
// addr <<= A::PAGE_ENTRY_SHIFT;
|
||||
// addr |= index << A::PAGE_SHIFT;
|
||||
// }
|
||||
// VirtualAddress::new(addr)
|
||||
// Recursive mapping
|
||||
// let mut addr = 0xFFFF_FFFF_FFFF_F000;
|
||||
// for level in (self.level + 1 .. A::PAGE_LEVELS).rev() {
|
||||
// let index = (self.base.0 >> (level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT)) & A::PAGE_ENTRY_MASK;
|
||||
// addr <<= A::PAGE_ENTRY_SHIFT;
|
||||
// addr |= index << A::PAGE_SHIFT;
|
||||
// }
|
||||
// VirtualAddress::new(addr)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn entry_base(&self, i: usize) -> Option<VirtualAddress> {
|
||||
@@ -63,22 +67,28 @@ impl<A: Arch> PageTable<A> {
|
||||
}
|
||||
|
||||
pub unsafe fn entry_virt(&self, i: usize) -> Option<VirtualAddress> {
|
||||
if i < A::PAGE_ENTRIES {
|
||||
Some(self.virt().add(i * A::PAGE_ENTRY_SIZE))
|
||||
} else {
|
||||
None
|
||||
unsafe {
|
||||
if i < A::PAGE_ENTRIES {
|
||||
Some(self.virt().add(i * A::PAGE_ENTRY_SIZE))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn entry(&self, i: usize) -> Option<PageEntry<A>> {
|
||||
let addr = self.entry_virt(i)?;
|
||||
Some(PageEntry::from_data(A::read::<usize>(addr)))
|
||||
unsafe {
|
||||
let addr = self.entry_virt(i)?;
|
||||
Some(PageEntry::from_data(A::read::<usize>(addr)))
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn set_entry(&mut self, i: usize, entry: PageEntry<A>) -> Option<()> {
|
||||
let addr = self.entry_virt(i)?;
|
||||
A::write::<usize>(addr, entry.data());
|
||||
Some(())
|
||||
unsafe {
|
||||
let addr = self.entry_virt(i)?;
|
||||
A::write::<usize>(addr, entry.data());
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn index_of(&self, address: VirtualAddress) -> Option<usize> {
|
||||
@@ -98,14 +108,16 @@ impl<A: Arch> PageTable<A> {
|
||||
}
|
||||
|
||||
pub unsafe fn next(&self, i: usize) -> Option<Self> {
|
||||
if self.level == 0 {
|
||||
return None;
|
||||
}
|
||||
unsafe {
|
||||
if self.level == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(PageTable::new(
|
||||
self.entry_base(i)?,
|
||||
self.entry(i)?.address().ok()?,
|
||||
self.level - 1,
|
||||
))
|
||||
Some(PageTable::new(
|
||||
self.entry_base(i)?,
|
||||
self.entry(i)?.address().ok()?,
|
||||
self.level - 1,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user