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 => SurrogateMapping::ReadOnlyFile,
162 _ => SurrogateMapping::SandboxMemory,
163 }
164 }
165}
166
167pub trait MemoryRegionKind {
171 type HostBaseType: Copy;
173
174 fn add(base: Self::HostBaseType, size: usize) -> Self::HostBaseType;
184}
185
186#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
189pub struct HostGuestMemoryRegion {}
190
191#[cfg(not(target_os = "windows"))]
192impl MemoryRegionKind for HostGuestMemoryRegion {
193 type HostBaseType = usize;
194
195 fn add(base: Self::HostBaseType, size: usize) -> Self::HostBaseType {
196 base + size
197 }
198}
199#[cfg(target_os = "windows")]
205#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
206pub enum SurrogateMapping {
207 SandboxMemory,
210 ReadOnlyFile,
213}
214
215#[cfg(target_os = "windows")]
220#[derive(Debug, PartialEq, Eq, Copy, Clone)]
221pub struct HostRegionBase {
222 pub from_handle: HandleWrapper,
224 pub handle_base: usize,
226 pub handle_size: usize,
228 pub offset: usize,
231}
232#[cfg(target_os = "windows")]
233impl std::hash::Hash for HostRegionBase {
234 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
235 self.handle_base.hash(state);
240 self.handle_size.hash(state);
241 self.offset.hash(state);
242 }
243}
244#[cfg(target_os = "windows")]
245impl From<HostRegionBase> for usize {
246 fn from(x: HostRegionBase) -> usize {
247 x.handle_base + x.offset
248 }
249}
250#[cfg(target_os = "windows")]
251impl TryFrom<HostRegionBase> for isize {
252 type Error = <isize as TryFrom<usize>>::Error;
253 fn try_from(x: HostRegionBase) -> Result<isize, Self::Error> {
254 <isize as TryFrom<usize>>::try_from(x.into())
255 }
256}
257#[cfg(target_os = "windows")]
258impl MemoryRegionKind for HostGuestMemoryRegion {
259 type HostBaseType = HostRegionBase;
260
261 fn add(base: Self::HostBaseType, size: usize) -> Self::HostBaseType {
262 HostRegionBase {
263 from_handle: base.from_handle,
264 handle_base: base.handle_base,
265 handle_size: base.handle_size,
266 offset: base.offset + size,
267 }
268 }
269}
270
271#[cfg_attr(feature = "nanvix-unstable", allow(dead_code))]
274#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
275pub(crate) struct GuestMemoryRegion {}
276
277impl MemoryRegionKind for GuestMemoryRegion {
278 type HostBaseType = ();
279
280 fn add(_base: Self::HostBaseType, _size: usize) -> Self::HostBaseType {}
281}
282
283#[derive(Debug, Clone, PartialEq, Eq, Hash)]
286pub struct MemoryRegion_<K: MemoryRegionKind> {
287 pub guest_region: Range<usize>,
289 pub host_region: Range<K::HostBaseType>,
293 pub flags: MemoryRegionFlags,
295 pub region_type: MemoryRegionType,
297}
298
299pub type MemoryRegion = MemoryRegion_<HostGuestMemoryRegion>;
301
302#[cfg(crashdump)]
307#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
308pub(crate) struct CrashDumpMemoryRegion;
309
310#[cfg(crashdump)]
311impl MemoryRegionKind for CrashDumpMemoryRegion {
312 type HostBaseType = usize;
313
314 fn add(base: Self::HostBaseType, size: usize) -> Self::HostBaseType {
315 base + size
316 }
317}
318
319#[cfg(crashdump)]
324pub(crate) type CrashDumpRegion = MemoryRegion_<CrashDumpMemoryRegion>;
325
326#[cfg(crashdump)]
327impl HostGuestMemoryRegion {
328 pub(crate) fn to_addr(val: <Self as MemoryRegionKind>::HostBaseType) -> usize {
335 #[cfg(not(target_os = "windows"))]
336 {
337 val
338 }
339 #[cfg(target_os = "windows")]
340 {
341 val.into()
342 }
343 }
344}
345
346#[cfg_attr(feature = "nanvix-unstable", allow(unused))]
347pub(crate) struct MemoryRegionVecBuilder<K: MemoryRegionKind> {
348 guest_base_phys_addr: usize,
349 host_base_virt_addr: K::HostBaseType,
350 regions: Vec<MemoryRegion_<K>>,
351}
352
353impl<K: MemoryRegionKind> MemoryRegionVecBuilder<K> {
354 pub(crate) fn new(guest_base_phys_addr: usize, host_base_virt_addr: K::HostBaseType) -> Self {
355 Self {
356 guest_base_phys_addr,
357 host_base_virt_addr,
358 regions: Vec::new(),
359 }
360 }
361
362 fn push(
363 &mut self,
364 size: usize,
365 flags: MemoryRegionFlags,
366 region_type: MemoryRegionType,
367 ) -> usize {
368 if self.regions.is_empty() {
369 let guest_end = self.guest_base_phys_addr + size;
370 let host_end = <K as MemoryRegionKind>::add(self.host_base_virt_addr, size);
371 self.regions.push(MemoryRegion_ {
372 guest_region: self.guest_base_phys_addr..guest_end,
373 host_region: self.host_base_virt_addr..host_end,
374 flags,
375 region_type,
376 });
377 return guest_end - self.guest_base_phys_addr;
378 }
379
380 #[allow(clippy::unwrap_used)]
381 let last_region = self.regions.last().unwrap();
383 let host_end = <K as MemoryRegionKind>::add(last_region.host_region.end, size);
384 let new_region = MemoryRegion_ {
385 guest_region: last_region.guest_region.end..last_region.guest_region.end + size,
386 host_region: last_region.host_region.end..host_end,
387 flags,
388 region_type,
389 };
390 let ret = new_region.guest_region.end;
391 self.regions.push(new_region);
392 ret - self.guest_base_phys_addr
393 }
394
395 pub(crate) fn push_page_aligned(
400 &mut self,
401 size: usize,
402 flags: MemoryRegionFlags,
403 region_type: MemoryRegionType,
404 ) -> usize {
405 let aligned_size = (size + PAGE_SIZE_USIZE - 1) & !(PAGE_SIZE_USIZE - 1);
406 self.push(aligned_size, flags, region_type)
407 }
408
409 pub(crate) fn build(self) -> Vec<MemoryRegion_<K>> {
412 self.regions
413 }
414}
415
416#[cfg(mshv3)]
417impl From<&MemoryRegion> for mshv_user_mem_region {
418 fn from(region: &MemoryRegion) -> Self {
419 let size = (region.guest_region.end - region.guest_region.start) as u64;
420 let guest_pfn = region.guest_region.start as u64 >> PAGE_SHIFT;
421 let userspace_addr = region.host_region.start as u64;
422
423 let flags: u8 = region.flags.iter().fold(0, |acc, flag| {
424 let flag_value = match flag {
425 MemoryRegionFlags::NONE => 1 << MSHV_SET_MEM_BIT_UNMAP,
426 MemoryRegionFlags::READ => 0,
427 MemoryRegionFlags::WRITE => 1 << MSHV_SET_MEM_BIT_WRITABLE,
428 MemoryRegionFlags::EXECUTE => 1 << MSHV_SET_MEM_BIT_EXECUTABLE,
429 _ => 0, };
431 acc | flag_value
432 });
433
434 mshv_user_mem_region {
435 guest_pfn,
436 size,
437 userspace_addr,
438 flags,
439 ..Default::default()
440 }
441 }
442}
443
444#[cfg(kvm)]
445impl From<&MemoryRegion> for kvm_bindings::kvm_userspace_memory_region {
446 fn from(region: &MemoryRegion) -> Self {
447 let perm_flags =
448 MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE;
449
450 let perm_flags = perm_flags.intersection(region.flags);
451
452 kvm_userspace_memory_region {
453 slot: 0,
454 guest_phys_addr: region.guest_region.start as u64,
455 memory_size: (region.guest_region.end - region.guest_region.start) as u64,
456 userspace_addr: region.host_region.start as u64,
457 flags: if perm_flags.contains(MemoryRegionFlags::WRITE) {
458 0 } else {
460 KVM_MEM_READONLY },
463 }
464 }
465}