@@ -8,15 +8,15 @@ pub use x86_64::structures::idt::InterruptStackFrame as ExceptionStackFrame;
88use x86_64:: structures:: idt:: PageFaultErrorCode ;
99pub use x86_64:: structures:: paging:: PageTableFlags as PageTableEntryFlags ;
1010use x86_64:: structures:: paging:: frame:: PhysFrameRange ;
11- use x86_64:: structures:: paging:: mapper:: { MapToError , MappedFrame , TranslateResult , UnmapError } ;
11+ use x86_64:: structures:: paging:: mapper:: { MapToError , MappedFrame , MapperFlush , TranslateResult , UnmapError } ;
1212use x86_64:: structures:: paging:: page:: PageRange ;
1313use x86_64:: structures:: paging:: {
14- FrameAllocator , Mapper , OffsetPageTable , Page , PageTable , PhysFrame , Size4KiB , Translate ,
14+ FrameAllocator , Mapper , OffsetPageTable , Page , PageTable , PhysFrame , Size1GiB , Size2MiB , Size4KiB , Translate ,
1515} ;
1616
1717use crate :: arch:: kernel:: processor;
1818use crate :: arch:: mm:: { PhysAddr , VirtAddr } ;
19- use crate :: mm:: { FrameAlloc , PageRangeAllocator } ;
19+ use crate :: mm:: { FrameAlloc , PageAlloc , PageRangeAllocator } ;
2020use crate :: scheduler;
2121
2222unsafe impl FrameAllocator < Size4KiB > for FrameAlloc {
@@ -191,15 +191,30 @@ pub fn map<S>(
191191 where
192192 M : Mapper < S > ,
193193 S : PageSize + fmt:: Debug ,
194+ for < ' a > OffsetPageTable < ' a > : Mapper < S > ,
194195 {
195196 let mut unmapped = false ;
196197 for ( page, frame) in pages. zip ( frames) {
197198 // TODO: Require explicit unmaps
198- let unmap = mapper. unmap ( page) ;
199- if let Ok ( ( _frame, flush) ) = unmap {
200- unmapped = true ;
201- flush. flush ( ) ;
202- debug ! ( "Had to unmap page {page:?} before mapping." ) ;
199+ let unmap_result = mapper. unmap ( page) ;
200+ match unmap_result {
201+ Ok ( ( _, flush) ) => {
202+ unmapped = true ;
203+ flush. flush ( ) ;
204+ debug ! ( "Had to unmap page {page:?} before mapping." ) ;
205+ }
206+ Err ( UnmapError :: PageNotMapped ) => {
207+ // Expected case
208+ }
209+ Err ( UnmapError :: ParentEntryHugePage ) => {
210+ // Must unmap completely
211+ unmapped = true ;
212+ unmap :: < S > ( page. start_address ( ) . into ( ) , 1 ) ;
213+ debug ! ( "Had to unmap page {page:?} before mapping." ) ;
214+ }
215+ Err ( other) => {
216+ panic ! ( "Failed to unmap page during mapping: {other:?}" ) ;
217+ }
203218 }
204219 let map = unsafe { mapper. map_to ( page, frame, flags, & mut FrameAlloc ) } ;
205220 match map {
@@ -268,6 +283,107 @@ where
268283 }
269284}
270285
286+ /// Prepare a new page table
287+ fn split_page < S : PageSize > ( page : Page < S > ) {
288+ assert_ne ! ( S :: SIZE , Size4KiB :: SIZE , "cannot split small page" ) ;
289+ let is_huge_page = S :: SIZE == Size1GiB :: SIZE ;
290+
291+ // Allocate new page table, map it temporarily
292+ let pt_frame: PhysFrame < Size4KiB > = FrameAlloc . allocate_frame ( ) . unwrap ( ) ;
293+ let pt_page = PageAlloc :: allocate ( PageLayout :: from_size ( Size4KiB :: SIZE as usize ) . unwrap ( ) ) . unwrap ( ) ;
294+ let pt_page = VirtAddr :: new ( pt_page. start ( ) as u64 ) ;
295+
296+ let flags = PageTableEntryFlags :: WRITABLE | PageTableEntryFlags :: NO_EXECUTE | PageTableEntryFlags :: PRESENT ;
297+ map :: < Size4KiB > ( pt_page, pt_frame. start_address ( ) . into ( ) , 1 , flags) ;
298+
299+ // Fill it with entries
300+ let mut table_explorer = unsafe { identity_mapped_page_table ( ) } ;
301+ let ( start_addr, flags) = match table_explorer. translate ( page. start_address ( ) ) {
302+ TranslateResult :: Mapped { frame, flags, .. } => {
303+ let start_addr = match frame {
304+ MappedFrame :: Size2MiB ( frame) if S :: SIZE == Size2MiB :: SIZE => {
305+ frame. start_address ( )
306+ }
307+ MappedFrame :: Size1GiB ( frame) if S :: SIZE == Size1GiB :: SIZE => {
308+ frame. start_address ( )
309+ }
310+ MappedFrame :: Size1GiB ( _) if S :: SIZE == Size2MiB :: SIZE => {
311+ // We were trying to split a large page, and we got a huge page -- we should split it
312+ // Split the parent page first, then retry
313+ split_page ( Page :: < Size1GiB > :: containing_address ( page. start_address ( ) ) ) ;
314+ return split_page ( page) ;
315+ }
316+ other => {
317+ panic ! ( "Unexpected frame mapping {other:?} when trying to split {page:?}" )
318+ }
319+ } ;
320+
321+ ( start_addr, flags)
322+ }
323+ TranslateResult :: NotMapped => {
324+ panic ! ( "Tried to split a page that is not mapped!" )
325+ }
326+ TranslateResult :: InvalidFrameAddress ( addr) => {
327+ panic ! ( "Tried to split a page that maps to invalid physical address {addr:x?}" )
328+ }
329+ } ;
330+
331+ let flags = if is_huge_page {
332+ flags // keep the HUGE flag
333+ } else {
334+ // Remove the large page flag, because we map to 4KiB frames
335+ flags. difference ( PageTableEntryFlags :: HUGE_PAGE )
336+ } ;
337+
338+ // Build the page table!
339+ let pt = pt_page. as_mut_ptr :: < PageTable > ( ) ;
340+ let pt = unsafe {
341+ let pt = pt. as_mut ( ) . unwrap ( ) ;
342+ pt. zero ( ) ;
343+ pt
344+ } ;
345+
346+ let child_page_size = if is_huge_page {
347+ Size2MiB :: SIZE
348+ } else {
349+ Size4KiB :: SIZE
350+ } ;
351+
352+ for ( offset, entry) in pt. iter_mut ( ) . enumerate ( ) {
353+ let offset = ( offset as u64 ) * child_page_size;
354+
355+ entry. set_addr (
356+ start_addr + offset,
357+ flags,
358+ ) ;
359+ }
360+
361+ // We can now replace the entry in the page table
362+ let offset = table_explorer. phys_offset ( ) ;
363+ let p4 = table_explorer. level_4_table_mut ( ) ;
364+ let p3 = & mut p4[ page. p4_index ( ) ] ;
365+ let p3 = offset + p3. addr ( ) . as_u64 ( ) ;
366+ let p3 = unsafe { & mut * p3. as_mut_ptr :: < PageTable > ( ) } ;
367+
368+ if is_huge_page {
369+ let flags = p3[ page. p3_index ( ) ] . flags ( ) - PageTableEntryFlags :: HUGE_PAGE ;
370+ p3[ page. p3_index ( ) ] . set_frame ( pt_frame, flags) ;
371+
372+ } else {
373+ let p2 = & mut p3[ page. p3_index ( ) ] ;
374+ let p2 = offset + p2. addr ( ) . as_u64 ( ) ;
375+ let p2 = unsafe { & mut * p2. as_mut_ptr :: < PageTable > ( ) } ;
376+
377+ let flags = p2[ page. start_address ( ) . p2_index ( ) ] . flags ( ) - PageTableEntryFlags :: HUGE_PAGE ;
378+ p2[ page. start_address ( ) . p2_index ( ) ] . set_frame ( pt_frame, flags) ;
379+ }
380+
381+ // Unmap temporary pt mapping
382+ unmap :: < Size4KiB > ( pt_page, 1 ) ;
383+
384+ MapperFlush :: new ( page) . flush ( ) ;
385+ }
386+
271387pub fn unmap < S > ( virtual_address : VirtAddr , count : usize )
272388where
273389 S : PageSize + fmt:: Debug ,
@@ -279,7 +395,10 @@ where
279395 let last_page = first_page + count as u64 ;
280396 let range = Page :: range ( first_page, last_page) ;
281397
282- for page in range {
398+ fn unmap_page < S > ( page : Page < S > )
399+ where
400+ S : PageSize + fmt:: Debug ,
401+ for < ' a > OffsetPageTable < ' a > : Mapper < S > , {
283402 let unmap_result = unsafe { identity_mapped_page_table ( ) } . unmap ( page) ;
284403 match unmap_result {
285404 Ok ( ( _frame, flush) ) => flush. flush ( ) ,
@@ -288,9 +407,23 @@ where
288407 Err ( UnmapError :: PageNotMapped ) => {
289408 debug ! ( "Tried to unmap {page:?}, which was not mapped." ) ;
290409 }
410+ Err ( UnmapError :: ParentEntryHugePage ) if S :: SIZE == Size4KiB :: SIZE => {
411+ // Prepare new page and retry
412+ split_page ( Page :: < Size2MiB > :: containing_address ( page. start_address ( ) ) ) ;
413+ unmap_page ( page) ;
414+ }
415+ Err ( UnmapError :: ParentEntryHugePage ) if S :: SIZE == Size2MiB :: SIZE => {
416+ // Prepare new page and retry
417+ split_page ( Page :: < Size1GiB > :: containing_address ( page. start_address ( ) ) ) ;
418+ unmap_page ( page) ;
419+ }
291420 Err ( err) => panic ! ( "{err:?}" ) ,
292421 }
293422 }
423+
424+ for page in range {
425+ unmap_page ( page) ;
426+ }
294427}
295428
296429#[ cfg( not( feature = "common-os" ) ) ]
0 commit comments