#include "opt_reset.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/interrupt.h>
#include <sys/vnode.h>
#include <sys/vmmeter.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/unistd.h>
#include <sys/lwp.h>
#include <machine/clock.h>
#include <machine/cpu.h>
#include <machine/md_var.h>
#include <machine/smp.h>
#include <machine/pcb.h>
#include <machine/pcb_ext.h>
#include <machine/segments.h>
#include <machine/globaldata.h>
#include <machine/specialreg.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <sys/lock.h>
#include <vm/vm_kern.h>
#include <vm/vm_page.h>
#include <vm/vm_map.h>
#include <vm/vm_extern.h>
#include <sys/thread2.h>
#include <bus/isa/isa.h>
static void cpu_reset_real (void);
static int spectre_mitigation = -1;
static int spectre_support = 0;
static int spectre_mode = 0;
SYSCTL_INT(_machdep, OID_AUTO, spectre_mode, CTLFLAG_RD,
&spectre_mode, 0, "current Spectre enablements");
static int mds_mitigation = -1;
static int mds_support = 0;
static int mds_mode = 0;
SYSCTL_INT(_machdep, OID_AUTO, mds_mode, CTLFLAG_RD,
&mds_mode, 0, "current MDS enablements");
void
cpu_fork(struct lwp *lp1, struct lwp *lp2, int flags)
{
struct pcb *pcb2;
struct pmap *pmap2;
if ((flags & RFPROC) == 0) {
if ((flags & RFMEM) == 0) {
struct pcb *pcb1 = lp1->lwp_thread->td_pcb;
struct pcb_ldt *pcb_ldt = pcb1->pcb_ldt;
if (pcb_ldt && pcb_ldt->ldt_refcnt > 1) {
pcb_ldt = user_ldt_alloc(pcb1,pcb_ldt->ldt_len);
user_ldt_free(pcb1);
pcb1->pcb_ldt = pcb_ldt;
set_user_ldt(pcb1);
}
}
return;
}
if (mdcpu->gd_npxthread == lp1->lwp_thread)
npxsave(lp1->lwp_thread->td_savefpu);
pcb2 = lp2->lwp_thread->td_pcb;
*pcb2 = *lp1->lwp_thread->td_pcb;
lp2->lwp_md.md_regs = (struct trapframe *)pcb2 - 1;
bcopy(lp1->lwp_md.md_regs, lp2->lwp_md.md_regs, sizeof(*lp2->lwp_md.md_regs));
pmap2 = vmspace_pmap(lp2->lwp_proc->p_vmspace);
pcb2->pcb_cr3 = vtophys(pmap2->pm_pml4);
if ((pcb2->pcb_flags & PCB_ISOMMU) && pmap2->pm_pmlpv_iso) {
pcb2->pcb_cr3_iso = vtophys(pmap2->pm_pml4_iso);
} else {
pcb2->pcb_flags &= ~PCB_ISOMMU;
pcb2->pcb_cr3_iso = 0;
}
#if 0
pcb2->pcb_flags &= ~(PCB_IBRS1 | PCB_IBRS2);
switch (spectre_mitigation) {
case 1:
pcb2->pcb_flags |= PCB_IBRS1;
break;
case 2:
pcb2->pcb_flags |= PCB_IBRS2;
break;
default:
break;
}
#endif
pcb2->pcb_rbx = (unsigned long)fork_return;
pcb2->pcb_rbp = 0;
pcb2->pcb_rsp = (unsigned long)lp2->lwp_md.md_regs - sizeof(void *);
pcb2->pcb_r12 = (unsigned long)lp2;
pcb2->pcb_r13 = 0;
pcb2->pcb_r14 = 0;
pcb2->pcb_r15 = 0;
pcb2->pcb_rip = (unsigned long)fork_trampoline;
lp2->lwp_thread->td_sp = (char *)(pcb2->pcb_rsp - sizeof(void *));
*(u_int64_t *)lp2->lwp_thread->td_sp = PSL_USER;
lp2->lwp_thread->td_sp -= sizeof(void *);
*(void **)lp2->lwp_thread->td_sp = (void *)cpu_heavy_restore;
pcb2->pcb_ext = NULL;
if (pcb2->pcb_ldt != NULL) {
if (flags & RFMEM) {
atomic_add_int(&pcb2->pcb_ldt->ldt_refcnt, 1);
} else {
pcb2->pcb_ldt = user_ldt_alloc(pcb2,
pcb2->pcb_ldt->ldt_len);
}
}
bcopy(&lp1->lwp_thread->td_tls, &lp2->lwp_thread->td_tls,
sizeof(lp2->lwp_thread->td_tls));
}
int
cpu_prepare_lwp(struct lwp *lp, struct lwp_params *params)
{
struct trapframe *regs = lp->lwp_md.md_regs;
void *bad_return = NULL;
int error;
regs->tf_rip = (long)params->lwp_func;
regs->tf_rsp = (long)params->lwp_stack;
regs->tf_rdi = (long)params->lwp_arg;
regs->tf_rsp -= sizeof(void *);
error = copyout(&bad_return, (void *)regs->tf_rsp, sizeof(bad_return));
if (error)
return (error);
cpu_set_fork_handler(lp,
(void (*)(void *, struct trapframe *))generic_lwp_return, lp);
return (0);
}
void
cpu_set_fork_handler(struct lwp *lp, void (*func)(void *, struct trapframe *),
void *arg)
{
lp->lwp_thread->td_pcb->pcb_rbx = (long)func;
lp->lwp_thread->td_pcb->pcb_r12 = (long)arg;
}
void
cpu_set_thread_handler(thread_t td, void (*rfunc)(void), void *func, void *arg)
{
td->td_pcb->pcb_rbx = (long)func;
td->td_pcb->pcb_r12 = (long)arg;
td->td_switch = cpu_lwkt_switch;
td->td_sp -= sizeof(void *);
*(void **)td->td_sp = rfunc;
td->td_sp -= sizeof(void *);
*(void **)td->td_sp = cpu_kthread_restore;
}
void
cpu_lwp_exit(void)
{
struct thread *td = curthread;
struct pcb *pcb;
pcb = td->td_pcb;
KKASSERT(pcb->pcb_ext == NULL);
if (pcb->pcb_flags & PCB_DBREGS) {
reset_dbregs();
pcb->pcb_flags &= ~PCB_DBREGS;
}
td->td_gd->gd_cnt.v_swtch++;
crit_enter_quick(td);
if (td->td_flags & TDF_TSLEEPQ)
tsleep_remove(td);
lwkt_deschedule_self(td);
lwkt_remove_tdallq(td);
cpu_thread_exit();
}
void
cpu_thread_exit(void)
{
npxexit();
curthread->td_switch = cpu_exit_switch;
curthread->td_flags |= TDF_EXITING;
lwkt_switch();
panic("cpu_thread_exit: lwkt_switch() unexpectedly returned");
}
void
cpu_reset(void)
{
cpu_reset_real();
}
static void
cpu_reset_real(void)
{
#if !defined(BROKEN_KEYBOARD_RESET)
outb(IO_KBD + 4, 0xFE);
DELAY(500000);
kprintf("Keyboard reset did not work, attempting CPU shutdown\n");
DELAY(1000000);
#endif
#if 0
bzero((caddr_t) PTD, PAGE_SIZE);
#endif
cpu_invltlb();
while(1);
}
static void
swi_vm(void *arg, void *frame)
{
if (busdma_swi_pending != 0)
busdma_swi();
}
static void
swi_vm_setup(void *arg)
{
register_swi_mp(SWI_VM, swi_vm, NULL, "swi_vm", NULL, 0);
}
SYSINIT(swi_vm_setup, SI_BOOT2_MACHDEP, SI_ORDER_ANY, swi_vm_setup, NULL);
void mitigation_vm_setup(void *arg);
#define IBRS_SUPPORTED 0x0001
#define STIBP_SUPPORTED 0x0002
#define IBPB_SUPPORTED 0x0004
#define IBRS_AUTO_SUPPORTED 0x0008
#define STIBP_AUTO_SUPPORTED 0x0010
#define IBRS_PREFERRED_REQUEST 0x0020
static
int
spectre_check_support(void)
{
uint32_t p[4];
int rv = 0;
if (cpu_vendor_id == CPU_VENDOR_INTEL) {
p[0] = 0;
p[1] = 0;
p[2] = 0;
p[3] = 0;
cpuid_count(7, 0, p);
if (p[3] & CPUID_STDEXT3_IBPB)
rv |= IBRS_SUPPORTED | IBPB_SUPPORTED;
if (p[3] & CPUID_STDEXT3_STIBP)
rv |= STIBP_SUPPORTED;
p[0] = 0;
p[1] = 0;
p[2] = 0;
p[3] = 0;
do_cpuid(0x80000008U, p);
if (p[1] & CPUID_CAPEX_IBPB)
rv |= IBPB_SUPPORTED;
} else if (cpu_vendor_id == CPU_VENDOR_AMD) {
p[0] = 0;
p[1] = 0;
p[2] = 0;
p[3] = 0;
do_cpuid(0x80000008U, p);
if (p[1] & CPUID_CAPEX_IBPB)
rv |= IBPB_SUPPORTED;
if (p[1] & CPUID_CAPEX_IBRS)
rv |= IBRS_SUPPORTED;
if (p[1] & CPUID_CAPEX_STIBP)
rv |= STIBP_SUPPORTED;
if (p[1] & CPUID_CAPEX_IBRS_ALWAYSON)
rv |= IBRS_AUTO_SUPPORTED;
if (p[1] & CPUID_CAPEX_STIBP_ALWAYSON)
rv |= STIBP_AUTO_SUPPORTED;
if (p[1] & CPUID_CAPEX_PREFER_IBRS)
rv |= IBRS_PREFERRED_REQUEST;
}
return rv;
}
#define CHECK(flag) (spectre_mitigation & spectre_support & (flag))
static
void
spectre_sysctl_changed(void)
{
globaldata_t save_gd;
struct trampframe *tr;
int spec_ctrl;
int spec_mask;
int mode;
int n;
spec_mask = SPEC_CTRL_IBRS | SPEC_CTRL_STIBP |
SPEC_CTRL_DUMMY_ENABLE | SPEC_CTRL_DUMMY_IBPB;
mode = 0;
save_gd = mycpu;
for (n = 0; n < ncpus; ++n) {
lwkt_setcpu_self(globaldata_find(n));
cpu_ccfence();
tr = &pscpu->trampoline;
tr->tr_pcb_spec_ctrl[0] &= ~spec_mask;
tr->tr_pcb_spec_ctrl[1] &= ~spec_mask;
if (spectre_mitigation < 0)
continue;
spec_ctrl = 0;
if (CHECK(IBRS_AUTO_SUPPORTED)) {
spec_ctrl |= SPEC_CTRL_IBRS;
mode |= IBRS_AUTO_SUPPORTED;
} else if (CHECK(IBRS_SUPPORTED)) {
spec_ctrl |= SPEC_CTRL_IBRS | SPEC_CTRL_DUMMY_ENABLE;
mode |= IBRS_SUPPORTED;
}
if (CHECK(STIBP_AUTO_SUPPORTED)) {
spec_ctrl |= SPEC_CTRL_STIBP;
mode |= STIBP_AUTO_SUPPORTED;
} else if (CHECK(STIBP_SUPPORTED)) {
spec_ctrl |= SPEC_CTRL_STIBP | SPEC_CTRL_DUMMY_ENABLE;
mode |= STIBP_SUPPORTED;
}
if (CHECK(IBPB_SUPPORTED)) {
spec_ctrl |= SPEC_CTRL_DUMMY_IBPB;
mode |= IBPB_SUPPORTED;
}
if (spectre_support & (IBRS_SUPPORTED | IBRS_AUTO_SUPPORTED |
STIBP_SUPPORTED | STIBP_AUTO_SUPPORTED)) {
wrmsr(MSR_SPEC_CTRL,
spec_ctrl & (SPEC_CTRL_IBRS|SPEC_CTRL_STIBP));
}
tr->tr_pcb_spec_ctrl[0] |= spec_ctrl;
if (CHECK(IBRS_AUTO_SUPPORTED) == 0)
spec_ctrl &= ~SPEC_CTRL_IBRS;
if (CHECK(STIBP_AUTO_SUPPORTED) == 0)
spec_ctrl &= ~SPEC_CTRL_STIBP;
tr->tr_pcb_spec_ctrl[1] |= spec_ctrl;
spectre_mode = mode;
}
lwkt_setcpu_self(save_gd);
cpu_ccfence();
kprintf("Spectre: support=(");
if (spectre_support == 0) {
kprintf(" none");
} else {
if (spectre_support & IBRS_SUPPORTED)
kprintf(" IBRS");
if (spectre_support & STIBP_SUPPORTED)
kprintf(" STIBP");
if (spectre_support & IBPB_SUPPORTED)
kprintf(" IBPB");
if (spectre_support & IBRS_AUTO_SUPPORTED)
kprintf(" IBRS_AUTO");
if (spectre_support & STIBP_AUTO_SUPPORTED)
kprintf(" STIBP_AUTO");
if (spectre_support & IBRS_PREFERRED_REQUEST)
kprintf(" IBRS_REQUESTED");
}
kprintf(" ) req=%04x operating=(", (uint16_t)spectre_mitigation);
if (spectre_mode == 0) {
kprintf(" none");
} else {
if (spectre_mode & IBRS_SUPPORTED)
kprintf(" IBRS");
if (spectre_mode & STIBP_SUPPORTED)
kprintf(" STIBP");
if (spectre_mode & IBPB_SUPPORTED)
kprintf(" IBPB");
if (spectre_mode & IBRS_AUTO_SUPPORTED)
kprintf(" IBRS_AUTO");
if (spectre_mode & STIBP_AUTO_SUPPORTED)
kprintf(" STIBP_AUTO");
if (spectre_mode & IBRS_PREFERRED_REQUEST)
kprintf(" IBRS_REQUESTED");
}
kprintf(" )\n");
}
#undef CHECK
static int
sysctl_spectre_mitigation(SYSCTL_HANDLER_ARGS)
{
char buf[128];
char *ptr;
char *iter;
size_t len;
int spectre;
int error = 0;
int loop = 0;
if (oidp->oid_kind & CTLFLAG_WR)
spectre = spectre_mode;
else
spectre = spectre_support;
spectre &= (IBRS_SUPPORTED | IBRS_AUTO_SUPPORTED |
STIBP_SUPPORTED | STIBP_AUTO_SUPPORTED |
IBPB_SUPPORTED);
while (spectre) {
if (error)
break;
if (loop++) {
error = SYSCTL_OUT(req, " ", 1);
if (error)
break;
}
if (spectre & IBRS_SUPPORTED) {
spectre &= ~IBRS_SUPPORTED;
error = SYSCTL_OUT(req, "IBRS", 4);
} else
if (spectre & IBRS_AUTO_SUPPORTED) {
spectre &= ~IBRS_AUTO_SUPPORTED;
error = SYSCTL_OUT(req, "IBRS_AUTO", 9);
} else
if (spectre & STIBP_SUPPORTED) {
spectre &= ~STIBP_SUPPORTED;
error = SYSCTL_OUT(req, "STIBP", 5);
} else
if (spectre & STIBP_AUTO_SUPPORTED) {
spectre &= ~STIBP_AUTO_SUPPORTED;
error = SYSCTL_OUT(req, "STIBP_AUTO", 10);
} else
if (spectre & IBPB_SUPPORTED) {
spectre &= ~IBPB_SUPPORTED;
error = SYSCTL_OUT(req, "IBPB", 4);
}
}
if (loop == 0) {
error = SYSCTL_OUT(req, "NONE", 4);
}
if (error || req->newptr == NULL)
return error;
if ((oidp->oid_kind & CTLFLAG_WR) == 0)
return error;
len = req->newlen - req->newidx;
if (len >= sizeof(buf)) {
error = EINVAL;
len = 0;
} else {
error = SYSCTL_IN(req, buf, len);
}
buf[len] = 0;
iter = &buf[0];
spectre = 0;
while (error == 0 && iter) {
ptr = strsep(&iter, " ,\t\r\n");
if (*ptr == 0)
continue;
if (strcasecmp(ptr, "NONE") == 0)
spectre |= 0;
else if (strcasecmp(ptr, "IBRS") == 0)
spectre |= IBRS_SUPPORTED;
else if (strcasecmp(ptr, "IBRS_AUTO") == 0)
spectre |= IBRS_AUTO_SUPPORTED;
else if (strcasecmp(ptr, "STIBP") == 0)
spectre |= STIBP_SUPPORTED;
else if (strcasecmp(ptr, "STIBP_AUTO") == 0)
spectre |= STIBP_AUTO_SUPPORTED;
else if (strcasecmp(ptr, "IBPB") == 0)
spectre |= IBPB_SUPPORTED;
else
error = ENOENT;
}
if (error == 0) {
spectre_mitigation = spectre;
spectre_sysctl_changed();
}
return error;
}
SYSCTL_PROC(_machdep, OID_AUTO, spectre_mitigation,
CTLTYPE_STRING | CTLFLAG_RW,
0, 0, sysctl_spectre_mitigation, "A", "Spectre exploit mitigation");
SYSCTL_PROC(_machdep, OID_AUTO, spectre_support,
CTLTYPE_STRING | CTLFLAG_RD,
0, 0, sysctl_spectre_mitigation, "A", "Spectre supported features");
static void
spectre_vm_setup(void *arg)
{
int inconsistent = 0;
int supmask;
if (spectre_mitigation < 0) {
TUNABLE_INT_FETCH("machdep.spectre_mitigation",
&spectre_mitigation);
}
if ((supmask = spectre_check_support()) != 0) {
if (arg != NULL) {
globaldata_t save_gd;
int n;
save_gd = mycpu;
for (n = 0; n < ncpus; ++n) {
lwkt_setcpu_self(globaldata_find(n));
cpu_ccfence();
if (spectre_check_support() !=
supmask) {
inconsistent = 1;
break;
}
}
lwkt_setcpu_self(save_gd);
cpu_ccfence();
}
}
if (inconsistent) {
spectre_mitigation = -1;
spectre_support = 0;
return;
}
spectre_support = supmask;
if (spectre_support) {
if (spectre_mitigation < 0) {
spectre_mitigation = 0;
if (spectre_support & IBRS_AUTO_SUPPORTED)
spectre_mitigation |= IBRS_AUTO_SUPPORTED;
else if (spectre_support & IBRS_SUPPORTED)
spectre_mitigation |= 0;
if (spectre_support & STIBP_AUTO_SUPPORTED)
spectre_mitigation |= STIBP_AUTO_SUPPORTED;
else if (spectre_support & STIBP_SUPPORTED)
spectre_mitigation |= 0;
if (spectre_support & IBPB_SUPPORTED)
spectre_mitigation |= 0;
}
} else {
spectre_mitigation = -1;
}
if (spectre_mitigation < 0)
sysctl___machdep_spectre_mitigation.oid_kind &= ~CTLFLAG_WR;
else
sysctl___machdep_spectre_mitigation.oid_kind |= CTLFLAG_WR;
spectre_sysctl_changed();
}
#define MDS_AVX512_4VNNIW_SUPPORTED 0x0001
#define MDS_AVX512_4FMAPS_SUPPORTED 0x0002
#define MDS_MD_CLEAR_SUPPORTED 0x0004
#define MDS_TSX_FORCE_ABORT_SUPPORTED 0x0008
#define MDS_NOT_REQUIRED 0x8000
static
int
mds_check_support(void)
{
uint64_t msr;
uint32_t p[4];
int rv = 0;
if (cpu_vendor_id == CPU_VENDOR_INTEL) {
p[0] = 0;
p[1] = 0;
p[2] = 0;
p[3] = 0;
cpuid_count(7, 0, p);
if (p[3] & CPUID_STDEXT3_ARCH_CAP) {
msr = 0;
if (rdmsr_safe(MSR_IA32_ARCH_CAPABILITIES, &msr)) {
kprintf("Warning: MSR_IA32_ARCH_CAPABILITIES "
"cannot be accessed\n");
}
if (msr & IA32_ARCH_CAP_MDS_NO)
rv = MDS_NOT_REQUIRED;
}
if (p[3] & CPUID_STDEXT3_AVX5124VNNIW)
rv |= MDS_AVX512_4VNNIW_SUPPORTED;
if (p[3] & CPUID_STDEXT3_AVX5124FMAPS)
rv |= MDS_AVX512_4FMAPS_SUPPORTED;
if (p[3] & CPUID_STDEXT3_MD_CLEAR)
rv |= MDS_MD_CLEAR_SUPPORTED;
if (p[3] & CPUID_STDEXT3_TSXFA)
rv |= MDS_TSX_FORCE_ABORT_SUPPORTED;
} else {
rv = MDS_NOT_REQUIRED;
}
return rv;
}
#define CHECK(flag) (mds_mitigation & mds_support & (flag))
static
void
mds_sysctl_changed(void)
{
globaldata_t save_gd;
struct trampframe *tr;
int spec_ctrl;
int spec_mask;
int mode;
int n;
spec_mask = SPEC_CTRL_MDS_ENABLE;
mode = 0;
save_gd = mycpu;
for (n = 0; n < ncpus; ++n) {
lwkt_setcpu_self(globaldata_find(n));
cpu_ccfence();
tr = &pscpu->trampoline;
tr->tr_pcb_spec_ctrl[0] &= ~spec_mask;
tr->tr_pcb_spec_ctrl[1] &= ~spec_mask;
if (mds_mitigation < 0)
continue;
spec_ctrl = 0;
if (CHECK(MDS_MD_CLEAR_SUPPORTED)) {
spec_ctrl |= SPEC_CTRL_MDS_ENABLE;
mode |= MDS_MD_CLEAR_SUPPORTED;
}
tr->tr_pcb_spec_ctrl[1] |= spec_ctrl;
mds_mode = mode;
}
lwkt_setcpu_self(save_gd);
cpu_ccfence();
kprintf("MDS: support=(");
if (mds_support == 0) {
kprintf(" none");
} else {
if (mds_support & MDS_AVX512_4VNNIW_SUPPORTED)
kprintf(" AVX512_4VNNIW");
if (mds_support & MDS_AVX512_4FMAPS_SUPPORTED)
kprintf(" AVX512_4FMAPS");
if (mds_support & MDS_MD_CLEAR_SUPPORTED)
kprintf(" MD_CLEAR");
if (mds_support & MDS_TSX_FORCE_ABORT_SUPPORTED)
kprintf(" TSX_FORCE_ABORT");
if (mds_support & MDS_NOT_REQUIRED)
kprintf(" MDS_NOT_REQUIRED");
}
kprintf(" ) req=%04x operating=(", (uint16_t)mds_mitigation);
if (mds_mode == 0) {
kprintf(" none");
} else {
if (mds_mode & MDS_AVX512_4VNNIW_SUPPORTED)
kprintf(" AVX512_4VNNIW");
if (mds_mode & MDS_AVX512_4FMAPS_SUPPORTED)
kprintf(" AVX512_4FMAPS");
if (mds_mode & MDS_MD_CLEAR_SUPPORTED)
kprintf(" MD_CLEAR");
if (mds_mode & MDS_TSX_FORCE_ABORT_SUPPORTED)
kprintf(" TSX_FORCE_ABORT");
if (mds_mode & MDS_NOT_REQUIRED)
kprintf(" MDS_NOT_REQUIRED");
}
kprintf(" )\n");
}
#undef CHECK
static int
sysctl_mds_mitigation(SYSCTL_HANDLER_ARGS)
{
char buf[128];
char *ptr;
char *iter;
size_t len;
int mds;
int error = 0;
int loop = 0;
if (oidp->oid_kind & CTLFLAG_WR)
mds = mds_mode;
else
mds = mds_support;
mds &= MDS_AVX512_4VNNIW_SUPPORTED |
MDS_AVX512_4FMAPS_SUPPORTED |
MDS_MD_CLEAR_SUPPORTED |
MDS_TSX_FORCE_ABORT_SUPPORTED |
MDS_NOT_REQUIRED;
while (mds) {
if (error)
break;
if (loop++) {
error = SYSCTL_OUT(req, " ", 1);
if (error)
break;
}
if (mds & MDS_AVX512_4VNNIW_SUPPORTED) {
mds &= ~MDS_AVX512_4VNNIW_SUPPORTED;
error = SYSCTL_OUT(req, "AVX512_4VNNIW", 13);
} else
if (mds & MDS_AVX512_4FMAPS_SUPPORTED) {
mds &= ~MDS_AVX512_4FMAPS_SUPPORTED;
error = SYSCTL_OUT(req, "AVX512_4FMAPS", 13);
} else
if (mds & MDS_MD_CLEAR_SUPPORTED) {
mds &= ~MDS_MD_CLEAR_SUPPORTED;
error = SYSCTL_OUT(req, "MD_CLEAR", 8);
} else
if (mds & MDS_TSX_FORCE_ABORT_SUPPORTED) {
mds &= ~MDS_TSX_FORCE_ABORT_SUPPORTED;
error = SYSCTL_OUT(req, "TSX_FORCE_ABORT", 15);
} else
if (mds & MDS_NOT_REQUIRED) {
mds &= ~MDS_NOT_REQUIRED;
error = SYSCTL_OUT(req, "MDS_NOT_REQUIRED", 16);
}
}
if (loop == 0) {
error = SYSCTL_OUT(req, "NONE", 4);
}
if (error || req->newptr == NULL)
return error;
if ((oidp->oid_kind & CTLFLAG_WR) == 0)
return error;
len = req->newlen - req->newidx;
if (len >= sizeof(buf)) {
error = EINVAL;
len = 0;
} else {
error = SYSCTL_IN(req, buf, len);
}
buf[len] = 0;
iter = &buf[0];
mds = 0;
while (error == 0 && iter) {
ptr = strsep(&iter, " ,\t\r\n");
if (*ptr == 0)
continue;
if (strcasecmp(ptr, "NONE") == 0)
mds |= 0;
else if (strcasecmp(ptr, "AVX512_4VNNIW") == 0)
mds |= MDS_AVX512_4VNNIW_SUPPORTED;
else if (strcasecmp(ptr, "AVX512_4FMAPS") == 0)
mds |= MDS_AVX512_4FMAPS_SUPPORTED;
else if (strcasecmp(ptr, "MD_CLEAR") == 0)
mds |= MDS_MD_CLEAR_SUPPORTED;
else if (strcasecmp(ptr, "TSX_FORCE_ABORT") == 0)
mds |= MDS_TSX_FORCE_ABORT_SUPPORTED;
else if (strcasecmp(ptr, "MDS_NOT_REQUIRED") == 0)
mds |= MDS_NOT_REQUIRED;
else
error = ENOENT;
}
if (error == 0) {
mds_mitigation = mds;
mds_sysctl_changed();
}
return error;
}
SYSCTL_PROC(_machdep, OID_AUTO, mds_mitigation,
CTLTYPE_STRING | CTLFLAG_RW,
0, 0, sysctl_mds_mitigation, "A", "MDS exploit mitigation");
SYSCTL_PROC(_machdep, OID_AUTO, mds_support,
CTLTYPE_STRING | CTLFLAG_RD,
0, 0, sysctl_mds_mitigation, "A", "MDS supported features");
static void
mds_vm_setup(void *arg)
{
int inconsistent = 0;
int supmask;
if (mds_mitigation < 0) {
TUNABLE_INT_FETCH("machdep.mds_mitigation", &mds_mitigation);
}
if ((supmask = mds_check_support()) != 0) {
if (arg != NULL) {
globaldata_t save_gd;
int n;
save_gd = mycpu;
for (n = 0; n < ncpus; ++n) {
lwkt_setcpu_self(globaldata_find(n));
cpu_ccfence();
if (mds_check_support() != supmask) {
inconsistent = 1;
break;
}
}
lwkt_setcpu_self(save_gd);
cpu_ccfence();
}
}
if (inconsistent) {
mds_mitigation = -1;
mds_support = 0;
return;
}
mds_support = supmask;
if (mds_support) {
if (mds_mitigation < 0) {
mds_mitigation = 0;
if ((mds_support & MDS_NOT_REQUIRED) == 0 &&
(mds_support & MDS_MD_CLEAR_SUPPORTED)) {
}
}
} else {
mds_mitigation = -1;
}
if (mds_mitigation < 0)
sysctl___machdep_mds_mitigation.oid_kind &= ~CTLFLAG_WR;
else
sysctl___machdep_mds_mitigation.oid_kind |= CTLFLAG_WR;
mds_sysctl_changed();
}
void
mitigation_vm_setup(void *arg)
{
spectre_vm_setup(arg);
mds_vm_setup(arg);
}
SYSINIT(mitigation_vm_setup, SI_BOOT2_MACHDEP, SI_ORDER_ANY,
mitigation_vm_setup, NULL);
void
cpu_vmspace_alloc(struct vmspace *vm __unused)
{
}
void
cpu_vmspace_free(struct vmspace *vm __unused)
{
}
int
kvm_access_check(vm_offset_t saddr, vm_offset_t eaddr, int prot)
{
vm_offset_t addr;
if (saddr < KvaStart)
return EFAULT;
if (eaddr >= KvaEnd)
return EFAULT;
for (addr = saddr; addr < eaddr; addr += PAGE_SIZE) {
if (pmap_kextract(addr) == 0)
return EFAULT;
}
if (!kernacc((caddr_t)saddr, eaddr - saddr, prot))
return EFAULT;
return 0;
}
#if 0
void _test_frame_enter(struct trapframe *frame);
void _test_frame_exit(struct trapframe *frame);
void
_test_frame_enter(struct trapframe *frame)
{
thread_t td = curthread;
if (ISPL(frame->tf_cs) == SEL_UPL) {
KKASSERT(td->td_lwp);
KASSERT(td->td_lwp->lwp_md.md_regs == frame,
("_test_frame_exit: Frame mismatch %p %p",
td->td_lwp->lwp_md.md_regs, frame));
td->td_lwp->lwp_saveusp = (void *)frame->tf_rsp;
td->td_lwp->lwp_saveupc = (void *)frame->tf_rip;
}
if ((char *)frame < td->td_kstack ||
(char *)frame > td->td_kstack + td->td_kstack_size) {
panic("_test_frame_exit: frame not on kstack %p kstack=%p",
frame, td->td_kstack);
}
}
void
_test_frame_exit(struct trapframe *frame)
{
thread_t td = curthread;
if (ISPL(frame->tf_cs) == SEL_UPL) {
KKASSERT(td->td_lwp);
KASSERT(td->td_lwp->lwp_md.md_regs == frame,
("_test_frame_exit: Frame mismatch %p %p",
td->td_lwp->lwp_md.md_regs, frame));
if (td->td_lwp->lwp_saveusp != (void *)frame->tf_rsp) {
kprintf("_test_frame_exit: %s:%d usp mismatch %p/%p\n",
td->td_comm, td->td_proc->p_pid,
td->td_lwp->lwp_saveusp,
(void *)frame->tf_rsp);
}
if (td->td_lwp->lwp_saveupc != (void *)frame->tf_rip) {
kprintf("_test_frame_exit: %s:%d upc mismatch %p/%p\n",
td->td_comm, td->td_proc->p_pid,
td->td_lwp->lwp_saveupc,
(void *)frame->tf_rip);
}
td->td_lwp->lwp_saveusp =
(void *)~(intptr_t)td->td_lwp->lwp_saveusp;
td->td_lwp->lwp_saveupc =
(void *)~(intptr_t)td->td_lwp->lwp_saveupc;
}
if ((char *)frame < td->td_kstack ||
(char *)frame > td->td_kstack + td->td_kstack_size) {
panic("_test_frame_exit: frame not on kstack %p kstack=%p",
frame, td->td_kstack);
}
}
#endif