From 57dde98a366aba7308dd6000b878726f102df6f9 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Wed, 29 Jun 2022 13:05:18 +0200 Subject: [PATCH] make the GDT base address configurable --- api/build.rs | 9 +++++---- api/src/config.rs | 24 ++++++++++++++++-------- common/src/lib.rs | 48 ++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 64 insertions(+), 17 deletions(-) diff --git a/api/build.rs b/api/build.rs index 142c2e72..1742b17f 100644 --- a/api/build.rs +++ b/api/build.rs @@ -15,13 +15,14 @@ fn main() { (31, 9), (40, 9), (49, 9), - (58, 10), - (68, 10), - (78, 1), - (79, 9), + (58, 9), + (67, 10), + (77, 10), + (87, 1), (88, 9), (97, 9), (106, 9), + (115, 9), ]; let mut code = String::new(); diff --git a/api/src/config.rs b/api/src/config.rs index 4f69174c..0df663c7 100644 --- a/api/src/config.rs +++ b/api/src/config.rs @@ -35,7 +35,7 @@ impl BootloaderConfig { 0x3D, ]; #[doc(hidden)] - pub const SERIALIZED_LEN: usize = 115; + pub const SERIALIZED_LEN: usize = 124; /// Creates a new default configuration with the following values: /// @@ -72,6 +72,7 @@ impl BootloaderConfig { kernel_stack, boot_info, framebuffer, + gdt, physical_memory, page_table_recursive, aslr, @@ -94,30 +95,31 @@ impl BootloaderConfig { let buf = concat_31_9(buf, kernel_stack.serialize()); let buf = concat_40_9(buf, boot_info.serialize()); let buf = concat_49_9(buf, framebuffer.serialize()); + let buf = concat_58_9(buf, gdt.serialize()); - let buf = concat_58_10( + let buf = concat_67_10( buf, match physical_memory { Option::None => [0; 10], Option::Some(m) => concat_1_9([1], m.serialize()), }, ); - let buf = concat_68_10( + let buf = concat_77_10( buf, match page_table_recursive { Option::None => [0; 10], Option::Some(m) => concat_1_9([1], m.serialize()), }, ); - let buf = concat_78_1(buf, [(*aslr) as u8]); - let buf = concat_79_9( + let buf = concat_87_1(buf, [(*aslr) as u8]); + let buf = concat_88_9( buf, match dynamic_range_start { Option::None => [0; 9], Option::Some(addr) => concat_1_8([1], addr.to_le_bytes()), }, ); - let buf = concat_88_9( + let buf = concat_97_9( buf, match dynamic_range_end { Option::None => [0; 9], @@ -125,14 +127,14 @@ impl BootloaderConfig { }, ); - let buf = concat_97_9( + let buf = concat_106_9( buf, match minimum_framebuffer_height { Option::None => [0; 9], Option::Some(addr) => concat_1_8([1], addr.to_le_bytes()), }, ); - let buf = concat_106_9( + let buf = concat_115_9( buf, match minimum_framebuffer_width { Option::None => [0; 9], @@ -189,6 +191,7 @@ impl BootloaderConfig { let (&kernel_stack, s) = split_array_ref(s); let (&boot_info, s) = split_array_ref(s); let (&framebuffer, s) = split_array_ref(s); + let (&gdt, s) = split_array_ref(s); let (&physical_memory_some, s) = split_array_ref(s); let (&physical_memory, s) = split_array_ref(s); let (&page_table_recursive_some, s) = split_array_ref(s); @@ -203,6 +206,7 @@ impl BootloaderConfig { kernel_stack: Mapping::deserialize(&kernel_stack)?, boot_info: Mapping::deserialize(&boot_info)?, framebuffer: Mapping::deserialize(&framebuffer)?, + gdt: Mapping::deserialize(&gdt)?, physical_memory: match physical_memory_some { [0] if physical_memory == [0; 9] => Option::None, [1] => Option::Some(Mapping::deserialize(&physical_memory)?), @@ -351,6 +355,8 @@ pub struct Mappings { pub boot_info: Mapping, /// Specifies the mapping of the frame buffer memory region. pub framebuffer: Mapping, + /// Specifies the mapping of the GDT. + pub gdt: Mapping, /// The bootloader supports to map the whole physical memory into the virtual address /// space at some offset. This is useful for accessing and modifying the page tables set /// up by the bootloader. @@ -388,6 +394,7 @@ impl Mappings { kernel_stack: Mapping::new_default(), boot_info: Mapping::new_default(), framebuffer: Mapping::new_default(), + gdt: Mapping::new_default(), physical_memory: Option::None, page_table_recursive: Option::None, aslr: false, @@ -404,6 +411,7 @@ impl Mappings { kernel_stack: Mapping::random(), boot_info: Mapping::random(), framebuffer: Mapping::random(), + gdt: Mapping::random(), physical_memory: if phys { Option::Some(Mapping::random()) } else { diff --git a/common/src/lib.rs b/common/src/lib.rs index c132b97a..b5c2ca04 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -8,13 +8,22 @@ use bootloader_api::{ info::{FrameBuffer, FrameBufferInfo, MemoryRegion, TlsTemplate}, BootInfo, BootloaderConfig, }; -use core::{alloc::Layout, arch::asm, mem::MaybeUninit, slice}; +use core::{ + alloc::Layout, + arch::asm, + mem::{size_of, MaybeUninit}, + slice, +}; use level_4_entries::UsedLevel4Entries; use usize_conversions::{FromUsize, IntoUsize}; use x86_64::{ - structures::paging::{ - page_table::PageTableLevel, FrameAllocator, Mapper, OffsetPageTable, Page, PageSize, - PageTable, PageTableFlags, PageTableIndex, PhysFrame, Size2MiB, Size4KiB, + instructions::tables::{lgdt, sgdt}, + structures::{ + gdt::GlobalDescriptorTable, + paging::{ + page_table::PageTableLevel, FrameAllocator, Mapper, OffsetPageTable, Page, PageSize, + PageTable, PageTableFlags, PageTableIndex, PhysFrame, Size2MiB, Size4KiB, + }, }, PhysAddr, VirtAddr, }; @@ -189,8 +198,20 @@ where .allocate_frame() .expect("failed to allocate GDT frame"); gdt::create_and_load(gdt_frame); + let gdt = mapping_addr( + config.mappings.gdt, + u64::from_usize(size_of::()), + 4096, + &mut used_entries, + ); + let gdt_page = Page::from_start_address(gdt).unwrap(); match unsafe { - kernel_page_table.identity_map(gdt_frame, PageTableFlags::PRESENT, frame_allocator) + kernel_page_table.map_to( + gdt_page, + gdt_frame, + PageTableFlags::PRESENT, + frame_allocator, + ) } { Ok(tlb) => tlb.flush(), Err(err) => panic!("failed to identity map frame {:?}: {:?}", gdt_frame, err), @@ -442,6 +463,7 @@ where physical_memory_offset, recursive_index, tls_template, + gdt, context_switch_trampoline: trampoline_page.start_address(), context_switch_page_table, context_switch_page_table_frame, @@ -466,6 +488,8 @@ pub struct Mappings { pub recursive_index: Option, /// The thread local storage template of the kernel executable, if it contains one. pub tls_template: Option, + /// The address of the GDT in the kernel's address space. + pub gdt: VirtAddr, /// The address of the context switch trampoline in the bootloader's address space. pub context_switch_trampoline: VirtAddr, /// The page table used for context switch from the bootloader to the kernel. @@ -591,6 +615,7 @@ pub fn switch_to_kernel( .. } = page_tables; let addresses = Addresses { + gdt: mappings.gdt, context_switch_trampoline: mappings.context_switch_trampoline, context_switch_page_table: mappings.context_switch_page_table_frame, context_switch_addr: mappings.context_switch_addr, @@ -625,6 +650,18 @@ pub struct PageTables { /// Performs the actual context switch. unsafe fn context_switch(addresses: Addresses) -> ! { + // Update the GDT base address. + let mut gdt_pointer = sgdt(); + gdt_pointer.base = addresses.gdt; + unsafe { + // SAFETY: Note that the base address points to memory that is only + // mapped in the kernel's page table. We can do this because + // just loading the GDT doesn't cause any immediate loads and + // by the time the base address is dereferenced the context + // switch will be done. + lgdt(&gdt_pointer); + } + unsafe { asm!( "mov rsp, {}; sub rsp, 8; jmp {}", @@ -641,6 +678,7 @@ unsafe fn context_switch(addresses: Addresses) -> ! { /// Memory addresses required for the context switch. struct Addresses { + gdt: VirtAddr, context_switch_trampoline: VirtAddr, context_switch_page_table: PhysFrame, context_switch_addr: VirtAddr,