From patchwork Fri Mar 18 22:39:12 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Feiner X-Patchwork-Id: 8624141 Return-Path: X-Original-To: patchwork-kvm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 82AEAC0553 for ; Fri, 18 Mar 2016 22:39:30 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 51DB720390 for ; Fri, 18 Mar 2016 22:39:29 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 0D98B20392 for ; Fri, 18 Mar 2016 22:39:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753033AbcCRWjY (ORCPT ); Fri, 18 Mar 2016 18:39:24 -0400 Received: from mail-pf0-f180.google.com ([209.85.192.180]:35228 "EHLO mail-pf0-f180.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753795AbcCRWjU (ORCPT ); Fri, 18 Mar 2016 18:39:20 -0400 Received: by mail-pf0-f180.google.com with SMTP id n5so184959174pfn.2 for ; Fri, 18 Mar 2016 15:39:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references :in-reply-to:references; bh=9VxFBoAqIr+YFLdxmTQLMm/RSF62vyuHKVjUT7vx65U=; b=hK/I6bJBWHfLny5QjPH3EOqa0MVRv3noPC/A+UzKKtX0pe5xwQbnY5Bo18DDEbcfqx worjLt9s2SBhHw2Yw7nEf5arz0SvnAP5tDx3c3wkeFrEeBOKBeHhgM01cXdenmh2dfN1 z8QbdizsWtL7kYmYvv09uWLrtCIP0wtW5RIzqRynm3V/rHVhAi2ErCCF7s5x1c1kHvRC WHiCBpQqwU5eisSOR3CQXY3h8XLoLOH9GoNe7ZMdCHQdY3/00NH5xtmgLJR1tOuMGfJv xao76Qf4yXHJkobdG+r2qq6oIOCEo2wAcoSdmQ89M1BQf7kwZuaAlGH5kpRHvU0ZribK LOZQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:in-reply-to:references; bh=9VxFBoAqIr+YFLdxmTQLMm/RSF62vyuHKVjUT7vx65U=; b=RtJTdxht8DJ4pRDmCh8N2f6VdyQLo7vC4XCUokd6OVbd/1sSJYd+j9S+kjSxWvNyPj 7cFwQsif5T7rJsRMRmNzayNDm/6HsNjIRsfMT3TGGutaSlXaU1Lr8TfrcvLEpehBwCQP hEya0Q1+I18IVg2y67IXqaRrrD53GZCJQqN5JQfpnTW6rOikV8+2oalqcefF+MfAiuYO o3TqdglfIHy+oKkVNnIMHiYRrmxjAFqwcJ/6SXgc9NKzT7B75mxOA2hBrRVRuX4jiI0x wSpb/Fw2hJVwmjWjR7keYzabM7X4gGEdT6gyqQlzu+e3o0bY1gGVh7sWPlHBlk/0XB8Q 3gig== X-Gm-Message-State: AD7BkJKqtiQZntxeyVt756tqD84oytCqdYPdF69IQ8y/ZZjPoFXVU38J2WWyJGmrfhUcLHfU X-Received: by 10.98.1.74 with SMTP id 71mr27770911pfb.10.1458340759601; Fri, 18 Mar 2016 15:39:19 -0700 (PDT) Received: from localhost ([2620:0:1009:3:cd03:ef39:7648:4957]) by smtp.gmail.com with ESMTPSA id i11sm23297378pfi.55.2016.03.18.15.39.18 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Fri, 18 Mar 2016 15:39:18 -0700 (PDT) From: Peter Feiner To: jan.kiszka@siemens.com, pbonzini@redhat.com, kvm@vger.kernel.org Cc: pfeiner@google.com Subject: [PATCH kvm-unit-tests 4/4] x86: vmx: better vm{launch, resume} error handling Date: Fri, 18 Mar 2016 15:39:12 -0700 Message-Id: X-Mailer: git-send-email 2.8.0.rc3.226.g39d4020 In-Reply-To: References: In-Reply-To: References: Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch splits out entry error handling from other exit handling since most tests don't expect entry errors and thus don't check the conditions properly. Also enables tests for early entry errors (i.e., an entry_error_handler can return VMX_TEST_RESUME). Consolidates and simplifies control flow. Now, vmx_run is the central validation point for exit handler statuses rather than splitting the responsibility between exit_handler and vmx_run. Signed-off-by: Peter Feiner --- lib/x86/processor.h | 4 ++ x86/vmx.c | 142 ++++++++++++++++++++++++++++++++++++---------------- x86/vmx.h | 19 ++++++- x86/vmx_tests.c | 49 +++++++++++------- 4 files changed, 151 insertions(+), 63 deletions(-) diff --git a/lib/x86/processor.h b/lib/x86/processor.h index ce779d1..ee7f180 100644 --- a/lib/x86/processor.h +++ b/lib/x86/processor.h @@ -31,7 +31,11 @@ #define X86_CR4_PKE 0x00400000 #define X86_EFLAGS_CF 0x00000001 +#define X86_EFLAGS_PF 0x00000004 +#define X86_EFLAGS_AF 0x00000010 #define X86_EFLAGS_ZF 0x00000040 +#define X86_EFLAGS_SF 0x00000080 +#define X86_EFLAGS_OF 0x00000800 #define X86_EFLAGS_AC 0x00040000 #define X86_IA32_EFER 0xc0000080 diff --git a/x86/vmx.c b/x86/vmx.c index 67f8650..6618008 100644 --- a/x86/vmx.c +++ b/x86/vmx.c @@ -157,6 +157,52 @@ void print_vmexit_info() regs.r12, regs.r13, regs.r14, regs.r15); } +void +print_vmentry_failure_info(struct vmentry_failure *failure) { + if (failure->early) { + printf("Early %s failure: ", failure->instr); + switch (failure->flags & VMX_ENTRY_FLAGS) { + case X86_EFLAGS_ZF: + printf("current-VMCS pointer is not valid.\n"); + break; + case X86_EFLAGS_CF: + printf("error number is %ld. See Intel 30.4.\n", + vmcs_read(VMX_INST_ERROR)); + break; + default: + printf("unexpected flags %lx!\n", failure->flags); + } + } else { + u64 reason = vmcs_read(EXI_REASON); + u64 qual = vmcs_read(EXI_QUALIFICATION); + + printf("Non-early %s failure (reason=0x%lx, qual=0x%lx): ", + failure->instr, reason, qual); + + switch (reason & 0xff) { + case VMX_FAIL_STATE: + printf("invalid guest state\n"); + break; + case VMX_FAIL_MSR: + printf("MSR loading\n"); + break; + case VMX_FAIL_MCHECK: + printf("machine-check event\n"); + break; + default: + printf("unexpected basic exit reason %ld\n", + reason & 0xff); + } + + if (!(reason & VMX_ENTRY_FAILURE)) + printf("\tVMX_ENTRY_FAILURE BIT NOT SET!\n"); + + if (reason & 0x7fff0000) + printf("\tRESERVED BITS SET!\n"); + } +} + + static void test_vmclear(void) { struct vmcs *tmp_root; @@ -890,27 +936,34 @@ static int exit_handler() else ret = current->exit_handler(); vmcs_write(GUEST_RFLAGS, regs.rflags); - switch (ret) { - case VMX_TEST_VMEXIT: - case VMX_TEST_RESUME: - return ret; - case VMX_TEST_EXIT: - break; - default: - printf("ERROR : Invalid exit_handler return val %d.\n" - , ret); - } - print_vmexit_info(); - abort(); - return 0; + + return ret; +} + +/* + * Called if vmlaunch or vmresume fails. + * @early - failure due to "VMX controls and host-state area" (26.2) + * @vmlaunch - was this a vmlaunch or vmresume + * @rflags - host rflags + */ +static int +entry_failure_handler(struct vmentry_failure *failure) +{ + if (current->entry_failure_handler) + return current->entry_failure_handler(failure); + else + return VMX_TEST_EXIT; } static int vmx_run() { - u32 ret = 0, fail = 0; unsigned long host_rflags; while (1) { + u32 ret; + u32 fail = 0; + bool entered; + struct vmentry_failure failure; asm volatile ( "mov %[HOST_RSP], %%rdi\n\t" @@ -937,39 +990,44 @@ static int vmx_run() : "rdi", "memory", "cc" ); - if (fail) - ret = launched ? VMX_TEST_RESUME_ERR : - VMX_TEST_LAUNCH_ERR; - else { + + entered = !fail && !(vmcs_read(EXI_REASON) & VMX_ENTRY_FAILURE); + + if (entered) { + /* + * VMCS isn't in "launched" state if there's been any + * entry failure (early or otherwise). + */ launched = 1; ret = exit_handler(); + } else { + failure.flags = host_rflags; + failure.vmlaunch = !launched; + failure.instr = launched ? "vmresume" : "vmlaunch"; + failure.early = fail; + ret = entry_failure_handler(&failure); } - if (ret != VMX_TEST_RESUME) + + switch (ret) { + case VMX_TEST_RESUME: + continue; + case VMX_TEST_VMEXIT: + return 0; + case VMX_TEST_EXIT: break; + default: + printf("ERROR : Invalid %s_handler return val %d.\n", + entered ? "exit" : "entry_failure", + ret); + break; + } + + if (entered) + print_vmexit_info(); + else + print_vmentry_failure_info(&failure); + abort(); } - launched = 0; - switch (ret) { - case VMX_TEST_VMEXIT: - return 0; - case VMX_TEST_LAUNCH_ERR: - printf("%s : vmlaunch failed.\n", __func__); - if ((!(host_rflags & X86_EFLAGS_CF) && !(host_rflags & X86_EFLAGS_ZF)) - || ((host_rflags & X86_EFLAGS_CF) && (host_rflags & X86_EFLAGS_ZF))) - printf("\tvmlaunch set wrong flags\n"); - report("test vmlaunch", 0); - break; - case VMX_TEST_RESUME_ERR: - printf("%s : vmresume failed.\n", __func__); - if ((!(host_rflags & X86_EFLAGS_CF) && !(host_rflags & X86_EFLAGS_ZF)) - || ((host_rflags & X86_EFLAGS_CF) && (host_rflags & X86_EFLAGS_ZF))) - printf("\tvmresume set wrong flags\n"); - report("test vmresume", 0); - break; - default: - printf("%s : unhandled ret from exit_handler, ret=%d.\n", __func__, ret); - break; - } - return 1; } static int test_run(struct vmx_test *test) diff --git a/x86/vmx.h b/x86/vmx.h index aba5642..0cb995d 100644 --- a/x86/vmx.h +++ b/x86/vmx.h @@ -32,6 +32,17 @@ struct regs { u64 rflags; }; +struct vmentry_failure { + /* Did a vmlaunch or vmresume fail? */ + bool vmlaunch; + /* Instruction mnemonic (for convenience). */ + const char *instr; + /* Did the instruction return right away, or did we jump to HOST_RIP? */ + bool early; + /* Contents of [re]flags after failed entry. */ + unsigned long flags; +}; + struct vmx_test { const char *name; int (*init)(struct vmcs *vmcs); @@ -39,6 +50,7 @@ struct vmx_test { int (*exit_handler)(); void (*syscall_handler)(u64 syscall_no); struct regs guest_regs; + int (*entry_failure_handler)(struct vmentry_failure *failure); struct vmcs *vmcs; int exits; }; @@ -249,6 +261,10 @@ enum Encoding { HOST_RIP = 0x6c16ul }; +#define VMX_ENTRY_FAILURE (1ul << 31) +#define VMX_ENTRY_FLAGS (X86_EFLAGS_CF | X86_EFLAGS_PF | X86_EFLAGS_AF | \ + X86_EFLAGS_ZF | X86_EFLAGS_SF | X86_EFLAGS_OF) + enum Reason { VMX_EXC_NMI = 0, VMX_EXTINT = 1, @@ -413,8 +429,6 @@ enum Ctrl1 { #define VMX_TEST_VMEXIT 1 #define VMX_TEST_EXIT 2 #define VMX_TEST_RESUME 3 -#define VMX_TEST_LAUNCH_ERR 4 -#define VMX_TEST_RESUME_ERR 5 #define HYPERCALL_BIT (1ul << 12) #define HYPERCALL_MASK 0xFFF @@ -554,6 +568,7 @@ static inline void invvpid(unsigned long type, u16 vpid, u64 gva) } void print_vmexit_info(); +void print_vmentry_failure_info(struct vmentry_failure *failure); void ept_sync(int type, u64 eptp); void vpid_sync(int type, u16 vpid); void install_ept_entry(unsigned long *pml4, int pte_level, diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index 1ed0b0c..0145cad 100644 --- a/x86/vmx_tests.c +++ b/x86/vmx_tests.c @@ -1523,24 +1523,35 @@ static int msr_switch_exit_handler() ulong reason; reason = vmcs_read(EXI_REASON); - switch (reason) { - case 0x80000000 | VMX_FAIL_MSR: - if (vmx_get_test_stage() == 3) { - report("VM entry MSR load: try to load FS_BASE", - vmcs_read(EXI_QUALIFICATION) == 1); - return VMX_TEST_VMEXIT; - } - break; - case VMX_VMCALL: - if (vmx_get_test_stage() == 2) { - report("VM exit MSR store", - exit_msr_store[0].value == MSR_MAGIC + 1); - report("VM exit MSR load", - rdmsr(MSR_KERNEL_GS_BASE) == MSR_MAGIC + 2); - vmx_set_test_stage(3); - entry_msr_load[0].index = MSR_FS_BASE; - return VMX_TEST_RESUME; - } + if (reason == VMX_VMCALL && vmx_get_test_stage() == 2) { + report("VM exit MSR store", + exit_msr_store[0].value == MSR_MAGIC + 1); + report("VM exit MSR load", + rdmsr(MSR_KERNEL_GS_BASE) == MSR_MAGIC + 2); + vmx_set_test_stage(3); + entry_msr_load[0].index = MSR_FS_BASE; + return VMX_TEST_RESUME; + } + printf("ERROR %s: unexpected stage=%u or reason=%lu\n", + __func__, vmx_get_test_stage(), reason); + return VMX_TEST_EXIT; +} + +static int msr_switch_entry_failure(struct vmentry_failure *failure) +{ + ulong reason; + + if (failure->early) { + printf("ERROR %s: early exit\n", __func__); + return VMX_TEST_EXIT; + } + + reason = vmcs_read(EXI_REASON); + if (reason == (VMX_ENTRY_FAILURE | VMX_FAIL_MSR) && + vmx_get_test_stage() == 3) { + report("VM entry MSR load: try to load FS_BASE", + vmcs_read(EXI_QUALIFICATION) == 1); + return VMX_TEST_VMEXIT; } printf("ERROR %s: unexpected stage=%u or reason=%lu\n", __func__, vmx_get_test_stage(), reason); @@ -1608,7 +1619,7 @@ struct vmx_test vmx_tests[] = { { "debug controls", dbgctls_init, dbgctls_main, dbgctls_exit_handler, NULL, {0} }, { "MSR switch", msr_switch_init, msr_switch_main, - msr_switch_exit_handler, NULL, {0} }, + msr_switch_exit_handler, NULL, {0}, msr_switch_entry_failure }, { "vmmcall", vmmcall_init, vmmcall_main, vmmcall_exit_handler, NULL, {0} }, { NULL, NULL, NULL, NULL, NULL, {0} }, };