dbs_address_space/
address_space.rs

1// Copyright (C) 2021 Alibaba Cloud. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Physical address space manager for virtual machines.
5
6use std::sync::Arc;
7
8use arc_swap::ArcSwap;
9use vm_memory::{GuestAddress, GuestMemoryMmap};
10
11use crate::{AddressSpaceError, AddressSpaceLayout, AddressSpaceRegion, AddressSpaceRegionType};
12
13/// Base implementation to manage guest physical address space, without support of region hotplug.
14#[derive(Clone)]
15pub struct AddressSpaceBase {
16    regions: Vec<Arc<AddressSpaceRegion>>,
17    layout: AddressSpaceLayout,
18}
19
20impl AddressSpaceBase {
21    /// Create an instance of `AddressSpaceBase` from an `AddressSpaceRegion` array.
22    ///
23    /// To achieve better performance by using binary search algorithm, the `regions` vector
24    /// will gotten sorted by guest physical address.
25    ///
26    /// Note, panicking if some regions intersects with each other.
27    ///
28    /// # Arguments
29    /// * `regions` - prepared regions to managed by the address space instance.
30    /// * `layout` - prepared address space layout configuration.
31    pub fn from_regions(
32        mut regions: Vec<Arc<AddressSpaceRegion>>,
33        layout: AddressSpaceLayout,
34    ) -> Self {
35        regions.sort_unstable_by_key(|v| v.base);
36        for region in regions.iter() {
37            if !layout.is_region_valid(region) {
38                panic!(
39                    "Invalid region {:?} for address space layout {:?}",
40                    region, layout
41                );
42            }
43        }
44        for idx in 1..regions.len() {
45            if regions[idx].intersect_with(&regions[idx - 1]) {
46                panic!("address space regions intersect with each other");
47            }
48        }
49        AddressSpaceBase { regions, layout }
50    }
51
52    /// Insert a new address space region into the address space.
53    ///
54    /// # Arguments
55    /// * `region` - the new region to be inserted.
56    pub fn insert_region(
57        &mut self,
58        region: Arc<AddressSpaceRegion>,
59    ) -> Result<(), AddressSpaceError> {
60        if !self.layout.is_region_valid(&region) {
61            return Err(AddressSpaceError::InvalidAddressRange(
62                region.start_addr().0,
63                region.len(),
64            ));
65        }
66        for idx in 0..self.regions.len() {
67            if self.regions[idx].intersect_with(&region) {
68                return Err(AddressSpaceError::InvalidAddressRange(
69                    region.start_addr().0,
70                    region.len(),
71                ));
72            }
73        }
74        self.regions.push(region);
75        Ok(())
76    }
77
78    /// Enumerate all regions in the address space.
79    ///
80    /// # Arguments
81    /// * `cb` - the callback function to apply to each region.
82    pub fn walk_regions<F>(&self, mut cb: F) -> Result<(), AddressSpaceError>
83    where
84        F: FnMut(&Arc<AddressSpaceRegion>) -> Result<(), AddressSpaceError>,
85    {
86        for reg in self.regions.iter() {
87            cb(reg)?;
88        }
89
90        Ok(())
91    }
92
93    /// Get address space layout associated with the address space.
94    pub fn layout(&self) -> AddressSpaceLayout {
95        self.layout.clone()
96    }
97
98    /// Get maximum of guest physical address in the address space.
99    pub fn last_addr(&self) -> GuestAddress {
100        let mut last_addr = GuestAddress(self.layout.mem_start);
101        for reg in self.regions.iter() {
102            if reg.ty != AddressSpaceRegionType::DAXMemory && reg.last_addr() > last_addr {
103                last_addr = reg.last_addr();
104            }
105        }
106        last_addr
107    }
108
109    /// Check whether the guest physical address `guest_addr` belongs to a DAX memory region.
110    ///
111    /// # Arguments
112    /// * `guest_addr` - the guest physical address to inquire
113    pub fn is_dax_region(&self, guest_addr: GuestAddress) -> bool {
114        for reg in self.regions.iter() {
115            // Safe because we have validate the region when creating the address space object.
116            if reg.region_type() == AddressSpaceRegionType::DAXMemory
117                && reg.start_addr() <= guest_addr
118                && reg.start_addr().0 + reg.len() > guest_addr.0
119            {
120                return true;
121            }
122        }
123        false
124    }
125
126    /// Get protection flags of memory region that guest physical address `guest_addr` belongs to.
127    ///
128    /// # Arguments
129    /// * `guest_addr` - the guest physical address to inquire
130    pub fn prot_flags(&self, guest_addr: GuestAddress) -> Result<i32, AddressSpaceError> {
131        for reg in self.regions.iter() {
132            if reg.start_addr() <= guest_addr && reg.start_addr().0 + reg.len() > guest_addr.0 {
133                return Ok(reg.prot_flags());
134            }
135        }
136
137        Err(AddressSpaceError::InvalidRegionType)
138    }
139
140    /// Get optional NUMA node id associated with guest physical address `gpa`.
141    ///
142    /// # Arguments
143    /// * `gpa` - guest physical address to query.
144    pub fn numa_node_id(&self, gpa: u64) -> Option<u32> {
145        for reg in self.regions.iter() {
146            if gpa >= reg.base.0 && gpa < (reg.base.0 + reg.size) {
147                return reg.host_numa_node_id;
148            }
149        }
150        None
151    }
152}
153
154/// An address space implementation with region hotplug capability.
155///
156/// The `AddressSpace` is a wrapper over [AddressSpaceBase] to support hotplug of
157/// address space regions.
158#[derive(Clone)]
159pub struct AddressSpace {
160    state: Arc<ArcSwap<AddressSpaceBase>>,
161}
162
163impl AddressSpace {
164    /// Convert a [GuestMemoryMmap] object into `GuestMemoryAtomic<GuestMemoryMmap>`.
165    pub fn convert_into_vm_as(
166        gm: GuestMemoryMmap,
167    ) -> vm_memory::atomic::GuestMemoryAtomic<GuestMemoryMmap> {
168        vm_memory::atomic::GuestMemoryAtomic::from(Arc::new(gm))
169    }
170
171    /// Create an instance of `AddressSpace` from an `AddressSpaceRegion` array.
172    ///
173    /// To achieve better performance by using binary search algorithm, the `regions` vector
174    /// will gotten sorted by guest physical address.
175    ///
176    /// Note, panicking if some regions intersects with each other.
177    ///
178    /// # Arguments
179    /// * `regions` - prepared regions to managed by the address space instance.
180    /// * `layout` - prepared address space layout configuration.
181    pub fn from_regions(regions: Vec<Arc<AddressSpaceRegion>>, layout: AddressSpaceLayout) -> Self {
182        let base = AddressSpaceBase::from_regions(regions, layout);
183
184        AddressSpace {
185            state: Arc::new(ArcSwap::new(Arc::new(base))),
186        }
187    }
188
189    /// Insert a new address space region into the address space.
190    ///
191    /// # Arguments
192    /// * `region` - the new region to be inserted.
193    pub fn insert_region(
194        &mut self,
195        region: Arc<AddressSpaceRegion>,
196    ) -> Result<(), AddressSpaceError> {
197        let curr = self.state.load().regions.clone();
198        let layout = self.state.load().layout.clone();
199        let mut base = AddressSpaceBase::from_regions(curr, layout);
200        base.insert_region(region)?;
201        let _old = self.state.swap(Arc::new(base));
202
203        Ok(())
204    }
205
206    /// Enumerate all regions in the address space.
207    ///
208    /// # Arguments
209    /// * `cb` - the callback function to apply to each region.
210    pub fn walk_regions<F>(&self, cb: F) -> Result<(), AddressSpaceError>
211    where
212        F: FnMut(&Arc<AddressSpaceRegion>) -> Result<(), AddressSpaceError>,
213    {
214        self.state.load().walk_regions(cb)
215    }
216
217    /// Get address space layout associated with the address space.
218    pub fn layout(&self) -> AddressSpaceLayout {
219        self.state.load().layout()
220    }
221
222    /// Get maximum of guest physical address in the address space.
223    pub fn last_addr(&self) -> GuestAddress {
224        self.state.load().last_addr()
225    }
226
227    /// Check whether the guest physical address `guest_addr` belongs to a DAX memory region.
228    ///
229    /// # Arguments
230    /// * `guest_addr` - the guest physical address to inquire
231    pub fn is_dax_region(&self, guest_addr: GuestAddress) -> bool {
232        self.state.load().is_dax_region(guest_addr)
233    }
234
235    /// Get protection flags of memory region that guest physical address `guest_addr` belongs to.
236    ///
237    /// # Arguments
238    /// * `guest_addr` - the guest physical address to inquire
239    pub fn prot_flags(&self, guest_addr: GuestAddress) -> Result<i32, AddressSpaceError> {
240        self.state.load().prot_flags(guest_addr)
241    }
242
243    /// Get optional NUMA node id associated with guest physical address `gpa`.
244    ///
245    /// # Arguments
246    /// * `gpa` - guest physical address to query.
247    pub fn numa_node_id(&self, gpa: u64) -> Option<u32> {
248        self.state.load().numa_node_id(gpa)
249    }
250}
251
252#[cfg(test)]
253mod tests {
254    use super::*;
255    use std::io::Write;
256    use vm_memory::GuestUsize;
257    use vmm_sys_util::tempfile::TempFile;
258
259    // define macros for unit test
260    const GUEST_PHYS_END: u64 = (1 << 46) - 1;
261    const GUEST_MEM_START: u64 = 0;
262    const GUEST_MEM_END: u64 = GUEST_PHYS_END >> 1;
263    const GUEST_DEVICE_START: u64 = GUEST_MEM_END + 1;
264
265    #[test]
266    fn test_address_space_base_from_regions() {
267        let mut file = TempFile::new().unwrap().into_file();
268        let sample_buf = &[1, 2, 3, 4, 5];
269        assert!(file.write_all(sample_buf).is_ok());
270        file.set_len(0x10000).unwrap();
271
272        let reg = Arc::new(
273            AddressSpaceRegion::create_device_region(GuestAddress(GUEST_DEVICE_START), 0x1000)
274                .unwrap(),
275        );
276        let regions = vec![reg];
277        let layout = AddressSpaceLayout::new(GUEST_PHYS_END, GUEST_MEM_START, GUEST_MEM_END);
278        let address_space = AddressSpaceBase::from_regions(regions, layout.clone());
279        assert_eq!(address_space.layout(), layout);
280    }
281
282    #[test]
283    #[should_panic(expected = "Invalid region")]
284    fn test_address_space_base_from_regions_when_region_invalid() {
285        let reg = Arc::new(AddressSpaceRegion::build(
286            AddressSpaceRegionType::DefaultMemory,
287            GuestAddress(0x100),
288            0x1000,
289            None,
290            None,
291            0,
292            0,
293            false,
294        ));
295        let regions = vec![reg];
296        let layout = AddressSpaceLayout::new(0x2000, 0x200, 0x1800);
297        let _address_space = AddressSpaceBase::from_regions(regions, layout);
298    }
299
300    #[test]
301    #[should_panic(expected = "address space regions intersect with each other")]
302    fn test_address_space_base_from_regions_when_region_intersected() {
303        let reg1 = Arc::new(AddressSpaceRegion::build(
304            AddressSpaceRegionType::DefaultMemory,
305            GuestAddress(0x100),
306            0x200,
307            None,
308            None,
309            0,
310            0,
311            false,
312        ));
313        let reg2 = Arc::new(AddressSpaceRegion::build(
314            AddressSpaceRegionType::DefaultMemory,
315            GuestAddress(0x200),
316            0x200,
317            None,
318            None,
319            0,
320            0,
321            false,
322        ));
323        let regions = vec![reg1, reg2];
324        let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
325        let _address_space = AddressSpaceBase::from_regions(regions, layout);
326    }
327
328    #[test]
329    fn test_address_space_base_insert_region() {
330        let reg1 = Arc::new(AddressSpaceRegion::build(
331            AddressSpaceRegionType::DefaultMemory,
332            GuestAddress(0x100),
333            0x200,
334            None,
335            None,
336            0,
337            0,
338            false,
339        ));
340        let reg2 = Arc::new(AddressSpaceRegion::build(
341            AddressSpaceRegionType::DefaultMemory,
342            GuestAddress(0x300),
343            0x200,
344            None,
345            None,
346            0,
347            0,
348            false,
349        ));
350        let regions = vec![reg1];
351        let layout = AddressSpaceLayout::new(0x2000, 0x100, 0x1800);
352        let mut address_space = AddressSpaceBase::from_regions(regions, layout);
353
354        // Normal case.
355        address_space.insert_region(reg2).unwrap();
356        assert!(!address_space.regions[1].intersect_with(&address_space.regions[0]));
357
358        // Error invalid address range case when region invaled.
359        let invalid_reg = Arc::new(AddressSpaceRegion::build(
360            AddressSpaceRegionType::DefaultMemory,
361            GuestAddress(0x0),
362            0x100,
363            None,
364            None,
365            0,
366            0,
367            false,
368        ));
369        assert_eq!(
370            format!(
371                "{:?}",
372                address_space.insert_region(invalid_reg).err().unwrap()
373            ),
374            format!("InvalidAddressRange({:?}, {:?})", 0x0, 0x100)
375        );
376
377        // Error Error invalid address range case when region to be inserted will intersect
378        // exsisting regions.
379        let intersected_reg = Arc::new(AddressSpaceRegion::build(
380            AddressSpaceRegionType::DefaultMemory,
381            GuestAddress(0x400),
382            0x200,
383            None,
384            None,
385            0,
386            0,
387            false,
388        ));
389        assert_eq!(
390            format!(
391                "{:?}",
392                address_space.insert_region(intersected_reg).err().unwrap()
393            ),
394            format!("InvalidAddressRange({:?}, {:?})", 0x400, 0x200)
395        );
396    }
397
398    #[test]
399    fn test_address_space_base_walk_regions() {
400        let reg1 = Arc::new(AddressSpaceRegion::build(
401            AddressSpaceRegionType::DefaultMemory,
402            GuestAddress(0x100),
403            0x200,
404            None,
405            None,
406            0,
407            0,
408            false,
409        ));
410        let reg2 = Arc::new(AddressSpaceRegion::build(
411            AddressSpaceRegionType::DefaultMemory,
412            GuestAddress(0x300),
413            0x200,
414            None,
415            None,
416            0,
417            0,
418            false,
419        ));
420        let regions = vec![reg1, reg2];
421        let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
422        let address_space = AddressSpaceBase::from_regions(regions, layout);
423
424        // The argument of walk_regions is a function which takes a &Arc<AddressSpaceRegion>
425        // and returns result. This function will be applied to all regions.
426        fn do_not_have_hotplug(region: &Arc<AddressSpaceRegion>) -> Result<(), AddressSpaceError> {
427            if region.is_hotplug() {
428                Err(AddressSpaceError::InvalidRegionType) // The Error type is dictated to AddressSpaceError.
429            } else {
430                Ok(())
431            }
432        }
433        assert!(matches!(
434            address_space.walk_regions(do_not_have_hotplug).unwrap(),
435            ()
436        ));
437    }
438
439    #[test]
440    fn test_address_space_base_last_addr() {
441        let reg1 = Arc::new(AddressSpaceRegion::build(
442            AddressSpaceRegionType::DefaultMemory,
443            GuestAddress(0x100),
444            0x200,
445            None,
446            None,
447            0,
448            0,
449            false,
450        ));
451        let reg2 = Arc::new(AddressSpaceRegion::build(
452            AddressSpaceRegionType::DefaultMemory,
453            GuestAddress(0x300),
454            0x200,
455            None,
456            None,
457            0,
458            0,
459            false,
460        ));
461        let regions = vec![reg1, reg2];
462        let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
463        let address_space = AddressSpaceBase::from_regions(regions, layout);
464
465        assert_eq!(address_space.last_addr(), GuestAddress(0x500 - 1));
466    }
467
468    #[test]
469    fn test_address_space_base_is_dax_region() {
470        let page_size = 4096;
471        let address_space_region = vec![
472            Arc::new(AddressSpaceRegion::new(
473                AddressSpaceRegionType::DefaultMemory,
474                GuestAddress(page_size),
475                page_size as GuestUsize,
476            )),
477            Arc::new(AddressSpaceRegion::new(
478                AddressSpaceRegionType::DefaultMemory,
479                GuestAddress(page_size * 2),
480                page_size as GuestUsize,
481            )),
482            Arc::new(AddressSpaceRegion::new(
483                AddressSpaceRegionType::DAXMemory,
484                GuestAddress(GUEST_DEVICE_START),
485                page_size as GuestUsize,
486            )),
487        ];
488        let layout = AddressSpaceLayout::new(GUEST_PHYS_END, GUEST_MEM_START, GUEST_MEM_END);
489        let address_space = AddressSpaceBase::from_regions(address_space_region, layout);
490
491        assert!(!address_space.is_dax_region(GuestAddress(page_size)));
492        assert!(!address_space.is_dax_region(GuestAddress(page_size * 2)));
493        assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START)));
494        assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + 1)));
495        assert!(!address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + page_size)));
496        assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + page_size - 1)));
497    }
498
499    #[test]
500    fn test_address_space_base_prot_flags() {
501        let reg1 = Arc::new(AddressSpaceRegion::build(
502            AddressSpaceRegionType::DefaultMemory,
503            GuestAddress(0x100),
504            0x200,
505            Some(0),
506            None,
507            0,
508            0,
509            false,
510        ));
511        let reg2 = Arc::new(AddressSpaceRegion::new(
512            AddressSpaceRegionType::DefaultMemory,
513            GuestAddress(0x300),
514            0x300,
515        ));
516        let regions = vec![reg1, reg2];
517        let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
518        let address_space = AddressSpaceBase::from_regions(regions, layout);
519
520        // Normal case, reg1.
521        assert_eq!(address_space.prot_flags(GuestAddress(0x200)).unwrap(), 0);
522        // Normal case, reg2.
523        assert_eq!(
524            address_space.prot_flags(GuestAddress(0x500)).unwrap(),
525            libc::PROT_READ | libc::PROT_WRITE
526        );
527        // Inquire gpa where no region is set.
528        assert!(matches!(
529            address_space.prot_flags(GuestAddress(0x600)),
530            Err(AddressSpaceError::InvalidRegionType)
531        ));
532    }
533
534    #[test]
535    fn test_address_space_base_numa_node_id() {
536        let reg1 = Arc::new(AddressSpaceRegion::build(
537            AddressSpaceRegionType::DefaultMemory,
538            GuestAddress(0x100),
539            0x200,
540            Some(0),
541            None,
542            0,
543            0,
544            false,
545        ));
546        let reg2 = Arc::new(AddressSpaceRegion::build(
547            AddressSpaceRegionType::DefaultMemory,
548            GuestAddress(0x300),
549            0x300,
550            None,
551            None,
552            0,
553            0,
554            false,
555        ));
556        let regions = vec![reg1, reg2];
557        let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
558        let address_space = AddressSpaceBase::from_regions(regions, layout);
559
560        // Normal case.
561        assert_eq!(address_space.numa_node_id(0x200).unwrap(), 0);
562        // Inquire region with None as its numa node id.
563        assert_eq!(address_space.numa_node_id(0x400), None);
564        // Inquire gpa where no region is set.
565        assert_eq!(address_space.numa_node_id(0x600), None);
566    }
567
568    #[test]
569    fn test_address_space_convert_into_vm_as() {
570        // ! Further and detailed test is needed here.
571        let gmm = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0x0), 0x400)]).unwrap();
572        let _vm = AddressSpace::convert_into_vm_as(gmm);
573    }
574
575    #[test]
576    fn test_address_space_insert_region() {
577        let reg1 = Arc::new(AddressSpaceRegion::build(
578            AddressSpaceRegionType::DefaultMemory,
579            GuestAddress(0x100),
580            0x200,
581            None,
582            None,
583            0,
584            0,
585            false,
586        ));
587        let reg2 = Arc::new(AddressSpaceRegion::build(
588            AddressSpaceRegionType::DefaultMemory,
589            GuestAddress(0x300),
590            0x200,
591            None,
592            None,
593            0,
594            0,
595            false,
596        ));
597        let regions = vec![reg1];
598        let layout = AddressSpaceLayout::new(0x2000, 0x100, 0x1800);
599        let mut address_space = AddressSpace::from_regions(regions, layout);
600
601        // Normal case.
602        assert!(matches!(address_space.insert_region(reg2).unwrap(), ()));
603
604        // Error invalid address range case when region invaled.
605        let invalid_reg = Arc::new(AddressSpaceRegion::build(
606            AddressSpaceRegionType::DefaultMemory,
607            GuestAddress(0x0),
608            0x100,
609            None,
610            None,
611            0,
612            0,
613            false,
614        ));
615        assert_eq!(
616            format!(
617                "{:?}",
618                address_space.insert_region(invalid_reg).err().unwrap()
619            ),
620            format!("InvalidAddressRange({:?}, {:?})", 0x0, 0x100)
621        );
622
623        // Error Error invalid address range case when region to be inserted will intersect
624        // exsisting regions.
625        let intersected_reg = Arc::new(AddressSpaceRegion::build(
626            AddressSpaceRegionType::DefaultMemory,
627            GuestAddress(0x400),
628            0x200,
629            None,
630            None,
631            0,
632            0,
633            false,
634        ));
635        assert_eq!(
636            format!(
637                "{:?}",
638                address_space.insert_region(intersected_reg).err().unwrap()
639            ),
640            format!("InvalidAddressRange({:?}, {:?})", 0x400, 0x200)
641        );
642    }
643
644    #[test]
645    fn test_address_space_walk_regions() {
646        let reg1 = Arc::new(AddressSpaceRegion::build(
647            AddressSpaceRegionType::DefaultMemory,
648            GuestAddress(0x100),
649            0x200,
650            None,
651            None,
652            0,
653            0,
654            false,
655        ));
656        let reg2 = Arc::new(AddressSpaceRegion::build(
657            AddressSpaceRegionType::DefaultMemory,
658            GuestAddress(0x300),
659            0x200,
660            None,
661            None,
662            0,
663            0,
664            false,
665        ));
666        let regions = vec![reg1, reg2];
667        let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
668        let address_space = AddressSpace::from_regions(regions, layout);
669
670        fn access_all_hotplug_flag(
671            region: &Arc<AddressSpaceRegion>,
672        ) -> Result<(), AddressSpaceError> {
673            region.is_hotplug();
674            Ok(())
675        }
676
677        assert!(matches!(
678            address_space.walk_regions(access_all_hotplug_flag).unwrap(),
679            ()
680        ));
681    }
682
683    #[test]
684    fn test_address_space_layout() {
685        let reg = Arc::new(AddressSpaceRegion::build(
686            AddressSpaceRegionType::DefaultMemory,
687            GuestAddress(0x100),
688            0x1000,
689            None,
690            None,
691            0,
692            0,
693            false,
694        ));
695        let regions = vec![reg];
696        let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
697        let address_space = AddressSpace::from_regions(regions, layout.clone());
698
699        assert_eq!(layout, address_space.layout());
700    }
701
702    #[test]
703    fn test_address_space_last_addr() {
704        let reg1 = Arc::new(AddressSpaceRegion::build(
705            AddressSpaceRegionType::DefaultMemory,
706            GuestAddress(0x100),
707            0x200,
708            None,
709            None,
710            0,
711            0,
712            false,
713        ));
714        let reg2 = Arc::new(AddressSpaceRegion::build(
715            AddressSpaceRegionType::DefaultMemory,
716            GuestAddress(0x300),
717            0x200,
718            None,
719            None,
720            0,
721            0,
722            false,
723        ));
724        let regions = vec![reg1, reg2];
725        let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
726        let address_space = AddressSpace::from_regions(regions, layout);
727
728        assert_eq!(address_space.last_addr(), GuestAddress(0x500 - 1));
729    }
730
731    #[test]
732    fn test_address_space_is_dax_region() {
733        let page_size = 4096;
734        let address_space_region = vec![
735            Arc::new(AddressSpaceRegion::new(
736                AddressSpaceRegionType::DefaultMemory,
737                GuestAddress(page_size),
738                page_size as GuestUsize,
739            )),
740            Arc::new(AddressSpaceRegion::new(
741                AddressSpaceRegionType::DefaultMemory,
742                GuestAddress(page_size * 2),
743                page_size as GuestUsize,
744            )),
745            Arc::new(AddressSpaceRegion::new(
746                AddressSpaceRegionType::DAXMemory,
747                GuestAddress(GUEST_DEVICE_START),
748                page_size as GuestUsize,
749            )),
750        ];
751        let layout = AddressSpaceLayout::new(GUEST_PHYS_END, GUEST_MEM_START, GUEST_MEM_END);
752        let address_space = AddressSpace::from_regions(address_space_region, layout);
753
754        assert!(!address_space.is_dax_region(GuestAddress(page_size)));
755        assert!(!address_space.is_dax_region(GuestAddress(page_size * 2)));
756        assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START)));
757        assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + 1)));
758        assert!(!address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + page_size)));
759        assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + page_size - 1)));
760    }
761
762    #[test]
763    fn test_address_space_prot_flags() {
764        let reg1 = Arc::new(AddressSpaceRegion::build(
765            AddressSpaceRegionType::DefaultMemory,
766            GuestAddress(0x100),
767            0x200,
768            Some(0),
769            None,
770            0,
771            0,
772            false,
773        ));
774        let reg2 = Arc::new(AddressSpaceRegion::new(
775            AddressSpaceRegionType::DefaultMemory,
776            GuestAddress(0x300),
777            0x300,
778        ));
779        let regions = vec![reg1, reg2];
780        let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
781        let address_space = AddressSpace::from_regions(regions, layout);
782
783        // Normal case, reg1.
784        assert_eq!(address_space.prot_flags(GuestAddress(0x200)).unwrap(), 0);
785        // Normal case, reg2.
786        assert_eq!(
787            address_space.prot_flags(GuestAddress(0x500)).unwrap(),
788            libc::PROT_READ | libc::PROT_WRITE
789        );
790        // Inquire gpa where no region is set.
791        assert!(matches!(
792            address_space.prot_flags(GuestAddress(0x600)),
793            Err(AddressSpaceError::InvalidRegionType)
794        ));
795    }
796
797    #[test]
798    fn test_address_space_numa_node_id() {
799        let reg1 = Arc::new(AddressSpaceRegion::build(
800            AddressSpaceRegionType::DefaultMemory,
801            GuestAddress(0x100),
802            0x200,
803            Some(0),
804            None,
805            0,
806            0,
807            false,
808        ));
809        let reg2 = Arc::new(AddressSpaceRegion::build(
810            AddressSpaceRegionType::DefaultMemory,
811            GuestAddress(0x300),
812            0x300,
813            None,
814            None,
815            0,
816            0,
817            false,
818        ));
819        let regions = vec![reg1, reg2];
820        let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
821        let address_space = AddressSpace::from_regions(regions, layout);
822
823        // Normal case.
824        assert_eq!(address_space.numa_node_id(0x200).unwrap(), 0);
825        // Inquire region with None as its numa node id.
826        assert_eq!(address_space.numa_node_id(0x400), None);
827        // Inquire gpa where no region is set.
828        assert_eq!(address_space.numa_node_id(0x600), None);
829    }
830}