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(feature = "init-paging")]
35use super::mgr::{PAGE_NX, PAGE_PRESENT, PAGE_RW, PAGE_USER};
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 const STACK_GUARD = 8;
53 }
54}
55
56impl MemoryRegionFlags {
57 #[cfg(feature = "init-paging")]
58 pub(crate) fn translate_flags(&self) -> u64 {
59 let mut page_flags = 0;
60
61 page_flags |= PAGE_PRESENT; if self.contains(MemoryRegionFlags::WRITE) {
64 page_flags |= PAGE_RW; }
66
67 if self.contains(MemoryRegionFlags::STACK_GUARD) {
68 page_flags |= PAGE_RW; }
70
71 if self.contains(MemoryRegionFlags::EXECUTE) {
72 page_flags |= PAGE_USER; } else {
74 page_flags |= PAGE_NX; }
76
77 page_flags
78 }
79}
80
81impl std::fmt::Display for MemoryRegionFlags {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 if self.is_empty() {
84 write!(f, "NONE")
85 } else {
86 let mut first = true;
87 if self.contains(MemoryRegionFlags::READ) {
88 write!(f, "READ")?;
89 first = false;
90 }
91 if self.contains(MemoryRegionFlags::WRITE) {
92 if !first {
93 write!(f, " | ")?;
94 }
95 write!(f, "WRITE")?;
96 first = false;
97 }
98 if self.contains(MemoryRegionFlags::EXECUTE) {
99 if !first {
100 write!(f, " | ")?;
101 }
102 write!(f, "EXECUTE")?;
103 }
104 Ok(())
105 }
106 }
107}
108
109#[cfg(target_os = "windows")]
110impl TryFrom<WHV_MEMORY_ACCESS_TYPE> for MemoryRegionFlags {
111 type Error = crate::HyperlightError;
112
113 fn try_from(flags: WHV_MEMORY_ACCESS_TYPE) -> crate::Result<Self> {
114 match flags {
115 Hypervisor::WHvMemoryAccessRead => Ok(MemoryRegionFlags::READ),
116 Hypervisor::WHvMemoryAccessWrite => Ok(MemoryRegionFlags::WRITE),
117 Hypervisor::WHvMemoryAccessExecute => Ok(MemoryRegionFlags::EXECUTE),
118 _ => Err(crate::HyperlightError::Error(
119 "unknown memory access type".to_string(),
120 )),
121 }
122 }
123}
124
125#[cfg(mshv3)]
126impl TryFrom<hv_x64_memory_intercept_message> for MemoryRegionFlags {
127 type Error = crate::HyperlightError;
128
129 fn try_from(msg: hv_x64_memory_intercept_message) -> crate::Result<Self> {
130 let access_type = msg.header.intercept_access_type;
131 match access_type {
132 0 => Ok(MemoryRegionFlags::READ),
133 1 => Ok(MemoryRegionFlags::WRITE),
134 2 => Ok(MemoryRegionFlags::EXECUTE),
135 _ => Err(crate::HyperlightError::Error(
136 "unknown memory access type".to_string(),
137 )),
138 }
139 }
140}
141
142#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
144pub enum MemoryRegionType {
146 PageTables,
148 Code,
150 InitData,
152 Peb,
154 HostFunctionDefinitions,
156 InputData,
158 OutputData,
160 Heap,
162 GuardPage,
164 Stack,
166}
167
168#[derive(Debug, Clone, PartialEq, Eq, Hash)]
171pub struct MemoryRegion {
172 pub guest_region: Range<usize>,
174 pub host_region: Range<usize>,
176 pub flags: MemoryRegionFlags,
178 pub region_type: MemoryRegionType,
180}
181
182pub(crate) struct MemoryRegionVecBuilder {
183 guest_base_phys_addr: usize,
184 host_base_virt_addr: usize,
185 regions: Vec<MemoryRegion>,
186}
187
188impl MemoryRegionVecBuilder {
189 pub(crate) fn new(guest_base_phys_addr: usize, host_base_virt_addr: usize) -> Self {
190 Self {
191 guest_base_phys_addr,
192 host_base_virt_addr,
193 regions: Vec::new(),
194 }
195 }
196
197 fn push(
198 &mut self,
199 size: usize,
200 flags: MemoryRegionFlags,
201 region_type: MemoryRegionType,
202 ) -> usize {
203 if self.regions.is_empty() {
204 let guest_end = self.guest_base_phys_addr + size;
205 let host_end = self.host_base_virt_addr + size;
206 self.regions.push(MemoryRegion {
207 guest_region: self.guest_base_phys_addr..guest_end,
208 host_region: self.host_base_virt_addr..host_end,
209 flags,
210 region_type,
211 });
212 return guest_end - self.guest_base_phys_addr;
213 }
214
215 #[allow(clippy::unwrap_used)]
216 let last_region = self.regions.last().unwrap();
218 let new_region = MemoryRegion {
219 guest_region: last_region.guest_region.end..last_region.guest_region.end + size,
220 host_region: last_region.host_region.end..last_region.host_region.end + size,
221 flags,
222 region_type,
223 };
224 let ret = new_region.guest_region.end;
225 self.regions.push(new_region);
226 ret - self.guest_base_phys_addr
227 }
228
229 pub(crate) fn push_page_aligned(
234 &mut self,
235 size: usize,
236 flags: MemoryRegionFlags,
237 region_type: MemoryRegionType,
238 ) -> usize {
239 let aligned_size = (size + PAGE_SIZE_USIZE - 1) & !(PAGE_SIZE_USIZE - 1);
240 self.push(aligned_size, flags, region_type)
241 }
242
243 pub(crate) fn build(self) -> Vec<MemoryRegion> {
246 self.regions
247 }
248}
249
250#[cfg(mshv3)]
251impl From<&MemoryRegion> for mshv_user_mem_region {
252 fn from(region: &MemoryRegion) -> Self {
253 let size = (region.guest_region.end - region.guest_region.start) as u64;
254 let guest_pfn = region.guest_region.start as u64 >> PAGE_SHIFT;
255 let userspace_addr = region.host_region.start as u64;
256
257 let flags: u8 = region.flags.iter().fold(0, |acc, flag| {
258 let flag_value = match flag {
259 MemoryRegionFlags::NONE => 1 << MSHV_SET_MEM_BIT_UNMAP,
260 MemoryRegionFlags::READ => 0,
261 MemoryRegionFlags::WRITE => 1 << MSHV_SET_MEM_BIT_WRITABLE,
262 MemoryRegionFlags::EXECUTE => 1 << MSHV_SET_MEM_BIT_EXECUTABLE,
263 _ => 0, };
265 acc | flag_value
266 });
267
268 mshv_user_mem_region {
269 guest_pfn,
270 size,
271 userspace_addr,
272 flags,
273 ..Default::default()
274 }
275 }
276}
277
278#[cfg(kvm)]
279impl From<&MemoryRegion> for kvm_bindings::kvm_userspace_memory_region {
280 fn from(region: &MemoryRegion) -> Self {
281 let perm_flags =
282 MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE;
283
284 let perm_flags = perm_flags.intersection(region.flags);
285
286 kvm_userspace_memory_region {
287 slot: 0,
288 guest_phys_addr: region.guest_region.start as u64,
289 memory_size: (region.guest_region.end - region.guest_region.start) as u64,
290 userspace_addr: region.host_region.start as u64,
291 flags: if perm_flags.contains(MemoryRegionFlags::WRITE) {
292 0 } else {
294 KVM_MEM_READONLY },
297 }
298 }
299}