@@ -296,6 +296,27 @@ impl GuestRegionMmapExt {
296296 } )
297297 }
298298
299+ /// Check whether the given guest address range falls within plugged slots.
300+ pub ( crate ) fn check_range_plugged (
301+ & self ,
302+ caddr : MemoryRegionAddress ,
303+ len : usize ,
304+ ) -> Result < ( ) , GuestMemoryError > {
305+ // caddr is guaranteed to be within the region by the caller
306+ // (try_for_each_region_in_range validates this).
307+ let from = self
308+ . start_addr ( )
309+ . checked_add ( caddr. raw_value ( ) )
310+ . expect ( "caddr should be within the region" ) ;
311+ if self
312+ . slots_intersecting_range ( from, len)
313+ . any ( |( _, plugged) | !plugged)
314+ {
315+ return Err ( GuestMemoryError :: HostAddressNotAvailable ) ;
316+ }
317+ Ok ( ( ) )
318+ }
319+
299320 pub ( crate ) fn slot_cnt ( & self ) -> u32 {
300321 u32:: try_from ( u64_to_usize ( self . len ( ) ) / self . slot_size ) . unwrap ( )
301322 }
@@ -347,8 +368,8 @@ impl GuestRegionMmapExt {
347368 & self ,
348369 from : GuestAddress ,
349370 len : usize ,
350- ) -> impl Iterator < Item = GuestMemorySlot < ' _ > > {
351- self . slots ( ) . map ( |( slot, _) | slot ) . filter ( move |slot | {
371+ ) -> impl Iterator < Item = ( GuestMemorySlot < ' _ > , bool ) > {
372+ self . slots ( ) . filter ( move |( slot, _) | {
352373 // Two intervals [a, b) and [c, d) intersect iff a < d && c < b.
353374 // This correctly handles the containment case where the slot fully
354375 // contains the range (or vice versa).
@@ -640,6 +661,10 @@ where
640661
641662 /// Discards a memory range, freeing up memory pages
642663 fn discard_range ( & self , addr : GuestAddress , range_len : usize ) -> Result < ( ) , GuestMemoryError > ;
664+
665+ /// Check whether the given guest address range falls entirely within plugged memory.
666+ /// Returns Err if the address is not in any region or is in an unplugged slot.
667+ fn check_range_plugged ( & self , addr : GuestAddress , len : usize ) -> Result < ( ) , GuestMemoryError > ;
643668}
644669
645670/// State of a guest memory region saved to file/buffer.
@@ -822,6 +847,12 @@ impl GuestMemoryExtension for GuestMemoryMmap {
822847 region. discard_range ( start, len)
823848 } )
824849 }
850+
851+ fn check_range_plugged ( & self , addr : GuestAddress , len : usize ) -> Result < ( ) , GuestMemoryError > {
852+ self . try_for_each_region_in_range ( addr, len, |region, offset, chunk_len| {
853+ region. check_range_plugged ( offset, chunk_len)
854+ } )
855+ }
825856}
826857
827858fn create_memfd (
@@ -1520,7 +1551,7 @@ mod tests {
15201551 let from = base. unchecked_add ( ( offset_pages * page_size) as u64 ) ;
15211552 let len = len_pages * page_size;
15221553 let found: Vec < _ > = region. slots_intersecting_range ( from, len) . collect ( ) ;
1523- let addrs: Vec < _ > = found. iter ( ) . map ( |s | s. guest_addr ) . collect ( ) ;
1554+ let addrs: Vec < _ > = found. iter ( ) . map ( |( s , _ ) | s. guest_addr ) . collect ( ) ;
15241555 assert_eq ! (
15251556 addrs, expected,
15261557 "offset={offset_pages} pages, len={len_pages} pages"
@@ -1812,4 +1843,51 @@ mod tests {
18121843 }
18131844 }
18141845 }
1846+
1847+ #[ test]
1848+ fn test_check_range_plugged ( ) {
1849+ let region_size = 0x4000usize ; // 4 slots of 0x1000
1850+ let regions = anonymous (
1851+ vec ! [ ( GuestAddress ( 0x10_0000 ) , region_size) ] . into_iter ( ) ,
1852+ false ,
1853+ HugePageConfig :: None ,
1854+ )
1855+ . unwrap ( ) ;
1856+ let region = regions. into_iter ( ) . next ( ) . unwrap ( ) ;
1857+
1858+ let state = GuestMemoryRegionState {
1859+ base_address : 0x10_0000 ,
1860+ size : region_size,
1861+ region_type : GuestRegionType :: Hotpluggable ,
1862+ plugged : vec ! [ true , true , false , true ] ,
1863+ } ;
1864+
1865+ let ext = GuestRegionMmapExt :: from_state ( region, & state, 0 ) . unwrap ( ) ;
1866+
1867+ // Slot 0 (offset 0..0x1000): plugged
1868+ ext. check_range_plugged ( MemoryRegionAddress ( 0 ) , 0x100 )
1869+ . unwrap ( ) ;
1870+ // Slot 1 (offset 0x1000..0x2000): plugged
1871+ ext. check_range_plugged ( MemoryRegionAddress ( 0x1000 ) , 0x100 )
1872+ . unwrap ( ) ;
1873+ // Slot 2 (offset 0x2000..0x3000): unplugged
1874+ assert ! (
1875+ ext. check_range_plugged( MemoryRegionAddress ( 0x2000 ) , 0x100 )
1876+ . is_err( )
1877+ ) ;
1878+ // Spanning slots 1-2: fails because slot 2 is unplugged
1879+ assert ! (
1880+ ext. check_range_plugged( MemoryRegionAddress ( 0x1800 ) , 0x1000 )
1881+ . is_err( )
1882+ ) ;
1883+ // Spanning slots 0-1: both plugged
1884+ ext. check_range_plugged ( MemoryRegionAddress ( 0x800 ) , 0x1000 )
1885+ . unwrap ( ) ;
1886+ // Slot 3 (offset 0x3000..0x4000): plugged
1887+ ext. check_range_plugged ( MemoryRegionAddress ( 0x3000 ) , 0x100 )
1888+ . unwrap ( ) ;
1889+ // Zero length: always ok
1890+ ext. check_range_plugged ( MemoryRegionAddress ( 0x2000 ) , 0 )
1891+ . unwrap ( ) ;
1892+ }
18151893}
0 commit comments