@@ -8,13 +8,22 @@ use bootloader_api::{
88 info:: { FrameBuffer , FrameBufferInfo , MemoryRegion , TlsTemplate } ,
99 BootInfo , BootloaderConfig ,
1010} ;
11- use core:: { alloc:: Layout , arch:: asm, mem:: MaybeUninit , slice} ;
11+ use core:: {
12+ alloc:: Layout ,
13+ arch:: asm,
14+ mem:: { size_of, MaybeUninit } ,
15+ slice,
16+ } ;
1217use level_4_entries:: UsedLevel4Entries ;
1318use usize_conversions:: { FromUsize , IntoUsize } ;
1419use x86_64:: {
15- structures:: paging:: {
16- page_table:: PageTableLevel , FrameAllocator , Mapper , OffsetPageTable , Page , PageSize ,
17- PageTable , PageTableFlags , PageTableIndex , PhysFrame , Size2MiB , Size4KiB ,
20+ instructions:: tables:: { lgdt, sgdt} ,
21+ structures:: {
22+ gdt:: GlobalDescriptorTable ,
23+ paging:: {
24+ page_table:: PageTableLevel , FrameAllocator , Mapper , OffsetPageTable , Page , PageSize ,
25+ PageTable , PageTableFlags , PageTableIndex , PhysFrame , Size2MiB , Size4KiB ,
26+ } ,
1827 } ,
1928 PhysAddr , VirtAddr ,
2029} ;
@@ -201,8 +210,20 @@ where
201210 . allocate_frame ( )
202211 . expect ( "failed to allocate GDT frame" ) ;
203212 gdt:: create_and_load ( gdt_frame) ;
213+ let gdt = mapping_addr (
214+ config. mappings . gdt ,
215+ u64:: from_usize ( size_of :: < GlobalDescriptorTable > ( ) ) ,
216+ 4096 ,
217+ & mut used_entries,
218+ ) ;
219+ let gdt_page = Page :: from_start_address ( gdt) . unwrap ( ) ;
204220 match unsafe {
205- kernel_page_table. identity_map ( gdt_frame, PageTableFlags :: PRESENT , frame_allocator)
221+ kernel_page_table. map_to (
222+ gdt_page,
223+ gdt_frame,
224+ PageTableFlags :: PRESENT ,
225+ frame_allocator,
226+ )
206227 } {
207228 Ok ( tlb) => tlb. flush ( ) ,
208229 Err ( err) => panic ! ( "failed to identity map frame {:?}: {:?}" , gdt_frame, err) ,
@@ -459,6 +480,7 @@ where
459480
460481 kernel_slice_start,
461482 kernel_slice_len,
483+ gdt,
462484 context_switch_trampoline : trampoline_page. start_address ( ) ,
463485 context_switch_page_table,
464486 context_switch_page_table_frame,
@@ -488,6 +510,8 @@ pub struct Mappings {
488510 pub kernel_slice_start : u64 ,
489511 /// Size of the kernel slice allocation in memory.
490512 pub kernel_slice_len : u64 ,
513+ /// The address of the GDT in the kernel's address space.
514+ pub gdt : VirtAddr ,
491515 /// The address of the context switch trampoline in the bootloader's address space.
492516 pub context_switch_trampoline : VirtAddr ,
493517 /// The page table used for context switch from the bootloader to the kernel.
@@ -611,6 +635,7 @@ pub fn switch_to_kernel(
611635 ..
612636 } = page_tables;
613637 let addresses = Addresses {
638+ gdt : mappings. gdt ,
614639 context_switch_trampoline : mappings. context_switch_trampoline ,
615640 context_switch_page_table : mappings. context_switch_page_table_frame ,
616641 context_switch_addr : mappings. context_switch_addr ,
@@ -645,6 +670,18 @@ pub struct PageTables {
645670
646671/// Performs the actual context switch.
647672unsafe fn context_switch ( addresses : Addresses ) -> ! {
673+ // Update the GDT base address.
674+ let mut gdt_pointer = sgdt ( ) ;
675+ gdt_pointer. base = addresses. gdt ;
676+ unsafe {
677+ // SAFETY: Note that the base address points to memory that is only
678+ // mapped in the kernel's page table. We can do this because
679+ // just loading the GDT doesn't cause any immediate loads and
680+ // by the time the base address is dereferenced the context
681+ // switch will be done.
682+ lgdt ( & gdt_pointer) ;
683+ }
684+
648685 unsafe {
649686 asm ! (
650687 "mov rsp, {}; sub rsp, 8; jmp {}" ,
@@ -661,6 +698,7 @@ unsafe fn context_switch(addresses: Addresses) -> ! {
661698
662699/// Memory addresses required for the context switch.
663700struct Addresses {
701+ gdt : VirtAddr ,
664702 context_switch_trampoline : VirtAddr ,
665703 context_switch_page_table : PhysFrame ,
666704 context_switch_addr : VirtAddr ,
0 commit comments