@@ -37,6 +37,17 @@ pub enum AddressRequest {
3737 } ,
3838}
3939
40+ /// Describes where an `mremap` operation may place the remapped VMA.
41+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
42+ pub enum RemapDestination {
43+ /// Resize in place only.
44+ InPlaceOnly ,
45+ /// Resize in place if possible, otherwise move to any free region.
46+ MayMove ,
47+ /// Move the mapping to exactly this address.
48+ Fixed ( VA ) ,
49+ }
50+
4051impl < AS : UserAddressSpace > MemoryMap < AS > {
4152 /// Creates a new, empty address space.
4253 pub fn new ( ) -> Result < Self > {
@@ -243,6 +254,257 @@ impl<AS: UserAddressSpace> MemoryMap<AS> {
243254 Err ( KernelError :: NoMemory )
244255 }
245256
257+ /// Remaps an existing mapping
258+ pub fn mremap (
259+ & mut self ,
260+ old_addr : VA ,
261+ old_len : usize ,
262+ new_len : usize ,
263+ destination : RemapDestination ,
264+ ) -> Result < ( VA , Vec < PageFrame > ) > {
265+ if !old_addr. is_page_aligned ( ) || old_len == 0 || new_len == 0 {
266+ return Err ( KernelError :: InvalidValue ) ;
267+ }
268+
269+ let old_len = Self :: align_len ( old_len) ;
270+ let new_len = Self :: align_len ( new_len) ;
271+ let old_region = VirtMemoryRegion :: new ( old_addr, old_len) ;
272+
273+ let source_vma = self . find_vma ( old_addr) . cloned ( ) . ok_or ( KernelError :: Fault ) ?;
274+
275+ if old_region. end_address ( ) > source_vma. region . end_address ( ) {
276+ return Err ( KernelError :: Fault ) ;
277+ }
278+
279+ if let RemapDestination :: Fixed ( new_addr) = destination {
280+ if !new_addr. is_page_aligned ( ) {
281+ return Err ( KernelError :: InvalidValue ) ;
282+ }
283+
284+ let new_region = VirtMemoryRegion :: new ( new_addr, new_len) ;
285+
286+ if new_region. overlaps ( old_region) || new_region. overlaps ( source_vma. region ) {
287+ return Err ( KernelError :: InvalidValue ) ;
288+ }
289+ }
290+
291+ if old_len == new_len && !matches ! ( destination, RemapDestination :: Fixed ( _) ) {
292+ return Ok ( ( old_addr, Vec :: new ( ) ) ) ;
293+ }
294+
295+ if let RemapDestination :: Fixed ( new_addr) = destination {
296+ return self . move_selected_mapping (
297+ source_vma,
298+ old_region,
299+ VirtMemoryRegion :: new ( new_addr, new_len) ,
300+ true ,
301+ ) ;
302+ }
303+
304+ if new_len <= old_len {
305+ return self . shrink_in_place ( source_vma, old_region, new_len) ;
306+ }
307+
308+ if self . can_expand_in_place ( & source_vma, old_region, new_len) {
309+ return self . expand_in_place ( source_vma, old_region, new_len) ;
310+ }
311+
312+ let new_region = match destination {
313+ RemapDestination :: InPlaceOnly => return Err ( KernelError :: NoMemory ) ,
314+ RemapDestination :: MayMove => self
315+ . find_free_region ( new_len)
316+ . ok_or ( KernelError :: NoMemory ) ?,
317+ RemapDestination :: Fixed ( _) => unreachable ! ( ) ,
318+ } ;
319+
320+ self . move_selected_mapping (
321+ source_vma,
322+ old_region,
323+ new_region,
324+ matches ! ( destination, RemapDestination :: Fixed ( _) ) ,
325+ )
326+ }
327+
328+ fn align_len ( len : usize ) -> usize {
329+ if len & PAGE_MASK != 0 {
330+ ( len & !PAGE_MASK ) + PAGE_SIZE
331+ } else {
332+ len
333+ }
334+ }
335+
336+ fn can_expand_in_place (
337+ & self ,
338+ source_vma : & VMArea ,
339+ old_region : VirtMemoryRegion ,
340+ new_len : usize ,
341+ ) -> bool {
342+ let new_end = old_region. start_address ( ) . add_bytes ( new_len) ;
343+
344+ if new_end <= source_vma. region . end_address ( ) {
345+ return true ;
346+ }
347+
348+ self . is_region_free ( VirtMemoryRegion :: from_start_end_address (
349+ source_vma. region . end_address ( ) ,
350+ new_end,
351+ ) )
352+ }
353+
354+ fn expand_in_place (
355+ & mut self ,
356+ source_vma : VMArea ,
357+ old_region : VirtMemoryRegion ,
358+ new_len : usize ,
359+ ) -> Result < ( VA , Vec < PageFrame > ) > {
360+ let new_end = old_region. start_address ( ) . add_bytes ( new_len) ;
361+
362+ if new_end <= source_vma. region . end_address ( ) {
363+ return Ok ( ( old_region. start_address ( ) , Vec :: new ( ) ) ) ;
364+ }
365+
366+ self . vmas
367+ . remove ( & source_vma. region . start_address ( ) )
368+ . unwrap ( ) ;
369+
370+ let mut expanded_vma = source_vma;
371+ expanded_vma. region =
372+ VirtMemoryRegion :: from_start_end_address ( expanded_vma. region . start_address ( ) , new_end) ;
373+
374+ self . merge_vma ( expanded_vma) ;
375+
376+ Ok ( ( old_region. start_address ( ) , Vec :: new ( ) ) )
377+ }
378+
379+ fn shrink_in_place (
380+ & mut self ,
381+ source_vma : VMArea ,
382+ old_region : VirtMemoryRegion ,
383+ new_len : usize ,
384+ ) -> Result < ( VA , Vec < PageFrame > ) > {
385+ let new_region = VirtMemoryRegion :: new ( old_region. start_address ( ) , new_len) ;
386+ let removed_region = VirtMemoryRegion :: from_start_end_address (
387+ new_region. end_address ( ) ,
388+ old_region. end_address ( ) ,
389+ ) ;
390+
391+ let freed_pages = self . address_space . unmap_range ( removed_region) ?;
392+
393+ self . vmas
394+ . remove ( & source_vma. region . start_address ( ) )
395+ . unwrap ( ) ;
396+
397+ if source_vma. region . start_address ( ) < old_region. start_address ( ) {
398+ self . merge_vma (
399+ source_vma. shrink_to ( VirtMemoryRegion :: from_start_end_address (
400+ source_vma. region . start_address ( ) ,
401+ old_region. start_address ( ) ,
402+ ) ) ,
403+ ) ;
404+ }
405+
406+ self . merge_vma ( source_vma. shrink_to ( new_region) ) ;
407+
408+ if old_region. end_address ( ) < source_vma. region . end_address ( ) {
409+ self . merge_vma (
410+ source_vma. shrink_to ( VirtMemoryRegion :: from_start_end_address (
411+ old_region. end_address ( ) ,
412+ source_vma. region . end_address ( ) ,
413+ ) ) ,
414+ ) ;
415+ }
416+
417+ Ok ( ( old_region. start_address ( ) , freed_pages) )
418+ }
419+
420+ fn relocate_vma ( vma : VMArea , new_region : VirtMemoryRegion ) -> VMArea {
421+ let mut moved_vma = vma;
422+ moved_vma. region = new_region;
423+
424+ if let VMAreaKind :: File ( mapping) = & mut moved_vma. kind {
425+ mapping. len = core:: cmp:: min ( mapping. len , new_region. size ( ) as u64 ) ;
426+ }
427+
428+ moved_vma
429+ }
430+
431+ fn move_selected_mapping (
432+ & mut self ,
433+ source_vma : VMArea ,
434+ old_region : VirtMemoryRegion ,
435+ new_region : VirtMemoryRegion ,
436+ clobber_target : bool ,
437+ ) -> Result < ( VA , Vec < PageFrame > ) > {
438+ let mut freed_pages = Vec :: new ( ) ;
439+
440+ if clobber_target {
441+ freed_pages. append ( & mut self . unmap_region ( new_region, None ) ?) ;
442+ }
443+
444+ let preserved_len = core:: cmp:: min ( old_region. size ( ) , new_region. size ( ) ) ;
445+ let mut newly_mapped = Vec :: new ( ) ;
446+
447+ if preserved_len != 0 {
448+ let preserved_old = VirtMemoryRegion :: new ( old_region. start_address ( ) , preserved_len) ;
449+ let preserved_new = VirtMemoryRegion :: new ( new_region. start_address ( ) , preserved_len) ;
450+
451+ for ( old_page, new_page) in preserved_old. iter_pages ( ) . zip ( preserved_new. iter_pages ( ) ) {
452+ if let Some ( page_info) = self . address_space . translate ( old_page) {
453+ if let Err ( err) =
454+ self . address_space
455+ . map_page ( page_info. pfn , new_page, page_info. perms )
456+ {
457+ for mapped_page in newly_mapped {
458+ let _ = self . address_space . unmap ( mapped_page) ;
459+ }
460+
461+ return Err ( err) ;
462+ }
463+
464+ newly_mapped. push ( new_page) ;
465+ }
466+ }
467+
468+ let _ = self . address_space . unmap_range ( preserved_old) ?;
469+ }
470+
471+ if old_region. size ( ) > preserved_len {
472+ freed_pages. append ( & mut self . address_space . unmap_range (
473+ VirtMemoryRegion :: from_start_end_address (
474+ old_region. start_address ( ) . add_bytes ( preserved_len) ,
475+ old_region. end_address ( ) ,
476+ ) ,
477+ ) ?) ;
478+ }
479+
480+ self . vmas
481+ . remove ( & source_vma. region . start_address ( ) )
482+ . unwrap ( ) ;
483+
484+ if source_vma. region . start_address ( ) < old_region. start_address ( ) {
485+ self . merge_vma (
486+ source_vma. shrink_to ( VirtMemoryRegion :: from_start_end_address (
487+ source_vma. region . start_address ( ) ,
488+ old_region. start_address ( ) ,
489+ ) ) ,
490+ ) ;
491+ }
492+
493+ if old_region. end_address ( ) < source_vma. region . end_address ( ) {
494+ self . merge_vma (
495+ source_vma. shrink_to ( VirtMemoryRegion :: from_start_end_address (
496+ old_region. end_address ( ) ,
497+ source_vma. region . end_address ( ) ,
498+ ) ) ,
499+ ) ;
500+ }
501+
502+ let selected_vma = source_vma. shrink_to ( old_region) ;
503+ self . merge_vma ( Self :: relocate_vma ( selected_vma, new_region) ) ;
504+
505+ Ok ( ( new_region. start_address ( ) , freed_pages) )
506+ }
507+
246508 /// Checks if a given virtual memory region is completely free.
247509 fn is_region_free ( & self , region : VirtMemoryRegion ) -> bool {
248510 // Find the VMA that might overlap with the start of our desired region.
@@ -304,9 +566,12 @@ impl<AS: UserAddressSpace> MemoryMap<AS> {
304566
305567 /// Inserts a new VMA, handling overlaps and merging it with neighbors if
306568 /// possible.
307- pub ( super ) fn insert_and_merge ( & mut self , mut vma : VMArea ) {
569+ pub ( super ) fn insert_and_merge ( & mut self , vma : VMArea ) {
308570 let _ = self . unmap_region ( vma. region , Some ( vma. clone ( ) ) ) ;
571+ self . merge_vma ( vma) ;
572+ }
309573
574+ fn merge_vma ( & mut self , mut vma : VMArea ) {
310575 // Try to merge with next VMA.
311576 if let Some ( next_vma) = self . vmas . get ( & vma. region . end_address ( ) )
312577 && vma. can_merge_with ( next_vma)
@@ -320,24 +585,21 @@ impl<AS: UserAddressSpace> MemoryMap<AS> {
320585 . unwrap ( ) // Should not fail, as we just got this VMA.
321586 . region ;
322587 vma. region . expand_by ( next_vma_region. size ( ) ) ;
323- // `vma` now represents the merged region of [new, next].
324588 }
325589
326590 // Try to merge with the previous VMA.
327591 if let Some ( ( _key, prev_vma) ) = self
328592 . vmas
329593 . range_mut ( ..vma. region . start_address ( ) )
330594 . next_back ( )
331- {
332595 // Check if it's contiguous and compatible.
333- if prev_vma. region . end_address ( ) == vma. region . start_address ( )
334- && prev_vma. can_merge_with ( & vma)
335- {
336- // The VMAs are mergeable. Expand the previous VMA to absorb the
337- // new one's region.
338- prev_vma. region . expand_by ( vma. region . size ( ) ) ;
339- return ;
340- }
596+ && prev_vma. region . end_address ( ) == vma. region . start_address ( )
597+ && prev_vma. can_merge_with ( & vma)
598+ {
599+ // The VMAs are mergeable. Expand the previous VMA to absorb the
600+ // new one's region.
601+ prev_vma. region . expand_by ( vma. region . size ( ) ) ;
602+ return ;
341603 }
342604
343605 // If we didn't merge into a previous VMA, insert the new (and possibly
0 commit comments