Skip to main content

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

1// SPDX-License-Identifier: GPL-3.0-only
2//! VMM Paging (x86_64): `map_page`
3//!
4//! Authors: MarioS271
5
6use super::super::page_type::PageType;
7use crate::lib::addr::{PhysAddr, VirtAddr};
8use crate::mm::pmm::Pmm;
9use crate::mm::vmm::{VmmError, VmmResult};
10use crate::state::kstate::KSTATE;
11use x86_64::structures::paging::{PageTable, PageTableFlags, PageTableIndex, PhysFrame};
12
13pub unsafe fn map_page(
14    pmm: &mut Pmm,
15    page_ptr: VirtAddr,
16    virt: VirtAddr,
17    phys: PhysAddr,
18    page_type: PageType,
19    flags: PageTableFlags,
20) -> VmmResult {
21    #[cfg(feature = "debug-checks")]
22    {
23        use super::helpers::misaligned_address_panic;
24        let align = &(page_type as u64);
25        if phys.as_u64() % align != 0 || virt.as_u64() % align != 0 {
26            misaligned_address_panic(page_type);
27        }
28    }
29
30    let hhdm_offset = &KSTATE.mm.hhdm_offset();
31
32    let lowest_iter_page = match page_type {
33        PageType::Normal => 2,
34        PageType::HugePage2MiB => 3,
35        PageType::HugePage1GiB => 4
36    };
37
38    let mut current_pagetable: *mut PageTable = page_ptr.as_mut_ptr::<PageTable>();
39    let intermediate_flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE | (flags & PageTableFlags::USER_ACCESSIBLE);
40
41    for level in (lowest_iter_page..=4).rev() {
42        let index: PageTableIndex = match level {
43            4 => virt.p4_index(),
44            3 => virt.p3_index(),
45            2 => virt.p2_index(),
46            _ => unreachable!()
47        };
48
49        let entry = unsafe { &mut current_pagetable.as_mut().unwrap()[index] };
50
51        if !entry.flags().contains(PageTableFlags::PRESENT) {
52            let frame = pmm.alloc_frame_zeroed().ok_or(VmmError::OutOfMemory)?;
53            entry.set_frame(
54                PhysFrame::containing_address(frame.as_x86_64()),
55                intermediate_flags,
56            );
57        }
58
59        current_pagetable = (entry.frame().unwrap().start_address().as_u64() + hhdm_offset) as *mut PageTable;
60    }
61
62    // Safe: current_pagetable is a valid
63    let entry = unsafe {
64        &mut current_pagetable.as_mut().unwrap()[match page_type {
65            PageType::Normal => virt.p1_index(),
66            PageType::HugePage2MiB => virt.p2_index(),
67            PageType::HugePage1GiB => virt.p3_index(),
68        }]
69    };
70
71    if page_type == PageType::Normal {
72        entry.set_frame(PhysFrame::containing_address(phys.as_x86_64()), flags | PageTableFlags::PRESENT);
73    } else {
74        entry.set_addr(phys.as_x86_64(), flags | PageTableFlags::PRESENT | PageTableFlags::HUGE_PAGE);
75    }
76
77    #[cfg(feature = "vmm-debug-logging")]
78    crate::kdebug!("[VMM] map new page at virt {virt:#x}, phys {phys:#x}, type {page_type}, flags {flags:?}");
79
80    Ok(())
81}