use std::ops::Range;
use bitflags::bitflags;
#[cfg(mshv)]
use hyperlight_common::mem::PAGE_SHIFT;
use hyperlight_common::mem::PAGE_SIZE_USIZE;
#[cfg(mshv)]
use mshv_bindings::{
hv_x64_memory_intercept_message, mshv_user_mem_region, HV_MAP_GPA_EXECUTABLE,
HV_MAP_GPA_PERMISSIONS_NONE, HV_MAP_GPA_READABLE, HV_MAP_GPA_WRITABLE,
};
#[cfg(target_os = "windows")]
use windows::Win32::System::Hypervisor::{self, WHV_MEMORY_ACCESS_TYPE};
bitflags! {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct MemoryRegionFlags: u32 {
const NONE = 0;
const READ = 1;
const WRITE = 2;
const EXECUTE = 4;
const STACK_GUARD = 8;
}
}
impl std::fmt::Display for MemoryRegionFlags {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_empty() {
write!(f, "NONE")
} else {
let mut first = true;
if self.contains(MemoryRegionFlags::READ) {
write!(f, "READ")?;
first = false;
}
if self.contains(MemoryRegionFlags::WRITE) {
if !first {
write!(f, " | ")?;
}
write!(f, "WRITE")?;
first = false;
}
if self.contains(MemoryRegionFlags::EXECUTE) {
if !first {
write!(f, " | ")?;
}
write!(f, "EXECUTE")?;
}
Ok(())
}
}
}
#[cfg(target_os = "windows")]
impl TryFrom<WHV_MEMORY_ACCESS_TYPE> for MemoryRegionFlags {
type Error = crate::HyperlightError;
fn try_from(flags: WHV_MEMORY_ACCESS_TYPE) -> crate::Result<Self> {
match flags {
Hypervisor::WHvMemoryAccessRead => Ok(MemoryRegionFlags::READ),
Hypervisor::WHvMemoryAccessWrite => Ok(MemoryRegionFlags::WRITE),
Hypervisor::WHvMemoryAccessExecute => Ok(MemoryRegionFlags::EXECUTE),
_ => Err(crate::HyperlightError::Error(
"unknown memory access type".to_string(),
)),
}
}
}
#[cfg(mshv)]
impl TryFrom<hv_x64_memory_intercept_message> for MemoryRegionFlags {
type Error = crate::HyperlightError;
fn try_from(msg: hv_x64_memory_intercept_message) -> crate::Result<Self> {
let access_type = msg.header.intercept_access_type;
match access_type {
0 => Ok(MemoryRegionFlags::READ),
1 => Ok(MemoryRegionFlags::WRITE),
2 => Ok(MemoryRegionFlags::EXECUTE),
_ => Err(crate::HyperlightError::Error(
"unknown memory access type".to_string(),
)),
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum MemoryRegionType {
PageTables,
Code,
Peb,
HostFunctionDefinitions,
HostExceptionData,
GuestErrorData,
InputData,
OutputData,
PanicContext,
Heap,
GuardPage,
Stack,
KernelStack,
BootStack,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MemoryRegion {
pub(crate) guest_region: Range<usize>,
pub(crate) host_region: Range<usize>,
pub(crate) flags: MemoryRegionFlags,
pub(crate) region_type: MemoryRegionType,
}
pub(crate) struct MemoryRegionVecBuilder {
guest_base_phys_addr: usize,
host_base_virt_addr: usize,
regions: Vec<MemoryRegion>,
}
impl MemoryRegionVecBuilder {
pub(crate) fn new(guest_base_phys_addr: usize, host_base_virt_addr: usize) -> Self {
Self {
guest_base_phys_addr,
host_base_virt_addr,
regions: Vec::new(),
}
}
fn push(
&mut self,
size: usize,
flags: MemoryRegionFlags,
region_type: MemoryRegionType,
) -> usize {
if self.regions.is_empty() {
let guest_end = self.guest_base_phys_addr + size;
let host_end = self.host_base_virt_addr + size;
self.regions.push(MemoryRegion {
guest_region: self.guest_base_phys_addr..guest_end,
host_region: self.host_base_virt_addr..host_end,
flags,
region_type,
});
return guest_end - self.guest_base_phys_addr;
}
let last_region = self.regions.last().unwrap();
let new_region = MemoryRegion {
guest_region: last_region.guest_region.end..last_region.guest_region.end + size,
host_region: last_region.host_region.end..last_region.host_region.end + size,
flags,
region_type,
};
let ret = new_region.guest_region.end;
self.regions.push(new_region);
ret - self.guest_base_phys_addr
}
pub(crate) fn push_page_aligned(
&mut self,
size: usize,
flags: MemoryRegionFlags,
region_type: MemoryRegionType,
) -> usize {
let aligned_size = (size + PAGE_SIZE_USIZE - 1) & !(PAGE_SIZE_USIZE - 1);
self.push(aligned_size, flags, region_type)
}
pub(crate) fn build(self) -> Vec<MemoryRegion> {
self.regions
}
}
#[cfg(mshv)]
impl From<MemoryRegion> for mshv_user_mem_region {
fn from(region: MemoryRegion) -> Self {
let size = (region.guest_region.end - region.guest_region.start) as u64;
let guest_pfn = region.guest_region.start as u64 >> PAGE_SHIFT;
let userspace_addr = region.host_region.start as u64;
let flags = region.flags.iter().fold(0, |acc, flag| {
let flag_value = match flag {
MemoryRegionFlags::NONE => HV_MAP_GPA_PERMISSIONS_NONE,
MemoryRegionFlags::READ => HV_MAP_GPA_READABLE,
MemoryRegionFlags::WRITE => HV_MAP_GPA_WRITABLE,
MemoryRegionFlags::EXECUTE => HV_MAP_GPA_EXECUTABLE,
_ => 0, };
acc | flag_value
});
mshv_user_mem_region {
guest_pfn,
size,
userspace_addr,
flags,
}
}
}