Print this page
OS-7125 Need mitigation of L1TF (CVE-2018-3646)
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>

@@ -22,33 +22,27 @@
  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 /*
- * Copyright (c) 2018 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
  */
 
 /*
  * Process switching routines.
  */
 
-#if defined(__lint)
-#include <sys/thread.h>
-#include <sys/systm.h>
-#include <sys/time.h>
-#else   /* __lint */
-#include "assym.h"
-#endif  /* __lint */
-
 #include <sys/asm_linkage.h>
 #include <sys/asm_misc.h>
 #include <sys/regset.h>
 #include <sys/privregs.h>
 #include <sys/stack.h>
 #include <sys/segments.h>
 #include <sys/psw.h>
 
+#include "assym.h"
+
 /*
  * resume(thread_id_t t);
  *
  * a thread can only run on one processor at a time. there
  * exists a window on MPs where the current thread on one

@@ -72,20 +66,14 @@
  * resume_from_zombie() is the same as resume except the calling thread
  * is a zombie and must be put on the deathrow list after the CPU is
  * off the stack.
  */
 
-#if !defined(__lint)
-
 #if LWP_PCB_FPU != 0
 #error LWP_PCB_FPU MUST be defined as 0 for code in swtch.s to work
 #endif  /* LWP_PCB_FPU != 0 */
 
-#endif  /* !__lint */
-
-#if defined(__amd64)
-
 /*
  * Save non-volatile regs other than %rsp (%rbx, %rbp, and %r12 - %r15)
  *
  * The stack frame must be created before the save of %rsp so that tracebacks
  * of swtch()ed-out processes show the process as having last called swtch().

@@ -151,92 +139,10 @@
         movq    T_INTR_START(thread_t), %rax;           \
         cmpxchgq %r14, T_INTR_START(thread_t);          \
         jnz     0b;                                     \
 1:
 
-#elif defined (__i386)
-
-/*
- * Save non-volatile registers (%ebp, %esi, %edi and %ebx)
- *
- * The stack frame must be created before the save of %esp so that tracebacks
- * of swtch()ed-out processes show the process as having last called swtch().
- */
-#define SAVE_REGS(thread_t, retaddr)                    \
-        movl    %ebp, T_EBP(thread_t);                  \
-        movl    %ebx, T_EBX(thread_t);                  \
-        movl    %esi, T_ESI(thread_t);                  \
-        movl    %edi, T_EDI(thread_t);                  \
-        pushl   %ebp;                                   \
-        movl    %esp, %ebp;                             \
-        movl    %esp, T_SP(thread_t);                   \
-        movl    retaddr, T_PC(thread_t);                \
-        movl    8(%ebp), %edi;                          \
-        pushl   %edi;                                   \
-        call    __dtrace_probe___sched_off__cpu;        \
-        addl    $CLONGSIZE, %esp
-
-/*
- * Restore non-volatile registers (%ebp, %esi, %edi and %ebx)
- *
- * We don't do a 'leave,' because reloading %rsp/%rbp from the label_t
- * already has the effect of putting the stack back the way it was when
- * we came in.
- */
-#define RESTORE_REGS(scratch_reg)                       \
-        movl    %gs:CPU_THREAD, scratch_reg;            \
-        movl    T_EBP(scratch_reg), %ebp;               \
-        movl    T_EBX(scratch_reg), %ebx;               \
-        movl    T_ESI(scratch_reg), %esi;               \
-        movl    T_EDI(scratch_reg), %edi
-
-/*
- * Get pointer to a thread's hat structure
- */
-#define GET_THREAD_HATP(hatp, thread_t, scratch_reg)    \
-        movl    T_PROCP(thread_t), hatp;                \
-        movl    P_AS(hatp), scratch_reg;                \
-        movl    A_HAT(scratch_reg), hatp
-
-/*
- * If we are resuming an interrupt thread, store a timestamp in the thread
- * structure.  If an interrupt occurs between tsc_read() and its subsequent
- * store, the timestamp will be stale by the time it is stored.  We can detect
- * this by doing a compare-and-swap on the thread's timestamp, since any
- * interrupt occurring in this window will put a new timestamp in the thread's
- * t_intr_start field.
- */
-#define STORE_INTR_START(thread_t)                      \
-        testw   $T_INTR_THREAD, T_FLAGS(thread_t);      \
-        jz      1f;                                     \
-        pushl   %ecx;                                   \
-0:                                                      \
-        pushl   T_INTR_START(thread_t);                 \
-        pushl   T_INTR_START+4(thread_t);               \
-        call    tsc_read;                               \
-        movl    %eax, %ebx;                             \
-        movl    %edx, %ecx;                             \
-        popl    %edx;                                   \
-        popl    %eax;                                   \
-        cmpxchg8b T_INTR_START(thread_t);               \
-        jnz     0b;                                     \
-        popl    %ecx;                                   \
-1:
-
-#endif  /* __amd64 */
-
-#if defined(__lint)
-
-/* ARGSUSED */
-void
-resume(kthread_t *t)
-{}
-
-#else   /* __lint */
-
-#if defined(__amd64)
-
         .global kpti_enable
 
         ENTRY(resume)
         movq    %gs:CPU_THREAD, %rax
         leaq    resume_return(%rip), %r11

@@ -434,10 +340,12 @@
         xorq    %rax, %rax
         movq    %rax, T_USERACC(%r12)
         call    smap_disable
 .nosmap:
 
+        call    ht_mark
+
         /*
          * Restore non-volatile registers, then have spl0 return to the
          * resuming thread's PC after first setting the priority as low as
          * possible and blocking all interrupt threads that may be active.
          */

@@ -454,206 +362,10 @@
         addq    $CLONGSIZE, %rsp
         ret
         SET_SIZE(_resume_from_idle)
         SET_SIZE(resume)
 
-#elif defined (__i386)
-
-        ENTRY(resume)
-        movl    %gs:CPU_THREAD, %eax
-        movl    $resume_return, %ecx
-
-        /*
-         * Save non-volatile registers, and set return address for current
-         * thread to resume_return.
-         *
-         * %edi = t (new thread) when done.
-         */
-        SAVE_REGS(%eax,  %ecx)
-
-        LOADCPU(%ebx)                   /* %ebx = CPU */
-        movl    CPU_THREAD(%ebx), %esi  /* %esi = curthread */
-
-#ifdef DEBUG
-        call    assert_ints_enabled     /* panics if we are cli'd */
-#endif
-        /*
-         * Call savectx if thread has installed context ops.
-         *
-         * Note that if we have floating point context, the save op
-         * (either fpsave_begin or fpxsave_begin) will issue the
-         * async save instruction (fnsave or fxsave respectively)
-         * that we fwait for below.
-         */
-        movl    T_CTX(%esi), %eax       /* should current thread savectx? */
-        testl   %eax, %eax
-        jz      .nosavectx              /* skip call when zero */
-        pushl   %esi                    /* arg = thread pointer */
-        call    savectx                 /* call ctx ops */
-        addl    $4, %esp                /* restore stack pointer */
-.nosavectx:
-
-        /*
-         * Call savepctx if process has installed context ops.
-         */
-        movl    T_PROCP(%esi), %eax     /* %eax = proc */
-        cmpl    $0, P_PCTX(%eax)        /* should current thread savectx? */
-        je      .nosavepctx             /* skip call when zero */
-        pushl   %eax                    /* arg = proc pointer */
-        call    savepctx                /* call ctx ops */
-        addl    $4, %esp
-.nosavepctx:
-
-        /*
-         * Temporarily switch to the idle thread's stack
-         */
-        movl    CPU_IDLE_THREAD(%ebx), %eax     /* idle thread pointer */
-
-        /*
-         * Set the idle thread as the current thread
-         */
-        movl    T_SP(%eax), %esp        /* It is safe to set esp */
-        movl    %eax, CPU_THREAD(%ebx)
-
-        /* switch in the hat context for the new thread */
-        GET_THREAD_HATP(%ecx, %edi, %ecx)
-        pushl   %ecx
-        call    hat_switch
-        addl    $4, %esp
-
-        /*
-         * Clear and unlock previous thread's t_lock
-         * to allow it to be dispatched by another processor.
-         */
-        movb    $0, T_LOCK(%esi)
-
-        /*
-         * IMPORTANT: Registers at this point must be:
-         *       %edi = new thread
-         *
-         * Here we are in the idle thread, have dropped the old thread.
-         */
-        ALTENTRY(_resume_from_idle)
-        /*
-         * spin until dispatched thread's mutex has
-         * been unlocked. this mutex is unlocked when
-         * it becomes safe for the thread to run.
-         */
-.L4:
-        lock
-        btsl    $0, T_LOCK(%edi) /* lock new thread's mutex */
-        jc      .L4_2                   /* lock did not succeed */
-
-        /*
-         * Fix CPU structure to indicate new running thread.
-         * Set pointer in new thread to the CPU structure.
-         */
-        LOADCPU(%esi)                   /* load current CPU pointer */
-        movl    T_STACK(%edi), %eax     /* here to use v pipeline of */
-                                        /* Pentium. Used few lines below */
-        cmpl    %esi, T_CPU(%edi)
-        jne     .L5_2
-.L5_1:
-        /*
-         * Setup esp0 (kernel stack) in TSS to curthread's stack.
-         * (Note: Since we don't have saved 'regs' structure for all
-         *        the threads we can't easily determine if we need to
-         *        change esp0. So, we simply change the esp0 to bottom 
-         *        of the thread stack and it will work for all cases.)
-         */
-        movl    CPU_TSS(%esi), %ecx
-        addl    $REGSIZE+MINFRAME, %eax /* to the bottom of thread stack */
-#if !defined(__xpv)
-        movl    %eax, TSS_ESP0(%ecx)
-#else
-        pushl   %eax
-        pushl   $KDS_SEL
-        call    HYPERVISOR_stack_switch
-        addl    $8, %esp
-#endif  /* __xpv */
-
-        movl    %edi, CPU_THREAD(%esi)  /* set CPU's thread pointer */
-        mfence                          /* synchronize with mutex_exit() */
-        xorl    %ebp, %ebp              /* make $<threadlist behave better */
-        movl    T_LWP(%edi), %eax       /* set associated lwp to  */
-        movl    %eax, CPU_LWP(%esi)     /* CPU's lwp ptr */
-
-        movl    T_SP(%edi), %esp        /* switch to outgoing thread's stack */
-        movl    T_PC(%edi), %esi        /* saved return addr */
-
-        /*
-         * Call restorectx if context ops have been installed.
-         */
-        movl    T_CTX(%edi), %eax       /* should resumed thread restorectx? */
-        testl   %eax, %eax
-        jz      .norestorectx           /* skip call when zero */
-        pushl   %edi                    /* arg = thread pointer */
-        call    restorectx              /* call ctx ops */
-        addl    $4, %esp                /* restore stack pointer */
-.norestorectx:
-
-        /*
-         * Call restorepctx if context ops have been installed for the proc.
-         */
-        movl    T_PROCP(%edi), %eax
-        cmpl    $0, P_PCTX(%eax)
-        je      .norestorepctx
-        pushl   %eax                    /* arg = proc pointer */
-        call    restorepctx
-        addl    $4, %esp                /* restore stack pointer */
-.norestorepctx:
-
-        STORE_INTR_START(%edi)
-
-        /*
-         * Restore non-volatile registers, then have spl0 return to the
-         * resuming thread's PC after first setting the priority as low as
-         * possible and blocking all interrupt threads that may be active.
-         */
-        movl    %esi, %eax              /* save return address */
-        RESTORE_REGS(%ecx)
-        pushl   %eax                    /* push return address for spl0() */
-        call    __dtrace_probe___sched_on__cpu
-        jmp     spl0
-
-resume_return:
-        /*
-         * Remove stack frame created in SAVE_REGS()
-         */
-        addl    $CLONGSIZE, %esp
-        ret
-
-.L4_2:
-        pause
-        cmpb    $0, T_LOCK(%edi)
-        je      .L4
-        jmp     .L4_2
-
-.L5_2:
-        /* cp->cpu_stats.sys.cpumigrate++ */
-        addl    $1, CPU_STATS_SYS_CPUMIGRATE(%esi)
-        adcl    $0, CPU_STATS_SYS_CPUMIGRATE+4(%esi)
-        movl    %esi, T_CPU(%edi)       /* set new thread's CPU pointer */
-        jmp     .L5_1
-
-        SET_SIZE(_resume_from_idle)
-        SET_SIZE(resume)
-
-#endif  /* __amd64 */
-#endif  /* __lint */
-
-#if defined(__lint)
-
-/* ARGSUSED */
-void
-resume_from_zombie(kthread_t *t)
-{}
-
-#else   /* __lint */
-
-#if defined(__amd64)
-
         ENTRY(resume_from_zombie)
         movq    %gs:CPU_THREAD, %rax
         leaq    resume_from_zombie_return(%rip), %r11
 
         /*

@@ -724,92 +436,10 @@
          */
         addq    $CLONGSIZE, %rsp
         ret
         SET_SIZE(resume_from_zombie)
 
-#elif defined (__i386)
-
-        ENTRY(resume_from_zombie)
-        movl    %gs:CPU_THREAD, %eax
-        movl    $resume_from_zombie_return, %ecx
-
-        /*
-         * Save non-volatile registers, and set return address for current
-         * thread to resume_from_zombie_return.
-         *
-         * %edi = t (new thread) when done.
-         */
-        SAVE_REGS(%eax, %ecx)
-
-#ifdef DEBUG
-        call    assert_ints_enabled     /* panics if we are cli'd */
-#endif
-        movl    %gs:CPU_THREAD, %esi    /* %esi = curthread */
-
-        /* clean up the fp unit. It might be left enabled */
-
-        movl    %cr0, %eax
-        testl   $CR0_TS, %eax
-        jnz     .zfpu_disabled          /* if TS already set, nothing to do */
-        fninit                          /* init fpu & discard pending error */
-        orl     $CR0_TS, %eax
-        movl    %eax, %cr0
-.zfpu_disabled:
-
-        /*
-         * Temporarily switch to the idle thread's stack so that the zombie
-         * thread's stack can be reclaimed by the reaper.
-         */
-        movl    %gs:CPU_IDLE_THREAD, %eax /* idle thread pointer */
-        movl    T_SP(%eax), %esp        /* get onto idle thread stack */
-
-        /*
-         * Set the idle thread as the current thread.
-         */
-        movl    %eax, %gs:CPU_THREAD
-
-        /*
-         * switch in the hat context for the new thread
-         */
-        GET_THREAD_HATP(%ecx, %edi, %ecx)
-        pushl   %ecx
-        call    hat_switch
-        addl    $4, %esp
-
-        /*
-         * Put the zombie on death-row.
-         */
-        pushl   %esi
-        call    reapq_add
-        addl    $4, %esp
-        jmp     _resume_from_idle       /* finish job of resume */
-
-resume_from_zombie_return:
-        RESTORE_REGS(%ecx)              /* restore non-volatile registers */
-        call    __dtrace_probe___sched_on__cpu
-
-        /*
-         * Remove stack frame created in SAVE_REGS()
-         */
-        addl    $CLONGSIZE, %esp
-        ret
-        SET_SIZE(resume_from_zombie)
-
-#endif  /* __amd64 */
-#endif  /* __lint */
-
-#if defined(__lint)
-
-/* ARGSUSED */
-void
-resume_from_intr(kthread_t *t)
-{}
-
-#else   /* __lint */
-
-#if defined(__amd64)
-
         ENTRY(resume_from_intr)
         movq    %gs:CPU_THREAD, %rax
         leaq    resume_from_intr_return(%rip), %r11
 
         /*

@@ -832,10 +462,12 @@
         xorl    %eax, %eax
         xchgb   %al, T_LOCK(%r13)
 
         STORE_INTR_START(%r12)
 
+        call    ht_mark
+
         /*
          * Restore non-volatile registers, then have spl0 return to the
          * resuming thread's PC after first setting the priority as low as
          * possible and blocking all interrupt threads that may be active.
          */

@@ -851,93 +483,14 @@
          */
         addq    $CLONGSIZE, %rsp
         ret
         SET_SIZE(resume_from_intr)
 
-#elif defined (__i386)
-
-        ENTRY(resume_from_intr)
-        movl    %gs:CPU_THREAD, %eax
-        movl    $resume_from_intr_return, %ecx
-
-        /*
-         * Save non-volatile registers, and set return address for current
-         * thread to resume_return.
-         *
-         * %edi = t (new thread) when done.
-         */
-        SAVE_REGS(%eax, %ecx)
-
-#ifdef DEBUG
-        call    assert_ints_enabled     /* panics if we are cli'd */
-#endif
-        movl    %gs:CPU_THREAD, %esi    /* %esi = curthread */
-        movl    %edi, %gs:CPU_THREAD    /* set CPU's thread pointer */
-        mfence                          /* synchronize with mutex_exit() */
-        movl    T_SP(%edi), %esp        /* restore resuming thread's sp */
-        xorl    %ebp, %ebp              /* make $<threadlist behave better */
-
-        /*
-         * Unlock outgoing thread's mutex dispatched by another processor.
-         */
-        xorl    %eax,%eax
-        xchgb   %al, T_LOCK(%esi)
-
-        STORE_INTR_START(%edi)
-
-        /*
-         * Restore non-volatile registers, then have spl0 return to the
-         * resuming thread's PC after first setting the priority as low as
-         * possible and blocking all interrupt threads that may be active.
-         */
-        movl    T_PC(%edi), %eax        /* saved return addr */
-        RESTORE_REGS(%ecx)
-        pushl   %eax                    /* push return address for spl0() */
-        call    __dtrace_probe___sched_on__cpu
-        jmp     spl0
-
-resume_from_intr_return:
-        /*
-         * Remove stack frame created in SAVE_REGS()
-         */
-        addl    $CLONGSIZE, %esp
-        ret
-        SET_SIZE(resume_from_intr)
-
-#endif  /* __amd64 */
-#endif /* __lint */
-
-#if defined(__lint)
-
-void
-thread_start(void)
-{}
-
-#else   /* __lint */
-
-#if defined(__amd64)
-
         ENTRY(thread_start)
         popq    %rax            /* start() */
         popq    %rdi            /* arg */
         popq    %rsi            /* len */
         movq    %rsp, %rbp
         call    *%rax
         call    thread_exit     /* destroy thread if it returns. */
         /*NOTREACHED*/
         SET_SIZE(thread_start)
-
-#elif defined(__i386)
-
-        ENTRY(thread_start)
-        popl    %eax
-        movl    %esp, %ebp
-        addl    $8, %ebp
-        call    *%eax
-        addl    $8, %esp
-        call    thread_exit     /* destroy thread if it returns. */
-        /*NOTREACHED*/
-        SET_SIZE(thread_start)
-
-#endif  /* __i386 */
-
-#endif  /* __lint */