From patchwork Tue Mar 1 19:30:21 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Feiner X-Patchwork-Id: 8469661 Return-Path: X-Original-To: patchwork-kvm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id B5D879F2F0 for ; Tue, 1 Mar 2016 19:31:12 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id C886720165 for ; Tue, 1 Mar 2016 19:31:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D5FCE20304 for ; Tue, 1 Mar 2016 19:31:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752103AbcCATbG (ORCPT ); Tue, 1 Mar 2016 14:31:06 -0500 Received: from mail-pf0-f173.google.com ([209.85.192.173]:32847 "EHLO mail-pf0-f173.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752083AbcCATbE (ORCPT ); Tue, 1 Mar 2016 14:31:04 -0500 Received: by mail-pf0-f173.google.com with SMTP id 124so56567519pfg.0 for ; Tue, 01 Mar 2016 11:31:04 -0800 (PST) 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; bh=XF4n2oNlw06bPM455NoD47kYnlJCqN8sbm0fiplsGVc=; b=csQobY76sjNgabAK+5P3eNUBiLUAYTWsGv8+8iSYj2eDO+LiKYlY7/v8sLcuf4MjVd fCEoq3+3PGjPGUa5kQAm41qMCloBeIBMQxg+BvbrMe3zWpZGOnNIAn1beOLXMvZC61so WXfYtY/l4IqBDGtArYmAc1MpP1c5ADGQdHOOfKYTTSoMDXEQpQEdMKAtQ5VzQZo+o7Fg CC3vrOus55RZFBI8QQ/93tf03cCN29UOKjE0/LC5aFFLEcA76lLijgUyoOk7NpYD+rs0 Z4sVEQ1GTGMQw4kERupkt0eLC3vLgHgTVoaXMbrDWKNMzNjTav9AIXS/T+XrO0FtF2X8 ZYwA== 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; bh=XF4n2oNlw06bPM455NoD47kYnlJCqN8sbm0fiplsGVc=; b=MdUDM0t7GRqPxjWFWKkUETtxmo5xSZKcJqqpyfRBtNYplKbiKV9xiPSZ87u1qI7am0 M8PWXvumeGouPdC/+7i7TfOyvbDhcGaDHpVkmHcWnOuKI4JPFEWpQEA4owsbAwxcntEz xPUg5LbtsCFVU6QlnWg+fG3pfkBo8eTRWW6kMcpJTL2W5kma/38GTCZ2H/cTMk+Ic0xa CPW1tnRhUza6Fe86fHs44RzI/2XISNG8BAE+fGir/OR0z2OpWzu8g32P3uXCfk0E/rK9 cY7laO1LnNosuo7YfRmiDQRNdx3s6J6fHOeeB+/kwfwVx3ujHLtGx42Yc+4fU1nRq0e+ cJRg== X-Gm-Message-State: AD7BkJLT16EU0lxvacb4j1OpjTvbklkN0Vpw74TJ0ZJn2ExaWyZhKsL4tZXq9+U8TiYUjkQC X-Received: by 10.98.34.28 with SMTP id i28mr4325966pfi.160.1456860664078; Tue, 01 Mar 2016 11:31:04 -0800 (PST) Received: from localhost ([2620:0:1009:3:b1e9:1d95:1ce5:e70c]) by smtp.gmail.com with ESMTPSA id o184sm47373054pfo.36.2016.03.01.11.31.03 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Tue, 01 Mar 2016 11:31:03 -0800 (PST) From: Peter Feiner To: kvm@vger.kernel.org, jan.kiszka@siemens.com, pbonzini@redhat.com Cc: pfeiner@google.com Subject: [kvm-unit-tests 4/5] x86: vmx: split large EPTEs in install_ept_entry Date: Tue, 1 Mar 2016 11:30:21 -0800 Message-Id: <1456860622-31251-5-git-send-email-pfeiner@google.com> X-Mailer: git-send-email 2.7.0.rc3.207.g0ac5344 In-Reply-To: <1456860622-31251-1-git-send-email-pfeiner@google.com> References: <1456860622-31251-1-git-send-email-pfeiner@google.com> 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 When install_ept_entry encountered a leaf entry above pte_level, it just cleared the EPT_LARGE_PAGE bit and continued the traversal _using the first 4K page from the large page as the next level of the page table_! This is broken because (1) the data in the large page would be overwritten, and (2) all of other entires in the new level of the page table would contain garbage. Now, install_ept_entry splits the large mapping by allocating a new page and filling it in with 512 PTEs that point to the large page's constituent 2M or 4K pages. This path is exercised in the VMX EPT test when 2m EPT pages are enabled. The bug wasn't obvious because the free list is sorted in descending order of HPA, thus the large page being overwritten with an EPTE happened to only contain 0s. Fixes: 04b0e0f342978f08b8b0b068c08c9d45ee80e3f7 ("nEPT: Fix test cases for 2M huge pages"). Signed-off-by: Peter Feiner --- x86/vmx.c | 42 ++++++++++++++++++++++++++++++++++++++++-- x86/vmx.h | 1 + 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/x86/vmx.c b/x86/vmx.c index 140ad86..d3fdc71 100644 --- a/x86/vmx.c +++ b/x86/vmx.c @@ -215,6 +215,44 @@ asm( ); /* EPT paging structure related functions */ +/* split_large_ept_entry: Split a 2M/1G large page into 512 smaller PTEs. + @ptep : large page table entry to split + @level : level of ptep (2 or 3) + */ +static void split_large_ept_entry(unsigned long *ptep, int level) +{ + unsigned long *new_pt; + unsigned long gpa; + unsigned long pte; + unsigned long prototype; + int i; + + pte = *ptep; + assert(pte & EPT_PRESENT); + assert(pte & EPT_LARGE_PAGE); + assert(level == 2 || level == 3); + + new_pt = alloc_page(); + assert(new_pt); + memset(new_pt, 0, PAGE_SIZE); + + prototype = pte & ~EPT_ADDR_MASK; + if (level == 2) + prototype &= ~EPT_LARGE_PAGE; + + gpa = pte & EPT_ADDR_MASK; + for (i = 0; i < EPT_PGDIR_ENTRIES; i++) { + new_pt[i] = prototype | gpa; + gpa += 1ul << EPT_LEVEL_SHIFT(level - 1); + } + + pte &= ~EPT_LARGE_PAGE; + pte &= ~EPT_ADDR_MASK; + pte |= virt_to_phys(new_pt); + + *ptep = pte; +} + /* install_ept_entry : Install a page to a given level in EPT @pml4 : addr of pml4 table @pte_level : level of PTE to set @@ -244,8 +282,8 @@ void install_ept_entry(unsigned long *pml4, memset(new_pt, 0, PAGE_SIZE); pt[offset] = virt_to_phys(new_pt) | EPT_RA | EPT_WA | EPT_EA; - } else - pt[offset] &= ~EPT_LARGE_PAGE; + } else if (pt[offset] & EPT_LARGE_PAGE) + split_large_ept_entry(&pt[offset], level); pt = phys_to_virt(pt[offset] & EPT_ADDR_MASK); } offset = (guest_addr >> EPT_LEVEL_SHIFT(level)) & EPT_PGDIR_MASK; diff --git a/x86/vmx.h b/x86/vmx.h index 0b646fa..11ece90 100644 --- a/x86/vmx.h +++ b/x86/vmx.h @@ -466,6 +466,7 @@ enum Ctrl1 { #define EPT_PAGE_LEVEL 4 #define EPT_PGDIR_WIDTH 9 #define EPT_PGDIR_MASK 511 +#define EPT_PGDIR_ENTRIES (1 << EPT_PGDIR_WIDTH) #define EPT_LEVEL_SHIFT(level) (((level)-1) * EPT_PGDIR_WIDTH + 12) #define EPT_ADDR_MASK 0xffffffffff000ul #define PAGE_MASK (~(PAGE_SIZE-1))