diff --git a/src/arch/x86_64/gdt.rs b/src/arch/x86_64/gdt.rs index c171314..7878451 100644 --- a/src/arch/x86_64/gdt.rs +++ b/src/arch/x86_64/gdt.rs @@ -14,11 +14,12 @@ pub const GDT_NULL: usize = 0; pub const GDT_KERNEL_CODE: usize = 1; pub const GDT_KERNEL_DATA: usize = 2; pub const GDT_KERNEL_TLS: usize = 3; -pub const GDT_USER_CODE: usize = 4; +pub const GDT_USER_CODE32_UNUSED: usize = 4; pub const GDT_USER_DATA: usize = 5; -pub const GDT_USER_TLS: usize = 6; -pub const GDT_TSS: usize = 7; -pub const GDT_TSS_HIGH: usize = 8; +pub const GDT_USER_CODE: usize = 6; +pub const GDT_USER_TLS: usize = 7; +pub const GDT_TSS: usize = 8; +pub const GDT_TSS_HIGH: usize = 9; pub const GDT_A_PRESENT: u8 = 1 << 7; pub const GDT_A_RING_0: u8 = 0 << 5; @@ -61,7 +62,7 @@ pub static mut GDTR: DescriptorTablePointer = DescriptorTable }; #[thread_local] -pub static mut GDT: [GdtEntry; 9] = [ +pub static mut GDT: [GdtEntry; 10] = [ // Null GdtEntry::new(0, 0, 0, 0), // Kernel code @@ -70,10 +71,12 @@ pub static mut GDT: [GdtEntry; 9] = [ GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE), // Kernel TLS GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE), - // User code - GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, GDT_F_LONG_MODE), + // Dummy 32-bit user code - apparently necessary for SYSEXIT. We restrict it to ring 0 anyway. + GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, GDT_F_PROTECTED_MODE), // User data GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE), + // User (64-bit) code + GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, GDT_F_LONG_MODE), // User TLS GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE), // TSS diff --git a/src/arch/x86_64/idt.rs b/src/arch/x86_64/idt.rs index bb0eb29..4a85aa8 100644 --- a/src/arch/x86_64/idt.rs +++ b/src/arch/x86_64/idt.rs @@ -285,6 +285,6 @@ impl IdtEntry { // A function to set the offset more easily pub fn set_func(&mut self, func: unsafe extern fn()) { self.set_flags(IdtFlags::PRESENT | IdtFlags::RING_0 | IdtFlags::INTERRUPT); - self.set_offset(8, func as usize); + self.set_offset((crate::gdt::GDT_KERNEL_CODE as u16) << 3, func as usize); } } diff --git a/src/arch/x86_64/interrupt/handler.rs b/src/arch/x86_64/interrupt/handler.rs index 9e75a22..696be7e 100644 --- a/src/arch/x86_64/interrupt/handler.rs +++ b/src/arch/x86_64/interrupt/handler.rs @@ -75,6 +75,11 @@ impl IretRegisters { println!("RFLAG: {:>016X}", { self.rflags }); println!("CS: {:>016X}", { self.cs }); println!("RIP: {:>016X}", { self.rip }); + + if self.cs & 0b11 != 0b00 { + println!("RSP: {:>016X}", { self.rsp }); + println!("SS: {:>016X}", { self.ss }); + } } } diff --git a/src/arch/x86_64/interrupt/syscall.rs b/src/arch/x86_64/interrupt/syscall.rs index 9c5364e..b526263 100644 --- a/src/arch/x86_64/interrupt/syscall.rs +++ b/src/arch/x86_64/interrupt/syscall.rs @@ -8,7 +8,18 @@ use crate::{ use x86::msr; pub unsafe fn init() { - msr::wrmsr(msr::IA32_STAR, ((gdt::GDT_KERNEL_CODE as u64) << 3) << 32); + // IA32_STAR[31:0] are reserved. + + // The base selector of the two consecutive segments for kernel code and the immediately + // suceeding stack (data). + let syscall_cs_ss_base = (gdt::GDT_KERNEL_CODE as u16) << 3; + // The base selector of the three consecutive segments (of which two are used) for user code + // and user data. It points to a 32-bit code segment, which must be followed by a data segment + // (stack), and a 64-bit code segment. + let sysret_cs_ss_base = ((gdt::GDT_USER_CODE32_UNUSED as u16) << 3) | u16::from(gdt::GDT_A_RING_3); + let star_high = u32::from(syscall_cs_ss_base) | (u32::from(sysret_cs_ss_base) << 16); + + msr::wrmsr(msr::IA32_STAR, u64::from(star_high) << 32); msr::wrmsr(msr::IA32_LSTAR, syscall_instruction as u64); msr::wrmsr(msr::IA32_FMASK, 0x0300); // Clear trap flag and interrupt enable msr::wrmsr(msr::IA32_KERNEL_GSBASE, &gdt::TSS as *const _ as u64); @@ -52,15 +63,13 @@ function!(syscall_instruction => { // Yes, this is magic. No, you don't need to understand " swapgs // Set gs segment to TSS - mov gs:[28], rsp // Save userspace rsp - mov rsp, gs:[4] // Load kernel rsp - push 5 * 8 + 3 // Push userspace data segment + mov gs:[28], rsp // Save userspace stack pointer + mov rsp, gs:[4] // Load kernel stack pointer + push WORD PTR 5 * 8 + 3 // Push fake userspace SS (resembling iret frame) push QWORD PTR gs:[28] // Push userspace rsp - mov QWORD PTR gs:[28], 0 // Clear userspace rsp push r11 // Push rflags - push 4 * 8 + 3 // Push userspace code segment + push WORD PTR 6 * 8 + 3 // Push fake userspace CS (resembling iret frame) push rcx // Push userspace return pointer - swapgs // Restore gs ", // Push context registers @@ -85,7 +94,15 @@ function!(syscall_instruction => { pop_scratch!(), // Return - "iretq\n", + " + pop rcx // Pop userspace return pointer + add rsp, 2 + pop r11 // Pop rflags + add rsp, 10 // Pop SS and rsp + mov rsp, gs:[28] // Restore userspace stack pointer + swapgs // Restore gs from TSS to kernel data + sysretq // Return into userspace; RCX=>RIP,R11=>RFLAGS + ", }); interrupt_stack!(syscall, |stack| { diff --git a/src/arch/x86_64/pti.rs b/src/arch/x86_64/pti.rs index eb5841c..0f5cc07 100644 --- a/src/arch/x86_64/pti.rs +++ b/src/arch/x86_64/pti.rs @@ -58,7 +58,7 @@ pub unsafe fn map() { #[cfg(feature = "pti")] #[inline(always)] -pub unsafe fn unmap() { +pub unsafe extern "C" fn unmap() { // Switch to per-CPU stack switch_stack(PTI_CONTEXT_STACK, PTI_CPU_STACK.as_ptr() as usize + PTI_CPU_STACK.len()); @@ -83,4 +83,4 @@ pub unsafe fn map() {} #[cfg(not(feature = "pti"))] #[inline(always)] -pub unsafe fn unmap() {} +pub unsafe extern "C" fn unmap() {}