diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/compat/freebsd/sys/smp.h | 5 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/vmm/intel/ept.c | 11 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/vmm/vmm_sol_glue.c | 13 | ||||
-rw-r--r-- | usr/src/uts/i86pc/ml/hma_asm.s | 32 | ||||
-rw-r--r-- | usr/src/uts/i86pc/os/hma.c | 181 | ||||
-rw-r--r-- | usr/src/uts/i86pc/sys/hma.h | 7 | ||||
-rw-r--r-- | usr/src/uts/intel/sys/x86_archext.h | 19 |
7 files changed, 205 insertions, 63 deletions
diff --git a/usr/src/compat/freebsd/sys/smp.h b/usr/src/compat/freebsd/sys/smp.h index 8a91c6c8d7..3d6413ce16 100644 --- a/usr/src/compat/freebsd/sys/smp.h +++ b/usr/src/compat/freebsd/sys/smp.h @@ -19,11 +19,6 @@ #include <sys/cpuset.h> -void smp_rendezvous(void (*)(void *), - void (*)(void *), - void (*)(void *), - void *arg); - #define IPI_AST 0 void ipi_cpu(int cpu, u_int ipi); diff --git a/usr/src/uts/i86pc/io/vmm/intel/ept.c b/usr/src/uts/i86pc/io/vmm/intel/ept.c index a9838793e5..4915537b0a 100644 --- a/usr/src/uts/i86pc/io/vmm/intel/ept.c +++ b/usr/src/uts/i86pc/io/vmm/intel/ept.c @@ -49,6 +49,9 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/smp.h> #include <sys/sysctl.h> +#ifndef __FreeBSD__ +#include <sys/hma.h> +#endif #include <vm/vm.h> #include <vm/pmap.h> @@ -167,6 +170,7 @@ ept_dump(uint64_t *ptp, int nlevels) } #endif +#ifdef __FreeBSD__ static void invept_single_context(void *arg) { @@ -184,6 +188,13 @@ ept_invalidate_mappings(u_long eptp) smp_rendezvous(NULL, invept_single_context, NULL, &invept_desc); } +#else /* __FreeBSD__ */ +void +ept_invalidate_mappings(u_long eptp) +{ + hma_vmx_invept_allcpus((uintptr_t)eptp); +} +#endif /* __FreeBSD__ */ static int ept_pinit(pmap_t pmap) diff --git a/usr/src/uts/i86pc/io/vmm/vmm_sol_glue.c b/usr/src/uts/i86pc/io/vmm/vmm_sol_glue.c index e42e0c4025..d03955ec06 100644 --- a/usr/src/uts/i86pc/io/vmm/vmm_sol_glue.c +++ b/usr/src/uts/i86pc/io/vmm/vmm_sol_glue.c @@ -142,19 +142,6 @@ cpusetobj_ffs(const cpuset_t *set) #endif } -void -smp_rendezvous(void (* setup_func)(void *), void (* action_func)(void *), - void (* teardown_func)(void *), void *arg) -{ - cpuset_t cpuset; - - ASSERT(setup_func == NULL); - ASSERT(teardown_func == NULL); - - CPUSET_ALL(cpuset); - xc_sync((xc_arg_t)arg, 0, 0, CPUSET2BV(cpuset), (xc_func_t)action_func); -} - struct kmem_item { void *addr; size_t size; diff --git a/usr/src/uts/i86pc/ml/hma_asm.s b/usr/src/uts/i86pc/ml/hma_asm.s index 3b5995bb1a..49afbdd240 100644 --- a/usr/src/uts/i86pc/ml/hma_asm.s +++ b/usr/src/uts/i86pc/ml/hma_asm.s @@ -10,21 +10,12 @@ */ /* - * Copyright 2018 Joyent, Inc. + * Copyright 2019 Joyent, Inc. */ #include <sys/asm_linkage.h> -#if defined(__lint) - -int -hma_vmx_vmxon(uintptr_t arg) -{ - return (0); -} - -#else /* __lint */ ENTRY_NP(hma_vmx_vmxon) push %rbp movq %rsp, %rbp @@ -39,4 +30,23 @@ hma_vmx_vmxon(uintptr_t arg) leave ret SET_SIZE(hma_vmx_vmxon) -#endif /* __lint */ + + ENTRY_NP(hma_vmx_do_invept) + push %rbp + movq %rsp, %rbp + pushq %rdi + pushq %rsi + + /* build INVEPT descriptor on stack */ + xorl %eax, %eax + pushq %rax; + pushq %rsi + + invept (%rsp), %rdi + ja 1f /* CF=0, ZF=0 (success) */ + incl %eax +1: + + leave + ret + SET_SIZE(hma_vmx_do_invept) diff --git a/usr/src/uts/i86pc/os/hma.c b/usr/src/uts/i86pc/os/hma.c index c7c0984db7..a41ff3e0d1 100644 --- a/usr/src/uts/i86pc/os/hma.c +++ b/usr/src/uts/i86pc/os/hma.c @@ -10,7 +10,7 @@ */ /* - * Copyright 2018 Joyent, Inc. + * Copyright 2019 Joyent, Inc. */ #include <sys/cpuvar.h> @@ -51,9 +51,18 @@ typedef enum hma_cpu_state { } hma_cpu_state_t; static hma_cpu_state_t hma_cpu_status[NCPU]; +/* HMA-internal tracking of optional VMX capabilities */ +typedef enum { + HVC_EPT = (1 << 0), + HVC_VPID = (1 << 1), + HVC_INVEPT_ONE = (1 << 2), + HVC_INVEPT_ALL = (1 << 3), +} hma_vmx_capab_t; + static void *hma_vmx_vmxon_page[NCPU]; static uintptr_t hma_vmx_vmxon_pa[NCPU]; static uint32_t hma_vmx_revision; +static hma_vmx_capab_t hma_vmx_capabs = 0; static boolean_t hma_svm_ready = B_FALSE; static const char *hma_svm_error = NULL; @@ -69,6 +78,10 @@ static hma_svm_asid_t hma_svm_cpu_asid[NCPU]; static int hma_vmx_init(void); static int hma_svm_init(void); +/* Helpers from ml/hma_asm.s */ +int hma_vmx_do_invept(int, uintptr_t); +int hma_vmx_vmxon(uintptr_t); + void hma_init(void) { @@ -181,6 +194,11 @@ hma_vmx_vpid_alloc(void) { id_t res; + /* Do not bother if the CPU lacks support */ + if ((hma_vmx_capabs & HVC_VPID) == 0) { + return (0); + } + res = id_alloc_nosleep(hma_vmx_vpid); if (res == -1) { return (0); @@ -197,8 +215,45 @@ hma_vmx_vpid_free(uint16_t vpid) id_free(hma_vmx_vpid, (id_t)vpid); } +#define INVEPT_SINGLE_CONTEXT 1 +#define INVEPT_ALL_CONTEXTS 2 + +static int +hma_vmx_invept_xcall(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3 __unused) +{ + int flag = (int)arg1; + uintptr_t eptp = (uintptr_t)arg2; + + ASSERT(flag == INVEPT_SINGLE_CONTEXT || flag == INVEPT_ALL_CONTEXTS); + + VERIFY0(hma_vmx_do_invept(flag, eptp)); + return (0); +} + +void +hma_vmx_invept_allcpus(uintptr_t eptp) +{ + int flag = -1; + cpuset_t set; + + if ((hma_vmx_capabs & HVC_INVEPT_ONE) != 0) { + flag = INVEPT_SINGLE_CONTEXT; + } else if ((hma_vmx_capabs & HVC_INVEPT_ALL) != 0) { + flag = INVEPT_ALL_CONTEXTS; + eptp = 0; + } else { + return; + } + + cpuset_zero(&set); + mutex_enter(&cpu_lock); + + cpuset_or(&set, &cpu_active_set); + xc_call((xc_arg_t)flag, (xc_arg_t)eptp, 0, CPUSET2BV(set), + hma_vmx_invept_xcall); -extern int hma_vmx_vmxon(uintptr_t); + mutex_exit(&cpu_lock); +} static int hma_vmx_cpu_vmxon(xc_arg_t arg1 __unused, xc_arg_t arg2 __unused, @@ -245,18 +300,16 @@ hma_vmx_cpu_vmxon(xc_arg_t arg1 __unused, xc_arg_t arg2 __unused, static int hma_vmx_cpu_setup(cpu_setup_t what, int id, void *arg __unused) { + hma_cpu_state_t state; + ASSERT(MUTEX_HELD(&cpu_lock)); ASSERT(id >= 0 && id < NCPU); - switch (what) { - case CPU_CONFIG: - case CPU_ON: - case CPU_INIT: - break; - default: + if (what != CPU_ON) { /* - * Other events, such as CPU offlining, are of no interest. - * Letting the VMX state linger should not cause any harm. + * For the purposes of VMX setup, only the CPU_ON event is of + * interest. Letting VMX state linger on an offline CPU should + * not cause any harm. * * This logic assumes that any offlining activity is strictly * administrative in nature and will not alter any existing @@ -265,12 +318,12 @@ hma_vmx_cpu_setup(cpu_setup_t what, int id, void *arg __unused) return (0); } - /* Perform initialization if it has not been previously attempted. */ - if (hma_cpu_status[id] != HCS_UNINITIALIZED) { - return ((hma_cpu_status[id] == HCS_READY) ? 0 : -1); + state = hma_cpu_status[id]; + if (state == HCS_ERROR) { + return (-1); } - /* Allocate the VMXON page for this CPU */ + /* Allocate the VMXON page for this CPU, if not already done */ if (hma_vmx_vmxon_page[id] == NULL) { caddr_t va; pfn_t pfn; @@ -293,24 +346,96 @@ hma_vmx_cpu_setup(cpu_setup_t what, int id, void *arg __unused) VERIFY(hma_vmx_vmxon_pa[id] != 0); } - kpreempt_disable(); - if (CPU->cpu_seqid == id) { - /* Perform vmxon setup directly if this CPU is the target */ - (void) hma_vmx_cpu_vmxon(0, 0, 0); - kpreempt_enable(); - } else { + if (state == HCS_UNINITIALIZED) { cpuset_t set; - /* Use a cross-call if a remote CPU is the target */ - kpreempt_enable(); + /* Activate VMX on this CPU */ cpuset_zero(&set); cpuset_add(&set, id); - xc_sync(0, 0, 0, CPUSET2BV(set), hma_vmx_cpu_vmxon); + xc_call(0, 0, 0, CPUSET2BV(set), hma_vmx_cpu_vmxon); + } else { + VERIFY3U(state, ==, HCS_READY); + + /* + * If an already-initialized CPU is going back online, perform + * an all-contexts invept to eliminate the possibility of + * cached EPT state causing issues. + */ + if ((hma_vmx_capabs & HVC_INVEPT_ALL) != 0) { + cpuset_t set; + + cpuset_zero(&set); + cpuset_add(&set, id); + xc_call((xc_arg_t)INVEPT_ALL_CONTEXTS, 0, 0, + CPUSET2BV(set), hma_vmx_invept_xcall); + } } return (hma_cpu_status[id] != HCS_READY); } +/* + * Determining the availability of VM execution controls is somewhat different + * from conventional means, where one simply checks for asserted bits in the + * MSR value. Instead, these execution control MSRs are split into two halves: + * the lower 32-bits indicating capabilities which can be zeroed in the VMCS + * field and the upper 32-bits indicating capabilities which can be set to one. + * + * It is described in detail in Appendix A.3 of SDM volume 3. + */ +#define VMX_CTL_ONE_SETTING(val, flag) \ + (((val) & ((uint64_t)(flag) << 32)) != 0) + +static const char * +hma_vmx_query_details(void) +{ + boolean_t query_true_ctl = B_FALSE; + uint64_t msr; + + /* The basic INS/OUTS functionality is cited as a necessary prereq */ + msr = rdmsr(MSR_IA32_VMX_BASIC); + if ((msr & IA32_VMX_BASIC_INS_OUTS) == 0) { + return ("VMX does not support INS/OUTS"); + } + + /* Record the VMX revision for later VMXON usage */ + hma_vmx_revision = (uint32_t)msr; + + /* + * Bit 55 in the VMX_BASIC MSR determines how VMX control information + * can be queried. + */ + query_true_ctl = (msr & IA32_VMX_BASIC_TRUE_CTRLS) != 0; + + /* Check for EPT and VPID support */ + msr = rdmsr(query_true_ctl ? + MSR_IA32_VMX_TRUE_PROCBASED_CTLS : MSR_IA32_VMX_PROCBASED_CTLS); + if (VMX_CTL_ONE_SETTING(msr, IA32_VMX_PROCBASED_2ND_CTLS)) { + msr = rdmsr(MSR_IA32_VMX_PROCBASED2_CTLS); + if (VMX_CTL_ONE_SETTING(msr, IA32_VMX_PROCBASED2_EPT)) { + hma_vmx_capabs |= HVC_EPT; + } + if (VMX_CTL_ONE_SETTING(msr, IA32_VMX_PROCBASED2_VPID)) { + hma_vmx_capabs |= HVC_VPID; + } + } + + /* Check for INVEPT support */ + if ((hma_vmx_capabs & HVC_EPT) != 0) { + msr = rdmsr(MSR_IA32_VMX_EPT_VPID_CAP); + if ((msr & IA32_VMX_EPT_VPID_INVEPT) != 0) { + if ((msr & IA32_VMX_EPT_VPID_INVEPT_SINGLE) != 0) { + hma_vmx_capabs |= HVC_INVEPT_ONE; + } + if ((msr & IA32_VMX_EPT_VPID_INVEPT_ALL) != 0) { + hma_vmx_capabs |= HVC_INVEPT_ALL; + } + } + } + + return (NULL); +} + static int hma_vmx_init(void) { @@ -332,14 +457,10 @@ hma_vmx_init(void) goto bail; } - /* Does VMX support basic INS/OUTS functionality */ - msr = rdmsr(MSR_IA32_VMX_BASIC); - if ((msr & IA32_VMX_BASIC_INS_OUTS) == 0) { - msg = "VMX does not support INS/OUTS"; + msg = hma_vmx_query_details(); + if (msg != NULL) { goto bail; } - /* Record the VMX revision for later VMXON usage */ - hma_vmx_revision = (uint32_t)msr; mutex_enter(&cpu_lock); /* Perform VMX configuration for already-online CPUs. */ @@ -518,7 +639,7 @@ hma_svm_cpu_setup(cpu_setup_t what, int id, void *arg __unused) kpreempt_enable(); cpuset_zero(&set); cpuset_add(&set, id); - xc_sync(0, 0, 0, CPUSET2BV(set), hma_svm_cpu_activate); + xc_call(0, 0, 0, CPUSET2BV(set), hma_svm_cpu_activate); } return (hma_cpu_status[id] != HCS_READY); diff --git a/usr/src/uts/i86pc/sys/hma.h b/usr/src/uts/i86pc/sys/hma.h index 0f4beb0452..16ab708896 100644 --- a/usr/src/uts/i86pc/sys/hma.h +++ b/usr/src/uts/i86pc/sys/hma.h @@ -10,7 +10,7 @@ */ /* - * Copyright (c) 2018, Joyent, Inc. + * Copyright 2019 Joyent, Inc. */ #ifndef _SYS_HMA_H @@ -50,6 +50,11 @@ extern void hma_unregister(hma_reg_t *); extern uint16_t hma_vmx_vpid_alloc(void); extern void hma_vmx_vpid_free(uint16_t); +/* + * On all active CPUs, perform a single-context INVEPT on the given EPTP. + */ +extern void hma_vmx_invept_allcpus(uintptr_t); + struct hma_svm_asid { uint64_t hsa_gen; uint32_t hsa_asid; diff --git a/usr/src/uts/intel/sys/x86_archext.h b/usr/src/uts/intel/sys/x86_archext.h index 59a974dfd2..0b94d9f986 100644 --- a/usr/src/uts/intel/sys/x86_archext.h +++ b/usr/src/uts/intel/sys/x86_archext.h @@ -460,9 +460,22 @@ extern "C" { #define IA32_FEAT_CTRL_SMX_EN 0x2 #define IA32_FEAT_CTRL_VMX_EN 0x4 -#define MSR_IA32_VMX_BASIC 0x480 -#define IA32_VMX_BASIC_INS_OUTS (1UL << 54) - +#define MSR_IA32_VMX_BASIC 0x480 +#define IA32_VMX_BASIC_INS_OUTS (1UL << 54) +#define IA32_VMX_BASIC_TRUE_CTRLS (1UL << 55) + +#define MSR_IA32_VMX_PROCBASED_CTLS 0x482 +#define MSR_IA32_VMX_TRUE_PROCBASED_CTLS 0x48e +#define IA32_VMX_PROCBASED_2ND_CTLS (1UL << 31) + +#define MSR_IA32_VMX_PROCBASED2_CTLS 0x48b +#define IA32_VMX_PROCBASED2_EPT (1UL << 1) +#define IA32_VMX_PROCBASED2_VPID (1UL << 5) + +#define MSR_IA32_VMX_EPT_VPID_CAP 0x48c +#define IA32_VMX_EPT_VPID_INVEPT (1UL << 20) +#define IA32_VMX_EPT_VPID_INVEPT_SINGLE (1UL << 25) +#define IA32_VMX_EPT_VPID_INVEPT_ALL (1UL << 26) #define MCI_CTL_VALUE 0xffffffff |