From patchwork Wed Mar 8 18:07:19 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 9611787 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 9980260414 for ; Wed, 8 Mar 2017 18:16:45 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 963ED27B13 for ; Wed, 8 Mar 2017 18:16:45 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8AE4F2862B; Wed, 8 Mar 2017 18:16:45 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.3 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E1C8C27B13 for ; Wed, 8 Mar 2017 18:16:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753635AbdCHSQo (ORCPT ); Wed, 8 Mar 2017 13:16:44 -0500 Received: from mail-wm0-f66.google.com ([74.125.82.66]:33562 "EHLO mail-wm0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752052AbdCHSQm (ORCPT ); Wed, 8 Mar 2017 13:16:42 -0500 Received: by mail-wm0-f66.google.com with SMTP id n11so7395471wma.0 for ; Wed, 08 Mar 2017 10:16:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=3Ml02gwcLOr/AwaK3aBakbTD+W1HINrDOm2DyIafFSo=; b=lcrS6H+IQtpdOgE/URHRqeV/xae3FOJG7SYjLkPlG+JF2+sBt/F+bMi/VLot1vj18+ Aro7tCdfG1R30imukfkJt7ZDG7DAbU/5OuLiGdbSwPGrdybj0tRUsSBOeA2rbHnJj84+ pPTKjtDPrEslRx3u+P9cDJrJ/4NT7zFtLFcdKPynYSbFtDMZ0rsTfp0JuwSZX4dS9XGK NWLGeTGw8LlioJDLWRxOT76Erpd66gJbgaxvCewOqu4iw4pjC65PfCWz6xbM+wIVBPOu L2CRyHm3iWFVIT+AzDrLXaED71X+rNgOD1WdxXEGSRRLXmHgYS3zEHW5038mbXSG052o eEAw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=3Ml02gwcLOr/AwaK3aBakbTD+W1HINrDOm2DyIafFSo=; b=OjJTs3lgW6w0G/2bTYgHyIWjCgvRMf0fDFOWLDlYYWYfIPfAh3dBBLuf0//+wyixuC KbmB7pCpnxIXRglhrDjbVybXUnaLEZkgpn2h4x55VakIUifp0Ms/0maH8WSrdzmikK8W IOv7z4lwXEgEeGRY5rRvIRrY0lPTfyWFb/5MWA7x/zoKwwGA4Colkyr34fuYd/z/wgPL 62bJRVXVgEnzf+88BWH+BIkocS1KoM7dA8NnM0C30RvWGWwCyBCGpNi039lPRCHpp2Gk 7+TlpzAaBy/IppMvvq27dwA3oJQFFC8Zv9BbnaYZ+HQsR0BkdKhKWIUAEayoGZ9Wa4vE MYgg== X-Gm-Message-State: AMke39mpTCIhdw8oMgcKvzJQpIWoXHyvhYb/5nZc0TdsUj4+zuxA95wj2sdyfWTgtNMQuA== X-Received: by 10.28.59.193 with SMTP id i184mr7131401wma.97.1488996446058; Wed, 08 Mar 2017 10:07:26 -0800 (PST) Received: from 640k.lan (94-36-220-234.adsl-ull.clienti.tiscali.it. [94.36.220.234]) by smtp.gmail.com with ESMTPSA id 82sm4539485wmg.0.2017.03.08.10.07.25 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 08 Mar 2017 10:07:25 -0800 (PST) From: Paolo Bonzini To: kvm@vger.kernel.org Cc: bdas@redhat.com, dmatlack@google.com Subject: [PATCH kvm-unit-tests 3/4] VMX: add tests for EPT A/D bits Date: Wed, 8 Mar 2017 19:07:19 +0100 Message-Id: <1488996440-6493-4-git-send-email-pbonzini@redhat.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1488996440-6493-1-git-send-email-pbonzini@redhat.com> References: <1488996440-6493-1-git-send-email-pbonzini@redhat.com> Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Paolo Bonzini --- x86/vmx.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ x86/vmx.h | 5 +++ x86/vmx_tests.c | 31 ++++++++++++++++-- 3 files changed, 132 insertions(+), 3 deletions(-) diff --git a/x86/vmx.c b/x86/vmx.c index 5200dcd..0505534 100644 --- a/x86/vmx.c +++ b/x86/vmx.c @@ -421,6 +421,105 @@ unsigned long get_ept_pte(unsigned long *pml4, return pte; } +static void clear_ept_ad_pte(unsigned long *pml4, unsigned long guest_addr) +{ + int l; + unsigned long *pt = pml4; + u64 pte; + unsigned offset; + + for (l = EPT_PAGE_LEVEL; ; --l) { + offset = (guest_addr >> EPT_LEVEL_SHIFT(l)) & EPT_PGDIR_MASK; + pt[offset] &= ~(EPT_ACCESS_FLAG|EPT_DIRTY_FLAG); + pte = pt[offset]; + if (l == 1 || (l < 4 && (pte & EPT_LARGE_PAGE))) + break; + pt = (unsigned long *)(pte & EPT_ADDR_MASK); + } +} + +/* clear_ept_ad : Clear EPT A/D bits for the page table walk and the + final GPA of a guest address. */ +void clear_ept_ad(unsigned long *pml4, u64 guest_cr3, + unsigned long guest_addr) +{ + int l; + unsigned long *pt = (unsigned long *)guest_cr3, gpa; + u64 pte, offset_in_page; + unsigned offset; + + for (l = EPT_PAGE_LEVEL; ; --l) { + offset = (guest_addr >> EPT_LEVEL_SHIFT(l)) & EPT_PGDIR_MASK; + + clear_ept_ad_pte(pml4, (u64) &pt[offset]); + pte = pt[offset]; + if (l == 1 || (l < 4 && (pte & PT_PAGE_SIZE_MASK))) + break; + if (!(pte & PT_PRESENT_MASK)) + return; + pt = (unsigned long *)(pte & PT_ADDR_MASK); + } + + offset = (guest_addr >> EPT_LEVEL_SHIFT(l)) & EPT_PGDIR_MASK; + offset_in_page = guest_addr & ((1 << EPT_LEVEL_SHIFT(l)) - 1); + gpa = (pt[offset] & PT_ADDR_MASK) | (guest_addr & offset_in_page); + clear_ept_ad_pte(pml4, gpa); +} + +/* check_ept_ad : Check the content of EPT A/D bits for the page table + walk and the final GPA of a guest address. */ +void check_ept_ad(unsigned long *pml4, u64 guest_cr3, + unsigned long guest_addr, int expected_gpa_ad, + int expected_pt_ad) +{ + int l; + unsigned long *pt = (unsigned long *)guest_cr3, gpa; + u64 ept_pte, pte, offset_in_page; + unsigned offset; + bool bad_pt_ad = false; + + for (l = EPT_PAGE_LEVEL; ; --l) { + offset = (guest_addr >> EPT_LEVEL_SHIFT(l)) & EPT_PGDIR_MASK; + + ept_pte = get_ept_pte(pml4, (u64) &pt[offset], 1); + if (ept_pte == 0) + return; + + if (!bad_pt_ad) { + bad_pt_ad |= (ept_pte & (EPT_ACCESS_FLAG|EPT_DIRTY_FLAG)) != expected_pt_ad; + if (bad_pt_ad) + report("EPT - guest level %d page table A=%d/D=%d", + false, l, + !!(expected_pt_ad & EPT_ACCESS_FLAG), + !!(expected_pt_ad & EPT_DIRTY_FLAG)); + } + + pte = pt[offset]; + if (l == 1 || (l < 4 && (pte & PT_PAGE_SIZE_MASK))) + break; + if (!(pte & PT_PRESENT_MASK)) + return; + pt = (unsigned long *)(pte & PT_ADDR_MASK); + } + + if (!bad_pt_ad) + report("EPT - guest page table structures A=%d/D=%d", + true, + !!(expected_pt_ad & EPT_ACCESS_FLAG), + !!(expected_pt_ad & EPT_DIRTY_FLAG)); + + offset = (guest_addr >> EPT_LEVEL_SHIFT(l)) & EPT_PGDIR_MASK; + offset_in_page = guest_addr & ((1 << EPT_LEVEL_SHIFT(l)) - 1); + gpa = (pt[offset] & PT_ADDR_MASK) | (guest_addr & offset_in_page); + + ept_pte = get_ept_pte(pml4, gpa, 1); + report("EPT - guest physical address A=%d/D=%d", + (ept_pte & (EPT_ACCESS_FLAG|EPT_DIRTY_FLAG)) == expected_gpa_ad, + !!(expected_gpa_ad & EPT_ACCESS_FLAG), + !!(expected_gpa_ad & EPT_DIRTY_FLAG)); +} + + void ept_sync(int type, u64 eptp) { switch (type) { diff --git a/x86/vmx.h b/x86/vmx.h index a2bacd3..290e6bd 100644 --- a/x86/vmx.h +++ b/x86/vmx.h @@ -618,5 +618,10 @@ unsigned long get_ept_pte(unsigned long *pml4, unsigned long guest_addr, int level); int set_ept_pte(unsigned long *pml4, unsigned long guest_addr, int level, u64 pte_val); +void check_ept_ad(unsigned long *pml4, u64 guest_cr3, + unsigned long guest_addr, int expected_gpa_ad, + int expected_pt_ad); +void clear_ept_ad(unsigned long *pml4, u64 guest_cr3, + unsigned long guest_addr); #endif diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index 9dc64fc..a61c9f2 100644 --- a/x86/vmx_tests.c +++ b/x86/vmx_tests.c @@ -969,7 +969,11 @@ static int setup_ept(bool enable_ad) end_of_memory = fwcfg_get_u64(FW_CFG_RAM_SIZE); if (end_of_memory < (1ul << 32)) end_of_memory = (1ul << 32); - setup_ept_range(pml4, 0, end_of_memory, 0, support_2m, + /* Cannot use large EPT pages if we need to track EPT + * accessed/dirty bits at 4K granularity. + */ + setup_ept_range(pml4, 0, end_of_memory, + 0, !enable_ad && support_2m, EPT_WA | EPT_RA | EPT_EA); return 0; } @@ -1090,12 +1094,14 @@ bool invept_test(int type, u64 eptp) static int ept_exit_handler_common(bool have_ad) { u64 guest_rip; + u64 guest_cr3; ulong reason; u32 insn_len; u32 exit_qual; static unsigned long data_page1_pte, data_page1_pte_pte; guest_rip = vmcs_read(GUEST_RIP); + guest_cr3 = vmcs_read(GUEST_CR3); reason = vmcs_read(EXI_REASON) & 0xff; insn_len = vmcs_read(EXI_INST_LEN); exit_qual = vmcs_read(EXI_QUALIFICATION); @@ -1103,6 +1109,18 @@ static int ept_exit_handler_common(bool have_ad) case VMX_VMCALL: switch (vmx_get_test_stage()) { case 0: + check_ept_ad(pml4, guest_cr3, + (unsigned long)data_page1, + have_ad ? EPT_ACCESS_FLAG : 0, + have_ad ? EPT_ACCESS_FLAG | EPT_DIRTY_FLAG : 0); + check_ept_ad(pml4, guest_cr3, + (unsigned long)data_page2, + have_ad ? EPT_ACCESS_FLAG | EPT_DIRTY_FLAG : 0, + have_ad ? EPT_ACCESS_FLAG | EPT_DIRTY_FLAG : 0); + clear_ept_ad(pml4, guest_cr3, (unsigned long)data_page1); + clear_ept_ad(pml4, guest_cr3, (unsigned long)data_page2); + if (have_ad) + ept_sync(INVEPT_SINGLE, eptp);; if (*((u32 *)data_page1) == MAGIC_VAL_3 && *((u32 *)data_page2) == MAGIC_VAL_2) { vmx_inc_test_stage(); @@ -1125,10 +1143,11 @@ static int ept_exit_handler_common(bool have_ad) ept_sync(INVEPT_SINGLE, eptp); break; case 3: + clear_ept_ad(pml4, guest_cr3, (unsigned long)data_page1); 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)); + 1, data_page1_pte & ~EPT_PRESENT); ept_sync(INVEPT_SINGLE, eptp); break; case 4: @@ -1137,7 +1156,7 @@ static int ept_exit_handler_common(bool have_ad) 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)); + data_page1_pte_pte & ~EPT_PRESENT); ept_sync(INVEPT_SINGLE, eptp); break; case 6: @@ -1174,6 +1193,9 @@ static int ept_exit_handler_common(bool have_ad) case VMX_EPT_VIOLATION: switch(vmx_get_test_stage()) { case 3: + check_ept_ad(pml4, guest_cr3, (unsigned long)data_page1, 0, + have_ad ? EPT_ACCESS_FLAG | EPT_DIRTY_FLAG : 0); + clear_ept_ad(pml4, guest_cr3, (unsigned long)data_page1); if (exit_qual == (EPT_VLT_WR | EPT_VLT_LADDR_VLD | EPT_VLT_PADDR)) vmx_inc_test_stage(); @@ -1182,6 +1204,9 @@ static int ept_exit_handler_common(bool have_ad) ept_sync(INVEPT_SINGLE, eptp); break; case 4: + check_ept_ad(pml4, guest_cr3, (unsigned long)data_page1, 0, + have_ad ? EPT_ACCESS_FLAG | EPT_DIRTY_FLAG : 0); + clear_ept_ad(pml4, guest_cr3, (unsigned long)data_page1); if (exit_qual == (EPT_VLT_RD | (have_ad ? EPT_VLT_WR : 0) | EPT_VLT_LADDR_VLD))