mod paging;
pub(crate) mod registers;
pub(crate) use paging::BOOT_GDT_MAX;
use paging::initialize_pagetables;
use uhyve_interface::{GuestPhysAddr, GuestVirtAddr};
use x86_64::structures::paging::{
PageTable, PageTableIndex,
page_table::{FrameError, PageTableEntry},
};
use crate::{linux::x86_64::kvm_cpu::KVM_32BIT_GAP_START, mem::MmapMemory, paging::PagetableError};
pub const PAGE_SIZE: usize = 0x1000;
pub(crate) const GDT_OFFSET: u64 = 0x1000;
pub(crate) const PML4_OFFSET: u64 = 0x10000;
pub(crate) const RAM_START: GuestPhysAddr = GuestPhysAddr::new(0x0);
pub(crate) const V1_MAX_ADDR: u64 = KVM_32BIT_GAP_START as u64 - 1;
pub(crate) const V1_ADDR_RANGE: (u64, u64) = (RAM_START.as_u64(), V1_MAX_ADDR);
pub(crate) const V2_ADDR_RANGE: (u64, u64) = (0x0001_0000_0000u64, 0x0010_0000_0000u64);
pub(crate) fn virt_to_phys(
addr: GuestVirtAddr,
mem: &MmapMemory,
pml4: GuestPhysAddr,
) -> Result<GuestPhysAddr, PagetableError> {
pub const PAGE_BITS: u64 = 12;
pub const PAGE_MAP_BITS: usize = 9;
let mut page_table =
unsafe { (mem.host_address(pml4).unwrap() as *mut PageTable).as_mut() }.unwrap();
let mut page_bits = 39;
let mut entry = PageTableEntry::new();
for _i in 0..4 {
let index =
PageTableIndex::new(((addr.as_u64() >> page_bits) & ((1 << PAGE_MAP_BITS) - 1)) as u16);
entry = page_table[index].clone();
match entry.frame() {
Ok(frame) => {
page_table = unsafe {
(mem.host_address(frame.start_address().into()).unwrap() as *mut PageTable)
.as_mut()
}
.unwrap();
page_bits -= PAGE_MAP_BITS;
}
Err(FrameError::FrameNotPresent) => return Err(PagetableError::InvalidAddress),
Err(FrameError::HugeFrame) => {
return Ok((entry.addr() + (addr.as_u64() & !((!0_u64) << page_bits))).into());
}
}
}
Ok((entry.addr() + (addr.as_u64() & !((!0u64) << PAGE_BITS))).into())
}
pub fn init_guest_mem(
mem: &mut [u8],
guest_address: GuestPhysAddr,
length: u64,
legacy_mapping: bool,
) {
initialize_pagetables(mem, guest_address, length, legacy_mapping);
}
#[cfg(test)]
mod tests {
use x86_64::structures::paging::PageTableFlags;
use super::{paging::MIN_PHYSMEM_SIZE, *};
use crate::consts::{PAGETABLES_END, PAGETABLES_OFFSET};
#[test]
fn test_virt_to_phys() {
let _ = env_logger::builder()
.filter(None, log::LevelFilter::Trace)
.is_test(true)
.try_init();
let guest_address = GuestPhysAddr::new(0x11111000);
let mem = MmapMemory::new(MIN_PHYSMEM_SIZE * 2, guest_address, true, true);
log::debug!("mmap memory created {mem:x?}");
init_guest_mem(
unsafe { mem.as_slice_mut() },
guest_address,
MIN_PHYSMEM_SIZE as u64 * 2,
false,
);
let virt_addr = GuestVirtAddr::new(0xFFFFFFFFFFFFF000);
let p_addr = virt_to_phys(virt_addr, &mem, guest_address + PML4_OFFSET).unwrap();
assert_eq!(p_addr, guest_address + PML4_OFFSET);
let virt_addr = GuestVirtAddr::new(0xFFFFFFFFFFFFF000 | (4096 - 8));
let p_addr = virt_to_phys(virt_addr, &mem, guest_address + PML4_OFFSET).unwrap();
assert_eq!(
mem.read::<u64>(p_addr).unwrap(),
(guest_address + PML4_OFFSET).as_u64()
| (PageTableFlags::PRESENT | PageTableFlags::WRITABLE).bits()
);
let virt_addr = GuestVirtAddr::new(0xFFFFFFFFFFE00000);
let p_addr = virt_to_phys(virt_addr, &mem, guest_address + PML4_OFFSET).unwrap();
assert!(p_addr.as_u64() - guest_address.as_u64() >= PAGETABLES_OFFSET);
assert!(p_addr.as_u64() - guest_address.as_u64() <= PAGETABLES_END);
let idx2 = GuestVirtAddr::new(guest_address.as_u64()).p2_index();
let virt_addr = GuestVirtAddr::new(0xFFFFFFFFC0000000)
+ u64::from(idx2) * size_of::<PageTableEntry>() as u64;
let p_addr = virt_to_phys(virt_addr, &mem, guest_address + PML4_OFFSET).unwrap();
assert!(p_addr.as_u64() - guest_address.as_u64() >= PAGETABLES_OFFSET);
assert!(p_addr.as_u64() - guest_address.as_u64() <= PAGETABLES_END);
assert!(
PageTableFlags::from_bits_truncate(mem.read::<u64>(p_addr).unwrap()).contains(
PageTableFlags::HUGE_PAGE | PageTableFlags::PRESENT | PageTableFlags::WRITABLE
)
);
}
}