x86_64: implement proper user/kernel %gs base switching

the %gs base address is now always set to the current cpu block while
in kernel-mode, and is switched back to the userspace %gs base
when returning to user-mode.
This commit is contained in:
2026-02-23 18:26:21 +00:00
parent 273557fa9f
commit dbe117135b
4 changed files with 47 additions and 15 deletions

View File

@@ -4,6 +4,7 @@
#include <stdint.h> #include <stdint.h>
#define MSR_GS_BASE 0xC0000101 #define MSR_GS_BASE 0xC0000101
#define MSR_KERNEL_GS_BASE 0xC0000102
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {

View File

@@ -333,11 +333,30 @@ IRQ 223, 255
isr_common_stub: isr_common_stub:
PUSH_REGS PUSH_REGS
# When ISR occurs in Ring 3, CPU sets %ss (and other non-code selectors)
# to 0.
mov %ss, %ax
cmp $0, %ax
jne isr_skipgs1
mov $0x10, %ax
mov %ax, %ss
swapgs
isr_skipgs1:
mov %rsp, %rdi mov %rsp, %rdi
call isr_dispatch call isr_dispatch
POP_REGS POP_REGS
add $16, %rsp add $16, %rsp
cmpq $0x1b, 32(%rsp)
jne isr_skipgs2
swapgs
isr_skipgs2:
iretq iretq
@@ -347,11 +366,31 @@ isr_common_stub:
irq_common_stub: irq_common_stub:
PUSH_REGS PUSH_REGS
# When IRQ occurs in Ring 3, CPU sets %ss (and other non-code selectors)
# to 0.
mov %ss, %ax
cmp $0, %ax
jne irq_skipgs1
mov $0x10, %ax
mov %ax, %ss
swapgs
irq_skipgs1:
mov %rsp, %rdi mov %rsp, %rdi
call irq_dispatch call irq_dispatch
POP_REGS POP_REGS
add $16, %rsp add $16, %rsp
cmpq $0x1b, 32(%rsp)
jne isr_skipgs2
swapgs
irq_skipgs2:
iretq iretq
@@ -363,12 +402,12 @@ irq_common_stub:
syscall_gate: syscall_gate:
swapgs swapgs
movq %rsp, %gs:20 # GS+20 = rsp2 in the current TSS block (user stack storage) movq %rsp, %gs:94 # GS+20 = rsp2 in the current TSS block (user stack storage)
movq %gs:4, %rsp # GS+4 = rsp0 in the current TSS block (per-thread kstack) movq %gs:78, %rsp # GS+4 = rsp0 in the current TSS block (per-thread kstack)
# start building a ml_cpu_context # start building a ml_cpu_context
pushq $0x1b pushq $0x1b
pushq %gs:20 pushq %gs:94
push %r11 push %r11
push $0x23 push $0x23
push %rcx push %rcx
@@ -380,10 +419,6 @@ syscall_gate:
mov %rsp, %rdi mov %rsp, %rdi
# switch back to user gs while in syscall_dispatch. Interrupts are enabled in syscall_dispatch,
# and if the task gets pre-empted, the incoming task will expect %gs to have its usermode value.
swapgs
call syscall_dispatch call syscall_dispatch
POP_REGS POP_REGS
@@ -394,8 +429,8 @@ syscall_gate:
pop %r11 pop %r11
add $16, %rsp add $16, %rsp
swapgs movq %gs:94, %rsp # GS+20 = rsp2 in the current TSS block
movq %gs:20, %rsp # GS+20 = rsp2 in the current TSS block
swapgs swapgs
# back to usermode # back to usermode

View File

@@ -73,4 +73,5 @@ ml_thread_switch_user:
pop %rax pop %rax
add $16, %rsp add $16, %rsp
swapgs
iretq iretq

View File

@@ -1,5 +1,3 @@
#include "arch/msr.h"
#include <arch/gdt.h> #include <arch/gdt.h>
#include <arch/tss.h> #include <arch/tss.h>
#include <kernel/libc/string.h> #include <kernel/libc/string.h>
@@ -22,9 +20,6 @@ void tss_init(struct tss *tss, struct tss_ptr *ptr)
void tss_load(struct tss *tss) void tss_load(struct tss *tss)
{ {
tss_flush(TSS_GDT_INDEX); tss_flush(TSS_GDT_INDEX);
uintptr_t kernel_gs_base_reg = 0xC0000102;
wrmsr(kernel_gs_base_reg, (uintptr_t)tss);
} }
virt_addr_t tss_get_kstack(struct tss *tss) virt_addr_t tss_get_kstack(struct tss *tss)