1#[cfg(mshv2)]
18extern crate mshv_bindings2 as mshv_bindings;
19#[cfg(mshv2)]
20extern crate mshv_ioctls2 as mshv_ioctls;
21
22#[cfg(mshv3)]
23extern crate mshv_bindings3 as mshv_bindings;
24#[cfg(mshv3)]
25extern crate mshv_ioctls3 as mshv_ioctls;
26
27use std::ops::Range;
28
29use bitflags::bitflags;
30#[cfg(mshv)]
31use hyperlight_common::mem::PAGE_SHIFT;
32use hyperlight_common::mem::PAGE_SIZE_USIZE;
33#[cfg(mshv)]
34use mshv_bindings::{hv_x64_memory_intercept_message, mshv_user_mem_region};
35#[cfg(mshv2)]
36use mshv_bindings::{
37 HV_MAP_GPA_EXECUTABLE, HV_MAP_GPA_PERMISSIONS_NONE, HV_MAP_GPA_READABLE, HV_MAP_GPA_WRITABLE,
38};
39#[cfg(mshv3)]
40use mshv_bindings::{
41 MSHV_SET_MEM_BIT_EXECUTABLE, MSHV_SET_MEM_BIT_UNMAP, MSHV_SET_MEM_BIT_WRITABLE,
42};
43#[cfg(target_os = "windows")]
44use windows::Win32::System::Hypervisor::{self, WHV_MEMORY_ACCESS_TYPE};
45
46bitflags! {
47 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
49 pub struct MemoryRegionFlags: u32 {
50 const NONE = 0;
52 const READ = 1;
54 const WRITE = 2;
56 const EXECUTE = 4;
58 const STACK_GUARD = 8;
60 }
61}
62
63impl std::fmt::Display for MemoryRegionFlags {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 if self.is_empty() {
66 write!(f, "NONE")
67 } else {
68 let mut first = true;
69 if self.contains(MemoryRegionFlags::READ) {
70 write!(f, "READ")?;
71 first = false;
72 }
73 if self.contains(MemoryRegionFlags::WRITE) {
74 if !first {
75 write!(f, " | ")?;
76 }
77 write!(f, "WRITE")?;
78 first = false;
79 }
80 if self.contains(MemoryRegionFlags::EXECUTE) {
81 if !first {
82 write!(f, " | ")?;
83 }
84 write!(f, "EXECUTE")?;
85 }
86 Ok(())
87 }
88 }
89}
90
91#[cfg(target_os = "windows")]
92impl TryFrom<WHV_MEMORY_ACCESS_TYPE> for MemoryRegionFlags {
93 type Error = crate::HyperlightError;
94
95 fn try_from(flags: WHV_MEMORY_ACCESS_TYPE) -> crate::Result<Self> {
96 match flags {
97 Hypervisor::WHvMemoryAccessRead => Ok(MemoryRegionFlags::READ),
98 Hypervisor::WHvMemoryAccessWrite => Ok(MemoryRegionFlags::WRITE),
99 Hypervisor::WHvMemoryAccessExecute => Ok(MemoryRegionFlags::EXECUTE),
100 _ => Err(crate::HyperlightError::Error(
101 "unknown memory access type".to_string(),
102 )),
103 }
104 }
105}
106
107#[cfg(mshv)]
108impl TryFrom<hv_x64_memory_intercept_message> for MemoryRegionFlags {
109 type Error = crate::HyperlightError;
110
111 fn try_from(msg: hv_x64_memory_intercept_message) -> crate::Result<Self> {
112 let access_type = msg.header.intercept_access_type;
113 match access_type {
114 0 => Ok(MemoryRegionFlags::READ),
115 1 => Ok(MemoryRegionFlags::WRITE),
116 2 => Ok(MemoryRegionFlags::EXECUTE),
117 _ => Err(crate::HyperlightError::Error(
118 "unknown memory access type".to_string(),
119 )),
120 }
121 }
122}
123
124#[derive(Debug, PartialEq, Eq, Copy, Clone)]
126pub enum MemoryRegionType {
128 PageTables,
130 Code,
132 Peb,
134 HostFunctionDefinitions,
136 HostExceptionData,
138 GuestErrorData,
140 InputData,
142 OutputData,
144 PanicContext,
146 Heap,
148 GuardPage,
150 Stack,
152 KernelStack,
154 BootStack,
156}
157
158#[derive(Debug, Clone, PartialEq, Eq)]
161pub struct MemoryRegion {
162 pub(crate) guest_region: Range<usize>,
164 pub(crate) host_region: Range<usize>,
166 pub(crate) flags: MemoryRegionFlags,
168 pub(crate) region_type: MemoryRegionType,
170}
171
172pub(crate) struct MemoryRegionVecBuilder {
173 guest_base_phys_addr: usize,
174 host_base_virt_addr: usize,
175 regions: Vec<MemoryRegion>,
176}
177
178impl MemoryRegionVecBuilder {
179 pub(crate) fn new(guest_base_phys_addr: usize, host_base_virt_addr: usize) -> Self {
180 Self {
181 guest_base_phys_addr,
182 host_base_virt_addr,
183 regions: Vec::new(),
184 }
185 }
186
187 fn push(
188 &mut self,
189 size: usize,
190 flags: MemoryRegionFlags,
191 region_type: MemoryRegionType,
192 ) -> usize {
193 if self.regions.is_empty() {
194 let guest_end = self.guest_base_phys_addr + size;
195 let host_end = self.host_base_virt_addr + size;
196 self.regions.push(MemoryRegion {
197 guest_region: self.guest_base_phys_addr..guest_end,
198 host_region: self.host_base_virt_addr..host_end,
199 flags,
200 region_type,
201 });
202 return guest_end - self.guest_base_phys_addr;
203 }
204
205 #[allow(clippy::unwrap_used)]
206 let last_region = self.regions.last().unwrap();
208 let new_region = MemoryRegion {
209 guest_region: last_region.guest_region.end..last_region.guest_region.end + size,
210 host_region: last_region.host_region.end..last_region.host_region.end + size,
211 flags,
212 region_type,
213 };
214 let ret = new_region.guest_region.end;
215 self.regions.push(new_region);
216 ret - self.guest_base_phys_addr
217 }
218
219 pub(crate) fn push_page_aligned(
224 &mut self,
225 size: usize,
226 flags: MemoryRegionFlags,
227 region_type: MemoryRegionType,
228 ) -> usize {
229 let aligned_size = (size + PAGE_SIZE_USIZE - 1) & !(PAGE_SIZE_USIZE - 1);
230 self.push(aligned_size, flags, region_type)
231 }
232
233 pub(crate) fn build(self) -> Vec<MemoryRegion> {
236 self.regions
237 }
238}
239
240#[cfg(mshv)]
241impl From<MemoryRegion> for mshv_user_mem_region {
242 fn from(region: MemoryRegion) -> Self {
243 let size = (region.guest_region.end - region.guest_region.start) as u64;
244 let guest_pfn = region.guest_region.start as u64 >> PAGE_SHIFT;
245 let userspace_addr = region.host_region.start as u64;
246
247 #[cfg(mshv2)]
248 {
249 let flags = region.flags.iter().fold(0, |acc, flag| {
250 let flag_value = match flag {
251 MemoryRegionFlags::NONE => HV_MAP_GPA_PERMISSIONS_NONE,
252 MemoryRegionFlags::READ => HV_MAP_GPA_READABLE,
253 MemoryRegionFlags::WRITE => HV_MAP_GPA_WRITABLE,
254 MemoryRegionFlags::EXECUTE => HV_MAP_GPA_EXECUTABLE,
255 _ => 0, };
257 acc | flag_value
258 });
259 mshv_user_mem_region {
260 guest_pfn,
261 size,
262 userspace_addr,
263 flags,
264 }
265 }
266 #[cfg(mshv3)]
267 {
268 let flags: u8 = region.flags.iter().fold(0, |acc, flag| {
269 let flag_value = match flag {
270 MemoryRegionFlags::NONE => 1 << MSHV_SET_MEM_BIT_UNMAP,
271 MemoryRegionFlags::READ => 0,
272 MemoryRegionFlags::WRITE => 1 << MSHV_SET_MEM_BIT_WRITABLE,
273 MemoryRegionFlags::EXECUTE => 1 << MSHV_SET_MEM_BIT_EXECUTABLE,
274 _ => 0, };
276 acc | flag_value
277 });
278
279 mshv_user_mem_region {
280 guest_pfn,
281 size,
282 userspace_addr,
283 flags,
284 ..Default::default()
285 }
286 }
287 }
288}