1+ use core:: mem:: align_of;
2+
13use crate :: {
24 binary:: { level_4_entries:: UsedLevel4Entries , PAGE_SIZE } ,
35 boot_info:: TlsTemplate ,
46} ;
57use x86_64:: {
68 align_up,
79 structures:: paging:: {
8- mapper:: MapperAllSizes , FrameAllocator , Page , PageSize , PageTableFlags as Flags , PhysFrame ,
9- Size4KiB ,
10+ mapper:: { MappedFrame , MapperAllSizes , TranslateResult } ,
11+ FrameAllocator , Page , PageSize , PageTableFlags as Flags , PhysFrame , Size4KiB , Translate ,
1012 } ,
1113 PhysAddr , VirtAddr ,
1214} ;
@@ -17,6 +19,9 @@ use xmas_elf::{
1719 ElfFile ,
1820} ;
1921
22+ /// Used by [`Inner::make_mut`] and [`Inner::clean_copied_flag`].
23+ const COPIED : Flags = Flags :: BIT_9 ;
24+
2025struct Loader < ' a , M , F > {
2126 elf_file : ElfFile < ' a > ,
2227 inner : Inner < ' a , M , F > ,
@@ -31,7 +36,7 @@ struct Inner<'a, M, F> {
3136
3237impl < ' a , M , F > Loader < ' a , M , F >
3338where
34- M : MapperAllSizes ,
39+ M : MapperAllSizes + Translate ,
3540 F : FrameAllocator < Size4KiB > ,
3641{
3742 fn new (
7580 program:: sanity_check ( program_header, & self . elf_file ) ?;
7681 }
7782
78- // Apply relocations in physical memory.
79- for program_header in self . elf_file . program_iter ( ) {
80- if let Type :: Dynamic = program_header. get_type ( ) ? {
81- self . inner
82- . handle_dynamic_segment ( program_header, & self . elf_file ) ?
83- }
84- }
85-
8683 // Load the segments into virtual memory.
8784 let mut tls_template = None ;
8885 for program_header in self . elf_file . program_iter ( ) {
@@ -106,6 +103,17 @@ where
106103 | Type :: ProcessorSpecific ( _) => { }
107104 }
108105 }
106+
107+ // Apply relocations in virtual memory.
108+ for program_header in self . elf_file . program_iter ( ) {
109+ if let Type :: Dynamic = program_header. get_type ( ) ? {
110+ self . inner
111+ . handle_dynamic_segment ( program_header, & self . elf_file ) ?
112+ }
113+ }
114+
115+ self . inner . remove_copied_flags ( & self . elf_file ) . unwrap ( ) ;
116+
109117 Ok ( tls_template)
110118 }
111119
@@ -123,7 +131,7 @@ where
123131
124132impl < ' a , M , F > Inner < ' a , M , F >
125133where
126- M : MapperAllSizes ,
134+ M : MapperAllSizes + Translate ,
127135 F : FrameAllocator < Size4KiB > ,
128136{
129137 fn handle_load_segment ( & mut self , segment : ProgramHeader ) -> Result < ( ) , & ' static str > {
@@ -175,7 +183,6 @@ where
175183 log:: info!( "Mapping bss section" ) ;
176184
177185 let virt_start_addr = VirtAddr :: new ( segment. virtual_addr ( ) ) + self . virtual_address_offset ;
178- let phys_start_addr = self . kernel_offset + segment. offset ( ) ;
179186 let mem_size = segment. mem_size ( ) ;
180187 let file_size = segment. file_size ( ) ;
181188
@@ -220,47 +227,16 @@ where
220227 // the remaining part of the frame since the frame is no longer shared with other
221228 // segments now.
222229
223- // calculate the frame where the last segment page is mapped
224- let orig_frame: PhysFrame =
225- PhysFrame :: containing_address ( phys_start_addr + file_size - 1u64 ) ;
226- // allocate a new frame to replace `orig_frame`
227- let new_frame = self . frame_allocator . allocate_frame ( ) . unwrap ( ) ;
228-
229- // zero new frame, utilizing that it's identity-mapped
230- {
231- let new_frame_ptr = new_frame. start_address ( ) . as_u64 ( ) as * mut PageArray ;
232- unsafe { new_frame_ptr. write ( ZERO_ARRAY ) } ;
233- }
234-
235- // copy the data bytes from orig_frame to new_frame
236- {
237- log:: info!( "Copy contents" ) ;
238- let orig_bytes_ptr = orig_frame. start_address ( ) . as_u64 ( ) as * mut u8 ;
239- let new_bytes_ptr = new_frame. start_address ( ) . as_u64 ( ) as * mut u8 ;
240-
241- for offset in 0 ..( data_bytes_before_zero as isize ) {
242- unsafe {
243- let orig_byte = orig_bytes_ptr. offset ( offset) . read ( ) ;
244- new_bytes_ptr. offset ( offset) . write ( orig_byte) ;
245- }
246- }
247- }
248-
249- // remap last page from orig_frame to `new_frame`
250- log:: info!( "Remap last page" ) ;
251230 let last_page = Page :: containing_address ( virt_start_addr + file_size - 1u64 ) ;
252- self . page_table
253- . unmap ( last_page . clone ( ) )
254- . map_err ( |_err| "Failed to unmap last segment page because of bss memory" ) ?
255- . 1
256- . ignore ( ) ;
257- let flusher = unsafe {
258- self . page_table
259- . map_to ( last_page , new_frame , segment_flags , self . frame_allocator )
231+ let new_frame = unsafe { self . make_mut ( last_page ) } ;
232+ let new_bytes_ptr = new_frame . start_address ( ) . as_u64 ( ) as * mut u8 ;
233+ unsafe {
234+ core :: ptr :: write_bytes (
235+ new_bytes_ptr ,
236+ 0 ,
237+ ( Size4KiB :: SIZE - data_bytes_before_zero ) as usize ,
238+ ) ;
260239 }
261- . map_err ( |_err| "Failed to remap last segment page because of bss memory" ) ?;
262- // we operate on an inactive page table, so we don't need to flush our changes
263- flusher. ignore ( ) ;
264240 }
265241
266242 // map additional frames for `.bss` memory that is not present in source file
@@ -288,6 +264,104 @@ where
288264 Ok ( ( ) )
289265 }
290266
267+ /// This method is intended for making the memory loaded by a Load segment mutable.
268+ ///
269+ /// All memory from a Load segment starts out by mapped to the same frames that
270+ /// contain the elf file. Thus writing to memory in that state will cause aliasing issues.
271+ /// To avoid that, we allocate a new frame, copy all bytes from the old frame to the new frame,
272+ /// and remap the page to the new frame. At this point the page no longer aliases the elf file
273+ /// and we can write to it.
274+ ///
275+ /// When we map the new frame we also set [`COPIED`] flag in the page table flags, so that
276+ /// we can detect if the frame has already been copied when we try to modify the page again.
277+ ///
278+ /// ## Safety
279+ /// - `page` should be a page mapped by a Load segment.
280+ ///
281+ /// ## Panics
282+ /// Panics if the page is not mapped in `self.page_table`.
283+ unsafe fn make_mut ( & mut self , page : Page ) -> PhysFrame {
284+ let ( frame, flags) = match self . page_table . translate ( page. start_address ( ) ) {
285+ TranslateResult :: Mapped {
286+ frame,
287+ offset : _,
288+ flags,
289+ } => ( frame, flags) ,
290+ TranslateResult :: NotMapped => panic ! ( "{:?} is not mapped" , page) ,
291+ TranslateResult :: InvalidFrameAddress ( _) => unreachable ! ( ) ,
292+ } ;
293+ let frame = if let MappedFrame :: Size4KiB ( frame) = frame {
294+ frame
295+ } else {
296+ // We only map 4k pages.
297+ unreachable ! ( )
298+ } ;
299+
300+ if flags. contains ( COPIED ) {
301+ // The frame was already copied, we are free to modify it.
302+ return frame;
303+ }
304+
305+ // Allocate a new frame and copy the memory, utilizing that both frames are identity mapped.
306+ let new_frame = self . frame_allocator . allocate_frame ( ) . unwrap ( ) ;
307+ let frame_ptr = frame. start_address ( ) . as_u64 ( ) as * const u8 ;
308+ let new_frame_ptr = new_frame. start_address ( ) . as_u64 ( ) as * mut u8 ;
309+ unsafe {
310+ core:: ptr:: copy_nonoverlapping ( frame_ptr, new_frame_ptr, Size4KiB :: SIZE as usize ) ;
311+ }
312+
313+ // Replace the underlying frame and update the flags.
314+ self . page_table . unmap ( page) . unwrap ( ) . 1 . ignore ( ) ;
315+ let new_flags = flags | COPIED ;
316+ unsafe {
317+ self . page_table
318+ . map_to ( page, new_frame, new_flags, self . frame_allocator )
319+ . unwrap ( )
320+ . ignore ( ) ;
321+ }
322+
323+ new_frame
324+ }
325+
326+ /// Cleans up the custom flags set by [`Inner::make_mut`].
327+ fn remove_copied_flags ( & mut self , elf_file : & ElfFile ) -> Result < ( ) , & ' static str > {
328+ for program_header in elf_file. program_iter ( ) {
329+ if let Type :: Load = program_header. get_type ( ) ? {
330+ let start = self . virtual_address_offset + program_header. virtual_addr ( ) ;
331+ let end = start + program_header. mem_size ( ) ;
332+ let start = VirtAddr :: new ( start) ;
333+ let end = VirtAddr :: new ( end) ;
334+ let start_page = Page :: containing_address ( start) ;
335+ let end_page = Page :: containing_address ( end - 1u64 ) ;
336+ for page in Page :: < Size4KiB > :: range_inclusive ( start_page, end_page) {
337+ // Translate the page and get the flags.
338+ let res = self . page_table . translate ( page. start_address ( ) ) ;
339+ let flags = match res {
340+ TranslateResult :: Mapped {
341+ frame : _,
342+ offset : _,
343+ flags,
344+ } => flags,
345+ TranslateResult :: NotMapped | TranslateResult :: InvalidFrameAddress ( _) => {
346+ unreachable ! ( "has the elf file not been mapped correctly?" )
347+ }
348+ } ;
349+
350+ if flags. contains ( COPIED ) {
351+ // Remove the flag.
352+ unsafe {
353+ self . page_table
354+ . update_flags ( page, flags & !COPIED )
355+ . unwrap ( )
356+ . ignore ( ) ;
357+ }
358+ }
359+ }
360+ }
361+ }
362+ Ok ( ( ) )
363+ }
364+
291365 fn handle_tls_segment ( & mut self , segment : ProgramHeader ) -> Result < TlsTemplate , & ' static str > {
292366 Ok ( TlsTemplate {
293367 start_addr : segment. virtual_addr ( ) + self . virtual_address_offset ,
@@ -385,19 +459,27 @@ where
385459 match rela. get_type ( ) {
386460 // R_AMD64_RELATIVE
387461 8 => {
388- let offset_in_file = find_offset ( elf_file, rela. get_offset ( ) ) ?
389- . ok_or ( "Destination of relocation is not mapped in physical memory" ) ?;
390- let dest_addr = self . kernel_offset + offset_in_file;
391- let dest_ptr = dest_addr. as_u64 ( ) as * mut u64 ;
392-
462+ check_is_in_load ( elf_file, rela. get_offset ( ) ) ?;
463+ let addr = self . virtual_address_offset + rela. get_offset ( ) ;
393464 let value = self
394465 . virtual_address_offset
395466 . checked_add ( rela. get_addend ( ) )
396467 . unwrap ( ) ;
397468
469+ let ptr = addr as * mut u64 ;
470+ if ptr as usize % align_of :: < u64 > ( ) != 0 {
471+ return Err ( "destination of relocation is not aligned" ) ;
472+ }
473+
474+ let virt_addr = VirtAddr :: from_ptr ( ptr) ;
475+ let page = Page :: containing_address ( virt_addr) ;
476+ let offset_in_page = virt_addr - page. start_address ( ) ;
477+
478+ let new_frame = unsafe { self . make_mut ( page) } ;
479+ let phys_addr = new_frame. start_address ( ) + offset_in_page;
480+ let addr = phys_addr. as_u64 ( ) as * mut u64 ;
398481 unsafe {
399- // write new value, utilizing that the address identity-mapped
400- dest_ptr. write ( value) ;
482+ addr. write ( value) ;
401483 }
402484 }
403485 ty => unimplemented ! ( "relocation type {:x} not supported" , ty) ,
@@ -408,19 +490,19 @@ where
408490 }
409491}
410492
411- /// Locate the offset into the elf file corresponding to a virtual address .
412- fn find_offset ( elf_file : & ElfFile , virt_offset : u64 ) -> Result < Option < u64 > , & ' static str > {
493+ /// Check that the virtual offset belongs to a load segment .
494+ fn check_is_in_load ( elf_file : & ElfFile , virt_offset : u64 ) -> Result < ( ) , & ' static str > {
413495 for program_header in elf_file. program_iter ( ) {
414496 if let Type :: Load = program_header. get_type ( ) ? {
415497 if program_header. virtual_addr ( ) <= virt_offset {
416498 let offset_in_segment = virt_offset - program_header. virtual_addr ( ) ;
417499 if offset_in_segment < program_header. file_size ( ) {
418- return Ok ( Some ( program_header . offset ( ) + offset_in_segment ) ) ;
500+ return Ok ( ( ) ) ;
419501 }
420502 }
421503 }
422504 }
423- Ok ( None )
505+ Err ( "offset is not in load segment" )
424506}
425507
426508/// Loads the kernel ELF file given in `bytes` in the given `page_table`.
@@ -429,7 +511,7 @@ fn find_offset(elf_file: &ElfFile, virt_offset: u64) -> Result<Option<u64>, &'st
429511/// and a structure describing which level 4 page table entries are in use.
430512pub fn load_kernel (
431513 bytes : & [ u8 ] ,
432- page_table : & mut impl MapperAllSizes ,
514+ page_table : & mut ( impl MapperAllSizes + Translate ) ,
433515 frame_allocator : & mut impl FrameAllocator < Size4KiB > ,
434516) -> Result < ( VirtAddr , Option < TlsTemplate > , UsedLevel4Entries ) , & ' static str > {
435517 let mut loader = Loader :: new ( bytes, page_table, frame_allocator) ?;
0 commit comments