--- old/usr/src/uts/common/disp/thread.c	Tue Jun 29 21:58:17 2021
+++ new/usr/src/uts/common/disp/thread.c	Tue Jun 29 21:58:17 2021
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2019 Joyent, Inc.
+ * Copyright 2021 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -1030,7 +1030,28 @@
 }
 
 /*
+ * Provide an allocation function for callers of installctx() that, for
+ * reasons of incomplete context-op initialization, must call installctx()
+ * in a kpreempt_disable() block.  The caller, therefore, must call this
+ * without being in such a block.
+ */
+struct ctxop *
+installctx_preallocate(void)
+{
+	/*
+	 * NOTE: We could ASSERT/VERIFY that we are not in a place where
+	 * a KM_SLEEP allocation could block indefinitely.
+	 *
+	 * ASSERT(curthread->t_preempt == 0);
+	 */
+
+	return (kmem_alloc(sizeof (struct ctxop), KM_SLEEP));
+}
+
+/*
  * Install thread context ops for the current thread.
+ * The caller can pass in a preallocated struct ctxop, eliminating the need
+ * for the requirement of entering with kernel preemption still enabled.
  */
 void
 installctx(
@@ -1041,11 +1062,12 @@
 	void	(*fork)(void *, void *),
 	void	(*lwp_create)(void *, void *),
 	void	(*exit)(void *),
-	void	(*free)(void *, int))
+	void	(*free)(void *, int),
+	struct ctxop *ctx)
 {
-	struct ctxop *ctx;
+	if (ctx == NULL)
+		ctx = kmem_alloc(sizeof (struct ctxop), KM_SLEEP);
 
-	ctx = kmem_alloc(sizeof (struct ctxop), KM_SLEEP);
 	ctx->save_op = save;
 	ctx->restore_op = restore;
 	ctx->fork_op = fork;
--- old/usr/src/uts/common/os/kcpc.c	Tue Jun 29 21:58:17 2021
+++ new/usr/src/uts/common/os/kcpc.c	Tue Jun 29 21:58:17 2021
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2021 Joyent, Inc.
  */
 
 #include <sys/param.h>
@@ -318,7 +319,7 @@
 	 * Add a device context to the subject thread.
 	 */
 	installctx(t, ctx, kcpc_save, kcpc_restore, NULL,
-	    kcpc_lwp_create, NULL, kcpc_free);
+	    kcpc_lwp_create, NULL, kcpc_free, NULL);
 
 	/*
 	 * Ask the backend to program the hardware.
@@ -1424,7 +1425,7 @@
 	}
 
 	installctx(ct, cctx, kcpc_save, kcpc_restore,
-	    NULL, kcpc_lwp_create, NULL, kcpc_free);
+	    NULL, kcpc_lwp_create, NULL, kcpc_free, NULL);
 }
 
 /*
--- old/usr/src/uts/common/os/schedctl.c	Tue Jun 29 21:58:18 2021
+++ new/usr/src/uts/common/os/schedctl.c	Tue Jun 29 21:58:18 2021
@@ -22,7 +22,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
- * Copyright 2016 Joyent, Inc.
+ * Copyright 2021 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -113,7 +113,7 @@
 		bzero(ssp, sizeof (*ssp));
 
 		installctx(t, ssp, schedctl_save, schedctl_restore,
-		    schedctl_fork, NULL, NULL, NULL);
+		    schedctl_fork, NULL, NULL, NULL, NULL);
 
 		thread_lock(t);	/* protect against ts_tick and ts_update */
 		t->t_schedctl = ssp;
--- old/usr/src/uts/common/sys/proc.h	Tue Jun 29 21:58:18 2021
+++ new/usr/src/uts/common/sys/proc.h	Tue Jun 29 21:58:18 2021
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2018 Joyent, Inc.
+ * Copyright 2021 Joyent, Inc.
  * Copyright 2020 Oxide Computer Company
  */
 
@@ -718,8 +718,9 @@
 extern	void	thread_rele(kthread_t *);
 extern	void	thread_join(kt_did_t);
 extern	int	reaper(void);
+extern	struct ctxop *installctx_preallocate(void);
 extern	void	installctx(kthread_t *, void *, void (*)(), void (*)(),
-    void (*)(), void (*)(), void (*)(), void (*)());
+    void (*)(), void (*)(), void (*)(), void (*)(), struct ctxop *);
 extern	int	removectx(kthread_t *, void *, void (*)(), void (*)(),
     void (*)(), void (*)(), void (*)(), void (*)());
 extern	void	savectx(kthread_t *);
--- old/usr/src/uts/i86pc/io/vmm/vmm.c	Tue Jun 29 21:58:18 2021
+++ new/usr/src/uts/i86pc/io/vmm/vmm.c	Tue Jun 29 21:58:18 2021
@@ -2216,7 +2216,7 @@
 	vtc.vtc_vcpuid = vcpuid;
 	vtc.vtc_status = 0;
 	installctx(curthread, &vtc, vmm_savectx, vmm_restorectx, NULL, NULL,
-	    NULL, vmm_freectx);
+	    NULL, vmm_freectx, NULL);
 
 	error = vm_entry_actions(vm, vcpuid, entry, vme);
 	if (error != 0) {
--- old/usr/src/uts/intel/ia32/os/cpc_subr.c	Tue Jun 29 21:58:18 2021
+++ new/usr/src/uts/intel/ia32/os/cpc_subr.c	Tue Jun 29 21:58:18 2021
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2019 Joyent, Inc.
+ * Copyright 2021 Joyent, Inc.
  */
 
 /*
@@ -169,7 +169,7 @@
 		return;
 
 	installctx(t, cp, kcpc_idle_save, kcpc_idle_restore,
-	    NULL, NULL, NULL, NULL);
+	    NULL, NULL, NULL, NULL, NULL);
 }
 
 void
--- old/usr/src/uts/intel/ia32/os/fpu.c	Tue Jun 29 21:58:18 2021
+++ new/usr/src/uts/intel/ia32/os/fpu.c	Tue Jun 29 21:58:18 2021
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2020 Joyent, Inc.
+ * Copyright 2021 Joyent, Inc.
  * Copyright 2021 RackTop Systems, Inc.
  */
 
@@ -670,7 +670,7 @@
 	 */
 
 	installctx(ct, cfp, fpsave_ctxt, fprestore_ctxt, fp_new_lwp,
-	    fp_new_lwp, NULL, fp_free);
+	    fp_new_lwp, NULL, fp_free, NULL);
 }
 
 /*
@@ -796,6 +796,7 @@
 fp_exec(void)
 {
 	struct fpu_ctx *fp = &ttolwp(curthread)->lwp_pcb.pcb_fpu;
+	struct ctxop *ctx = installctx_preallocate();
 
 	if (fp_save_mech == FP_XSAVE) {
 		fp->fpu_xsave_mask = XFEATURE_FP_ALL;
@@ -807,7 +808,7 @@
 	 */
 	kpreempt_disable();
 	installctx(curthread, fp, fpsave_ctxt, fprestore_ctxt, fp_new_lwp,
-	    fp_new_lwp, NULL, fp_free);
+	    fp_new_lwp, NULL, fp_free, ctx);
 	fpinit();
 	fp->fpu_flags = FPU_EN;
 	kpreempt_enable();
@@ -836,7 +837,7 @@
 	}
 
 	installctx(curthread, fp, fpsave_ctxt, fprestore_ctxt, fp_new_lwp,
-	    fp_new_lwp, NULL, fp_free);
+	    fp_new_lwp, NULL, fp_free, NULL);
 	fpinit();
 
 	/*
@@ -1318,6 +1319,7 @@
 kernel_fpu_begin(kfpu_state_t *kfpu, uint_t flags)
 {
 	klwp_t *pl = curthread->t_lwp;
+	struct ctxop *ctx;
 
 	if ((curthread->t_flag & T_KFPU) != 0) {
 		panic("curthread attempting to nest kernel FPU states");
@@ -1380,23 +1382,25 @@
 	 * FPU or another code path) so FPU_VALID could be set. This is handled
 	 * by fp_save, as is the FPU_EN check.
 	 */
+	ctx = installctx_preallocate();
+	kpreempt_disable();
 	if (pl != NULL) {
-		kpreempt_disable();
 		if ((flags & KFPU_USE_LWP) == 0)
 			fp_save(&pl->lwp_pcb.pcb_fpu);
 		pl->lwp_pcb.pcb_fpu.fpu_flags |= FPU_KERNEL;
-		kpreempt_enable();
 	}
 
 	/*
-	 * Set the context operations for kernel FPU usage. Note that this
-	 * cannot be done with pre-emption and interrupts disabled, since
-	 * installctx does a sleeping allocation. We haven't finished
-	 * initializing our kernel FPU state yet, but in the rare case that we
-	 * happen to save/restore before that, no harm is done.
+	 * Set the context operations for kernel FPU usage. Note that this is
+	 * done with a preallocated buffer and under kpreempt_disable because
+	 * without a preallocated buffer, installctx does a sleeping
+	 * allocation. We haven't finished initializing our kernel FPU state
+	 * yet, and in the rare case that we happen to save/restore just as
+	 * installctx() exits its own kpreempt_enable() internal call, we
+	 * guard against restoring an uninitialized buffer (0xbaddcafe).
 	 */
 	installctx(curthread, kfpu, kernel_fpu_ctx_save, kernel_fpu_ctx_restore,
-	    NULL, NULL, NULL, NULL);
+	    NULL, NULL, NULL, NULL, ctx);
 
 	curthread->t_flag |= T_KFPU;
 
@@ -1420,6 +1424,7 @@
 		/* initialize the kfpu state */
 		kernel_fpu_ctx_restore(kfpu);
 	}
+	kpreempt_enable();
 }
 
 void
--- old/usr/src/uts/intel/ia32/os/sundep.c	Tue Jun 29 21:58:18 2021
+++ new/usr/src/uts/intel/ia32/os/sundep.c	Tue Jun 29 21:58:18 2021
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2019 Joyent, Inc.
+ * Copyright 2021 Joyent, Inc.
  */
 
 /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
@@ -763,7 +763,7 @@
 	    brand_interpositioning_disable, NULL) == 0);
 	installctx(t, NULL, brand_interpositioning_disable,
 	    brand_interpositioning_enable, NULL, NULL,
-	    brand_interpositioning_disable, NULL);
+	    brand_interpositioning_disable, NULL, NULL);
 
 	if (t == curthread) {
 		kpreempt_disable();
@@ -829,6 +829,7 @@
 #else
 	void (*restop)(klwp_t *) = lwp_segregs_restore;
 #endif
+	struct ctxop *ctx;
 
 	/*
 	 * Install the basic lwp context handlers on each lwp.
@@ -844,10 +845,14 @@
 	 */
 	ASSERT(removectx(t, lwp, lwp_segregs_save, restop,
 	    NULL, NULL, NULL, NULL) == 0);
-	if (thisthread)
+	if (thisthread) {
+		ctx = installctx_preallocate();
 		kpreempt_disable();
+	} else {
+		ctx = NULL;
+	}
 	installctx(t, lwp, lwp_segregs_save, restop,
-	    NULL, NULL, NULL, NULL);
+	    NULL, NULL, NULL, NULL, ctx);
 	if (thisthread) {
 		/*
 		 * Since we're the right thread, set the values in the GDT
@@ -874,10 +879,14 @@
 		ASSERT(removectx(t, kstktop,
 		    sep_save, sep_restore, NULL, NULL, NULL, NULL) == 0);
 
-		if (thisthread)
+		if (thisthread) {
+			ctx = installctx_preallocate();
 			kpreempt_disable();
+		} else {
+			ctx = NULL;
+		}
 		installctx(t, kstktop,
-		    sep_save, sep_restore, NULL, NULL, NULL, NULL);
+		    sep_save, sep_restore, NULL, NULL, NULL, NULL, ctx);
 		if (thisthread) {
 			/*
 			 * We're the right thread, so set the stack pointer
--- old/usr/src/uts/intel/ia32/os/sysi86.c	Tue Jun 29 21:58:18 2021
+++ new/usr/src/uts/intel/ia32/os/sysi86.c	Tue Jun 29 21:58:18 2021
@@ -20,12 +20,12 @@
  */
 /*
  * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2018 Joyent, Inc.
+ * Copyright 2021 Joyent, Inc.
  */
 
 /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
-/*	  All Rights Reserved  	*/
+/*	  All Rights Reserved	*/
 
 /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
 /*	  All Rights Reserved	*/
@@ -93,6 +93,9 @@
 	 */
 	case SI86V86:
 		if (arg1 == V86SC_IOPL) {
+#if defined(__xpv)
+			struct ctxop *ctx;
+#endif
 			struct regs *rp = lwptoregs(ttolwp(curthread));
 			greg_t oldpl = rp->r_ps & PS_IOPL;
 			greg_t newpl = arg2 & PS_IOPL;
@@ -105,10 +108,11 @@
 			    secpolicy_sys_config(CRED(), B_FALSE)) != 0)
 				return (set_errno(error));
 #if defined(__xpv)
+			ctx = installctx_preallocate();
 			kpreempt_disable();
 			installctx(curthread, NULL, xen_disable_user_iopl,
 			    xen_enable_user_iopl, NULL, NULL,
-			    xen_disable_user_iopl, NULL);
+			    xen_disable_user_iopl, NULL, ctx);
 			xen_enable_user_iopl();
 			kpreempt_enable();
 #else
@@ -398,14 +402,14 @@
 	 * selectors when context switching away from a process that
 	 * has a private ldt. Consider the following example:
 	 *
-	 * 	Wine creats a ldt descriptor and points a segment register
-	 * 	to it.
+	 *	Wine creats a ldt descriptor and points a segment register
+	 *	to it.
 	 *
 	 *	We then context switch away from wine lwp to kernel
 	 *	thread and hit breakpoint in kernel with kmdb
 	 *
 	 *	When we continue and resume from kmdb we will #gp
-	 * 	fault since kmdb will have saved the stale ldt selector
+	 *	fault since kmdb will have saved the stale ldt selector
 	 *	from wine and will try to restore it but we are no longer in
 	 *	the context of the wine process and do not have our
 	 *	ldtr register pointing to the private ldt.
--- old/usr/src/uts/sun4u/os/cpc_subr.c	Tue Jun 29 21:58:19 2021
+++ new/usr/src/uts/sun4u/os/cpc_subr.c	Tue Jun 29 21:58:19 2021
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2021 Joyent, Inc.
  */
 
 /*
@@ -95,7 +96,7 @@
 		return;
 
 	installctx(t, cp, kcpc_idle_save, kcpc_idle_restore, NULL, NULL,
-	    NULL, NULL);
+	    NULL, NULL, NULL);
 }
 
 /*
--- old/usr/src/uts/sun4v/os/cpc_subr.c	Tue Jun 29 21:58:19 2021
+++ new/usr/src/uts/sun4v/os/cpc_subr.c	Tue Jun 29 21:58:19 2021
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2021 Joyent, Inc.
  */
 
 /*
@@ -94,7 +95,7 @@
 		return;
 
 	installctx(t, cp, kcpc_idle_save, kcpc_idle_restore, NULL, NULL,
-	    NULL, NULL);
+	    NULL, NULL, NULL);
 }
 
 /*
