Skip to main content

kernel/arch/x86_64/mm/vmm/paging/
unmap.rs

1// SPDX-License-Identifier: GPL-3.0-only
2//! VMM Paging (x86_64): `unmap_page`
3//!
4//! Authors: MarioS271
5
6use super::helpers::{advance_current_pagetable, is_huge_page, is_present};
7use crate::lib::addr::VirtAddr;
8use crate::mm::vmm::{VmmError, VmmResult};
9use x86_64::instructions::tlb;
10use x86_64::structures::paging::page_table::PageTableEntry;
11use x86_64::structures::paging::PageTable;
12
13pub unsafe fn unmap_page(
14    page_ptr: VirtAddr,
15    virt: VirtAddr
16) -> VmmResult {
17    #[cfg(feature = "debug-checks")]
18    {
19        use crate::mm::pmm::FRAME_SIZE;
20        use super::super::page_type::PageType;
21        use super::helpers::misaligned_address_panic;
22
23        if virt.as_u64() % FRAME_SIZE != 0 {
24            misaligned_address_panic(PageType::Normal);
25        }
26    }
27
28    let mut current_pagetable = page_ptr.as_mut_ptr::<PageTable>();
29
30    // PML4
31    // Safety:
32    // - current_pagetable is a valid, non-zero, page-aligned pointer allocated by the PMM and
33    //   initialized by setup_kernel_paging
34    // - the VMM is wrapped in an IrqMutex which guarantees serialized VMM method execution
35    let entry = unsafe { &mut current_pagetable.as_mut().unwrap()[virt.p4_index()] };
36    if !is_present(entry) { return Err(VmmError::InvalidUnmap) };
37    current_pagetable = advance_current_pagetable(entry);
38
39    // PDPT
40    // Safety:
41    // - current_pagetable is a valid, non-zero, page-aligned pointer computed from the previous
42    //   current_pagetable which was also valid
43    // - the VMM is wrapped in an IrqMutex which guarantees serialized VMM method execution
44    let entry = unsafe { &mut current_pagetable.as_mut().unwrap()[virt.p3_index()] };
45    if !is_present(entry) { return Err(VmmError::InvalidUnmap) };
46    if is_huge_page(entry) {
47        clear_and_flush(entry, virt);
48        debug_log(virt);
49        return Ok(());
50    }
51    current_pagetable = advance_current_pagetable(entry);
52
53    // PD
54    // Safety:
55    // - current_pagetable is a valid, non-zero, page-aligned pointer computed from the previous
56    //   current_pagetable which was also valid
57    // - the VMM is wrapped in an IrqMutex which guarantees serialized VMM method execution
58    let entry = unsafe { &mut current_pagetable.as_mut().unwrap()[virt.p2_index()] };
59    if !is_present(entry) { return Err(VmmError::InvalidUnmap) };
60    if is_huge_page(entry) {
61        clear_and_flush(entry, virt);
62        debug_log(virt);
63        return Ok(());
64    }
65    current_pagetable = advance_current_pagetable(entry);
66
67    // PT
68    // Safety:
69    // - current_pagetable is a valid, non-zero, page-aligned pointer computed from the previous
70    //   current_pagetable which was also valid
71    // - the VMM is wrapped in an IrqMutex which guarantees serialized VMM method execution
72    let entry = unsafe { &mut current_pagetable.as_mut().unwrap()[virt.p1_index()] };
73    if !is_present(entry) { return Err(VmmError::InvalidUnmap) };
74    clear_and_flush(entry, virt);
75    debug_log(virt);
76
77    Ok(())
78}
79
80/// Clears the given [`PageTableEntry`] and flushes the given [`VirtAddr`]
81fn clear_and_flush(entry: &mut PageTableEntry, virt: VirtAddr) {
82    entry.set_unused();
83    tlb::flush(virt.as_x86_64());
84}
85
86/// Debug log the unmapping of a page
87#[allow(unused_variables)]
88fn debug_log(virt: VirtAddr) {
89    #[cfg(feature = "vmm-debug-logging")]
90    crate::kdebug!("[VMM] unmap page at virt {virt:#x}");
91}