#define X86_OP_MOV_TO_MEM 0x89
#define X86_OP_MOV_FROM_MEM 0x8b
+#define DB_VECTOR 1
#define NMI_VECTOR 2
#define PF_VECTOR 14
+#define AC_VECTOR 17
#define DESC_TSS_BUSY (1UL << (9 + 32))
#define DESC_PRESENT (1UL << (15 + 32))
#define NPT_PAGE_DIR_LEVELS 4
+#define SVM_EVENTINJ_EXCEPTION (3UL << 8)
+#define SVM_EVENTINJ_ERR_VALID (1UL << 11)
+#define SVM_EVENTINJ_VALID (1UL << 31)
+
struct svm_segment {
u16 selector;
u16 access_rights;
};
typedef u64 vintr_t;
-typedef u64 eventinj_t;
typedef u64 lbrctrl_t;
struct vmcb {
u64 exitcode; /* offset 0x70 */
u64 exitinfo1; /* offset 0x78 */
u64 exitinfo2; /* offset 0x80 */
- eventinj_t exitintinfo; /* offset 0x88 */
+ u64 exitintinfo; /* offset 0x88 */
u64 np_enable; /* offset 0x90 */
u64 res08[2];
- eventinj_t eventinj; /* offset 0xA8 */
+ u32 eventinj; /* offset 0xA8 */
+ u32 eventinj_err; /* offset 0xAC */
u64 n_cr3; /* offset 0xB0 */
lbrctrl_t lbr_control; /* offset 0xB8 */
u64 clean_bits; /* offset 0xC0 */
#define VMX_MISC_ACTIVITY_HLT (1UL << 6)
+#define INTR_INFO_INTR_TYPE_MASK BIT_MASK(10, 8)
#define INTR_INFO_UNBLOCK_NMI (1UL << 12)
+#define INTR_TYPE_NMI_INTR (2UL << 8)
+
+#define INTR_TO_VECTORING_INFO_MASK ((1UL << 31) | BIT_MASK(11, 0))
+
#define EXIT_REASONS_FAILED_VMENTRY (1UL << 31)
#define EXIT_REASON_EXCEPTION_NMI 0
vmcb->general2_intercepts |= GENERAL2_INTERCEPT_VMRUN; /* Required */
vmcb->general2_intercepts |= GENERAL2_INTERCEPT_VMMCALL;
+ /*
+ * We only intercept #DB and #AC to prevent that malicious guests can
+ * trigger infinite loops in microcode (see e.g. CVE-2015-5307 and
+ * CVE-2015-8104).
+ */
+ vmcb->exception_intercepts |= (1 << DB_VECTOR) | (1 << AC_VECTOR);
+
vmcb->msrpm_base_pa = paging_hvirt2phys(msrpm);
vmcb->np_enable = 1;
vmcb->dr7 = 0x00000400;
+ vmcb->eventinj = 0;
+
/* Almost all of the guest state changed */
vmcb->clean_bits = 0;
if (vcpu_handle_io_access())
goto vmentry;
break;
+ case VMEXIT_EXCEPTION_DB:
+ case VMEXIT_EXCEPTION_AC:
+ cpu_data->stats[JAILHOUSE_CPU_STAT_VMEXITS_EXCEPTION]++;
+ /* Reinject exception, including error code if needed. */
+ vmcb->eventinj = (vmcb->exitcode - VMEXIT_EXCEPTION_DE) |
+ SVM_EVENTINJ_EXCEPTION | SVM_EVENTINJ_VALID;
+ if (vmcb->exitcode == VMEXIT_EXCEPTION_AC) {
+ vmcb->eventinj |= SVM_EVENTINJ_ERR_VALID;
+ vmcb->eventinj_err = vmcb->exitinfo1;
+ }
+ x86_check_events();
+ goto vmentry;
/* TODO: Handle VMEXIT_AVIC_NOACCEL and VMEXIT_AVIC_INCOMPLETE_IPI */
default:
panic_printk("FATAL: Unexpected #VMEXIT, exitcode %x, "
ok &= vmx_set_cell_config();
- ok &= vmcs_write32(EXCEPTION_BITMAP, 0);
+ /* see vmx_handle_exception_nmi for the interception reason */
+ ok &= vmcs_write32(EXCEPTION_BITMAP,
+ (1 << DB_VECTOR) | (1 << AC_VECTOR));
val = read_msr(MSR_IA32_VMX_EXIT_CTLS);
val |= VM_EXIT_HOST_ADDR_SPACE_SIZE |
ok &= vmcs_write32(GUEST_ACTIVITY_STATE, GUEST_ACTIVITY_ACTIVE);
ok &= vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, 0);
ok &= vmcs_write64(GUEST_PENDING_DBG_EXCEPTIONS, 0);
+ ok &= vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 0);
val = vmcs_read32(VM_ENTRY_CONTROLS);
val &= ~VM_ENTRY_IA32E_MODE;
x86_check_events();
}
+static void vmx_handle_exception_nmi(void)
+{
+ u32 intr_info = vmcs_read32(VM_EXIT_INTR_INFO);
+
+ if ((intr_info & INTR_INFO_INTR_TYPE_MASK) == INTR_TYPE_NMI_INTR) {
+ this_cpu_data()->stats[JAILHOUSE_CPU_STAT_VMEXITS_MANAGEMENT]++;
+ asm volatile("int %0" : : "i" (NMI_VECTOR));
+ } else {
+ this_cpu_data()->stats[JAILHOUSE_CPU_STAT_VMEXITS_EXCEPTION]++;
+ /*
+ * Reinject the event straight away. We only intercept #DB and
+ * #AC to prevent that malicious guests can trigger infinite
+ * loops in microcode (see e.g. CVE-2015-5307 and
+ * CVE-2015-8104).
+ */
+ vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
+ intr_info & INTR_TO_VECTORING_INFO_MASK);
+ vmcs_write32(VM_ENTRY_EXCEPTION_ERROR_CODE,
+ vmcs_read32(VM_EXIT_INTR_ERROR_CODE));
+ }
+
+ /*
+ * Check for events even in the exception case in order to maintain
+ * control over the guest if it triggered #DB or #AC loops.
+ */
+ vmx_check_events();
+}
+
static void update_efer(void)
{
unsigned long efer = vmcs_read64(GUEST_IA32_EFER);
switch (reason) {
case EXIT_REASON_EXCEPTION_NMI:
- asm volatile("int %0" : : "i" (NMI_VECTOR));
- /* fall through */
+ vmx_handle_exception_nmi();
+ return;
case EXIT_REASON_PREEMPTION_TIMER:
cpu_data->stats[JAILHOUSE_CPU_STAT_VMEXITS_MANAGEMENT]++;
vmx_check_events();