1use std::ops::Range;
18
19use bitflags::bitflags;
20#[cfg(mshv3)]
21use hyperlight_common::mem::PAGE_SHIFT;
22use hyperlight_common::mem::PAGE_SIZE_USIZE;
23#[cfg(kvm)]
24use kvm_bindings::{KVM_MEM_READONLY, kvm_userspace_memory_region};
25#[cfg(mshv3)]
26use mshv_bindings::{
27 MSHV_SET_MEM_BIT_EXECUTABLE, MSHV_SET_MEM_BIT_UNMAP, MSHV_SET_MEM_BIT_WRITABLE,
28};
29#[cfg(all(mshv3, target_arch = "aarch64"))]
30use mshv_bindings::{hv_arm64_memory_intercept_message, mshv_user_mem_region};
31#[cfg(all(mshv3, target_arch = "x86_64"))]
32use mshv_bindings::{hv_x64_memory_intercept_message, mshv_user_mem_region};
33#[cfg(target_os = "windows")]
34use windows::Win32::System::Hypervisor::{self, WHV_MEMORY_ACCESS_TYPE};
35
36#[cfg(target_os = "windows")]
37use crate::hypervisor::wrappers::HandleWrapper;
38
39pub(crate) const DEFAULT_GUEST_BLOB_MEM_FLAGS: MemoryRegionFlags = MemoryRegionFlags::READ;
40
41bitflags! {
42 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
44 pub struct MemoryRegionFlags: u32 {
45 const NONE = 0;
47 const READ = 1;
49 const WRITE = 2;
51 const EXECUTE = 4;
53 }
54}
55
56impl std::fmt::Display for MemoryRegionFlags {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 if self.is_empty() {
59 write!(f, "NONE")
60 } else {
61 let mut first = true;
62 if self.contains(MemoryRegionFlags::READ) {
63 write!(f, "READ")?;
64 first = false;
65 }
66 if self.contains(MemoryRegionFlags::WRITE) {
67 if !first {
68 write!(f, " | ")?;
69 }
70 write!(f, "WRITE")?;
71 first = false;
72 }
73 if self.contains(MemoryRegionFlags::EXECUTE) {
74 if !first {
75 write!(f, " | ")?;
76 }
77 write!(f, "EXECUTE")?;
78 }
79 Ok(())
80 }
81 }
82}
83
84#[cfg(target_os = "windows")]
85impl TryFrom<WHV_MEMORY_ACCESS_TYPE> for MemoryRegionFlags {
86 type Error = crate::HyperlightError;
87
88 fn try_from(flags: WHV_MEMORY_ACCESS_TYPE) -> crate::Result<Self> {
89 match flags {
90 Hypervisor::WHvMemoryAccessRead => Ok(MemoryRegionFlags::READ),
91 Hypervisor::WHvMemoryAccessWrite => Ok(MemoryRegionFlags::WRITE),
92 Hypervisor::WHvMemoryAccessExecute => Ok(MemoryRegionFlags::EXECUTE),
93 _ => Err(crate::HyperlightError::Error(
94 "unknown memory access type".to_string(),
95 )),
96 }
97 }
98}
99
100#[cfg(all(mshv3, target_arch = "x86_64"))]
101impl TryFrom<hv_x64_memory_intercept_message> for MemoryRegionFlags {
102 type Error = crate::HyperlightError;
103
104 fn try_from(msg: hv_x64_memory_intercept_message) -> crate::Result<Self> {
105 let access_type = msg.header.intercept_access_type;
106 match access_type {
107 0 => Ok(MemoryRegionFlags::READ),
108 1 => Ok(MemoryRegionFlags::WRITE),
109 2 => Ok(MemoryRegionFlags::EXECUTE),
110 _ => Err(crate::HyperlightError::Error(
111 "unknown memory access type".to_string(),
112 )),
113 }
114 }
115}
116
117#[cfg(all(mshv3, target_arch = "aarch64"))]
118impl TryFrom<hv_arm64_memory_intercept_message> for MemoryRegionFlags {
119 type Error = crate::HyperlightError;
120
121 fn try_from(_msg: hv_arm64_memory_intercept_message) -> crate::Result<Self> {
122 unimplemented!("try_from")
123 }
124}
125
126#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
131pub enum MemoryRegionType {
133 Code,
135 InitData,
137 Peb,
139 Heap,
141 Scratch,
143 Snapshot,
145 MappedFile,
150}
151
152#[cfg(target_os = "windows")]
153impl MemoryRegionType {
154 pub fn surrogate_mapping(&self) -> SurrogateMapping {
160 match self {
161 MemoryRegionType::MappedFile | MemoryRegionType::Snapshot => {
162 SurrogateMapping::ReadOnlyFile
163 }
164 _ => SurrogateMapping::SandboxMemory,
165 }
166 }
167}
168
169pub trait MemoryRegionKind {
173 type HostBaseType: Copy;
175
176 fn add(base: Self::HostBaseType, size: usize) -> Self::HostBaseType;
186}
187
188#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
197pub struct HostGuestMemoryRegion {}
198
199#[cfg(not(target_os = "windows"))]
200impl MemoryRegionKind for HostGuestMemoryRegion {
201 type HostBaseType = usize;
202
203 fn add(base: Self::HostBaseType, size: usize) -> Self::HostBaseType {
204 base + size
205 }
206}
207#[cfg(target_os = "windows")]
213#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
214pub enum SurrogateMapping {
215 SandboxMemory,
218 ReadOnlyFile,
221}
222
223#[cfg(target_os = "windows")]
228#[derive(Debug, PartialEq, Eq, Copy, Clone)]
229pub struct HostRegionBase {
230 pub from_handle: HandleWrapper,
232 pub handle_base: usize,
234 pub handle_size: usize,
236 pub offset: usize,
239}
240#[cfg(target_os = "windows")]
241impl std::hash::Hash for HostRegionBase {
242 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
243 self.handle_base.hash(state);
248 self.handle_size.hash(state);
249 self.offset.hash(state);
250 }
251}
252#[cfg(target_os = "windows")]
253impl From<HostRegionBase> for usize {
254 fn from(x: HostRegionBase) -> usize {
255 x.handle_base + x.offset
256 }
257}
258#[cfg(target_os = "windows")]
259impl TryFrom<HostRegionBase> for isize {
260 type Error = <isize as TryFrom<usize>>::Error;
261 fn try_from(x: HostRegionBase) -> Result<isize, Self::Error> {
262 <isize as TryFrom<usize>>::try_from(x.into())
263 }
264}
265#[cfg(target_os = "windows")]
266impl MemoryRegionKind for HostGuestMemoryRegion {
267 type HostBaseType = HostRegionBase;
268
269 fn add(base: Self::HostBaseType, size: usize) -> Self::HostBaseType {
270 HostRegionBase {
271 from_handle: base.from_handle,
272 handle_base: base.handle_base,
273 handle_size: base.handle_size,
274 offset: base.offset + size,
275 }
276 }
277}
278
279#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
282pub(crate) struct GuestMemoryRegion {}
283
284impl MemoryRegionKind for GuestMemoryRegion {
285 type HostBaseType = ();
286
287 fn add(_base: Self::HostBaseType, _size: usize) -> Self::HostBaseType {}
288}
289
290#[derive(Debug, Clone, PartialEq, Eq, Hash)]
293pub struct MemoryRegion_<K: MemoryRegionKind> {
294 pub guest_region: Range<usize>,
296 pub host_region: Range<K::HostBaseType>,
300 pub flags: MemoryRegionFlags,
302 pub region_type: MemoryRegionType,
304}
305
306pub type MemoryRegion = MemoryRegion_<HostGuestMemoryRegion>;
308
309#[cfg(crashdump)]
314#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
315pub(crate) struct CrashDumpMemoryRegion;
316
317#[cfg(crashdump)]
318impl MemoryRegionKind for CrashDumpMemoryRegion {
319 type HostBaseType = usize;
320
321 fn add(base: Self::HostBaseType, size: usize) -> Self::HostBaseType {
322 base + size
323 }
324}
325
326#[cfg(crashdump)]
331pub(crate) type CrashDumpRegion = MemoryRegion_<CrashDumpMemoryRegion>;
332
333pub(crate) struct MemoryRegionVecBuilder<K: MemoryRegionKind> {
334 guest_base_phys_addr: usize,
335 host_base_virt_addr: K::HostBaseType,
336 regions: Vec<MemoryRegion_<K>>,
337}
338
339impl<K: MemoryRegionKind> MemoryRegionVecBuilder<K> {
340 pub(crate) fn new(guest_base_phys_addr: usize, host_base_virt_addr: K::HostBaseType) -> Self {
341 Self {
342 guest_base_phys_addr,
343 host_base_virt_addr,
344 regions: Vec::new(),
345 }
346 }
347
348 fn push(
349 &mut self,
350 size: usize,
351 flags: MemoryRegionFlags,
352 region_type: MemoryRegionType,
353 ) -> usize {
354 if self.regions.is_empty() {
355 let guest_end = self.guest_base_phys_addr + size;
356 let host_end = <K as MemoryRegionKind>::add(self.host_base_virt_addr, size);
357 self.regions.push(MemoryRegion_ {
358 guest_region: self.guest_base_phys_addr..guest_end,
359 host_region: self.host_base_virt_addr..host_end,
360 flags,
361 region_type,
362 });
363 return guest_end - self.guest_base_phys_addr;
364 }
365
366 #[allow(clippy::unwrap_used)]
367 let last_region = self.regions.last().unwrap();
369 let host_end = <K as MemoryRegionKind>::add(last_region.host_region.end, size);
370 let new_region = MemoryRegion_ {
371 guest_region: last_region.guest_region.end..last_region.guest_region.end + size,
372 host_region: last_region.host_region.end..host_end,
373 flags,
374 region_type,
375 };
376 let ret = new_region.guest_region.end;
377 self.regions.push(new_region);
378 ret - self.guest_base_phys_addr
379 }
380
381 pub(crate) fn push_page_aligned(
386 &mut self,
387 size: usize,
388 flags: MemoryRegionFlags,
389 region_type: MemoryRegionType,
390 ) -> usize {
391 let aligned_size = (size + PAGE_SIZE_USIZE - 1) & !(PAGE_SIZE_USIZE - 1);
392 self.push(aligned_size, flags, region_type)
393 }
394
395 pub(crate) fn build(self) -> Vec<MemoryRegion_<K>> {
398 self.regions
399 }
400}
401
402#[cfg(mshv3)]
403impl From<&MemoryRegion> for mshv_user_mem_region {
404 fn from(region: &MemoryRegion) -> Self {
405 let size = (region.guest_region.end - region.guest_region.start) as u64;
406 let guest_pfn = region.guest_region.start as u64 >> PAGE_SHIFT;
407 let userspace_addr = region.host_region.start as u64;
408
409 let flags: u8 = region.flags.iter().fold(0, |acc, flag| {
410 let flag_value = match flag {
411 MemoryRegionFlags::NONE => 1 << MSHV_SET_MEM_BIT_UNMAP,
412 MemoryRegionFlags::READ => 0,
413 MemoryRegionFlags::WRITE => 1 << MSHV_SET_MEM_BIT_WRITABLE,
414 MemoryRegionFlags::EXECUTE => 1 << MSHV_SET_MEM_BIT_EXECUTABLE,
415 _ => 0, };
417 acc | flag_value
418 });
419
420 mshv_user_mem_region {
421 guest_pfn,
422 size,
423 userspace_addr,
424 flags,
425 ..Default::default()
426 }
427 }
428}
429
430#[cfg(kvm)]
431impl From<&MemoryRegion> for kvm_bindings::kvm_userspace_memory_region {
432 fn from(region: &MemoryRegion) -> Self {
433 let perm_flags =
434 MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE;
435
436 let perm_flags = perm_flags.intersection(region.flags);
437
438 kvm_userspace_memory_region {
439 slot: 0,
440 guest_phys_addr: region.guest_region.start as u64,
441 memory_size: (region.guest_region.end - region.guest_region.start) as u64,
442 userspace_addr: region.host_region.start as u64,
443 flags: if perm_flags.contains(MemoryRegionFlags::WRITE) {
444 0 } else {
446 KVM_MEM_READONLY },
449 }
450 }
451}