use crate::core::error::Result;
const FLAG_PRESENT: u64 = 0x01;
const FLAG_PAGE_SIZE: u64 = 0x80;
const MASK_PAGE_TABLE_ENTRY_TARGET: u64 = 0x000FFFFFFFFFF000;
const MASK_1GB_PAGE_PHYSICAL_ADDRESS: u64 = 0x000FFFFFC0000000;
const MASK_2MB_PAGE_PHYSICAL_ADDRESS: u64 = 0x000FFFFFFFE00000;
const MASK_4KB_PAGE_PHYSICAL_ADDRESS: u64 = 0x000FFFFFFFFFF000;
const SIZE_1GB_PAGE: u64 = 1024 * 1024 * 1024;
const SIZE_2MB_PAGE: u64 = 2 * 1024 * 1024;
const SIZE_4KB_PAGE: u64 = 4 * 1024;
pub const SHIFT_PML4_INDEX: u64 = 39;
pub const SHIFT_PDPT_INDEX: u64 = 30;
pub const SHIFT_PD_INDEX: u64 = 21;
pub const SHIFT_PT_INDEX: u64 = 12;
const MASK_PAGE_TABLE_INDEX: u64 = 0x1FF;
const MASK_PDPT_PAGE_OFFSET: u64 = 0x000000003FFFFFFF;
const MASK_PD_PAGE_OFFSET: u64 = 0x00000000001FFFFF;
const MASK_PT_PAGE_OFFSET: u64 = 0x0000000000000FFF;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PageTableLevel {
Pml4,
Pdpt,
Pd,
Pt,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PageSection {
pub offset: u64,
pub size: u64,
}
#[derive(Clone, Copy, Debug)]
pub struct VirtualAddressComponent {
pub page_table_index: usize,
pub page_section: Option<PageSection>,
}
#[derive(Clone, Copy, Debug)]
pub struct DecomposedVirtualAddress {
pub pml4: VirtualAddressComponent,
pub pdpt: VirtualAddressComponent,
pub pd: VirtualAddressComponent,
pub pt: VirtualAddressComponent,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Page {
pub physical_address: u64,
pub present: bool,
pub size: u64,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PageDirectory {
pub physical_address: u64,
pub present: bool,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PageTableEntry {
Page(Page),
PageDirectory(PageDirectory),
}
impl PageTableEntry {
pub fn new(page_table_level: PageTableLevel, page_table_entry: u64) -> Result<Self> {
let present_flag = (page_table_entry & FLAG_PRESENT) != 0;
let page_size_flag = (page_table_entry & FLAG_PAGE_SIZE) != 0;
let obj = match (page_table_level, page_size_flag) {
(PageTableLevel::Pml4, _) => Self::PageDirectory(PageDirectory {
physical_address: page_table_entry & MASK_PAGE_TABLE_ENTRY_TARGET,
present: present_flag,
}),
(PageTableLevel::Pdpt, true) => Self::Page(Page {
physical_address: page_table_entry & MASK_1GB_PAGE_PHYSICAL_ADDRESS,
size: SIZE_1GB_PAGE,
present: present_flag,
}),
(PageTableLevel::Pdpt, false) => Self::PageDirectory(PageDirectory {
physical_address: page_table_entry & MASK_PAGE_TABLE_ENTRY_TARGET,
present: present_flag,
}),
(PageTableLevel::Pd, true) => Self::Page(Page {
physical_address: page_table_entry & MASK_2MB_PAGE_PHYSICAL_ADDRESS,
size: SIZE_2MB_PAGE,
present: present_flag,
}),
(PageTableLevel::Pd, false) => Self::PageDirectory(PageDirectory {
physical_address: page_table_entry & MASK_PAGE_TABLE_ENTRY_TARGET,
present: present_flag,
}),
(PageTableLevel::Pt, _) => Self::Page(Page {
physical_address: page_table_entry & MASK_4KB_PAGE_PHYSICAL_ADDRESS,
size: SIZE_4KB_PAGE,
present: present_flag,
}),
};
Ok(obj)
}
pub fn present(&self) -> bool {
match self {
PageTableEntry::Page(page) => page.present,
PageTableEntry::PageDirectory(directory) => directory.present,
}
}
pub fn decompose_virtual_address(virtual_address: u64) -> DecomposedVirtualAddress {
DecomposedVirtualAddress {
pml4: VirtualAddressComponent {
page_table_index: ((virtual_address >> SHIFT_PML4_INDEX) & MASK_PAGE_TABLE_INDEX)
as usize,
page_section: None,
},
pdpt: VirtualAddressComponent {
page_table_index: ((virtual_address >> SHIFT_PDPT_INDEX) & MASK_PAGE_TABLE_INDEX)
as usize,
page_section: Some(PageSection {
offset: virtual_address & MASK_PDPT_PAGE_OFFSET,
size: SIZE_1GB_PAGE - (virtual_address & MASK_PDPT_PAGE_OFFSET),
}),
},
pd: VirtualAddressComponent {
page_table_index: ((virtual_address >> SHIFT_PD_INDEX) & MASK_PAGE_TABLE_INDEX)
as usize,
page_section: Some(PageSection {
offset: virtual_address & MASK_PD_PAGE_OFFSET,
size: SIZE_2MB_PAGE - (virtual_address & MASK_PD_PAGE_OFFSET),
}),
},
pt: VirtualAddressComponent {
page_table_index: ((virtual_address >> SHIFT_PT_INDEX) & MASK_PAGE_TABLE_INDEX)
as usize,
page_section: Some(PageSection {
offset: virtual_address & MASK_PT_PAGE_OFFSET,
size: SIZE_4KB_PAGE - (virtual_address & MASK_PT_PAGE_OFFSET),
}),
},
}
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::needless_range_loop, clippy::unwrap_used)]
use super::*;
#[test]
fn test_decompose_virtual_address() {
let decomposed_vaddr = PageTableEntry::decompose_virtual_address(0xAAAAAAAAAAAAAAAA);
assert_eq!(decomposed_vaddr.pml4.page_table_index, 0x0000000000000155);
assert_eq!(decomposed_vaddr.pml4.page_section, None);
assert_eq!(decomposed_vaddr.pdpt.page_table_index, 0x00000000000000AA);
assert_eq!(
decomposed_vaddr.pdpt.page_section,
Some(PageSection {
offset: 0x000000002AAAAAAA,
size: 0x0000000015555556,
})
);
assert_eq!(decomposed_vaddr.pd.page_table_index, 0x0000000000000155);
assert_eq!(
decomposed_vaddr.pd.page_section,
Some(PageSection {
offset: 0x00000000000AAAAA,
size: 0x0000000000155556,
})
);
assert_eq!(decomposed_vaddr.pt.page_table_index, 0x00000000000000AA);
assert_eq!(
decomposed_vaddr.pt.page_section,
Some(PageSection {
offset: 0x0000000000000AAA,
size: 0x0000000000000556,
})
);
}
const BASE_PAGE_TABLE_ENTRY_VALUE: u64 = 0x0005555555555000;
#[test]
fn test_new_pml4_page_table_entry() {
for present_flag in [false, true] {
let mut raw_table_entry = BASE_PAGE_TABLE_ENTRY_VALUE;
if present_flag {
raw_table_entry |= FLAG_PRESENT;
}
assert_eq!(
PageTableEntry::new(PageTableLevel::Pml4, raw_table_entry).unwrap(),
PageTableEntry::PageDirectory(PageDirectory {
physical_address: BASE_PAGE_TABLE_ENTRY_VALUE,
present: present_flag,
}),
);
}
}
#[test]
fn test_new_pdpt_page_table_entry() {
for present_flag in [false, true] {
let mut raw_table_entry = BASE_PAGE_TABLE_ENTRY_VALUE;
if present_flag {
raw_table_entry |= FLAG_PRESENT;
}
assert_eq!(
PageTableEntry::new(PageTableLevel::Pdpt, raw_table_entry | FLAG_PAGE_SIZE)
.unwrap(),
PageTableEntry::Page(Page {
physical_address: 0x0005555540000000,
size: SIZE_1GB_PAGE,
present: present_flag,
})
);
assert_eq!(
PageTableEntry::new(PageTableLevel::Pdpt, raw_table_entry).unwrap(),
PageTableEntry::PageDirectory(PageDirectory {
physical_address: BASE_PAGE_TABLE_ENTRY_VALUE,
present: present_flag,
})
);
}
}
#[test]
fn test_new_pd_page_table_entry() {
for present_flag in [false, true] {
let mut raw_table_entry = BASE_PAGE_TABLE_ENTRY_VALUE;
if present_flag {
raw_table_entry |= FLAG_PRESENT;
}
assert_eq!(
PageTableEntry::new(PageTableLevel::Pd, raw_table_entry | FLAG_PAGE_SIZE).unwrap(),
PageTableEntry::Page(Page {
physical_address: 0x0005555555400000,
size: SIZE_2MB_PAGE,
present: present_flag,
})
);
assert_eq!(
PageTableEntry::new(PageTableLevel::Pdpt, raw_table_entry).unwrap(),
PageTableEntry::PageDirectory(PageDirectory {
physical_address: BASE_PAGE_TABLE_ENTRY_VALUE,
present: present_flag,
})
);
}
}
#[test]
fn test_new_pt_page_table_entry() {
for present_flag in [false, true] {
for enable_bit_7 in [false, true] {
let mut raw_table_entry = BASE_PAGE_TABLE_ENTRY_VALUE;
if present_flag {
raw_table_entry |= FLAG_PRESENT;
}
if enable_bit_7 {
raw_table_entry |= FLAG_PAGE_SIZE;
}
assert_eq!(
PageTableEntry::new(PageTableLevel::Pt, raw_table_entry).unwrap(),
PageTableEntry::Page(Page {
physical_address: 0x0005555555555000,
size: SIZE_4KB_PAGE,
present: present_flag,
})
);
}
}
}
}