1use std::ops::Range;
5
6use bitflags::bitflags;
7use hyperlight_common::vmem::PAGE_SIZE;
8#[cfg(kvm)]
9use kvm_bindings::{KVM_MEM_READONLY, kvm_userspace_memory_region};
10#[cfg(mshv3)]
11use mshv_bindings::{
12 MSHV_SET_MEM_BIT_EXECUTABLE, MSHV_SET_MEM_BIT_UNMAP, MSHV_SET_MEM_BIT_WRITABLE,
13};
14#[cfg(all(mshv3, target_arch = "aarch64"))]
15use mshv_bindings::{hv_arm64_memory_intercept_message, mshv_user_mem_region};
16#[cfg(all(mshv3, target_arch = "x86_64"))]
17use mshv_bindings::{hv_x64_memory_intercept_message, mshv_user_mem_region};
18#[cfg(target_os = "windows")]
19use windows::Win32::System::Hypervisor::{self, WHV_MEMORY_ACCESS_TYPE};
20
21#[cfg(target_os = "windows")]
22use crate::hypervisor::wrappers::HandleWrapper;
23
24pub(crate) const DEFAULT_GUEST_BLOB_MEM_FLAGS: MemoryRegionFlags = MemoryRegionFlags::READ;
25
26bitflags! {
27 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
29 pub struct MemoryRegionFlags: u32 {
30 const NONE = 0;
32 const READ = 1;
34 const WRITE = 2;
36 const EXECUTE = 4;
38 }
39}
40
41impl std::fmt::Display for MemoryRegionFlags {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 if self.is_empty() {
44 write!(f, "NONE")
45 } else {
46 let mut first = true;
47 if self.contains(MemoryRegionFlags::READ) {
48 write!(f, "READ")?;
49 first = false;
50 }
51 if self.contains(MemoryRegionFlags::WRITE) {
52 if !first {
53 write!(f, " | ")?;
54 }
55 write!(f, "WRITE")?;
56 first = false;
57 }
58 if self.contains(MemoryRegionFlags::EXECUTE) {
59 if !first {
60 write!(f, " | ")?;
61 }
62 write!(f, "EXECUTE")?;
63 }
64 Ok(())
65 }
66 }
67}
68
69#[cfg(target_os = "windows")]
70impl TryFrom<WHV_MEMORY_ACCESS_TYPE> for MemoryRegionFlags {
71 type Error = crate::HyperlightError;
72
73 fn try_from(flags: WHV_MEMORY_ACCESS_TYPE) -> crate::Result<Self> {
74 match flags {
75 Hypervisor::WHvMemoryAccessRead => Ok(MemoryRegionFlags::READ),
76 Hypervisor::WHvMemoryAccessWrite => Ok(MemoryRegionFlags::WRITE),
77 Hypervisor::WHvMemoryAccessExecute => Ok(MemoryRegionFlags::EXECUTE),
78 _ => Err(crate::HyperlightError::Error(
79 "unknown memory access type".to_string(),
80 )),
81 }
82 }
83}
84
85#[cfg(all(mshv3, target_arch = "x86_64"))]
86impl TryFrom<hv_x64_memory_intercept_message> for MemoryRegionFlags {
87 type Error = crate::HyperlightError;
88
89 fn try_from(msg: hv_x64_memory_intercept_message) -> crate::Result<Self> {
90 let access_type = msg.header.intercept_access_type;
91 match access_type {
92 0 => Ok(MemoryRegionFlags::READ),
93 1 => Ok(MemoryRegionFlags::WRITE),
94 2 => Ok(MemoryRegionFlags::EXECUTE),
95 _ => Err(crate::HyperlightError::Error(
96 "unknown memory access type".to_string(),
97 )),
98 }
99 }
100}
101
102#[cfg(all(mshv3, target_arch = "aarch64"))]
103impl TryFrom<hv_arm64_memory_intercept_message> for MemoryRegionFlags {
104 type Error = crate::HyperlightError;
105
106 fn try_from(_msg: hv_arm64_memory_intercept_message) -> crate::Result<Self> {
107 unimplemented!("try_from")
108 }
109}
110
111#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
116pub enum MemoryRegionType {
118 Code,
120 InitData,
122 Peb,
124 Heap,
126 Scratch,
128 Snapshot,
130 MappedFile,
135}
136
137#[cfg(target_os = "windows")]
138impl MemoryRegionType {
139 pub fn surrogate_mapping(&self) -> SurrogateMapping {
145 match self {
146 MemoryRegionType::MappedFile | MemoryRegionType::Snapshot => {
147 SurrogateMapping::ReadOnlyFile
148 }
149 _ => SurrogateMapping::SandboxMemory,
150 }
151 }
152}
153
154pub trait MemoryRegionKind {
158 type HostBaseType: Copy;
160
161 fn add(base: Self::HostBaseType, size: usize) -> Self::HostBaseType;
171}
172
173#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
182pub struct HostGuestMemoryRegion {}
183
184#[cfg(not(target_os = "windows"))]
185impl MemoryRegionKind for HostGuestMemoryRegion {
186 type HostBaseType = usize;
187
188 fn add(base: Self::HostBaseType, size: usize) -> Self::HostBaseType {
189 base + size
190 }
191}
192#[cfg(target_os = "windows")]
198#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
199pub enum SurrogateMapping {
200 SandboxMemory,
203 ReadOnlyFile,
206}
207
208#[cfg(target_os = "windows")]
213#[derive(Debug, PartialEq, Eq, Copy, Clone)]
214pub struct HostRegionBase {
215 pub from_handle: HandleWrapper,
217 pub handle_base: usize,
219 pub handle_size: usize,
221 pub offset: usize,
224}
225#[cfg(target_os = "windows")]
226impl std::hash::Hash for HostRegionBase {
227 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
228 self.handle_base.hash(state);
233 self.handle_size.hash(state);
234 self.offset.hash(state);
235 }
236}
237#[cfg(target_os = "windows")]
238impl From<HostRegionBase> for usize {
239 fn from(x: HostRegionBase) -> usize {
240 x.handle_base + x.offset
241 }
242}
243#[cfg(target_os = "windows")]
244impl TryFrom<HostRegionBase> for isize {
245 type Error = <isize as TryFrom<usize>>::Error;
246 fn try_from(x: HostRegionBase) -> Result<isize, Self::Error> {
247 <isize as TryFrom<usize>>::try_from(x.into())
248 }
249}
250#[cfg(target_os = "windows")]
251impl MemoryRegionKind for HostGuestMemoryRegion {
252 type HostBaseType = HostRegionBase;
253
254 fn add(base: Self::HostBaseType, size: usize) -> Self::HostBaseType {
255 HostRegionBase {
256 from_handle: base.from_handle,
257 handle_base: base.handle_base,
258 handle_size: base.handle_size,
259 offset: base.offset + size,
260 }
261 }
262}
263
264#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
267pub(crate) struct GuestMemoryRegion {}
268
269impl MemoryRegionKind for GuestMemoryRegion {
270 type HostBaseType = ();
271
272 fn add(_base: Self::HostBaseType, _size: usize) -> Self::HostBaseType {}
273}
274
275#[derive(Debug, Clone, PartialEq, Eq, Hash)]
278pub struct MemoryRegion_<K: MemoryRegionKind> {
279 pub guest_region: Range<usize>,
281 pub host_region: Range<K::HostBaseType>,
285 pub flags: MemoryRegionFlags,
287 pub region_type: MemoryRegionType,
289}
290
291pub type MemoryRegion = MemoryRegion_<HostGuestMemoryRegion>;
293
294#[cfg(crashdump)]
299#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
300pub(crate) struct CrashDumpMemoryRegion;
301
302#[cfg(crashdump)]
303impl MemoryRegionKind for CrashDumpMemoryRegion {
304 type HostBaseType = usize;
305
306 fn add(base: Self::HostBaseType, size: usize) -> Self::HostBaseType {
307 base + size
308 }
309}
310
311#[cfg(crashdump)]
316pub(crate) type CrashDumpRegion = MemoryRegion_<CrashDumpMemoryRegion>;
317
318pub(crate) struct MemoryRegionVecBuilder<K: MemoryRegionKind> {
319 guest_base_phys_addr: usize,
320 host_base_virt_addr: K::HostBaseType,
321 regions: Vec<MemoryRegion_<K>>,
322}
323
324impl<K: MemoryRegionKind> MemoryRegionVecBuilder<K> {
325 pub(crate) fn new(guest_base_phys_addr: usize, host_base_virt_addr: K::HostBaseType) -> Self {
326 Self {
327 guest_base_phys_addr,
328 host_base_virt_addr,
329 regions: Vec::new(),
330 }
331 }
332
333 fn push(
334 &mut self,
335 size: usize,
336 flags: MemoryRegionFlags,
337 region_type: MemoryRegionType,
338 ) -> usize {
339 if self.regions.is_empty() {
340 let guest_end = self.guest_base_phys_addr + size;
341 let host_end = <K as MemoryRegionKind>::add(self.host_base_virt_addr, size);
342 self.regions.push(MemoryRegion_ {
343 guest_region: self.guest_base_phys_addr..guest_end,
344 host_region: self.host_base_virt_addr..host_end,
345 flags,
346 region_type,
347 });
348 return guest_end - self.guest_base_phys_addr;
349 }
350
351 #[allow(clippy::unwrap_used)]
352 let last_region = self.regions.last().unwrap();
354 let host_end = <K as MemoryRegionKind>::add(last_region.host_region.end, size);
355 let new_region = MemoryRegion_ {
356 guest_region: last_region.guest_region.end..last_region.guest_region.end + size,
357 host_region: last_region.host_region.end..host_end,
358 flags,
359 region_type,
360 };
361 let ret = new_region.guest_region.end;
362 self.regions.push(new_region);
363 ret - self.guest_base_phys_addr
364 }
365
366 pub(crate) fn push_page_aligned(
371 &mut self,
372 size: usize,
373 flags: MemoryRegionFlags,
374 region_type: MemoryRegionType,
375 ) -> usize {
376 let aligned_size = (size + PAGE_SIZE - 1) & !(PAGE_SIZE - 1);
377 self.push(aligned_size, flags, region_type)
378 }
379
380 pub(crate) fn build(self) -> Vec<MemoryRegion_<K>> {
383 self.regions
384 }
385}
386
387#[cfg(mshv3)]
388impl From<&MemoryRegion> for mshv_user_mem_region {
389 fn from(region: &MemoryRegion) -> Self {
390 let size = (region.guest_region.end - region.guest_region.start) as u64;
391 let guest_pfn = (region.guest_region.start / page_size::get()) as u64;
392 let userspace_addr = region.host_region.start as u64;
393
394 let flags: u8 = region.flags.iter().fold(0, |acc, flag| {
395 let flag_value = match flag {
396 MemoryRegionFlags::NONE => 1 << MSHV_SET_MEM_BIT_UNMAP,
397 MemoryRegionFlags::READ => 0,
398 MemoryRegionFlags::WRITE => 1 << MSHV_SET_MEM_BIT_WRITABLE,
399 MemoryRegionFlags::EXECUTE => 1 << MSHV_SET_MEM_BIT_EXECUTABLE,
400 _ => 0, };
402 acc | flag_value
403 });
404
405 mshv_user_mem_region {
406 guest_pfn,
407 size,
408 userspace_addr,
409 flags,
410 ..Default::default()
411 }
412 }
413}
414
415#[cfg(kvm)]
416impl From<&MemoryRegion> for kvm_bindings::kvm_userspace_memory_region {
417 fn from(region: &MemoryRegion) -> Self {
418 let perm_flags =
419 MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE;
420
421 let perm_flags = perm_flags.intersection(region.flags);
422
423 kvm_userspace_memory_region {
424 slot: 0,
425 guest_phys_addr: region.guest_region.start as u64,
426 memory_size: (region.guest_region.end - region.guest_region.start) as u64,
427 userspace_addr: region.host_region.start as u64,
428 flags: if perm_flags.contains(MemoryRegionFlags::WRITE) {
429 0 } else {
431 KVM_MEM_READONLY },
434 }
435 }
436}