From 05db0f59775ce6d44b1491542d364c8d3bc6be31 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 8 Feb 2021 13:46:49 +0100 Subject: [PATCH] Temporarily fix sysretq by swapping gs 4 times. In order words, it swaps gs both directly at the start of the syscall handler, then swaps it back, and the at the end of the syscall handler. I cannot tell for sure why this is necessary, but probably since some interrupt handler will execute swapgs in the wrong order or something. --- src/arch/x86_64/gdt.rs | 2 +- src/arch/x86_64/interrupt/exception.rs | 2 -- src/arch/x86_64/interrupt/handler.rs | 42 ++++++++++++++++++++------ src/arch/x86_64/interrupt/syscall.rs | 12 +++++--- src/arch/x86_64/start.rs | 5 +-- 5 files changed, 42 insertions(+), 21 deletions(-) diff --git a/src/arch/x86_64/gdt.rs b/src/arch/x86_64/gdt.rs index a2ad7d5d8a..a285d9a1b9 100644 --- a/src/arch/x86_64/gdt.rs +++ b/src/arch/x86_64/gdt.rs @@ -194,7 +194,7 @@ pub unsafe fn init_paging(tcb_offset: usize, stack_offset: usize) { segmentation::load_gs(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0)); // Ensure that GS always points to the TSS segment in kernel space. - x86::msr::wrmsr(x86::msr::IA32_GS_BASE, &TSS as *const _ as usize as u64); + //x86::msr::wrmsr(x86::msr::IA32_GS_BASE, &TSS as *const _ as usize as u64); segmentation::load_ss(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0)); diff --git a/src/arch/x86_64/interrupt/exception.rs b/src/arch/x86_64/interrupt/exception.rs index 9a1e526811..18b4f0626e 100644 --- a/src/arch/x86_64/interrupt/exception.rs +++ b/src/arch/x86_64/interrupt/exception.rs @@ -41,8 +41,6 @@ interrupt_stack!(debug, |stack| { } }); -// TODO: Give NMI and double faults a separate stack, as they can trigger a triple fault due to the -// way swapgs is used. interrupt_stack!(non_maskable, |stack| { println!("Non-maskable interrupt"); stack.dump(); diff --git a/src/arch/x86_64/interrupt/handler.rs b/src/arch/x86_64/interrupt/handler.rs index 18a8f8a1e3..776b98d994 100644 --- a/src/arch/x86_64/interrupt/handler.rs +++ b/src/arch/x86_64/interrupt/handler.rs @@ -309,14 +309,38 @@ macro_rules! pop_fs { " }; } -macro_rules! swapgs_if_ring3 { - () => { " +macro_rules! swapgs_iff_ring3 { + (error_code: true) => { " // Check whether the last two bits RSP+8 (code segment) are equal to zero. - test BYTE PTR [rsp + 8], 0x03 + test QWORD PTR [rsp + 8], 0x3 // Skip the SWAPGS instruction if CS & 0b11 == 0b00. jz 1f swapgs 1: + " }; + (error_code: false) => { " + test QWORD PTR [rsp + 16], 0x3 + jz 1f + swapgs + 1: + " }; +} +macro_rules! swapgs_iff_ring3_slow { + () => { " + push eax + push edx + push ecx + mov ecx, 0xC000_0102 + rdmsr + shl rdx, 32 + or eax, edx + test rdx, rdx + jnz 1f + swapgs + 1: + pop ecx + pop edx + pop eax " } } @@ -338,7 +362,7 @@ macro_rules! interrupt_stack { function!($name => { // Backup all userspace registers to stack - swapgs_if_ring3!(), + //swapgs_if_ring3!(error_code: false), "push rax\n", push_scratch!(), push_preserved!(), @@ -359,7 +383,7 @@ macro_rules! interrupt_stack { pop_preserved!(), pop_scratch!(), - swapgs_if_ring3!(), + //swapgs_if_ring3!(error_code: false), "iretq\n", }); } @@ -377,7 +401,7 @@ macro_rules! interrupt { function!($name => { // Backup all userspace registers to stack - swapgs_if_ring3!(), + //swapgs_if_ring3!(error_code: false), "push rax\n", push_scratch!(), push_fs!(), @@ -395,7 +419,7 @@ macro_rules! interrupt { pop_fs!(), pop_scratch!(), - swapgs_if_ring3!(), + //swapgs_if_ring3!(error_code: false), "iretq\n", }); } @@ -419,7 +443,7 @@ macro_rules! interrupt_error { } function!($name => { - swapgs_if_ring3!(), + //swapgs_if_ring3!(error_code: true), // Move rax into code's place, put code in last instead (to be // compatible with InterruptStack) "xchg [rsp], rax\n", @@ -450,7 +474,7 @@ macro_rules! interrupt_error { pop_preserved!(), pop_scratch!(), - swapgs_if_ring3!(), + //swapgs_if_ring3!(error_code: true), "iretq\n", }); } diff --git a/src/arch/x86_64/interrupt/syscall.rs b/src/arch/x86_64/interrupt/syscall.rs index cc844601c3..1df590695d 100644 --- a/src/arch/x86_64/interrupt/syscall.rs +++ b/src/arch/x86_64/interrupt/syscall.rs @@ -25,7 +25,7 @@ pub unsafe fn init() { // Inside kernel space, GS should _always_ point to the TSS. When leaving userspace, `swapgs` // is called again, making the userspace GS always point to user data. - msr::wrmsr(msr::IA32_KERNEL_GSBASE, 0); + x86::msr::wrmsr(x86::msr::IA32_KERNEL_GSBASE, &gdt::TSS as *const _ as usize as u64); let efer = msr::rdmsr(msr::IA32_EFER); msr::wrmsr(msr::IA32_EFER, efer | 1); @@ -68,11 +68,12 @@ function!(syscall_instruction => { swapgs // Set gs segment to TSS mov gs:[0x70], 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 5 * 8 + 3 // Push fake userspace SS (resembling iret frame) push QWORD PTR gs:[0x70] // Push userspace rsp push r11 // Push rflags - push WORD PTR 6 * 8 + 3 // Push fake userspace CS (resembling iret frame) + push QWORD PTR 6 * 8 + 3 // Push fake userspace CS (resembling iret frame) push rcx // Push userspace return pointer + swapgs ", // Push context registers @@ -99,10 +100,11 @@ function!(syscall_instruction => { // Return " pop rcx // Pop userspace return pointer - add rsp, 2 // Pop CS + add rsp, 8 // Pop CS pop r11 // Pop rflags + swapgs pop QWORD PTR gs:[0x70] // Pop userspace stack pointer - add rsp, 2 // Pop SS + add rsp, 8 // Pop SS mov rsp, gs:[0x70] // Restore userspace stack pointer swapgs // Restore gs from TSS to user data sysretq // Return into userspace; RCX=>RIP,R11=>RFLAGS diff --git a/src/arch/x86_64/start.rs b/src/arch/x86_64/start.rs index d33008296c..f13aeb93a6 100644 --- a/src/arch/x86_64/start.rs +++ b/src/arch/x86_64/start.rs @@ -267,9 +267,6 @@ pub unsafe extern "C" fn usermode(_ip: usize, _sp: usize, _arg: usize, _singlest mov ds, r8d mov es, r8d mov fs, r9d - // Exchange the old kernel GS (pointing to TSS) and kernel data - swapgs - // Replace kernel data segment with user data segment mov gs, r8d // Target RFLAGS @@ -279,7 +276,7 @@ pub unsafe extern "C" fn usermode(_ip: usize, _sp: usize, _arg: usize, _singlest // Target stack pointer mov rsp, r14 // Target argument - mov rdi, r13 + mov rdi, r15 xor rax, rax xor rbx, rbx