@@ -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:: x86_64:: kernel:: processor;
1818use crate :: arch:: x86_64:: mm:: { PhysAddr , VirtAddr } ;
19- use crate :: mm:: { FrameAlloc , PageRangeAllocator } ;
19+ use crate :: mm:: { FrameAlloc , PageAlloc , PageRangeAllocator } ;
2020use crate :: { env, scheduler} ;
2121
2222unsafe impl FrameAllocator < Size4KiB > for FrameAlloc {
@@ -173,15 +173,30 @@ pub fn map<S>(
173173 where
174174 M : Mapper < S > ,
175175 S : PageSize + fmt:: Debug ,
176+ for < ' a > OffsetPageTable < ' a > : Mapper < S > ,
176177 {
177178 let mut unmapped = false ;
178179 for ( page, frame) in pages. zip ( frames) {
179180 // TODO: Require explicit unmaps
180- let unmap = mapper. unmap ( page) ;
181- if let Ok ( ( _frame, flush) ) = unmap {
182- unmapped = true ;
183- flush. flush ( ) ;
184- debug ! ( "Had to unmap page {page:?} before mapping." ) ;
181+ let unmap_result = mapper. unmap ( page) ;
182+ match unmap_result {
183+ Ok ( ( _, flush) ) => {
184+ unmapped = true ;
185+ flush. flush ( ) ;
186+ debug ! ( "Had to unmap page {page:?} before mapping." ) ;
187+ }
188+ Err ( UnmapError :: PageNotMapped ) => {
189+ // Expected case
190+ }
191+ Err ( UnmapError :: ParentEntryHugePage ) => {
192+ // Must unmap completely
193+ unmapped = true ;
194+ unmap :: < S > ( page. start_address ( ) . into ( ) , 1 ) ;
195+ debug ! ( "Had to unmap page {page:?} before mapping." ) ;
196+ }
197+ Err ( other) => {
198+ panic ! ( "Failed to unmap page during mapping: {other:?}" ) ;
199+ }
185200 }
186201 let map = unsafe { mapper. map_to ( page, frame, flags, & mut FrameAlloc ) } ;
187202 match map {
@@ -250,6 +265,107 @@ where
250265 }
251266}
252267
268+ /// Prepare a new page table
269+ fn split_page < S : PageSize > ( page : Page < S > ) {
270+ assert_ne ! ( S :: SIZE , Size4KiB :: SIZE , "cannot split small page" ) ;
271+ let is_huge_page = S :: SIZE == Size1GiB :: SIZE ;
272+
273+ // Allocate new page table, map it temporarily
274+ let pt_frame: PhysFrame < Size4KiB > = FrameAlloc . allocate_frame ( ) . unwrap ( ) ;
275+ let pt_page = PageAlloc :: allocate ( PageLayout :: from_size ( Size4KiB :: SIZE as usize ) . unwrap ( ) ) . unwrap ( ) ;
276+ let pt_page = VirtAddr :: new ( pt_page. start ( ) as u64 ) ;
277+
278+ let flags = PageTableEntryFlags :: WRITABLE | PageTableEntryFlags :: NO_EXECUTE | PageTableEntryFlags :: PRESENT ;
279+ map :: < Size4KiB > ( pt_page, pt_frame. start_address ( ) . into ( ) , 1 , flags) ;
280+
281+ // Fill it with entries
282+ let mut table_explorer = unsafe { identity_mapped_page_table ( ) } ;
283+ let ( start_addr, flags) = match table_explorer. translate ( page. start_address ( ) ) {
284+ TranslateResult :: Mapped { frame, flags, .. } => {
285+ let start_addr = match frame {
286+ MappedFrame :: Size2MiB ( frame) if S :: SIZE == Size2MiB :: SIZE => {
287+ frame. start_address ( )
288+ }
289+ MappedFrame :: Size1GiB ( frame) if S :: SIZE == Size1GiB :: SIZE => {
290+ frame. start_address ( )
291+ }
292+ MappedFrame :: Size1GiB ( _) if S :: SIZE == Size2MiB :: SIZE => {
293+ // We were trying to split a large page, and we got a huge page -- we should split it
294+ // Split the parent page first, then retry
295+ split_page ( Page :: < Size1GiB > :: containing_address ( page. start_address ( ) ) ) ;
296+ return split_page ( page) ;
297+ }
298+ other => {
299+ panic ! ( "Unexpected frame mapping {other:?} when trying to split {page:?}" )
300+ }
301+ } ;
302+
303+ ( start_addr, flags)
304+ }
305+ TranslateResult :: NotMapped => {
306+ panic ! ( "Tried to split a page that is not mapped!" )
307+ }
308+ TranslateResult :: InvalidFrameAddress ( addr) => {
309+ panic ! ( "Tried to split a page that maps to invalid physical address {addr:x?}" )
310+ }
311+ } ;
312+
313+ let flags = if is_huge_page {
314+ flags // keep the HUGE flag
315+ } else {
316+ // Remove the large page flag, because we map to 4KiB frames
317+ flags. difference ( PageTableEntryFlags :: HUGE_PAGE )
318+ } ;
319+
320+ // Build the page table!
321+ let pt = pt_page. as_mut_ptr :: < PageTable > ( ) ;
322+ let pt = unsafe {
323+ let pt = pt. as_mut ( ) . unwrap ( ) ;
324+ pt. zero ( ) ;
325+ pt
326+ } ;
327+
328+ let child_page_size = if is_huge_page {
329+ Size2MiB :: SIZE
330+ } else {
331+ Size4KiB :: SIZE
332+ } ;
333+
334+ for ( offset, entry) in pt. iter_mut ( ) . enumerate ( ) {
335+ let offset = ( offset as u64 ) * child_page_size;
336+
337+ entry. set_addr (
338+ start_addr + offset,
339+ flags,
340+ ) ;
341+ }
342+
343+ // We can now replace the entry in the page table
344+ let offset = table_explorer. phys_offset ( ) ;
345+ let p4 = table_explorer. level_4_table_mut ( ) ;
346+ let p3 = & mut p4[ page. p4_index ( ) ] ;
347+ let p3 = offset + p3. addr ( ) . as_u64 ( ) ;
348+ let p3 = unsafe { & mut * p3. as_mut_ptr :: < PageTable > ( ) } ;
349+
350+ if is_huge_page {
351+ let flags = p3[ page. p3_index ( ) ] . flags ( ) - PageTableEntryFlags :: HUGE_PAGE ;
352+ p3[ page. p3_index ( ) ] . set_frame ( pt_frame, flags) ;
353+
354+ } else {
355+ let p2 = & mut p3[ page. p3_index ( ) ] ;
356+ let p2 = offset + p2. addr ( ) . as_u64 ( ) ;
357+ let p2 = unsafe { & mut * p2. as_mut_ptr :: < PageTable > ( ) } ;
358+
359+ let flags = p2[ page. start_address ( ) . p2_index ( ) ] . flags ( ) - PageTableEntryFlags :: HUGE_PAGE ;
360+ p2[ page. start_address ( ) . p2_index ( ) ] . set_frame ( pt_frame, flags) ;
361+ }
362+
363+ // Unmap temporary pt mapping
364+ unmap :: < Size4KiB > ( pt_page, 1 ) ;
365+
366+ MapperFlush :: new ( page) . flush ( ) ;
367+ }
368+
253369pub fn unmap < S > ( virtual_address : VirtAddr , count : usize )
254370where
255371 S : PageSize + fmt:: Debug ,
@@ -261,7 +377,10 @@ where
261377 let last_page = first_page + count as u64 ;
262378 let range = Page :: range ( first_page, last_page) ;
263379
264- for page in range {
380+ fn unmap_page < S > ( page : Page < S > )
381+ where
382+ S : PageSize + fmt:: Debug ,
383+ for < ' a > OffsetPageTable < ' a > : Mapper < S > , {
265384 let unmap_result = unsafe { identity_mapped_page_table ( ) } . unmap ( page) ;
266385 match unmap_result {
267386 Ok ( ( _frame, flush) ) => flush. flush ( ) ,
@@ -270,9 +389,23 @@ where
270389 Err ( UnmapError :: PageNotMapped ) => {
271390 debug ! ( "Tried to unmap {page:?}, which was not mapped." ) ;
272391 }
392+ Err ( UnmapError :: ParentEntryHugePage ) if S :: SIZE == Size4KiB :: SIZE => {
393+ // Prepare new page and retry
394+ split_page ( Page :: < Size2MiB > :: containing_address ( page. start_address ( ) ) ) ;
395+ unmap_page ( page) ;
396+ }
397+ Err ( UnmapError :: ParentEntryHugePage ) if S :: SIZE == Size2MiB :: SIZE => {
398+ // Prepare new page and retry
399+ split_page ( Page :: < Size1GiB > :: containing_address ( page. start_address ( ) ) ) ;
400+ unmap_page ( page) ;
401+ }
273402 Err ( err) => panic ! ( "{err:?}" ) ,
274403 }
275404 }
405+
406+ for page in range {
407+ unmap_page ( page) ;
408+ }
276409}
277410
278411#[ cfg( not( feature = "common-os" ) ) ]
0 commit comments