Skip to main content

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

1// SPDX-License-Identifier: GPL-3.0-only
2//! VMM Paging (x86_64): `remap_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, PageTableFlags, PhysFrame};
12
13pub unsafe fn remap_page(
14    page_ptr: VirtAddr,
15    virt: VirtAddr,
16    new_flags: PageTableFlags
17) -> VmmResult {
18    #[cfg(feature = "debug-checks")]
19    {
20        use crate::mm::pmm::FRAME_SIZE;
21        use super::super::page_type::PageType;
22        use super::helpers::misaligned_address_panic;
23
24        if virt.as_u64() % FRAME_SIZE != 0 {
25            misaligned_address_panic(PageType::Normal);
26        }
27    }
28
29    let mut current_pagetable: *mut PageTable = page_ptr.as_mut_ptr::<PageTable>();
30
31    // PML4
32    // Safety:
33    // - current_pagetable is a valid, non-zero, page-aligned pointer allocated by the PMM and
34    //   initialized by setup_kernel_paging
35    // - the VMM is wrapped in an IrqMutex which guarantees serialized VMM method execution
36    let entry = unsafe { &mut current_pagetable.as_mut().unwrap()[virt.p4_index()] };
37    if !is_present(entry) { return Err(VmmError::InvalidRemap) };
38    current_pagetable = advance_current_pagetable(entry);
39
40    // PDPT
41    // Safety:
42    // - current_pagetable is a valid, non-zero, page-aligned pointer computed from the previous
43    //   current_pagetable which was also valid
44    // - the VMM is wrapped in an IrqMutex which guarantees serialized VMM method execution
45    let entry = unsafe { &mut current_pagetable.as_mut().unwrap()[virt.p3_index()] };
46    if !is_present(entry) { return Err(VmmError::InvalidRemap) };
47    if is_huge_page(entry) {
48        remap_huge_page(entry, virt, new_flags);
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::InvalidRemap) };
60    if is_huge_page(entry) {
61        remap_huge_page(entry, virt, new_flags);
62        return Ok(())
63    }
64    current_pagetable = advance_current_pagetable(entry);
65
66    // PT
67    // Safety:
68    // - current_pagetable is a valid, non-zero, page-aligned pointer computed from the previous
69    //   current_pagetable which was also valid
70    // - the VMM is wrapped in an IrqMutex which guarantees serialized VMM method execution
71    let entry = unsafe { &mut current_pagetable.as_mut().unwrap()[virt.p1_index()] };
72    if !is_present(entry) { return Err(VmmError::InvalidRemap) };
73    entry.set_frame(
74        PhysFrame::containing_address(entry.frame().unwrap().start_address()),
75        new_flags | PageTableFlags::PRESENT
76    );
77    tlb::flush(virt.as_x86_64());
78    debug_log(virt, new_flags);
79
80    Ok(())
81}
82
83/// Wrapper for checking and remapping a huge page to avoid code duplication
84fn remap_huge_page(entry: &mut PageTableEntry, virt: VirtAddr, new_flags: PageTableFlags) {
85    entry.set_addr(entry.addr(), new_flags | PageTableFlags::PRESENT | PageTableFlags::HUGE_PAGE);
86    tlb::flush(virt.as_x86_64());
87    debug_log(virt, new_flags);
88}
89
90/// Debug log the remapping of a page
91#[allow(unused_variables)]
92fn debug_log(virt: VirtAddr, flags: PageTableFlags) {
93    #[cfg(feature = "vmm-debug-logging")]
94    crate::kdebug!("[VMM] remap page at virt {virt:#x} to flags {flags:?}");
95}