Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/amd64 Improve our segregs model. Pass 1/3.



details:   https://anonhg.NetBSD.org/src/rev/36114973cfff
branches:  trunk
changeset: 356892:36114973cfff
user:      maxv <maxv%NetBSD.org@localhost>
date:      Thu Oct 19 18:36:31 2017 +0000

description:
Improve our segregs model. Pass 1/3.

Right now, we are saving and restoring %ds/%es each time we enter/leave the
kernel. However, we let %fs/%gs live in the kernel space, and we rely on
the fact that when switching to an LWP, %fs/%gs are set right away (via
cpu_switchto or setregs).

It has two drawbacks: we are taking care of %ds/%es while they are
deprecated (useless) on 64bit LWPs, and we are restricting %fs/%gs while
they still have a meaning on 32bit LWPs.

Therefore, handle 32bit and 64bit LWPs differently:
 * 64bit LWPs use fixed segregs, which are not taken care of.
 * 32bit LWPs have dynamic segregs, always saved/restored.

For now, only %ds and %es are changed; %fs and %gs will be in the next
passes.

The trapframe is constructed as usual. In INTRFASTEXIT, we restore %ds/%es
depending on the %cs value. If %cs contains one of the two standard 64bit
selectors, don't do anything. Otherwise, restore everything.

When doing a context switch, just restore %ds/%es to their default values.
On a 32bit LWP they will be overwritten by INTRFASTEXIT; on a 64bit LWP
they won't be updated.

In the ACPI wakeup code, restore %ds/%es to the default 64bit user value.

diffstat:

 sys/arch/amd64/acpi/acpi_wakeup_low.S |  11 +++++--
 sys/arch/amd64/amd64/locore.S         |  47 +++++++++++++++++++++++-----------
 sys/arch/amd64/amd64/machdep.c        |  14 ++++++++--
 3 files changed, 50 insertions(+), 22 deletions(-)

diffs (185 lines):

diff -r 9fa022e1e697 -r 36114973cfff sys/arch/amd64/acpi/acpi_wakeup_low.S
--- a/sys/arch/amd64/acpi/acpi_wakeup_low.S     Thu Oct 19 16:01:58 2017 +0000
+++ b/sys/arch/amd64/acpi/acpi_wakeup_low.S     Thu Oct 19 18:36:31 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: acpi_wakeup_low.S,v 1.6 2017/09/23 10:18:49 maxv Exp $ */
+/*     $NetBSD: acpi_wakeup_low.S,v 1.7 2017/10/19 18:36:31 maxv Exp $ */
 
 /*-
  * Copyright (c) 2007 Joerg Sonnenberger <joerg%netbsd.org@localhost>
@@ -40,12 +40,17 @@
        lgdt    ACPI_SUSPEND_GDT(%r8)
 
        /* Reload fixed descriptors for new GDT */
-       movw    $GSEL(GDATA_SEL, SEL_KPL),%ax
+       movw    $GSEL(GUDATA_SEL, SEL_UPL),%ax
        movw    %ax,%ds
        movw    %ax,%es
+       movw    $GSEL(GDATA_SEL, SEL_KPL),%ax
        movw    %ax,%ss
 
-       /* FS and GS are driven by MSRs, so use NULL for them */
+       /*
+        * FS and GS are driven by MSRs, so use NULL for them.
+        * XXX XXX XXX That's not the case if we're returning to a 32bit
+        * LWP!
+        */
        xorw    %ax,%ax
        movw    %ax,%fs
        movw    %ax,%gs
diff -r 9fa022e1e697 -r 36114973cfff sys/arch/amd64/amd64/locore.S
--- a/sys/arch/amd64/amd64/locore.S     Thu Oct 19 16:01:58 2017 +0000
+++ b/sys/arch/amd64/amd64/locore.S     Thu Oct 19 18:36:31 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: locore.S,v 1.133 2017/10/17 07:48:10 maxv Exp $        */
+/*     $NetBSD: locore.S,v 1.134 2017/10/19 18:36:31 maxv Exp $        */
 
 /*
  * Copyright-o-rama!
@@ -1159,7 +1159,10 @@
        jnz     lwp_32bit
 
 lwp_64bit:
-       /* Zero out %fs/%gs registers. */
+       /* Set default 64bit values in %ds, %es, %fs and %gs. */
+       movq    $GSEL(GUDATA_SEL, SEL_UPL),%rax
+       movw    %ax,%ds
+       movw    %ax,%es
        xorq    %rax,%rax
        movw    %ax,%fs
        CLI(cx)
@@ -1193,8 +1196,11 @@
        movq    PCB_GS(%r14),%rax
        movq    %rax,(GUGS_SEL*8)(%rcx)
 
-       /* Reload %fs and %gs */
+       /* Set default 32bit values in %ds, %es. %fs and %gs are special. */
        movq    L_MD_REGS(%r12),%rbx
+       movq    $GSEL(GUDATA32_SEL, SEL_UPL),%rax
+       movw    %ax,%ds
+       movw    %ax,%es
        movw    TF_FS(%rbx),%fs
        CLI(ax)
        SWAPGS
@@ -1281,10 +1287,10 @@
        cld
 #endif
        INTR_SAVE_GPRS
-       movw    %es,TF_ES(%rsp)
-       movw    %fs,TF_FS(%rsp)
-       movw    %gs,TF_GS(%rsp)
-       movw    $(GSEL(GUDATA_SEL, SEL_UPL)),TF_DS(%rsp)
+       movw    $GSEL(GUDATA_SEL, SEL_UPL),TF_DS(%rsp)
+       movw    $GSEL(GUDATA_SEL, SEL_UPL),TF_ES(%rsp)
+       movw    $0,TF_FS(%rsp)
+       movw    $0,TF_GS(%rsp)
        STI(si)
 
 do_syscall:
@@ -1313,18 +1319,18 @@
 #endif
 
        /*
-        * If the syscall might have modified some registers, or we are a 32bit
-        * process we must return to user with an 'iret' instruction.
-        * If the iret faults in kernel (assumed due to illegal register values)
-        * then a SIGSEGV will be signalled.
+        * Decide if we need to take a slow path. That's the case when we
+        * want to reload %cs and %ss on a 64bit LWP (MDL_IRET set), or when
+        * we're returning to a 32bit LWP (MDL_COMPAT32 set).
+        *
+        * In either case, we jump into intrfastexit and return to userland
+        * with the iret instruction.
         */
        testl   $(MDL_IRET|MDL_COMPAT32),L_MD_FLAGS(%r14)
+       jnz     intrfastexit
+
        INTR_RESTORE_GPRS
-       movw    TF_ES(%rsp),%es
-       movw    TF_DS(%rsp),%ds
        SWAPGS
-       jnz     .Lkexit
-
 #ifndef XEN
        movq    TF_RIP(%rsp),%rcx       /* %rip for sysret */
        movq    TF_RFLAGS(%rsp),%r11    /* %flags for sysret */
@@ -1469,12 +1475,21 @@
        INTR_RESTORE_GPRS
        testq   $SEL_UPL,TF_CS(%rsp)    /* interrupted %cs */
        jz      .Lkexit
+       cmpq    $LSEL(LUCODE_SEL, SEL_UPL),TF_CS(%rsp)
+       je      .Luexit64
+       cmpq    $GSEL(GUCODE_SEL, SEL_UPL),TF_CS(%rsp)
+       je      .Luexit64
 
-       /* Disable interrupts until the 'iret', user registers loaded. */
+.Luexit32:
        NOT_XEN(cli;)
        movw    TF_ES(%rsp),%es
        movw    TF_DS(%rsp),%ds
        SWAPGS
+       jmp     .Lkexit
+
+.Luexit64:
+       NOT_XEN(cli;)
+       SWAPGS
 
 .Lkexit:
        addq    $TF_REGSIZE+16,%rsp     /* + T_xxx and error code */
diff -r 9fa022e1e697 -r 36114973cfff sys/arch/amd64/amd64/machdep.c
--- a/sys/arch/amd64/amd64/machdep.c    Thu Oct 19 16:01:58 2017 +0000
+++ b/sys/arch/amd64/amd64/machdep.c    Thu Oct 19 18:36:31 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: machdep.c,v 1.269 2017/10/19 10:01:09 maxv Exp $       */
+/*     $NetBSD: machdep.c,v 1.270 2017/10/19 18:36:31 maxv Exp $       */
 
 /*
  * Copyright (c) 1996, 1997, 1998, 2000, 2006, 2007, 2008, 2011
@@ -110,7 +110,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.269 2017/10/19 10:01:09 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.270 2017/10/19 18:36:31 maxv Exp $");
 
 /* #define XENDEBUG_LOW  */
 
@@ -447,15 +447,19 @@
                HYPERVISOR_fpu_taskswitch(1);
        }
 
-       /* Update TLS segment pointers */
+       /* Update segment registers */
        if (pcb->pcb_flags & PCB_COMPAT32) {
                update_descriptor(&curcpu()->ci_gdt[GUFS_SEL], &pcb->pcb_fs);
                update_descriptor(&curcpu()->ci_gdt[GUGS_SEL], &pcb->pcb_gs);
+               setds(GSEL(GUDATA32_SEL, SEL_UPL));
+               setes(GSEL(GUDATA32_SEL, SEL_UPL));
                setfs(tf->tf_fs);
                HYPERVISOR_set_segment_base(SEGBASE_GS_USER_SEL, tf->tf_gs);
        } else {
                update_descriptor(&curcpu()->ci_gdt[GUFS_SEL], &zero);
                update_descriptor(&curcpu()->ci_gdt[GUGS_SEL], &zero);
+               setds(GSEL(GUDATA_SEL, SEL_UPL));
+               setes(GSEL(GUDATA_SEL, SEL_UPL));
                setfs(0);
                HYPERVISOR_set_segment_base(SEGBASE_GS_USER_SEL, 0);
                HYPERVISOR_set_segment_base(SEGBASE_FS, pcb->pcb_fs);
@@ -2063,6 +2067,8 @@
        kpreempt_disable();
        tf->tf_fs = 0;
        tf->tf_gs = 0;
+       setds(GSEL(GUDATA_SEL, SEL_UPL));
+       setes(GSEL(GUDATA_SEL, SEL_UPL));
        setfs(0);
        setusergs(0);
 
@@ -2100,6 +2106,8 @@
        kpreempt_disable();
        tf->tf_fs = 0;
        tf->tf_gs = 0;
+       setds(GSEL(GUDATA32_SEL, SEL_UPL));
+       setes(GSEL(GUDATA32_SEL, SEL_UPL));
        setfs(0);
        setusergs(0);
        pcb->pcb_fs = 0;



Home | Main Index | Thread Index | Old Index