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(mshv3)]
30use mshv_bindings::{hv_x64_memory_intercept_message, mshv_user_mem_region};
31#[cfg(target_os = "windows")]
32use windows::Win32::System::Hypervisor::{self, WHV_MEMORY_ACCESS_TYPE};
33
34#[cfg(target_os = "windows")]
35use crate::hypervisor::wrappers::HandleWrapper;
36
37pub(crate) const DEFAULT_GUEST_BLOB_MEM_FLAGS: MemoryRegionFlags = MemoryRegionFlags::READ;
38
39bitflags! {
40 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
42 pub struct MemoryRegionFlags: u32 {
43 const NONE = 0;
45 const READ = 1;
47 const WRITE = 2;
49 const EXECUTE = 4;
51 }
52}
53
54impl std::fmt::Display for MemoryRegionFlags {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 if self.is_empty() {
57 write!(f, "NONE")
58 } else {
59 let mut first = true;
60 if self.contains(MemoryRegionFlags::READ) {
61 write!(f, "READ")?;
62 first = false;
63 }
64 if self.contains(MemoryRegionFlags::WRITE) {
65 if !first {
66 write!(f, " | ")?;
67 }
68 write!(f, "WRITE")?;
69 first = false;
70 }
71 if self.contains(MemoryRegionFlags::EXECUTE) {
72 if !first {
73 write!(f, " | ")?;
74 }
75 write!(f, "EXECUTE")?;
76 }
77 Ok(())
78 }
79 }
80}
81
82#[cfg(target_os = "windows")]
83impl TryFrom<WHV_MEMORY_ACCESS_TYPE> for MemoryRegionFlags {
84 type Error = crate::HyperlightError;
85
86 fn try_from(flags: WHV_MEMORY_ACCESS_TYPE) -> crate::Result<Self> {
87 match flags {
88 Hypervisor::WHvMemoryAccessRead => Ok(MemoryRegionFlags::READ),
89 Hypervisor::WHvMemoryAccessWrite => Ok(MemoryRegionFlags::WRITE),
90 Hypervisor::WHvMemoryAccessExecute => Ok(MemoryRegionFlags::EXECUTE),
91 _ => Err(crate::HyperlightError::Error(
92 "unknown memory access type".to_string(),
93 )),
94 }
95 }
96}
97
98#[cfg(mshv3)]
99impl TryFrom<hv_x64_memory_intercept_message> for MemoryRegionFlags {
100 type Error = crate::HyperlightError;
101
102 fn try_from(msg: hv_x64_memory_intercept_message) -> crate::Result<Self> {
103 let access_type = msg.header.intercept_access_type;
104 match access_type {
105 0 => Ok(MemoryRegionFlags::READ),
106 1 => Ok(MemoryRegionFlags::WRITE),
107 2 => Ok(MemoryRegionFlags::EXECUTE),
108 _ => Err(crate::HyperlightError::Error(
109 "unknown memory access type".to_string(),
110 )),
111 }
112 }
113}
114
115#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
117pub enum MemoryRegionType {
119 Code,
121 InitData,
123 Peb,
125 Heap,
127 Scratch,
129 Snapshot,
131}
132
133pub(crate) trait MemoryRegionKind {
137 type HostBaseType: Copy;
139
140 fn add(base: Self::HostBaseType, size: usize) -> Self::HostBaseType;
150}
151
152#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
155pub(crate) struct HostGuestMemoryRegion {}
156
157#[cfg(not(target_os = "windows"))]
158impl MemoryRegionKind for HostGuestMemoryRegion {
159 type HostBaseType = usize;
160
161 fn add(base: Self::HostBaseType, size: usize) -> Self::HostBaseType {
162 base + size
163 }
164}
165#[cfg(target_os = "windows")]
170#[derive(Debug, PartialEq, Eq, Copy, Clone)]
171pub struct HostRegionBase {
172 pub from_handle: HandleWrapper,
174 pub handle_base: usize,
176 pub handle_size: usize,
178 pub offset: usize,
181}
182#[cfg(target_os = "windows")]
183impl std::hash::Hash for HostRegionBase {
184 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
185 self.handle_base.hash(state);
190 self.handle_size.hash(state);
191 self.offset.hash(state);
192 }
193}
194#[cfg(target_os = "windows")]
195impl From<HostRegionBase> for usize {
196 fn from(x: HostRegionBase) -> usize {
197 x.handle_base + x.offset
198 }
199}
200#[cfg(target_os = "windows")]
201impl TryFrom<HostRegionBase> for isize {
202 type Error = <isize as TryFrom<usize>>::Error;
203 fn try_from(x: HostRegionBase) -> Result<isize, Self::Error> {
204 <isize as TryFrom<usize>>::try_from(x.into())
205 }
206}
207#[cfg(target_os = "windows")]
208impl MemoryRegionKind for HostGuestMemoryRegion {
209 type HostBaseType = HostRegionBase;
210
211 fn add(base: Self::HostBaseType, size: usize) -> Self::HostBaseType {
212 HostRegionBase {
213 from_handle: base.from_handle,
214 handle_base: base.handle_base,
215 handle_size: base.handle_size,
216 offset: base.offset + size,
217 }
218 }
219}
220
221#[cfg_attr(not(feature = "init-paging"), allow(dead_code))]
224#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
225pub(crate) struct GuestMemoryRegion {}
226
227impl MemoryRegionKind for GuestMemoryRegion {
228 type HostBaseType = ();
229
230 fn add(_base: Self::HostBaseType, _size: usize) -> Self::HostBaseType {}
231}
232
233#[derive(Debug, Clone, PartialEq, Eq, Hash)]
236pub(crate) struct MemoryRegion_<K: MemoryRegionKind> {
237 pub guest_region: Range<usize>,
239 pub host_region: Range<K::HostBaseType>,
243 pub flags: MemoryRegionFlags,
245 pub region_type: MemoryRegionType,
247}
248
249pub(crate) type MemoryRegion = MemoryRegion_<HostGuestMemoryRegion>;
250
251#[cfg(crashdump)]
256#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
257pub(crate) struct CrashDumpMemoryRegion;
258
259#[cfg(crashdump)]
260impl MemoryRegionKind for CrashDumpMemoryRegion {
261 type HostBaseType = usize;
262
263 fn add(base: Self::HostBaseType, size: usize) -> Self::HostBaseType {
264 base + size
265 }
266}
267
268#[cfg(crashdump)]
273pub(crate) type CrashDumpRegion = MemoryRegion_<CrashDumpMemoryRegion>;
274
275#[cfg(crashdump)]
276impl HostGuestMemoryRegion {
277 pub(crate) fn to_addr(val: <Self as MemoryRegionKind>::HostBaseType) -> usize {
284 #[cfg(not(target_os = "windows"))]
285 {
286 val
287 }
288 #[cfg(target_os = "windows")]
289 {
290 val.into()
291 }
292 }
293}
294
295#[cfg_attr(not(feature = "init-paging"), allow(unused))]
296pub(crate) struct MemoryRegionVecBuilder<K: MemoryRegionKind> {
297 guest_base_phys_addr: usize,
298 host_base_virt_addr: K::HostBaseType,
299 regions: Vec<MemoryRegion_<K>>,
300}
301
302impl<K: MemoryRegionKind> MemoryRegionVecBuilder<K> {
303 pub(crate) fn new(guest_base_phys_addr: usize, host_base_virt_addr: K::HostBaseType) -> Self {
304 Self {
305 guest_base_phys_addr,
306 host_base_virt_addr,
307 regions: Vec::new(),
308 }
309 }
310
311 fn push(
312 &mut self,
313 size: usize,
314 flags: MemoryRegionFlags,
315 region_type: MemoryRegionType,
316 ) -> usize {
317 if self.regions.is_empty() {
318 let guest_end = self.guest_base_phys_addr + size;
319 let host_end = <K as MemoryRegionKind>::add(self.host_base_virt_addr, size);
320 self.regions.push(MemoryRegion_ {
321 guest_region: self.guest_base_phys_addr..guest_end,
322 host_region: self.host_base_virt_addr..host_end,
323 flags,
324 region_type,
325 });
326 return guest_end - self.guest_base_phys_addr;
327 }
328
329 #[allow(clippy::unwrap_used)]
330 let last_region = self.regions.last().unwrap();
332 let host_end = <K as MemoryRegionKind>::add(last_region.host_region.end, size);
333 let new_region = MemoryRegion_ {
334 guest_region: last_region.guest_region.end..last_region.guest_region.end + size,
335 host_region: last_region.host_region.end..host_end,
336 flags,
337 region_type,
338 };
339 let ret = new_region.guest_region.end;
340 self.regions.push(new_region);
341 ret - self.guest_base_phys_addr
342 }
343
344 pub(crate) fn push_page_aligned(
349 &mut self,
350 size: usize,
351 flags: MemoryRegionFlags,
352 region_type: MemoryRegionType,
353 ) -> usize {
354 let aligned_size = (size + PAGE_SIZE_USIZE - 1) & !(PAGE_SIZE_USIZE - 1);
355 self.push(aligned_size, flags, region_type)
356 }
357
358 pub(crate) fn build(self) -> Vec<MemoryRegion_<K>> {
361 self.regions
362 }
363}
364
365#[cfg(mshv3)]
366impl From<&MemoryRegion> for mshv_user_mem_region {
367 fn from(region: &MemoryRegion) -> Self {
368 let size = (region.guest_region.end - region.guest_region.start) as u64;
369 let guest_pfn = region.guest_region.start as u64 >> PAGE_SHIFT;
370 let userspace_addr = region.host_region.start as u64;
371
372 let flags: u8 = region.flags.iter().fold(0, |acc, flag| {
373 let flag_value = match flag {
374 MemoryRegionFlags::NONE => 1 << MSHV_SET_MEM_BIT_UNMAP,
375 MemoryRegionFlags::READ => 0,
376 MemoryRegionFlags::WRITE => 1 << MSHV_SET_MEM_BIT_WRITABLE,
377 MemoryRegionFlags::EXECUTE => 1 << MSHV_SET_MEM_BIT_EXECUTABLE,
378 _ => 0, };
380 acc | flag_value
381 });
382
383 mshv_user_mem_region {
384 guest_pfn,
385 size,
386 userspace_addr,
387 flags,
388 ..Default::default()
389 }
390 }
391}
392
393#[cfg(kvm)]
394impl From<&MemoryRegion> for kvm_bindings::kvm_userspace_memory_region {
395 fn from(region: &MemoryRegion) -> Self {
396 let perm_flags =
397 MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE;
398
399 let perm_flags = perm_flags.intersection(region.flags);
400
401 kvm_userspace_memory_region {
402 slot: 0,
403 guest_phys_addr: region.guest_region.start as u64,
404 memory_size: (region.guest_region.end - region.guest_region.start) as u64,
405 userspace_addr: region.host_region.start as u64,
406 flags: if perm_flags.contains(MemoryRegionFlags::WRITE) {
407 0 } else {
409 KVM_MEM_READONLY },
412 }
413 }
414}