@@ -34,16 +34,41 @@ where
3434 Platform : RawSyncPrimitivesProvider + PageManagementProvider < ALIGN > ,
3535{
3636 vmem : RwLock < Platform , Vmem < Platform , ALIGN > > ,
37+ /// Lower bound (inclusive) of the VA range (cached for lock-free checks).
38+ addr_min : usize ,
39+ /// Upper bound (exclusive) of the VA range (cached for lock-free checks).
40+ addr_max : usize ,
3741}
3842
3943impl < Platform , const ALIGN : usize > PageManager < Platform , ALIGN >
4044where
4145 Platform : RawSyncPrimitivesProvider + PageManagementProvider < ALIGN > ,
4246{
43- /// Create a new `PageManager` instance.
44- pub fn new ( litebox : & LiteBox < Platform > ) -> Self {
45- let vmem = RwLock :: new ( linux:: Vmem :: new ( litebox. x . platform ) ) ;
46- Self { vmem }
47+ /// Lower bound (inclusive) of the managed VA range.
48+ pub fn addr_min ( & self ) -> usize {
49+ self . addr_min
50+ }
51+
52+ /// Upper bound (exclusive) of the managed VA range.
53+ pub fn addr_max ( & self ) -> usize {
54+ self . addr_max
55+ }
56+
57+ /// Create a new `PageManager` instance for the given VA `range`.
58+ ///
59+ /// For a single-process setup, pass the full platform range
60+ /// `Platform::TASK_ADDR_MIN..Platform::TASK_ADDR_MAX`. For multi-process
61+ /// setups, pass the sub-range obtained from
62+ /// [`AddressSpaceProvider::address_space_range()`](crate::platform::AddressSpaceProvider::address_space_range).
63+ pub fn new ( litebox : & LiteBox < Platform > , range : Range < usize > ) -> Self {
64+ let addr_min = range. start ;
65+ let addr_max = range. end ;
66+ let vmem = RwLock :: new ( linux:: Vmem :: new ( litebox. x . platform , range) ) ;
67+ Self {
68+ vmem,
69+ addr_min,
70+ addr_max,
71+ }
4772 }
4873
4974 /// Create a mapping with the given flags.
@@ -289,7 +314,40 @@ where
289314 pub fn set_initial_brk ( & self , brk : usize ) {
290315 let mut vmem = self . vmem . write ( ) ;
291316 assert_eq ! ( vmem. brk, 0 , "initial brk is already set" ) ;
317+ vmem. brk_base = brk;
292318 vmem. brk = brk;
319+ vmem. brk_frontier = brk. next_multiple_of ( linux:: PAGE_SIZE ) ;
320+ }
321+
322+ /// Returns the current logical program break without modifying mappings.
323+ pub fn current_brk ( & self ) -> usize {
324+ self . vmem . read ( ) . brk
325+ }
326+
327+ /// Returns the current page-aligned heap frontier that `brk` growth uses.
328+ pub fn current_brk_frontier ( & self ) -> usize {
329+ self . vmem . read ( ) . brk_frontier
330+ }
331+
332+ /// Advance the minimum program break, current break, and heap frontier to
333+ /// at least `min_brk` without allocating new heap pages.
334+ ///
335+ /// This is used only when already-mapped non-heap pages, such as a
336+ /// trampoline at the heap frontier, occupy space that future `brk` growth
337+ /// must skip over. The skipped gap becomes a persistent floor: later
338+ /// `brk` shrinks must not re-enter it, or subsequent growth would collide
339+ /// with the same non-heap pages again.
340+ pub fn ensure_brk_past ( & self , min_brk : usize ) {
341+ let mut vmem = self . vmem . write ( ) ;
342+ if vmem. brk_base < min_brk {
343+ vmem. brk_base = min_brk;
344+ }
345+ if vmem. brk < min_brk {
346+ vmem. brk = min_brk;
347+ }
348+ if vmem. brk_frontier < min_brk {
349+ vmem. brk_frontier = min_brk;
350+ }
293351 }
294352
295353 /// Set the program break to the given address.
@@ -319,10 +377,15 @@ where
319377 // Calling `brk` with 0 can be used to find the current location of the program break.
320378 return Ok ( vmem. brk ) ;
321379 }
380+ if brk < vmem. brk_base {
381+ // Linux keeps the current break unchanged when the request is
382+ // below the permitted heap base.
383+ return Ok ( vmem. brk ) ;
384+ }
322385
323- let old_brk = vmem. brk . next_multiple_of ( linux :: PAGE_SIZE ) ;
386+ let old_brk = vmem. brk_frontier ;
324387 let new_brk = brk. next_multiple_of ( linux:: PAGE_SIZE ) ;
325- if vmem . brk >= brk {
388+ if new_brk < old_brk {
326389 // Shrink the memory region
327390 let brk = match unsafe {
328391 vmem. remove_mapping (
@@ -331,6 +394,7 @@ where
331394 } {
332395 Ok ( ( ) ) => {
333396 vmem. brk = brk;
397+ vmem. brk_frontier = new_brk;
334398 brk
335399 }
336400 Err ( _) => {
@@ -340,20 +404,23 @@ where
340404 return Ok ( brk) ;
341405 }
342406
343- if vmem. overlapping ( old_brk..new_brk) . next ( ) . is_some ( ) {
344- return Err ( MappingError :: OutOfMemory ) ;
345- }
346- if let Some ( range) = PageRange :: < ALIGN > :: new ( old_brk, new_brk) {
347- let ( suggested_address, length) = range. start_and_length ( ) ;
348- let perms = MemoryRegionPermissions :: READ | MemoryRegionPermissions :: WRITE ;
349- unsafe {
350- vmem. create_pages (
351- Some ( suggested_address) ,
352- length,
353- CreatePagesFlags :: FIXED_ADDR | CreatePagesFlags :: POPULATE_PAGES_IMMEDIATELY ,
354- perms,
355- )
356- } ?;
407+ if new_brk > old_brk {
408+ if vmem. overlapping ( old_brk..new_brk) . next ( ) . is_some ( ) {
409+ return Err ( MappingError :: OutOfMemory ) ;
410+ }
411+ if let Some ( range) = PageRange :: < ALIGN > :: new ( old_brk, new_brk) {
412+ let ( suggested_address, length) = range. start_and_length ( ) ;
413+ let perms = MemoryRegionPermissions :: READ | MemoryRegionPermissions :: WRITE ;
414+ unsafe {
415+ vmem. create_pages (
416+ Some ( suggested_address) ,
417+ length,
418+ CreatePagesFlags :: FIXED_ADDR | CreatePagesFlags :: POPULATE_PAGES_IMMEDIATELY ,
419+ perms,
420+ )
421+ } ?;
422+ }
423+ vmem. brk_frontier = new_brk;
357424 }
358425 vmem. brk = brk;
359426 Ok ( brk)
@@ -381,7 +448,9 @@ where
381448
382449 // reset brk
383450 let mut vmem = self . vmem . write ( ) ;
451+ vmem. brk_base = 0 ;
384452 vmem. brk = 0 ;
453+ vmem. brk_frontier = 0 ;
385454
386455 Ok ( ( ) )
387456 }
@@ -442,6 +511,24 @@ where
442511 Err ( linux:: VmemResizeError :: NotExist ( _) ) => Err ( RemapError :: AlreadyUnallocated ) ,
443512 Err ( linux:: VmemResizeError :: InvalidAddr { .. } ) => Err ( RemapError :: AlreadyAllocated ) ,
444513 Err ( linux:: VmemResizeError :: OutOfMemory ) => Err ( RemapError :: OutOfMemory ) ,
514+ Err ( linux:: VmemResizeError :: OutOfRange ) => {
515+ // Expanded range exceeds address space limits — try moving
516+ if !may_move {
517+ return Err ( RemapError :: OutOfMemory ) ;
518+ }
519+ match unsafe {
520+ vmem. move_mappings (
521+ old_range,
522+ None ,
523+ NonZeroPageSize :: new ( new_size) . ok_or ( RemapError :: Unaligned ) ?,
524+ )
525+ } {
526+ Ok ( new_addr) => Ok ( new_addr) ,
527+ Err ( linux:: VmemMoveError :: OutOfMemory ) => Err ( RemapError :: OutOfMemory ) ,
528+ Err ( linux:: VmemMoveError :: UnAligned ) => Err ( RemapError :: Unaligned ) ,
529+ Err ( linux:: VmemMoveError :: RemapError ( err) ) => Err ( err) ,
530+ }
531+ }
445532 }
446533 }
447534
@@ -605,16 +692,19 @@ where
605692 ///
606693 /// The `range` must be an already-mapped region with the given `permissions`.
607694 #[ must_use]
695+ #[ allow( clippy:: fn_params_excessive_bools) ]
608696 pub unsafe fn register_existing_mapping (
609697 & self ,
610698 range : PageRange < ALIGN > ,
611699 permissions : MemoryRegionPermissions ,
612700 is_file_backed : bool ,
613701 replace : bool ,
614702 shared : bool ,
703+ fd_writable : bool ,
615704 ) -> Option < ( ) > {
616705 let vma = VmArea :: new (
617- VmFlags :: from ( permissions) | VmFlags :: may_flags_for_mapping ( shared, is_file_backed) ,
706+ VmFlags :: from ( permissions)
707+ | VmFlags :: may_flags_for_mapping ( shared, is_file_backed, fd_writable) ,
618708 is_file_backed,
619709 ) ;
620710 let mut vmem = self . vmem . write ( ) ;
@@ -665,22 +755,25 @@ where
665755 ///
666756 /// # Safety
667757 ///
668- /// This should only be called from the kernel page fault handler.
758+ /// This must only be called while servicing a real page fault for the
759+ /// current address space, and `fault_addr` / `error_code` must come from
760+ /// that trap context. Depending on the platform, that may be a kernel fault
761+ /// handler or an opted-in user-mode fault path.
669762 pub unsafe fn handle_page_fault (
670763 & self ,
671764 fault_addr : usize ,
672765 error_code : u64 ,
673766 ) -> Result < ( ) , PageFaultError > {
674767 let fault_addr = fault_addr & !( ALIGN - 1 ) ;
675- if !( Platform :: TASK_ADDR_MIN .. Platform :: TASK_ADDR_MAX ) . contains ( & fault_addr) {
768+ if !( self . addr_min .. self . addr_max ) . contains ( & fault_addr) {
676769 return Err ( PageFaultError :: AccessError ( "Invalid address" ) ) ;
677770 }
678771
679772 let mut vmem = self . vmem . write ( ) ;
680773 // Find the range closest to the fault address
681774 let ( start, vma) = {
682775 let ( r, vma) = vmem
683- . overlapping ( fault_addr..Platform :: TASK_ADDR_MAX )
776+ . overlapping ( fault_addr..vmem . addr_max ( ) )
684777 . next ( )
685778 . ok_or ( PageFaultError :: AccessError ( "no mapping" ) ) ?;
686779 ( r. start , * vma)
@@ -692,7 +785,7 @@ where
692785 }
693786
694787 if !vmem
695- . overlapping ( Platform :: TASK_ADDR_MIN ..fault_addr)
788+ . overlapping ( vmem . addr_min ( ) ..fault_addr)
696789 . next_back ( )
697790 . is_none_or ( |( prev_range, prev_vma) | {
698791 // Enforce gap between stack and other preceding non-stack mappings.
@@ -708,15 +801,19 @@ where
708801 let Some ( range) = PageRange :: new ( fault_addr, start) else {
709802 unreachable ! ( )
710803 } ;
711- if let Err ( err ) = unsafe {
804+ if unsafe {
712805 vmem. insert_mapping (
713806 range,
714807 vma,
715808 false ,
809+ vma. noreserve ( ) ,
716810 crate :: platform:: page_mgmt:: FixedAddressBehavior :: NoReplace ,
811+ false ,
717812 )
718- } {
719- unimplemented ! ( "failed to grow stack: {:?}" , err)
813+ }
814+ . is_err ( )
815+ {
816+ return Err ( PageFaultError :: AllocationFailed ) ;
720817 }
721818 }
722819
0 commit comments