1use std::ffi::CString;
5use std::fs::{File, OpenOptions};
6use std::os::unix::io::FromRawFd;
7use std::path::Path;
8use std::str::FromStr;
9
10use nix::sys::memfd;
11use vm_memory::{Address, FileOffset, GuestAddress, GuestUsize};
12
13use crate::memory::MemorySourceType;
14use crate::memory::MemorySourceType::MemFdShared;
15use crate::AddressSpaceError;
16
17#[derive(Clone, Copy, Debug, Eq, PartialEq)]
26pub enum AddressSpaceRegionType {
27 DefaultMemory,
29 DeviceMemory,
31 DAXMemory,
33}
34
35#[derive(Debug, Clone)]
37pub struct AddressSpaceRegion {
38 pub ty: AddressSpaceRegionType,
40 pub base: GuestAddress,
42 pub size: GuestUsize,
44 pub host_numa_node_id: Option<u32>,
46
47 file_offset: Option<FileOffset>,
49 perm_flags: i32,
51 prot_flags: i32,
53 is_hugepage: bool,
57 is_hotplug: bool,
59 is_anon: bool,
63}
64
65#[allow(clippy::too_many_arguments)]
66impl AddressSpaceRegion {
67 pub fn new(ty: AddressSpaceRegionType, base: GuestAddress, size: GuestUsize) -> Self {
69 AddressSpaceRegion {
70 ty,
71 base,
72 size,
73 host_numa_node_id: None,
74 file_offset: None,
75 perm_flags: libc::MAP_SHARED,
76 prot_flags: libc::PROT_READ | libc::PROT_WRITE,
77 is_hugepage: false,
78 is_hotplug: false,
79 is_anon: false,
80 }
81 }
82
83 pub fn build(
95 ty: AddressSpaceRegionType,
96 base: GuestAddress,
97 size: GuestUsize,
98 host_numa_node_id: Option<u32>,
99 file_offset: Option<FileOffset>,
100 perm_flags: i32,
101 prot_flags: i32,
102 is_hotplug: bool,
103 ) -> Self {
104 let mut region = Self::new(ty, base, size);
105
106 region.set_host_numa_node_id(host_numa_node_id);
107 region.set_file_offset(file_offset);
108 region.set_perm_flags(perm_flags);
109 region.set_prot_flags(prot_flags);
110 if is_hotplug {
111 region.set_hotplug();
112 }
113
114 region
115 }
116
117 pub fn create_default_memory_region(
128 base: GuestAddress,
129 size: GuestUsize,
130 numa_node_id: Option<u32>,
131 mem_type: &str,
132 mem_file_path: &str,
133 mem_prealloc: bool,
134 is_hotplug: bool,
135 ) -> Result<AddressSpaceRegion, AddressSpaceError> {
136 Self::create_memory_region(
137 base,
138 size,
139 numa_node_id,
140 mem_type,
141 mem_file_path,
142 mem_prealloc,
143 libc::PROT_READ | libc::PROT_WRITE,
144 is_hotplug,
145 )
146 }
147
148 pub fn create_memory_region(
160 base: GuestAddress,
161 size: GuestUsize,
162 numa_node_id: Option<u32>,
163 mem_type: &str,
164 mem_file_path: &str,
165 mem_prealloc: bool,
166 prot_flags: i32,
167 is_hotplug: bool,
168 ) -> Result<AddressSpaceRegion, AddressSpaceError> {
169 let perm_flags = if mem_prealloc {
170 libc::MAP_SHARED | libc::MAP_POPULATE
171 } else {
172 libc::MAP_SHARED
173 };
174 let source_type = MemorySourceType::from_str(mem_type)
175 .map_err(|_e| AddressSpaceError::InvalidMemorySourceType(mem_type.to_string()))?;
176 let mut reg = match source_type {
177 MemorySourceType::MemFdShared | MemorySourceType::MemFdOnHugeTlbFs => {
178 let fn_str = if source_type == MemFdShared {
179 CString::new("shmem").expect("CString::new('shmem') failed")
180 } else {
181 CString::new("hugeshmem").expect("CString::new('hugeshmem') failed")
182 };
183 let filename = fn_str.as_c_str();
184 let fd = memfd::memfd_create(filename, memfd::MemFdCreateFlag::empty())
185 .map_err(AddressSpaceError::CreateMemFd)?;
186 let file: File = unsafe { File::from_raw_fd(fd) };
188 file.set_len(size).map_err(AddressSpaceError::SetFileSize)?;
189 Self::build(
190 AddressSpaceRegionType::DefaultMemory,
191 base,
192 size,
193 numa_node_id,
194 Some(FileOffset::new(file, 0)),
195 perm_flags,
196 prot_flags,
197 is_hotplug,
198 )
199 }
200 MemorySourceType::MmapAnonymous | MemorySourceType::MmapAnonymousHugeTlbFs => {
201 let mut perm_flags = libc::MAP_PRIVATE | libc::MAP_ANONYMOUS;
202 if mem_prealloc {
203 perm_flags |= libc::MAP_POPULATE
204 }
205 Self::build(
206 AddressSpaceRegionType::DefaultMemory,
207 base,
208 size,
209 numa_node_id,
210 None,
211 perm_flags,
212 prot_flags,
213 is_hotplug,
214 )
215 }
216 MemorySourceType::FileOnHugeTlbFs => {
217 let path = Path::new(mem_file_path);
218 if let Some(parent_dir) = path.parent() {
219 std::fs::create_dir_all(parent_dir).map_err(AddressSpaceError::CreateDir)?;
221 }
222 let file = OpenOptions::new()
223 .read(true)
224 .write(true)
225 .create(true)
226 .open(mem_file_path)
227 .map_err(AddressSpaceError::OpenFile)?;
228 nix::unistd::unlink(mem_file_path).map_err(AddressSpaceError::UnlinkFile)?;
229 file.set_len(size).map_err(AddressSpaceError::SetFileSize)?;
230 let file_offset = FileOffset::new(file, 0);
231 Self::build(
232 AddressSpaceRegionType::DefaultMemory,
233 base,
234 size,
235 numa_node_id,
236 Some(file_offset),
237 perm_flags,
238 prot_flags,
239 is_hotplug,
240 )
241 }
242 };
243
244 if source_type.is_hugepage() {
245 reg.set_hugepage();
246 }
247 if source_type.is_mmap_anonymous() {
248 reg.set_anonpage();
249 }
250
251 Ok(reg)
252 }
253
254 pub fn create_device_region(
260 base: GuestAddress,
261 size: GuestUsize,
262 ) -> Result<AddressSpaceRegion, AddressSpaceError> {
263 Ok(Self::build(
264 AddressSpaceRegionType::DeviceMemory,
265 base,
266 size,
267 None,
268 None,
269 0,
270 0,
271 false,
272 ))
273 }
274
275 pub fn region_type(&self) -> AddressSpaceRegionType {
277 self.ty
278 }
279
280 pub fn len(&self) -> GuestUsize {
282 self.size
283 }
284
285 pub fn start_addr(&self) -> GuestAddress {
287 self.base
288 }
289
290 pub fn last_addr(&self) -> GuestAddress {
292 debug_assert!(self.size > 0 && self.base.checked_add(self.size).is_some());
293 GuestAddress(self.base.raw_value() + self.size - 1)
294 }
295
296 pub fn perm_flags(&self) -> i32 {
298 self.perm_flags
299 }
300
301 pub fn set_perm_flags(&mut self, perm_flags: i32) {
303 self.perm_flags = perm_flags;
304 }
305
306 pub fn prot_flags(&self) -> i32 {
308 self.prot_flags
309 }
310
311 pub fn set_prot_flags(&mut self, prot_flags: i32) {
313 self.prot_flags = prot_flags;
314 }
315
316 pub fn host_numa_node_id(&self) -> Option<u32> {
318 self.host_numa_node_id
319 }
320
321 pub fn set_host_numa_node_id(&mut self, host_numa_node_id: Option<u32>) {
323 self.host_numa_node_id = host_numa_node_id;
324 }
325
326 pub fn has_file(&self) -> bool {
328 self.file_offset.is_some()
329 }
330
331 pub fn file_offset(&self) -> Option<&FileOffset> {
333 self.file_offset.as_ref()
334 }
335
336 pub fn set_file_offset(&mut self, file_offset: Option<FileOffset>) {
338 self.file_offset = file_offset;
339 }
340
341 pub fn set_hotplug(&mut self) {
343 self.is_hotplug = true
344 }
345
346 pub fn is_hotplug(&self) -> bool {
348 self.is_hotplug
349 }
350
351 pub fn set_hugepage(&mut self) {
353 self.is_hugepage = true
354 }
355
356 pub fn is_hugepage(&self) -> bool {
358 self.is_hugepage
359 }
360
361 pub fn set_anonpage(&mut self) {
363 self.is_anon = true
364 }
365
366 pub fn is_anonpage(&self) -> bool {
368 self.is_anon
369 }
370
371 pub fn is_valid(&self) -> bool {
373 self.size > 0 && self.base.checked_add(self.size).is_some()
374 }
375
376 pub fn intersect_with(&self, other: &AddressSpaceRegion) -> bool {
378 let end1 = match self.base.checked_add(self.size) {
380 Some(addr) => addr,
381 None => return true,
382 };
383 let end2 = match other.base.checked_add(other.size) {
384 Some(addr) => addr,
385 None => return true,
386 };
387
388 !(end1 <= other.base || self.base >= end2)
389 }
390}
391
392#[cfg(test)]
393mod tests {
394 use super::*;
395 use std::io::Write;
396 use vmm_sys_util::tempfile::TempFile;
397
398 #[test]
399 fn test_address_space_region_valid() {
400 let reg1 = AddressSpaceRegion::new(
401 AddressSpaceRegionType::DefaultMemory,
402 GuestAddress(0xFFFFFFFFFFFFF000),
403 0x2000,
404 );
405 assert!(!reg1.is_valid());
406 let reg1 = AddressSpaceRegion::new(
407 AddressSpaceRegionType::DefaultMemory,
408 GuestAddress(0xFFFFFFFFFFFFF000),
409 0x1000,
410 );
411 assert!(!reg1.is_valid());
412 let reg1 = AddressSpaceRegion::new(
413 AddressSpaceRegionType::DeviceMemory,
414 GuestAddress(0xFFFFFFFFFFFFE000),
415 0x1000,
416 );
417 assert!(reg1.is_valid());
418 assert_eq!(reg1.start_addr(), GuestAddress(0xFFFFFFFFFFFFE000));
419 assert_eq!(reg1.len(), 0x1000);
420 assert!(!reg1.has_file());
421 assert!(reg1.file_offset().is_none());
422 assert_eq!(reg1.perm_flags(), libc::MAP_SHARED);
423 assert_eq!(reg1.prot_flags(), libc::PROT_READ | libc::PROT_WRITE);
424 assert_eq!(reg1.region_type(), AddressSpaceRegionType::DeviceMemory);
425
426 let tmp_file = TempFile::new().unwrap();
427 let mut f = tmp_file.into_file();
428 let sample_buf = &[1, 2, 3, 4, 5];
429 assert!(f.write_all(sample_buf).is_ok());
430 let reg2 = AddressSpaceRegion::build(
431 AddressSpaceRegionType::DefaultMemory,
432 GuestAddress(0x1000),
433 0x1000,
434 None,
435 Some(FileOffset::new(f, 0x0)),
436 0x5a,
437 0x5a,
438 false,
439 );
440 assert_eq!(reg2.region_type(), AddressSpaceRegionType::DefaultMemory);
441 assert!(reg2.is_valid());
442 assert_eq!(reg2.start_addr(), GuestAddress(0x1000));
443 assert_eq!(reg2.len(), 0x1000);
444 assert!(reg2.has_file());
445 assert!(reg2.file_offset().is_some());
446 assert_eq!(reg2.perm_flags(), 0x5a);
447 assert_eq!(reg2.prot_flags(), 0x5a);
448 }
449
450 #[test]
451 fn test_address_space_region_intersect() {
452 let reg1 = AddressSpaceRegion::new(
453 AddressSpaceRegionType::DefaultMemory,
454 GuestAddress(0x1000),
455 0x1000,
456 );
457 let reg2 = AddressSpaceRegion::new(
458 AddressSpaceRegionType::DefaultMemory,
459 GuestAddress(0x2000),
460 0x1000,
461 );
462 let reg3 = AddressSpaceRegion::new(
463 AddressSpaceRegionType::DefaultMemory,
464 GuestAddress(0x1000),
465 0x1001,
466 );
467 let reg4 = AddressSpaceRegion::new(
468 AddressSpaceRegionType::DefaultMemory,
469 GuestAddress(0x1100),
470 0x100,
471 );
472 let reg5 = AddressSpaceRegion::new(
473 AddressSpaceRegionType::DefaultMemory,
474 GuestAddress(0xFFFFFFFFFFFFF000),
475 0x2000,
476 );
477
478 assert!(!reg1.intersect_with(®2));
479 assert!(!reg2.intersect_with(®1));
480
481 assert!(reg1.intersect_with(®1));
483
484 assert!(reg3.intersect_with(®2));
486 assert!(reg2.intersect_with(®3));
487 assert!(reg1.intersect_with(®4));
488 assert!(reg4.intersect_with(®1));
489 assert!(reg1.intersect_with(®5));
490 assert!(reg5.intersect_with(®1));
491 }
492
493 #[test]
494 fn test_create_device_region() {
495 let reg = AddressSpaceRegion::create_device_region(GuestAddress(0x10000), 0x1000).unwrap();
496 assert_eq!(reg.region_type(), AddressSpaceRegionType::DeviceMemory);
497 assert_eq!(reg.start_addr(), GuestAddress(0x10000));
498 assert_eq!(reg.len(), 0x1000);
499 }
500
501 #[test]
502 fn test_create_default_memory_region() {
503 AddressSpaceRegion::create_default_memory_region(
504 GuestAddress(0x100000),
505 0x100000,
506 None,
507 "invalid",
508 "invalid",
509 false,
510 false,
511 )
512 .unwrap_err();
513
514 let reg = AddressSpaceRegion::create_default_memory_region(
515 GuestAddress(0x100000),
516 0x100000,
517 None,
518 "shmem",
519 "",
520 false,
521 false,
522 )
523 .unwrap();
524 assert_eq!(reg.region_type(), AddressSpaceRegionType::DefaultMemory);
525 assert_eq!(reg.start_addr(), GuestAddress(0x100000));
526 assert_eq!(reg.last_addr(), GuestAddress(0x1fffff));
527 assert_eq!(reg.len(), 0x100000);
528 assert!(reg.file_offset().is_some());
529
530 let reg = AddressSpaceRegion::create_default_memory_region(
531 GuestAddress(0x100000),
532 0x100000,
533 None,
534 "hugeshmem",
535 "",
536 true,
537 false,
538 )
539 .unwrap();
540 assert_eq!(reg.region_type(), AddressSpaceRegionType::DefaultMemory);
541 assert_eq!(reg.start_addr(), GuestAddress(0x100000));
542 assert_eq!(reg.last_addr(), GuestAddress(0x1fffff));
543 assert_eq!(reg.len(), 0x100000);
544 assert!(reg.file_offset().is_some());
545
546 let reg = AddressSpaceRegion::create_default_memory_region(
547 GuestAddress(0x100000),
548 0x100000,
549 None,
550 "mmap",
551 "",
552 true,
553 false,
554 )
555 .unwrap();
556 assert_eq!(reg.region_type(), AddressSpaceRegionType::DefaultMemory);
557 assert_eq!(reg.start_addr(), GuestAddress(0x100000));
558 assert_eq!(reg.last_addr(), GuestAddress(0x1fffff));
559 assert_eq!(reg.len(), 0x100000);
560 assert!(reg.file_offset().is_none());
561
562 }
564}