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.
This commit is contained in:
4lDO2
2021-02-08 13:46:49 +01:00
parent 1a8016b985
commit 05db0f5977
5 changed files with 42 additions and 21 deletions
+1 -1
View File
@@ -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));
-2
View File
@@ -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();
+33 -9
View File
@@ -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",
});
}
+7 -5
View File
@@ -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
+1 -4
View File
@@ -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