1#[cfg(mshv3)]
18extern crate mshv_bindings;
19#[cfg(mshv3)]
20extern crate mshv_ioctls;
21
22use std::ops::Range;
23
24use bitflags::bitflags;
25#[cfg(mshv3)]
26use hyperlight_common::mem::PAGE_SHIFT;
27use hyperlight_common::mem::PAGE_SIZE_USIZE;
28#[cfg(kvm)]
29use kvm_bindings::{KVM_MEM_READONLY, kvm_userspace_memory_region};
30#[cfg(mshv3)]
31use mshv_bindings::{
32 MSHV_SET_MEM_BIT_EXECUTABLE, MSHV_SET_MEM_BIT_UNMAP, MSHV_SET_MEM_BIT_WRITABLE,
33};
34#[cfg(mshv3)]
35use mshv_bindings::{hv_x64_memory_intercept_message, mshv_user_mem_region};
36#[cfg(target_os = "windows")]
37use windows::Win32::System::Hypervisor::{self, WHV_MEMORY_ACCESS_TYPE};
38
39#[cfg(feature = "init-paging")]
40use super::mgr::{PAGE_NX, PAGE_PRESENT, PAGE_RW, PAGE_USER};
41
42pub(crate) const DEFAULT_GUEST_BLOB_MEM_FLAGS: MemoryRegionFlags = MemoryRegionFlags::READ;
43
44bitflags! {
45 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
47 pub struct MemoryRegionFlags: u32 {
48 const NONE = 0;
50 const READ = 1;
52 const WRITE = 2;
54 const EXECUTE = 4;
56 const STACK_GUARD = 8;
58 }
59}
60
61impl MemoryRegionFlags {
62 #[cfg(feature = "init-paging")]
63 pub(crate) fn translate_flags(&self) -> u64 {
64 let mut page_flags = 0;
65
66 page_flags |= PAGE_PRESENT; if self.contains(MemoryRegionFlags::WRITE) {
69 page_flags |= PAGE_RW; }
71
72 if self.contains(MemoryRegionFlags::STACK_GUARD) {
73 page_flags |= PAGE_RW; }
75
76 if self.contains(MemoryRegionFlags::EXECUTE) {
77 page_flags |= PAGE_USER; } else {
79 page_flags |= PAGE_NX; }
81
82 page_flags
83 }
84}
85
86impl std::fmt::Display for MemoryRegionFlags {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 if self.is_empty() {
89 write!(f, "NONE")
90 } else {
91 let mut first = true;
92 if self.contains(MemoryRegionFlags::READ) {
93 write!(f, "READ")?;
94 first = false;
95 }
96 if self.contains(MemoryRegionFlags::WRITE) {
97 if !first {
98 write!(f, " | ")?;
99 }
100 write!(f, "WRITE")?;
101 first = false;
102 }
103 if self.contains(MemoryRegionFlags::EXECUTE) {
104 if !first {
105 write!(f, " | ")?;
106 }
107 write!(f, "EXECUTE")?;
108 }
109 Ok(())
110 }
111 }
112}
113
114#[cfg(target_os = "windows")]
115impl TryFrom<WHV_MEMORY_ACCESS_TYPE> for MemoryRegionFlags {
116 type Error = crate::HyperlightError;
117
118 fn try_from(flags: WHV_MEMORY_ACCESS_TYPE) -> crate::Result<Self> {
119 match flags {
120 Hypervisor::WHvMemoryAccessRead => Ok(MemoryRegionFlags::READ),
121 Hypervisor::WHvMemoryAccessWrite => Ok(MemoryRegionFlags::WRITE),
122 Hypervisor::WHvMemoryAccessExecute => Ok(MemoryRegionFlags::EXECUTE),
123 _ => Err(crate::HyperlightError::Error(
124 "unknown memory access type".to_string(),
125 )),
126 }
127 }
128}
129
130#[cfg(mshv3)]
131impl TryFrom<hv_x64_memory_intercept_message> for MemoryRegionFlags {
132 type Error = crate::HyperlightError;
133
134 fn try_from(msg: hv_x64_memory_intercept_message) -> crate::Result<Self> {
135 let access_type = msg.header.intercept_access_type;
136 match access_type {
137 0 => Ok(MemoryRegionFlags::READ),
138 1 => Ok(MemoryRegionFlags::WRITE),
139 2 => Ok(MemoryRegionFlags::EXECUTE),
140 _ => Err(crate::HyperlightError::Error(
141 "unknown memory access type".to_string(),
142 )),
143 }
144 }
145}
146
147#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
149pub enum MemoryRegionType {
151 PageTables,
153 Code,
155 InitData,
157 Peb,
159 HostFunctionDefinitions,
161 InputData,
163 OutputData,
165 Heap,
167 GuardPage,
169 Stack,
171}
172
173#[derive(Debug, Clone, PartialEq, Eq, Hash)]
176pub struct MemoryRegion {
177 pub guest_region: Range<usize>,
179 pub host_region: Range<usize>,
181 pub flags: MemoryRegionFlags,
183 pub region_type: MemoryRegionType,
185}
186
187pub(crate) struct MemoryRegionVecBuilder {
188 guest_base_phys_addr: usize,
189 host_base_virt_addr: usize,
190 regions: Vec<MemoryRegion>,
191}
192
193impl MemoryRegionVecBuilder {
194 pub(crate) fn new(guest_base_phys_addr: usize, host_base_virt_addr: usize) -> Self {
195 Self {
196 guest_base_phys_addr,
197 host_base_virt_addr,
198 regions: Vec::new(),
199 }
200 }
201
202 fn push(
203 &mut self,
204 size: usize,
205 flags: MemoryRegionFlags,
206 region_type: MemoryRegionType,
207 ) -> usize {
208 if self.regions.is_empty() {
209 let guest_end = self.guest_base_phys_addr + size;
210 let host_end = self.host_base_virt_addr + size;
211 self.regions.push(MemoryRegion {
212 guest_region: self.guest_base_phys_addr..guest_end,
213 host_region: self.host_base_virt_addr..host_end,
214 flags,
215 region_type,
216 });
217 return guest_end - self.guest_base_phys_addr;
218 }
219
220 #[allow(clippy::unwrap_used)]
221 let last_region = self.regions.last().unwrap();
223 let new_region = MemoryRegion {
224 guest_region: last_region.guest_region.end..last_region.guest_region.end + size,
225 host_region: last_region.host_region.end..last_region.host_region.end + size,
226 flags,
227 region_type,
228 };
229 let ret = new_region.guest_region.end;
230 self.regions.push(new_region);
231 ret - self.guest_base_phys_addr
232 }
233
234 pub(crate) fn push_page_aligned(
239 &mut self,
240 size: usize,
241 flags: MemoryRegionFlags,
242 region_type: MemoryRegionType,
243 ) -> usize {
244 let aligned_size = (size + PAGE_SIZE_USIZE - 1) & !(PAGE_SIZE_USIZE - 1);
245 self.push(aligned_size, flags, region_type)
246 }
247
248 pub(crate) fn build(self) -> Vec<MemoryRegion> {
251 self.regions
252 }
253}
254
255#[cfg(mshv3)]
256impl From<MemoryRegion> for mshv_user_mem_region {
257 fn from(region: MemoryRegion) -> Self {
258 let size = (region.guest_region.end - region.guest_region.start) as u64;
259 let guest_pfn = region.guest_region.start as u64 >> PAGE_SHIFT;
260 let userspace_addr = region.host_region.start as u64;
261
262 let flags: u8 = region.flags.iter().fold(0, |acc, flag| {
263 let flag_value = match flag {
264 MemoryRegionFlags::NONE => 1 << MSHV_SET_MEM_BIT_UNMAP,
265 MemoryRegionFlags::READ => 0,
266 MemoryRegionFlags::WRITE => 1 << MSHV_SET_MEM_BIT_WRITABLE,
267 MemoryRegionFlags::EXECUTE => 1 << MSHV_SET_MEM_BIT_EXECUTABLE,
268 _ => 0, };
270 acc | flag_value
271 });
272
273 mshv_user_mem_region {
274 guest_pfn,
275 size,
276 userspace_addr,
277 flags,
278 ..Default::default()
279 }
280 }
281}
282
283#[cfg(kvm)]
284impl From<MemoryRegion> for kvm_bindings::kvm_userspace_memory_region {
285 fn from(region: MemoryRegion) -> Self {
286 let perm_flags =
287 MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE;
288
289 let perm_flags = perm_flags.intersection(region.flags);
290
291 kvm_userspace_memory_region {
292 slot: 0,
293 guest_phys_addr: region.guest_region.start as u64,
294 memory_size: (region.guest_region.end - region.guest_region.start) as u64,
295 userspace_addr: region.host_region.start as u64,
296 flags: if perm_flags.contains(MemoryRegionFlags::WRITE) {
297 0 } else {
299 KVM_MEM_READONLY },
302 }
303 }
304}