From patchwork Mon Sep 9 04:57:24 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arthur Chunqi Li X-Patchwork-Id: 2859361 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.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id AB4A1BF43F for ; Mon, 9 Sep 2013 04:57:51 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 8F3F4203F4 for ; Mon, 9 Sep 2013 04:57:50 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 72D65203FB for ; Mon, 9 Sep 2013 04:57:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751980Ab3IIE5r (ORCPT ); Mon, 9 Sep 2013 00:57:47 -0400 Received: from mail-pa0-f48.google.com ([209.85.220.48]:34789 "EHLO mail-pa0-f48.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751919Ab3IIE5j (ORCPT ); Mon, 9 Sep 2013 00:57:39 -0400 Received: by mail-pa0-f48.google.com with SMTP id kp13so5782724pab.35 for ; Sun, 08 Sep 2013 21:57:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=50RrF+f8t8hT1/iVYQpXhZjC7YB7ug0RyRPGPZEMY+4=; b=OAdaEBI9sKOmwe5PG1RIeGmifWzMLPea/oV7znGKsFMEB5O3/khmJovhBGMGhvVvxM TsrGOFOaE4/qT5BK58Xl4OPqFa1TJ9Vb8dAa8QCKaNa0DhlQK47nz6eaGrtjXNJHC2xS DxJYnOmLvHqpstTrfRe4zo2KOLuAQZQ/OdTiCaXoHHoApRpWmDtwIoK3GPTHPcZ7PJA4 op+COfAK+ca1c3zKLpMoukxIaAOksgFEYVy+hlsB8WDie0Lj9KXj2/FGFkj8dcy5s8Me l+QMvU8kWlcCb+YEaPcHf8o2YkV7zbGCBbKBJFfHO0PUJzejZOGY5TpiQouaViHqvkRO SAiw== X-Received: by 10.68.129.201 with SMTP id ny9mr15847791pbb.70.1378702659428; Sun, 08 Sep 2013 21:57:39 -0700 (PDT) Received: from Blade1-01.Blade1-01 ([162.105.146.101]) by mx.google.com with ESMTPSA id oc10sm13713741pbb.3.1969.12.31.16.00.00 (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Sun, 08 Sep 2013 21:57:38 -0700 (PDT) From: Arthur Chunqi Li To: kvm@vger.kernel.org Cc: jan.kiszka@web.de, gleb@redhat.com, pbonzini@redhat.com, Arthur Chunqi Li Subject: [PATCH 2/2] kvm-unit-tests: VMX: Test cases for nested EPT Date: Mon, 9 Sep 2013 12:57:24 +0800 Message-Id: <1378702644-23655-3-git-send-email-yzt356@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1378702644-23655-1-git-send-email-yzt356@gmail.com> References: <1378702644-23655-1-git-send-email-yzt356@gmail.com> Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Spam-Status: No, score=-7.7 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=ham 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 Some test cases for nested EPT features, including: 1. EPT basic framework tests: read, write and remap. 2. EPT misconfigurations test cases: page permission mieconfiguration and memory type misconfiguration 3. EPT violations test cases: page permission violation and paging structure violation Signed-off-by: Arthur Chunqi Li --- x86/vmx_tests.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index c1b39f4..a0b9824 100644 --- a/x86/vmx_tests.c +++ b/x86/vmx_tests.c @@ -1,4 +1,36 @@ #include "vmx.h" +#include "processor.h" +#include "vm.h" +#include "msr.h" +#include "fwcfg.h" + +volatile u32 stage; +volatile bool init_fail; +unsigned long *pml4; +u64 eptp; +void *data_page1, *data_page2; + +static inline void set_stage(u32 s) +{ + barrier(); + stage = s; + barrier(); +} + +static inline u32 get_stage() +{ + u32 s; + + barrier(); + s = stage; + barrier(); + return s; +} + +static inline void vmcall() +{ + asm volatile ("vmcall"); +} void basic_init() { @@ -76,6 +108,238 @@ int vmenter_exit_handler() return VMX_TEST_VMEXIT; } +static int setup_ept() +{ + int support_2m; + unsigned long end_of_memory; + + if (!(ept_vpid.val & EPT_CAP_UC) && + !(ept_vpid.val & EPT_CAP_WB)) { + printf("\tEPT paging-structure memory type " + "UC&WB are not supported\n"); + return 1; + } + if (ept_vpid.val & EPT_CAP_UC) + eptp = EPT_MEM_TYPE_UC; + else + eptp = EPT_MEM_TYPE_WB; + if (!(ept_vpid.val & EPT_CAP_PWL4)) { + printf("\tPWL4 is not supported\n"); + return 1; + } + eptp |= (3 << EPTP_PG_WALK_LEN_SHIFT); + pml4 = alloc_page(); + memset(pml4, 0, PAGE_SIZE); + eptp |= virt_to_phys(pml4); + vmcs_write(EPTP, eptp); + support_2m = !!(ept_vpid.val & EPT_CAP_2M_PAGE); + end_of_memory = fwcfg_get_u64(FW_CFG_RAM_SIZE); + if (end_of_memory < (1ul << 32)) + end_of_memory = (1ul << 32); + if (setup_ept_range(pml4, 0, end_of_memory, + 0, support_2m, EPT_WA | EPT_RA | EPT_EA)) { + printf("\tSet ept tables failed.\n"); + return 1; + } + return 0; +} + +static void ept_init() +{ + u32 ctrl_cpu[2]; + + init_fail = false; + ctrl_cpu[0] = vmcs_read(CPU_EXEC_CTRL0); + ctrl_cpu[1] = vmcs_read(CPU_EXEC_CTRL1); + ctrl_cpu[0] = (ctrl_cpu[0] | CPU_SECONDARY) + & ctrl_cpu_rev[0].clr; + ctrl_cpu[1] = (ctrl_cpu[1] | CPU_EPT) + & ctrl_cpu_rev[1].clr; + vmcs_write(CPU_EXEC_CTRL0, ctrl_cpu[0]); + vmcs_write(CPU_EXEC_CTRL1, ctrl_cpu[1] | CPU_EPT); + if (setup_ept()) + init_fail = true; + data_page1 = alloc_page(); + data_page2 = alloc_page(); + memset(data_page1, 0x0, PAGE_SIZE); + memset(data_page2, 0x0, PAGE_SIZE); + *((u32 *)data_page1) = MAGIC_VAL_1; + *((u32 *)data_page2) = MAGIC_VAL_2; + install_ept(pml4, (unsigned long)data_page1, (unsigned long)data_page2, + EPT_RA | EPT_WA | EPT_EA); +} + +static void ept_main() +{ + if (init_fail) + return; + if (!(ctrl_cpu_rev[0].clr & CPU_SECONDARY) + && !(ctrl_cpu_rev[1].clr & CPU_EPT)) { + printf("\tEPT is not supported"); + return; + } + set_stage(0); + if (*((u32 *)data_page2) != MAGIC_VAL_1 && + *((u32 *)data_page1) != MAGIC_VAL_1) + report("EPT basic framework - read", 0); + else { + *((u32 *)data_page2) = MAGIC_VAL_3; + vmcall(); + if (get_stage() == 1) { + if (*((u32 *)data_page1) == MAGIC_VAL_3 && + *((u32 *)data_page2) == MAGIC_VAL_2) + report("EPT basic framework", 1); + else + report("EPT basic framework - remap", 1); + } + } + // Test EPT Misconfigurations + set_stage(1); + vmcall(); + *((u32 *)data_page1) = MAGIC_VAL_1; + if (get_stage() != 2) { + report("EPT misconfigurations", 0); + goto t1; + } + set_stage(2); + vmcall(); + *((u32 *)data_page1) = MAGIC_VAL_1; + if (get_stage() != 3) { + report("EPT misconfigurations", 0); + goto t1; + } + report("EPT misconfigurations", 1); +t1: + // Test EPT violation + set_stage(3); + vmcall(); + *((u32 *)data_page1) = MAGIC_VAL_1; + if (get_stage() == 4) + report("EPT violation - page permission", 1); + else + report("EPT violation - page permission", 0); + // Violation caused by EPT paging structure + set_stage(4); + vmcall(); + *((u32 *)data_page1) = MAGIC_VAL_2; + if (get_stage() == 5) + report("EPT violation - paging structure", 1); + else + report("EPT violation - paging structure", 0); + return; +} + +static int ept_exit_handler() +{ + u64 guest_rip; + ulong reason; + u32 insn_len; + u32 exit_qual; + static unsigned long data_page1_pte, data_page1_pte_pte; + + guest_rip = vmcs_read(GUEST_RIP); + reason = vmcs_read(EXI_REASON) & 0xff; + insn_len = vmcs_read(EXI_INST_LEN); + exit_qual = vmcs_read(EXI_QUALIFICATION); + switch (reason) { + case VMX_VMCALL: + switch (get_stage()) { + case 0: + if (*((u32 *)data_page1) == MAGIC_VAL_3 && + *((u32 *)data_page2) == MAGIC_VAL_2) { + set_stage(get_stage() + 1); + install_ept(pml4, (unsigned long)data_page2, + (unsigned long)data_page2, + EPT_RA | EPT_WA | EPT_EA); + } else + report("EPT basic framework - write\n", 0); + break; + case 1: + install_ept(pml4, (unsigned long)data_page1, + (unsigned long)data_page1, EPT_WA); + invept(INVEPT_SINGLE, eptp); + break; + case 2: + install_ept(pml4, (unsigned long)data_page1, + (unsigned long)data_page1, + EPT_RA | EPT_WA | EPT_EA | + (2 << EPT_MEM_TYPE_SHIFT)); + invept(INVEPT_SINGLE, eptp); + break; + case 3: + data_page1_pte = get_ept_pte(pml4, + (unsigned long)data_page1, 1); + set_ept_pte(pml4, (unsigned long)data_page1, + 1, data_page1_pte & (~EPT_PRESENT)); + invept(INVEPT_SINGLE, eptp); + break; + case 4: + data_page1_pte = get_ept_pte(pml4, + (unsigned long)data_page1, 2); + data_page1_pte &= PAGE_MASK; + data_page1_pte_pte = get_ept_pte(pml4, data_page1_pte, 2); + set_ept_pte(pml4, data_page1_pte, 2, + data_page1_pte_pte & (~EPT_PRESENT)); + invept(INVEPT_SINGLE, eptp); + break; + // Should not reach here + default: + printf("ERROR - unknown stage, %d.\n", get_stage()); + print_vmexit_info(); + return VMX_TEST_VMEXIT; + } + vmcs_write(GUEST_RIP, guest_rip + insn_len); + return VMX_TEST_RESUME; + case VMX_EPT_MISCONFIG: + switch (get_stage()) { + case 1: + case 2: + set_stage(get_stage() + 1); + install_ept(pml4, (unsigned long)data_page1, + (unsigned long)data_page1, + EPT_RA | EPT_WA | EPT_EA); + invept(INVEPT_SINGLE, eptp); + break; + // Should not reach here + default: + printf("ERROR - unknown stage, %d.\n", get_stage()); + print_vmexit_info(); + return VMX_TEST_VMEXIT; + } + vmcs_write(GUEST_RIP, guest_rip + insn_len); + return VMX_TEST_RESUME; + case VMX_EPT_VIOLATION: + switch(get_stage()) { + case 3: + if (exit_qual == (EPT_VLT_WR | EPT_VLT_LADDR_VLD | + EPT_VLT_PADDR)) + set_stage(get_stage() + 1); + set_ept_pte(pml4, (unsigned long)data_page1, + 1, data_page1_pte | (EPT_PRESENT)); + invept(INVEPT_SINGLE, eptp); + break; + case 4: + if (exit_qual == (EPT_VLT_RD | EPT_VLT_LADDR_VLD)) + set_stage(get_stage() + 1); + set_ept_pte(pml4, data_page1_pte, 2, + data_page1_pte_pte | (EPT_PRESENT)); + invept(INVEPT_SINGLE, eptp); + break; + default: + // Should not reach here + printf("ERROR : unknown stage, %d\n", get_stage()); + print_vmexit_info(); + return VMX_TEST_VMEXIT; + } + vmcs_write(GUEST_RIP, guest_rip + insn_len); + return VMX_TEST_RESUME; + default: + printf("Unknown exit reason, %d\n", reason); + print_vmexit_info(); + } + return VMX_TEST_VMEXIT; +} + /* name/init/guest_main/exit_handler/syscall_handler/guest_regs basic_* just implement some basic functions */ struct vmx_test vmx_tests[] = { @@ -83,5 +347,7 @@ struct vmx_test vmx_tests[] = { basic_syscall_handler, {0} }, { "vmenter", basic_init, vmenter_main, vmenter_exit_handler, basic_syscall_handler, {0} }, + { "EPT framework", ept_init, ept_main, ept_exit_handler, + basic_syscall_handler, {0} }, { NULL, NULL, NULL, NULL, NULL, {0} }, };