1111compile_error ! ( "The bootloader crate must be compiled for the `x86_64-bootloader.json` target" ) ;
1212
1313use bootloader:: bootinfo:: { BootInfo , FrameRange } ;
14+ use core:: convert:: TryInto ;
1415use core:: panic:: PanicInfo ;
1516use core:: { mem, slice} ;
1617use fixedvec:: alloc_stack;
1718use usize_conversions:: usize_from;
19+ use x86_64:: instructions:: tlb;
1820use x86_64:: structures:: paging:: {
19- frame:: PhysFrameRange , Mapper , Page , PageTableFlags , PhysFrame , RecursivePageTable , Size2MiB ,
20- Size4KiB ,
21+ frame:: PhysFrameRange , page_table :: PageTableEntry , Mapper , Page , PageTable , PageTableFlags ,
22+ PhysFrame , RecursivePageTable , Size2MiB , Size4KiB ,
2123} ;
2224use x86_64:: ux:: u9;
2325use x86_64:: { PhysAddr , VirtAddr } ;
@@ -26,6 +28,9 @@ use x86_64::{PhysAddr, VirtAddr};
2628// the `map_physical_memory` is activated. Set by the build script.
2729include ! ( concat!( env!( "OUT_DIR" ) , "/physical_memory_offset.rs" ) ) ;
2830
31+ // The virtual address of the kernel stack. Set by the build script.
32+ include ! ( concat!( env!( "OUT_DIR" ) , "/kernel_stack_address.rs" ) ) ;
33+
2934global_asm ! ( include_str!( "stage_1.s" ) ) ;
3035global_asm ! ( include_str!( "stage_2.s" ) ) ;
3136global_asm ! ( include_str!( "e820.s" ) ) ;
@@ -44,6 +49,7 @@ unsafe fn context_switch(boot_info: VirtAddr, entry_point: VirtAddr, stack_point
4449
4550mod boot_info;
4651mod frame_allocator;
52+ mod level4_entries;
4753mod page_table;
4854mod printer;
4955
@@ -74,6 +80,7 @@ extern "C" {
7480 static __page_table_end: usize ;
7581 static __bootloader_end: usize ;
7682 static __bootloader_start: usize ;
83+ static _p4: usize ;
7784}
7885
7986#[ no_mangle]
@@ -90,6 +97,7 @@ pub unsafe extern "C" fn stage_4() -> ! {
9097 let page_table_end = & __page_table_end as * const _ as u64 ;
9198 let bootloader_start = & __bootloader_start as * const _ as u64 ;
9299 let bootloader_end = & __bootloader_end as * const _ as u64 ;
100+ let p4_physical = & _p4 as * const _ as u64 ;
93101
94102 load_elf (
95103 IdentityMappedAddr ( PhysAddr :: new ( kernel_start) ) ,
@@ -100,6 +108,7 @@ pub unsafe extern "C" fn stage_4() -> ! {
100108 PhysAddr :: new ( page_table_end) ,
101109 PhysAddr :: new ( bootloader_start) ,
102110 PhysAddr :: new ( bootloader_end) ,
111+ PhysAddr :: new ( p4_physical) ,
103112 )
104113}
105114
@@ -112,6 +121,7 @@ fn load_elf(
112121 page_table_end : PhysAddr ,
113122 bootloader_start : PhysAddr ,
114123 bootloader_end : PhysAddr ,
124+ p4_physical : PhysAddr ,
115125) -> ! {
116126 use bootloader:: bootinfo:: { MemoryRegion , MemoryRegionType } ;
117127 use fixedvec:: FixedVec ;
@@ -149,11 +159,25 @@ fn load_elf(
149159 }
150160 }
151161
162+ // Mark used virtual addresses
163+ let mut level4_entries = level4_entries:: UsedLevel4Entries :: new ( & segments) ;
164+
152165 // Enable support for the no-execute bit in page tables.
153166 enable_nxe_bit ( ) ;
154167
155- // Create a RecursivePageTable
156- let recursive_index = u9:: new ( 511 ) ;
168+ // Create a recursive page table entry
169+ let recursive_index = u9:: new ( level4_entries. get_free_entry ( ) . try_into ( ) . unwrap ( ) ) ;
170+ let mut entry = PageTableEntry :: new ( ) ;
171+ entry. set_addr (
172+ p4_physical,
173+ PageTableFlags :: PRESENT | PageTableFlags :: WRITABLE ,
174+ ) ;
175+
176+ // Write the recursive entry into the page table
177+ let page_table = unsafe { & mut * ( p4_physical. as_u64 ( ) as * mut PageTable ) } ;
178+ page_table[ recursive_index] = entry;
179+ tlb:: flush_all ( ) ;
180+
157181 let recursive_page_table_addr = Page :: from_page_table_indices (
158182 recursive_index,
159183 recursive_index,
@@ -211,18 +235,14 @@ fn load_elf(
211235 rec_page_table. unmap ( page) . expect ( "dealloc error" ) . 1 . flush ( ) ;
212236 }
213237
214- // Map kernel segments.
215- let stack_end = page_table:: map_kernel (
216- kernel_start. phys ( ) ,
217- & segments,
218- & mut rec_page_table,
219- & mut frame_allocator,
220- )
221- . expect ( "kernel mapping failed" ) ;
222-
223238 // Map a page for the boot info structure
224239 let boot_info_page = {
225- let page: Page = Page :: containing_address ( VirtAddr :: new ( 0xb0071f0000 ) ) ;
240+ let page: Page = Page :: from_page_table_indices (
241+ level4_entries. get_free_entry ( ) ,
242+ u9:: new ( 0 ) ,
243+ u9:: new ( 0 ) ,
244+ u9:: new ( 0 ) ,
245+ ) ;
226246 let frame = frame_allocator
227247 . allocate_frame ( MemoryRegionType :: BootInfo )
228248 . expect ( "frame allocation failed" ) ;
@@ -241,13 +261,38 @@ fn load_elf(
241261 page
242262 } ;
243263
244- if cfg ! ( feature = "map_physical_memory" ) {
245- fn virt_for_phys ( phys : PhysAddr ) -> VirtAddr {
246- VirtAddr :: new ( phys. as_u64 ( ) + PHYSICAL_MEMORY_OFFSET )
247- }
264+ // If no kernel stack address is provided, map the kernel stack after the boot info page
265+ let kernel_stack_address = match KERNEL_STACK_ADDRESS {
266+ Some ( addr) => Page :: containing_address ( VirtAddr :: new ( addr) ) ,
267+ None => boot_info_page + 1 ,
268+ } ;
269+
270+ // Map kernel segments.
271+ let stack_end = page_table:: map_kernel (
272+ kernel_start. phys ( ) ,
273+ kernel_stack_address,
274+ & segments,
275+ & mut rec_page_table,
276+ & mut frame_allocator,
277+ )
278+ . expect ( "kernel mapping failed" ) ;
279+
280+ let physical_memory_offset = if cfg ! ( feature = "map_physical_memory" ) {
281+ let physical_memory_offset = PHYSICAL_MEMORY_OFFSET . unwrap_or_else ( || {
282+ // If offset not manually provided, find a free p4 entry and map memory here.
283+ // One level 4 entry spans 2^48/512 bytes (over 500gib) so this should suffice.
284+ assert ! ( max_phys_addr < ( 1 << 48 ) / 512 ) ;
285+ Page :: from_page_table_indices_1gib ( level4_entries. get_free_entry ( ) , u9:: new ( 0 ) )
286+ . start_address ( )
287+ . as_u64 ( )
288+ } ) ;
289+
290+ let virt_for_phys =
291+ |phys : PhysAddr | -> VirtAddr { VirtAddr :: new ( phys. as_u64 ( ) + physical_memory_offset) } ;
248292
249293 let start_frame = PhysFrame :: < Size2MiB > :: containing_address ( PhysAddr :: new ( 0 ) ) ;
250294 let end_frame = PhysFrame :: < Size2MiB > :: containing_address ( PhysAddr :: new ( max_phys_addr) ) ;
295+
251296 for frame in PhysFrame :: range_inclusive ( start_frame, end_frame) {
252297 let page = Page :: containing_address ( virt_for_phys ( frame. start_address ( ) ) ) ;
253298 let flags = PageTableFlags :: PRESENT | PageTableFlags :: WRITABLE ;
@@ -263,13 +308,17 @@ fn load_elf(
263308 . expect ( "Mapping of bootinfo page failed" )
264309 . flush ( ) ;
265310 }
266- }
311+
312+ physical_memory_offset
313+ } else {
314+ 0 // Value is unused by BootInfo::new, so this doesn't matter
315+ } ;
267316
268317 // Construct boot info structure.
269318 let mut boot_info = BootInfo :: new (
270319 memory_map,
271320 recursive_page_table_addr. as_u64 ( ) ,
272- PHYSICAL_MEMORY_OFFSET ,
321+ physical_memory_offset ,
273322 ) ;
274323 boot_info. memory_map . sort ( ) ;
275324
0 commit comments