Skip to content

Commit cd4d3c2

Browse files
authored
Add clean_up and clean_up_with_filter (#264)
1 parent 27ab505 commit cd4d3c2

File tree

10 files changed

+311
-9
lines changed

10 files changed

+311
-9
lines changed

src/addr.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use core::fmt;
44
use core::ops::{Add, AddAssign, Sub, SubAssign};
55

6+
use crate::structures::paging::page_table::PageTableLevel;
67
use crate::structures::paging::{PageOffset, PageTableIndex};
78
use bit_field::BitField;
89

@@ -198,6 +199,12 @@ impl VirtAddr {
198199
pub const fn p4_index(self) -> PageTableIndex {
199200
PageTableIndex::new_truncate((self.0 >> 12 >> 9 >> 9 >> 9) as u16)
200201
}
202+
203+
/// Returns the 9-bit level page table index.
204+
#[inline]
205+
pub const fn page_table_index(self, level: PageTableLevel) -> PageTableIndex {
206+
PageTableIndex::new_truncate((self.0 >> 12 >> ((level as u8 - 1) * 9)) as u16)
207+
}
201208
}
202209

203210
impl fmt::Debug for VirtAddr {

src/instructions/tlb.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ impl Pcid {
7272
/// Invalidate the given address in the TLB using the `invpcid` instruction.
7373
///
7474
/// ## Safety
75+
///
7576
/// This function is unsafe as it requires CPUID.(EAX=07H, ECX=0H):EBX.INVPCID to be 1.
7677
#[inline]
7778
pub unsafe fn flush_pcid(command: InvPicdCommand) {

src/registers/control.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ mod x86_64 {
301301
/// Write a new P4 table address into the CR3 register.
302302
///
303303
/// ## Safety
304+
///
304305
/// Changing the level 4 page table is unsafe, because it's possible to violate memory safety by
305306
/// changing the page mapping.
306307
#[inline]
@@ -311,6 +312,7 @@ mod x86_64 {
311312
/// Write a new P4 table address into the CR3 register.
312313
///
313314
/// ## Safety
315+
///
314316
/// Changing the level 4 page table is unsafe, because it's possible to violate memory safety by
315317
/// changing the page mapping.
316318
/// [`Cr4Flags::PCID`] must be set before calling this method.
@@ -322,6 +324,7 @@ mod x86_64 {
322324
/// Write a new P4 table address into the CR3 register.
323325
///
324326
/// ## Safety
327+
///
325328
/// Changing the level 4 page table is unsafe, because it's possible to violate memory safety by
326329
/// changing the page mapping.
327330
#[inline]
@@ -400,6 +403,7 @@ mod x86_64 {
400403
/// Updates CR4 flags.
401404
///
402405
/// Preserves the value of reserved fields.
406+
///
403407
/// ## Safety
404408
///
405409
/// This function is unsafe because it's possible to violate memory

src/structures/idt.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,7 @@ impl EntryOptions {
789789
/// This function panics if the index is not in the range 0..7.
790790
///
791791
/// ## Safety
792+
///
792793
/// This function is unsafe because the caller must ensure that the passed stack index is
793794
/// valid and not used by other interrupts. Otherwise, memory safety violations are possible.
794795
#[inline]

src/structures/paging/mapper/mapped_page_table.rs

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use crate::structures::paging::{
22
frame::PhysFrame,
3-
frame_alloc::FrameAllocator,
3+
frame_alloc::{FrameAllocator, FrameDeallocator},
44
mapper::*,
5-
page::{AddressNotAligned, Page, Size1GiB, Size2MiB, Size4KiB},
6-
page_table::{FrameError, PageTable, PageTableEntry, PageTableFlags},
5+
page::{AddressNotAligned, Page, PageRangeInclusive, Size1GiB, Size2MiB, Size4KiB},
6+
page_table::{FrameError, PageTable, PageTableEntry, PageTableFlags, PageTableLevel},
77
};
88

99
/// A Mapper implementation that relies on a PhysAddr to VirtAddr conversion function.
@@ -584,6 +584,90 @@ impl<'a, P: PageTableFrameMapping> Translate for MappedPageTable<'a, P> {
584584
}
585585
}
586586

587+
impl<'a, P: PageTableFrameMapping> CleanUp for MappedPageTable<'a, P> {
588+
#[inline]
589+
unsafe fn clean_up<D>(&mut self, frame_deallocator: &mut D)
590+
where
591+
D: FrameDeallocator<Size4KiB>,
592+
{
593+
self.clean_up_addr_range(
594+
PageRangeInclusive {
595+
start: Page::from_start_address(VirtAddr::new(0)).unwrap(),
596+
end: Page::from_start_address(VirtAddr::new(0xffff_ffff_ffff_f000)).unwrap(),
597+
},
598+
frame_deallocator,
599+
)
600+
}
601+
602+
unsafe fn clean_up_addr_range<D>(
603+
&mut self,
604+
range: PageRangeInclusive,
605+
frame_deallocator: &mut D,
606+
) where
607+
D: FrameDeallocator<Size4KiB>,
608+
{
609+
unsafe fn clean_up<P: PageTableFrameMapping>(
610+
page_table: &mut PageTable,
611+
page_table_walker: &PageTableWalker<P>,
612+
level: PageTableLevel,
613+
range: PageRangeInclusive,
614+
frame_deallocator: &mut impl FrameDeallocator<Size4KiB>,
615+
) -> bool {
616+
if range.is_empty() {
617+
return false;
618+
}
619+
620+
let table_addr = range
621+
.start
622+
.start_address()
623+
.align_down(level.table_address_space_alignment());
624+
625+
let start = range.start.page_table_index(level);
626+
let end = range.end.page_table_index(level);
627+
628+
if let Some(next_level) = level.next_lower_level() {
629+
let offset_per_entry = level.entry_address_space_alignment();
630+
for (i, entry) in page_table
631+
.iter_mut()
632+
.enumerate()
633+
.take(usize::from(end) + 1)
634+
.skip(usize::from(start))
635+
{
636+
if let Ok(page_table) = page_table_walker.next_table_mut(entry) {
637+
let start = table_addr + (offset_per_entry * (i as u64));
638+
let end = start + (offset_per_entry - 1);
639+
let start = Page::<Size4KiB>::containing_address(start);
640+
let start = start.max(range.start);
641+
let end = Page::<Size4KiB>::containing_address(end);
642+
let end = end.min(range.end);
643+
if clean_up(
644+
page_table,
645+
page_table_walker,
646+
next_level,
647+
Page::range_inclusive(start, end),
648+
frame_deallocator,
649+
) {
650+
let frame = entry.frame().unwrap();
651+
entry.set_unused();
652+
frame_deallocator.deallocate_frame(frame);
653+
}
654+
}
655+
}
656+
}
657+
658+
page_table.iter().all(PageTableEntry::is_unused)
659+
}
660+
661+
clean_up(
662+
self.level_4_table,
663+
&self.page_table_walker,
664+
PageTableLevel::Four,
665+
range,
666+
frame_deallocator,
667+
);
668+
}
669+
}
670+
587671
#[derive(Debug)]
588672
struct PageTableWalker<P: PageTableFrameMapping> {
589673
page_table_frame_mapping: P,

src/structures/paging/mapper/mod.rs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ pub use self::offset_page_table::OffsetPageTable;
77
pub use self::recursive_page_table::{InvalidPageTable, RecursivePageTable};
88

99
use crate::structures::paging::{
10-
frame_alloc::FrameAllocator, page_table::PageTableFlags, Page, PageSize, PhysFrame, Size1GiB,
11-
Size2MiB, Size4KiB,
10+
frame_alloc::{FrameAllocator, FrameDeallocator},
11+
page::PageRangeInclusive,
12+
page_table::PageTableFlags,
13+
Page, PageSize, PhysFrame, Size1GiB, Size2MiB, Size4KiB,
1214
};
1315
use crate::{PhysAddr, VirtAddr};
1416

@@ -480,3 +482,45 @@ pub enum TranslateError {
480482
}
481483

482484
static _ASSERT_OBJECT_SAFE: Option<&(dyn Translate + Sync)> = None;
485+
486+
/// Provides methods for cleaning up unused entries.
487+
pub trait CleanUp {
488+
/// Remove all empty P1-P3 tables
489+
///
490+
/// ## Safety
491+
///
492+
/// The caller has to guarantee that it's safe to free page table frames:
493+
/// All page table frames must only be used once and only in this page table
494+
/// (e.g. no reference counted page tables or reusing the same page tables for different virtual addresses ranges in the same page table).
495+
unsafe fn clean_up<D>(&mut self, frame_deallocator: &mut D)
496+
where
497+
D: FrameDeallocator<Size4KiB>;
498+
499+
/// Remove all empty P1-P3 tables in a certain range
500+
/// ```
501+
/// # use core::ops::RangeInclusive;
502+
/// # use x86_64::{VirtAddr, structures::paging::{
503+
/// # FrameDeallocator, Size4KiB, MappedPageTable, mapper::{RecursivePageTable, CleanUp}, page::{Page, PageRangeInclusive},
504+
/// # }};
505+
/// # unsafe fn test(page_table: &mut RecursivePageTable, frame_deallocator: &mut impl FrameDeallocator<Size4KiB>) {
506+
/// // clean up all page tables in the lower half of the address space
507+
/// let lower_half = Page::range_inclusive(
508+
/// Page::containing_address(VirtAddr::new(0)),
509+
/// Page::containing_address(VirtAddr::new(0x0000_7fff_ffff_ffff)),
510+
/// );
511+
/// page_table.clean_up_addr_range(lower_half, frame_deallocator);
512+
/// # }
513+
/// ```
514+
///
515+
/// ## Safety
516+
///
517+
/// The caller has to guarantee that it's safe to free page table frames:
518+
/// All page table frames must only be used once and only in this page table
519+
/// (e.g. no reference counted page tables or reusing the same page tables for different virtual addresses ranges in the same page table).
520+
unsafe fn clean_up_addr_range<D>(
521+
&mut self,
522+
range: PageRangeInclusive,
523+
frame_deallocator: &mut D,
524+
) where
525+
D: FrameDeallocator<Size4KiB>;
526+
}

src/structures/paging/mapper/offset_page_table.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#![cfg(target_pointer_width = "64")]
22

33
use crate::structures::paging::{
4-
frame::PhysFrame, mapper::*, page_table::PageTable, Page, PageTableFlags,
4+
frame::PhysFrame, mapper::*, page::PageRangeInclusive, page_table::PageTable, FrameDeallocator,
5+
Page, PageTableFlags,
56
};
67

78
/// A Mapper implementation that requires that the complete physically memory is mapped at some
@@ -264,3 +265,24 @@ impl<'a> Translate for OffsetPageTable<'a> {
264265
self.inner.translate(addr)
265266
}
266267
}
268+
269+
impl<'a> CleanUp for OffsetPageTable<'a> {
270+
#[inline]
271+
unsafe fn clean_up<D>(&mut self, frame_deallocator: &mut D)
272+
where
273+
D: FrameDeallocator<Size4KiB>,
274+
{
275+
self.inner.clean_up(frame_deallocator)
276+
}
277+
278+
#[inline]
279+
unsafe fn clean_up_addr_range<D>(
280+
&mut self,
281+
range: PageRangeInclusive,
282+
frame_deallocator: &mut D,
283+
) where
284+
D: FrameDeallocator<Size4KiB>,
285+
{
286+
self.inner.clean_up_addr_range(range, frame_deallocator)
287+
}
288+
}

src/structures/paging/mapper/recursive_page_table.rs

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ use core::fmt;
44

55
use super::*;
66
use crate::registers::control::Cr3;
7-
use crate::structures::paging::PageTableIndex;
7+
use crate::structures::paging::page_table::PageTableLevel;
88
use crate::structures::paging::{
99
frame_alloc::FrameAllocator,
10-
page::{AddressNotAligned, NotGiantPageSize},
10+
page::{AddressNotAligned, NotGiantPageSize, PageRangeInclusive},
1111
page_table::{FrameError, PageTable, PageTableEntry, PageTableFlags},
12-
Page, PageSize, PhysFrame, Size1GiB, Size2MiB, Size4KiB,
12+
FrameDeallocator, Page, PageSize, PageTableIndex, PhysFrame, Size1GiB, Size2MiB, Size4KiB,
1313
};
1414
use crate::VirtAddr;
1515

@@ -829,6 +829,97 @@ impl<'a> Translate for RecursivePageTable<'a> {
829829
}
830830
}
831831

832+
impl<'a> CleanUp for RecursivePageTable<'a> {
833+
#[inline]
834+
unsafe fn clean_up<D>(&mut self, frame_deallocator: &mut D)
835+
where
836+
D: FrameDeallocator<Size4KiB>,
837+
{
838+
self.clean_up_addr_range(
839+
PageRangeInclusive {
840+
start: Page::from_start_address(VirtAddr::new(0)).unwrap(),
841+
end: Page::from_start_address(VirtAddr::new(0xffff_ffff_ffff_f000)).unwrap(),
842+
},
843+
frame_deallocator,
844+
)
845+
}
846+
847+
unsafe fn clean_up_addr_range<D>(
848+
&mut self,
849+
range: PageRangeInclusive,
850+
frame_deallocator: &mut D,
851+
) where
852+
D: FrameDeallocator<Size4KiB>,
853+
{
854+
fn clean_up(
855+
recursive_index: PageTableIndex,
856+
page_table: &mut PageTable,
857+
level: PageTableLevel,
858+
range: PageRangeInclusive,
859+
frame_deallocator: &mut impl FrameDeallocator<Size4KiB>,
860+
) -> bool {
861+
if range.is_empty() {
862+
return false;
863+
}
864+
865+
let table_addr = range
866+
.start
867+
.start_address()
868+
.align_down(level.table_address_space_alignment());
869+
870+
let start = range.start.page_table_index(level);
871+
let end = range.end.page_table_index(level);
872+
873+
if let Some(next_level) = level.next_lower_level() {
874+
let offset_per_entry = level.entry_address_space_alignment();
875+
for (i, entry) in page_table
876+
.iter_mut()
877+
.enumerate()
878+
.take(usize::from(end) + 1)
879+
.skip(usize::from(start))
880+
.filter(|(i, _)| {
881+
!(level == PageTableLevel::Four && *i == recursive_index.into())
882+
})
883+
{
884+
if let Ok(frame) = entry.frame() {
885+
let start = table_addr + (offset_per_entry * (i as u64));
886+
let end = start + (offset_per_entry - 1);
887+
let start = Page::<Size4KiB>::containing_address(start);
888+
let start = start.max(range.start);
889+
let end = Page::<Size4KiB>::containing_address(end);
890+
let end = end.min(range.end);
891+
let page_table =
892+
[p1_ptr, p2_ptr, p3_ptr][level as usize - 2](start, recursive_index);
893+
let page_table = unsafe { &mut *page_table };
894+
if clean_up(
895+
recursive_index,
896+
page_table,
897+
next_level,
898+
Page::range_inclusive(start, end),
899+
frame_deallocator,
900+
) {
901+
entry.set_unused();
902+
unsafe {
903+
frame_deallocator.deallocate_frame(frame);
904+
}
905+
}
906+
}
907+
}
908+
}
909+
910+
page_table.iter().all(PageTableEntry::is_unused)
911+
}
912+
913+
clean_up(
914+
self.recursive_index,
915+
self.level_4_table(),
916+
PageTableLevel::Four,
917+
range,
918+
frame_deallocator,
919+
);
920+
}
921+
}
922+
832923
/// The given page table was not suitable to create a `RecursivePageTable`.
833924
#[derive(Debug)]
834925
pub enum InvalidPageTable {

0 commit comments

Comments
 (0)