aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2026-06-16 21:44:00 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2026-06-21 12:17:11 +0000
commitc7e5802ab6e6230a5782a09c914585d055eb5223 (patch)
tree8c9d114d0d3bcd824e1297b0ef396258dd304983
parentd0384f50dad25d3358d76f7556969ea1d515ba68 (diff)
amd64: do not restore fsbase/gsbase for 32bit processes
There is no WRFSBASE and WRGSBASE 32bit variants at all. As such, the fsbase and gsbase must always be equal to the bases in the corresponding segment descriptor, same as on real i386. If a 32bit program reloads either %fs or %gs using setcontext(9) or sigreturn(9), restoring bases from the syscall entry time is wrong. In all other cases, hardware already does the right action when the segment register is loaded, and the bases for the ufssel/ugssel in GDT are updated on the context switch. Reviewed by: markj (previous version) Tested by: terehovv@mail.ru Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D57611
-rw-r--r--sys/amd64/amd64/exception.S31
1 files changed, 31 insertions, 0 deletions
diff --git a/sys/amd64/amd64/exception.S b/sys/amd64/amd64/exception.S
index 5ff75fb65740..4d58e07caf4b 100644
--- a/sys/amd64/amd64/exception.S
+++ b/sys/amd64/amd64/exception.S
@@ -1161,12 +1161,15 @@ do_segs:
.globl ld_fs
ld_fs:
movw %ax,%fs
+ testl $PCB_32BIT,PCB_FLAGS(%r8)
+ jnz after_ld_fsbase
movl $MSR_FSBASE,%ecx
movl PCB_FSBASE(%r8),%eax
movl PCB_FSBASE+4(%r8),%edx
.globl ld_fsbase
ld_fsbase:
wrmsr
+after_ld_fsbase:
/* Restore %gs and gsbase */
movw TF_GS(%rsp),%si
pushfq
@@ -1179,6 +1182,8 @@ ld_fsbase:
.globl ld_gs
ld_gs:
movw %si,%gs
+ testl $PCB_32BIT,PCB_FLAGS(%r8)
+ jnz ld_gsbase_32
/* Restore kernel %gs base */
movl %r12d,%eax
movl %r13d,%edx
@@ -1194,6 +1199,8 @@ ld_gs:
.globl ld_gsbase
ld_gsbase:
wrmsr /* May trap if non-canonical, but only for TLS. */
+
+after_ld_gsbase:
.globl ld_es
ld_es:
movw TF_ES(%rsp),%es
@@ -1241,6 +1248,30 @@ ld_regs:
doreti_iret:
iretq
+ /*
+ * 32bit gsbase path.
+ * The KGSBASE is not loaded from the pcb_gsbase, instead we move
+ * the GSBASE value into KGSBASE right after loading user %gs, then
+ * restore kernel gsbase.
+ */
+ld_gsbase_32:
+ /* Read the user gsbase into %r14d:%r15d. */
+ movl $MSR_GSBASE,%ecx
+ rdmsr
+ movl %eax,%r14d
+ movl %edx,%r15d
+ /* Restore kernel %gs base */
+ movl %r12d,%eax
+ movl %r13d,%edx
+ wrmsr
+ popfq
+ /* Load the user gsbase into KGSBASE. */
+ movl $MSR_KGSBASE,%ecx
+ movl %r14d,%eax
+ movl %r15d,%edx
+ wrmsr
+ jmp after_ld_gsbase
+
set_segs:
movw $KUDSEL,%ax
movw %ax,TF_DS(%rsp)