diff --git a/Changelog.md b/Changelog.md index f365b38a..97d0b3f3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,6 @@ # Unreleased +- Identity-map GDT into kernel address space to fix `iretq` ([#175](https://github.com/rust-osdev/bootloader/pull/175)) - Uefi: Look for an ACPI2 RSDP first ([#174](https://github.com/rust-osdev/bootloader/pull/174)) # 0.10.5 – 2021-05-21 diff --git a/src/binary/gdt.rs b/src/binary/gdt.rs new file mode 100644 index 00000000..8a4610b3 --- /dev/null +++ b/src/binary/gdt.rs @@ -0,0 +1,32 @@ +use x86_64::{ + instructions::segmentation, + structures::{ + gdt::{Descriptor, GlobalDescriptorTable}, + paging::PhysFrame, + }, + VirtAddr, +}; + +pub fn create_and_load(frame: PhysFrame) { + let phys_addr = frame.start_address(); + log::info!("Creating GDT at {:?}", phys_addr); + let virt_addr = VirtAddr::new(phys_addr.as_u64()); // utilize identity mapping + + let ptr: *mut GlobalDescriptorTable = virt_addr.as_mut_ptr(); + + let mut gdt = GlobalDescriptorTable::new(); + let code_selector = gdt.add_entry(Descriptor::kernel_code_segment()); + let data_selector = gdt.add_entry(Descriptor::kernel_data_segment()); + let gdt = unsafe { + ptr.write(gdt); + &*ptr + }; + + gdt.load(); + unsafe { + segmentation::set_cs(code_selector); + segmentation::load_ds(data_selector); + segmentation::load_es(data_selector); + segmentation::load_ss(data_selector); + } +} diff --git a/src/binary/mod.rs b/src/binary/mod.rs index caa56748..59e494b4 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -24,6 +24,7 @@ pub mod bios; #[cfg(feature = "uefi_bin")] mod uefi; +mod gdt; /// Provides a frame allocator based on a BIOS or UEFI memory map. pub mod legacy_memory_region; /// Provides a type to keep track of used entries in a level 4 page table. @@ -170,6 +171,18 @@ where } } + // create, load, and identity-map GDT (required for working `iretq`) + let gdt_frame = frame_allocator + .allocate_frame() + .expect("failed to allocate GDT frame"); + gdt::create_and_load(gdt_frame); + match unsafe { + kernel_page_table.identity_map(gdt_frame, PageTableFlags::PRESENT, frame_allocator) + } { + Ok(tlb) => tlb.flush(), + Err(err) => panic!("failed to identity map frame {:?}: {:?}", gdt_frame, err), + } + // map framebuffer let framebuffer_virt_addr = if CONFIG.map_framebuffer { log::info!("Map framebuffer"); diff --git a/tests/test_kernels/default_settings/src/bin/basic_boot.rs b/tests/test_kernels/default_settings/src/bin/basic_boot.rs index 50157689..eef2bb3a 100644 --- a/tests/test_kernels/default_settings/src/bin/basic_boot.rs +++ b/tests/test_kernels/default_settings/src/bin/basic_boot.rs @@ -13,6 +13,9 @@ fn kernel_main(_boot_info: &'static mut BootInfo) -> ! { /// This function is called on panic. #[panic_handler] -fn panic(_info: &PanicInfo) -> ! { +fn panic(info: &PanicInfo) -> ! { + use core::fmt::Write; + + let _ = writeln!(test_kernel_default_settings::serial(), "PANIC: {}", info); exit_qemu(QemuExitCode::Failed); }