@@ -8,13 +8,22 @@ use bootloader_api::{
8
8
info:: { FrameBuffer , FrameBufferInfo , MemoryRegion , TlsTemplate } ,
9
9
BootInfo , BootloaderConfig ,
10
10
} ;
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
+ } ;
12
17
use level_4_entries:: UsedLevel4Entries ;
13
18
use usize_conversions:: { FromUsize , IntoUsize } ;
14
19
use 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
+ } ,
18
27
} ,
19
28
PhysAddr , VirtAddr ,
20
29
} ;
@@ -201,8 +210,20 @@ where
201
210
. allocate_frame ( )
202
211
. expect ( "failed to allocate GDT frame" ) ;
203
212
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 ( ) ;
204
220
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
+ )
206
227
} {
207
228
Ok ( tlb) => tlb. flush ( ) ,
208
229
Err ( err) => panic ! ( "failed to identity map frame {:?}: {:?}" , gdt_frame, err) ,
@@ -459,6 +480,7 @@ where
459
480
460
481
kernel_slice_start,
461
482
kernel_slice_len,
483
+ gdt,
462
484
context_switch_trampoline : trampoline_page. start_address ( ) ,
463
485
context_switch_page_table,
464
486
context_switch_page_table_frame,
@@ -488,6 +510,8 @@ pub struct Mappings {
488
510
pub kernel_slice_start : u64 ,
489
511
/// Size of the kernel slice allocation in memory.
490
512
pub kernel_slice_len : u64 ,
513
+ /// The address of the GDT in the kernel's address space.
514
+ pub gdt : VirtAddr ,
491
515
/// The address of the context switch trampoline in the bootloader's address space.
492
516
pub context_switch_trampoline : VirtAddr ,
493
517
/// The page table used for context switch from the bootloader to the kernel.
@@ -611,6 +635,7 @@ pub fn switch_to_kernel(
611
635
..
612
636
} = page_tables;
613
637
let addresses = Addresses {
638
+ gdt : mappings. gdt ,
614
639
context_switch_trampoline : mappings. context_switch_trampoline ,
615
640
context_switch_page_table : mappings. context_switch_page_table_frame ,
616
641
context_switch_addr : mappings. context_switch_addr ,
@@ -645,6 +670,18 @@ pub struct PageTables {
645
670
646
671
/// Performs the actual context switch.
647
672
unsafe 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
+
648
685
unsafe {
649
686
asm ! (
650
687
"mov rsp, {}; sub rsp, 8; jmp {}" ,
@@ -661,6 +698,7 @@ unsafe fn context_switch(addresses: Addresses) -> ! {
661
698
662
699
/// Memory addresses required for the context switch.
663
700
struct Addresses {
701
+ gdt : VirtAddr ,
664
702
context_switch_trampoline : VirtAddr ,
665
703
context_switch_page_table : PhysFrame ,
666
704
context_switch_addr : VirtAddr ,
0 commit comments