@@ -5,11 +5,12 @@ use axerrno::{AxError, AxResult, ax_err};
55use axhal:: mem:: phys_to_virt;
66use axhal:: paging:: { MappingFlags , PageTable , PagingError } ;
77use memory_addr:: {
8- MemoryAddr , PAGE_SIZE_4K , PageIter4K , PhysAddr , VirtAddr , VirtAddrRange , is_aligned_4k ,
8+ MemoryAddr , PAGE_SIZE_4K , PageIter4K , PhysAddr , VirtAddr , VirtAddrRange , is_aligned ,
99} ;
1010use memory_set:: { MemoryArea , MemorySet } ;
11+ use page_table_multiarch:: PageSize ;
1112
12- use crate :: backend:: { Backend , SharedPages } ;
13+ use crate :: backend:: { Backend , PageIterWrapper , SharedPages } ;
1314use crate :: mapping_err_to_ax_err;
1415
1516/// The virtual memory address space.
@@ -91,28 +92,84 @@ impl AddrSpace {
9192 self . pt . clear_copy_range ( range. start , range. size ( ) ) ;
9293 }
9394
94- fn validate_region ( & self , start : VirtAddr , size : usize ) -> AxResult {
95+ /// The page table hardware can only map address ranges that are page-aligned.
96+ /// During the memory region validation in AddrSpace,
97+ /// the system enforces address alignment,
98+ /// ensuring that all memory operations comply with page boundary requirements.
99+ fn validate_region ( & self , start : VirtAddr , size : usize , align : PageSize ) -> AxResult {
95100 if !self . contains_range ( start, size) {
96101 return ax_err ! ( InvalidInput , "address out of range" ) ;
97102 }
98- if !start. is_aligned_4k ( ) || !is_aligned_4k ( size) {
103+ if !start. is_aligned ( align ) || !is_aligned ( size, align . into ( ) ) {
99104 return ax_err ! ( InvalidInput , "address not aligned" ) ;
100105 }
101106 Ok ( ( ) )
102107 }
103108
104- /// Finds a free area that can accommodate the given size.
109+ /// Searches for a contiguous free region in the virtual address space
105110 ///
106- /// The search starts from the given hint address, and the area should be within the given limit range.
111+ /// This function searches for available virtual address space within a specified address range,
112+ /// based on the current memory region layout, that satisfies the size and alignment requirements.
107113 ///
108- /// Returns the start address of the free area. Returns None if no such area is found.
114+ /// # Parameters
115+ /// - `hint`: Suggested starting address for the search (may be adjusted due to alignment or overlapping regions)
116+ /// - `size`: Size of the contiguous address space to allocate (in bytes)
117+ /// - `limit`: Boundary of the allowed address range (inclusive of start and end addresses)
118+ /// - `align`: Address alignment requirement (e.g., page alignment like 4KB/2MB)
119+ ///
120+ /// # Return Value
121+ /// - `Some(VirtAddr)`: A starting virtual address that meets all requirements was found
122+ /// - `None`: No sufficient space was found within the specified range
123+ ///
124+ /// # Implementation Logic
125+ /// 1. Initialize `last_end` to the maximum aligned value between the hint and the start of the limit range
126+ /// 2. First pass: handle regions before the hint to determine the initial search position
127+ /// 3. Second pass: check gaps between regions:
128+ /// - Skip overlapping and already occupied regions
129+ /// - Check whether the gap between regions satisfies the `size + alignment` requirement
130+ /// 4. Finally, verify that the found address is within the specified `limit` range
131+ ///
132+ /// # Notes
133+ /// - Alignment is strictly enforced on candidate addresses (ensured via `align_up`)
134+ /// - The region must be fully contained within the `limit` range (`end <= limit.end`)
135+ /// - The search may ignore the `hint` if a better space is found in later regions
109136 pub fn find_free_area (
110137 & self ,
111138 hint : VirtAddr ,
112139 size : usize ,
113140 limit : VirtAddrRange ,
141+ align : PageSize ,
114142 ) -> Option < VirtAddr > {
115- self . areas . find_free_area ( hint, size, limit)
143+ let mut last_end = hint. max ( limit. start ) . align_up ( align) ;
144+ for area in self . areas . iter ( ) {
145+ if area. end ( ) <= last_end {
146+ last_end = last_end. max ( area. end ( ) . align_up ( align) ) ;
147+ } else {
148+ break ;
149+ }
150+ }
151+ for area in self . areas . iter ( ) {
152+ let area_start = area. start ( ) ;
153+ if area_start < last_end {
154+ continue ;
155+ }
156+ if last_end
157+ . checked_add ( size)
158+ . is_some_and ( |end| end <= area_start)
159+ {
160+ return Some ( last_end) ;
161+ }
162+ last_end = area. end ( ) . align_up ( align) ;
163+ }
164+
165+ if last_end
166+ . checked_add ( size)
167+ . is_some_and ( |end| end <= limit. end )
168+ {
169+ Some ( last_end)
170+ } else {
171+ None
172+ }
116173 }
117174
118175 /// Add a new linear mapping.
@@ -129,14 +186,16 @@ impl AddrSpace {
129186 start_paddr : PhysAddr ,
130187 size : usize ,
131188 flags : MappingFlags ,
189+ align : PageSize ,
132190 ) -> AxResult {
133- self . validate_region ( start_vaddr, size) ?;
134- if !start_paddr. is_aligned_4k ( ) {
191+ self . validate_region ( start_vaddr, size, align) ?;
192+
193+ if !start_paddr. is_aligned ( align) {
135194 return ax_err ! ( InvalidInput , "address not aligned" ) ;
136195 }
137196
138197 let offset = start_vaddr. as_usize ( ) - start_paddr. as_usize ( ) ;
139- let area = MemoryArea :: new ( start_vaddr, size, flags, Backend :: new_linear ( offset) ) ;
198+ let area = MemoryArea :: new ( start_vaddr, size, flags, Backend :: new_linear ( offset, align ) ) ;
140199 self . areas
141200 . map ( area, & mut self . pt , false )
142201 . map_err ( mapping_err_to_ax_err) ?;
@@ -157,10 +216,11 @@ impl AddrSpace {
157216 size : usize ,
158217 flags : MappingFlags ,
159218 populate : bool ,
219+ align : PageSize ,
160220 ) -> AxResult {
161- self . validate_region ( start, size) ?;
221+ self . validate_region ( start, size, align ) ?;
162222
163- let area = MemoryArea :: new ( start, size, flags, Backend :: new_alloc ( populate) ) ;
223+ let area = MemoryArea :: new ( start, size, flags, Backend :: new_alloc ( populate, align ) ) ;
164224 self . areas
165225 . map ( area, & mut self . pt , false )
166226 . map_err ( mapping_err_to_ax_err) ?;
@@ -181,17 +241,18 @@ impl AddrSpace {
181241 size : usize ,
182242 flags : MappingFlags ,
183243 source : Option < Arc < SharedPages > > ,
244+ align : PageSize ,
184245 ) -> AxResult < Arc < SharedPages > > {
185- self . validate_region ( start, size) ?;
246+ self . validate_region ( start, size, align ) ?;
186247
187248 let area = MemoryArea :: new (
188249 start,
189250 size,
190251 flags,
191- Backend :: new_shared ( size / PAGE_SIZE_4K , source) ,
252+ Backend :: new_shared ( start , size , source, align ) . ok_or ( AxError :: InvalidInput ) ? ,
192253 ) ;
193254 let result = match area. backend ( ) {
194- Backend :: Shared { pages } => pages. clone ( ) ,
255+ Backend :: Shared { pages, .. } => pages. clone ( ) ,
195256 _ => unreachable ! ( ) ,
196257 } ;
197258 self . areas
@@ -203,15 +264,15 @@ impl AddrSpace {
203264
204265 /// Populates the area with physical frames, returning false if the area
205266 /// contains unmapped area.
206- pub fn populate_area ( & mut self , mut start : VirtAddr , size : usize ) -> AxResult {
207- self . validate_region ( start, size) ?;
267+ pub fn populate_area ( & mut self , mut start : VirtAddr , size : usize , align : PageSize ) -> AxResult {
268+ self . validate_region ( start, size, align ) ?;
208269 let end = start + size;
209270
210271 while let Some ( area) = self . areas . find ( start) {
211272 let backend = area. backend ( ) ;
212- if let Backend :: Alloc { populate, .. } = backend {
213- if !* populate {
214- for addr in PageIter4K :: new ( start, area. end ( ) . min ( end) ) . unwrap ( ) {
273+ if let Backend :: Alloc { populate, align } = * backend {
274+ if !populate {
275+ for addr in PageIterWrapper :: new ( start, area. end ( ) . min ( end) , align ) . unwrap ( ) {
215276 match self . pt . query ( addr) {
216277 Ok ( _) => { }
217278 // If the page is not mapped, try map it.
@@ -226,7 +287,7 @@ impl AddrSpace {
226287 }
227288 }
228289 start = area. end ( ) ;
229- assert ! ( start. is_aligned_4k ( ) ) ;
290+ assert ! ( start. is_aligned ( align ) ) ;
230291 if start >= end {
231292 break ;
232293 }
@@ -245,7 +306,27 @@ impl AddrSpace {
245306 /// Returns an error if the address range is out of the address space or not
246307 /// aligned.
247308 pub fn unmap ( & mut self , start : VirtAddr , size : usize ) -> AxResult {
248- self . validate_region ( start, size) ?;
309+ self . validate_region ( start, size, PageSize :: Size4K ) ?;
310+
311+ let end = start + size;
312+ for area in self
313+ . areas
314+ . iter ( )
315+ . skip_while ( move |a| a. end ( ) <= start)
316+ . take_while ( move |a| a. start ( ) < end)
317+ {
318+ let area_align = match area. backend ( ) {
319+ Backend :: Alloc { align, .. } => * align,
320+ Backend :: Linear { align, .. } => * align,
321+ Backend :: Shared { pages } => pages. align ,
322+ } ;
323+
324+ let unmap_start = start. max ( area. start ( ) ) ;
325+ let unmap_size = end. min ( area. end ( ) ) - unmap_start;
326+ if !unmap_start. is_aligned ( area_align) || !is_aligned ( unmap_size, area_align. into ( ) ) {
327+ return ax_err ! ( InvalidInput , "address not aligned" ) ;
328+ }
329+ }
249330
250331 self . areas
251332 . unmap ( start, size, & mut self . pt )
@@ -255,16 +336,6 @@ impl AddrSpace {
255336
256337 /// To remove user area mappings from address space.
257338 pub fn unmap_user_areas ( & mut self ) -> AxResult {
258- for area in self . areas . iter ( ) {
259- assert ! ( area. start( ) . is_aligned_4k( ) ) ;
260- assert ! ( area. size( ) % PAGE_SIZE_4K == 0 ) ;
261- assert ! ( area. flags( ) . contains( MappingFlags :: USER ) ) ;
262- assert ! (
263- self . va_range
264- . contains_range( VirtAddrRange :: from_start_size( area. start( ) , area. size( ) ) ) ,
265- "MemorySet contains out-of-va-range area"
266- ) ;
267- }
268339 self . areas . clear ( & mut self . pt ) . unwrap ( ) ;
269340 Ok ( ( ) )
270341 }
@@ -281,18 +352,19 @@ impl AddrSpace {
281352 ///
282353 /// # Notes
283354 /// The caller must ensure that the permission of the operation is allowed.
284- fn process_area_data < F > ( & self , start : VirtAddr , size : usize , f : F ) -> AxResult
355+ fn process_area_data < F > ( & self , start : VirtAddr , size : usize , align : PageSize , f : F ) -> AxResult
285356 where
286357 F : FnMut ( VirtAddr , usize , usize ) ,
287358 {
288- Self :: process_area_data_with_page_table ( & self . pt , & self . va_range , start, size, f)
359+ Self :: process_area_data_with_page_table ( & self . pt , & self . va_range , start, size, align , f)
289360 }
290361
291362 fn process_area_data_with_page_table < F > (
292363 pt : & PageTable ,
293364 va_range : & VirtAddrRange ,
294365 start : VirtAddr ,
295366 size : usize ,
367+ align : PageSize ,
296368 mut f : F ,
297369 ) -> AxResult
298370 where
@@ -303,8 +375,9 @@ impl AddrSpace {
303375 }
304376 let mut cnt = 0 ;
305377 // If start is aligned to 4K, start_align_down will be equal to start_align_up.
306- let end_align_up = ( start + size) . align_up_4k ( ) ;
307- for vaddr in PageIter4K :: new ( start. align_down_4k ( ) , end_align_up)
378+ let end_align_up = ( start + size) . align_up ( align) ;
379+ let start_addr = start. align_down ( align) ;
380+ for vaddr in PageIterWrapper :: new ( start_addr, end_align_up, align)
308381 . expect ( "Failed to create page iterator" )
309382 {
310383 let ( mut paddr, _, _) = pt. query ( vaddr) . map_err ( |_| AxError :: BadAddress ) ?;
@@ -314,9 +387,9 @@ impl AddrSpace {
314387 if copy_size == 0 {
315388 break ;
316389 }
317- if vaddr == start. align_down_4k ( ) && start. align_offset_4k ( ) != 0 {
318- let align_offset = start. align_offset_4k ( ) ;
319- copy_size = copy_size. min ( PAGE_SIZE_4K - align_offset) ;
390+ if vaddr == start. align_down ( align ) && start. align_offset ( align ) != 0 {
391+ let align_offset = start. align_offset ( align ) ;
392+ copy_size = copy_size. min ( align as usize - align_offset) ;
320393 paddr += align_offset;
321394 }
322395 f ( phys_to_virt ( paddr) , cnt, copy_size) ;
@@ -331,8 +404,8 @@ impl AddrSpace {
331404 ///
332405 /// * `start` - The start virtual address to read.
333406 /// * `buf` - The buffer to store the data.
334- pub fn read ( & self , start : VirtAddr , buf : & mut [ u8 ] ) -> AxResult {
335- self . process_area_data ( start, buf. len ( ) , |src, offset, read_size| unsafe {
407+ pub fn read ( & self , start : VirtAddr , align : PageSize , buf : & mut [ u8 ] ) -> AxResult {
408+ self . process_area_data ( start, buf. len ( ) , align , |src, offset, read_size| unsafe {
336409 core:: ptr:: copy_nonoverlapping ( src. as_ptr ( ) , buf. as_mut_ptr ( ) . add ( offset) , read_size) ;
337410 } )
338411 }
@@ -343,8 +416,8 @@ impl AddrSpace {
343416 ///
344417 /// * `start_vaddr` - The start virtual address to write.
345418 /// * `buf` - The buffer to write to the address space.
346- pub fn write ( & self , start : VirtAddr , buf : & [ u8 ] ) -> AxResult {
347- self . process_area_data ( start, buf. len ( ) , |dst, offset, write_size| unsafe {
419+ pub fn write ( & self , start : VirtAddr , align : PageSize , buf : & [ u8 ] ) -> AxResult {
420+ self . process_area_data ( start, buf. len ( ) , align , |dst, offset, write_size| unsafe {
348421 core:: ptr:: copy_nonoverlapping ( buf. as_ptr ( ) . add ( offset) , dst. as_mut_ptr ( ) , write_size) ;
349422 } )
350423 }
@@ -353,9 +426,15 @@ impl AddrSpace {
353426 ///
354427 /// Returns an error if the address range is out of the address space or not
355428 /// aligned.
356- pub fn protect ( & mut self , start : VirtAddr , size : usize , flags : MappingFlags ) -> AxResult {
429+ pub fn protect (
430+ & mut self ,
431+ start : VirtAddr ,
432+ size : usize ,
433+ flags : MappingFlags ,
434+ align : PageSize ,
435+ ) -> AxResult {
357436 // Populate the area first, which also checks the address range for us.
358- self . populate_area ( start, size) ?;
437+ self . populate_area ( start, size, align ) ?;
359438
360439 self . areas
361440 . protect ( start, size, |_| Some ( flags) , & mut self . pt )
0 commit comments