From patchwork Wed Mar 2 17:10:56 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Feiner X-Patchwork-Id: 8483951 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 6044E9F96D for ; Wed, 2 Mar 2016 17:11:35 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 0344A20148 for ; Wed, 2 Mar 2016 17:11:33 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id EEBBE2038A for ; Wed, 2 Mar 2016 17:11:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754940AbcCBRLa (ORCPT ); Wed, 2 Mar 2016 12:11:30 -0500 Received: from mail-pf0-f170.google.com ([209.85.192.170]:35946 "EHLO mail-pf0-f170.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752448AbcCBRLK (ORCPT ); Wed, 2 Mar 2016 12:11:10 -0500 Received: by mail-pf0-f170.google.com with SMTP id 63so7803788pfe.3 for ; Wed, 02 Mar 2016 09:11:10 -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=k/Gg0WRydNmlZAOMNvEOqBauGq0UR0LRrCy5ICaDNEA=; b=nFvuc8zbz043IiwmxsKddn1jOLhK9wZS4WjEKgdQ/tzJtm4KCoa13QUQ8sh+Fqd+An Xk5ScdBa3h/B+G9ToOZaoTNj/goonAXshhwWUA9OBRCBUkuBjRNbx+lyK1+468DuFxCW 2PRX+7Ld0ZinSav7oFLZTEZ//Apw0Zlnz0e3dpG3aQyS4Wo4N1f9NwHqfHb4GbBbY28C kUtlgbWPTUVBZkliTAb9pO2QxAVx8ue2r3Xf6/qZnP5hpqgDFPMm6RqZa0+RxhEsTwgC fPGA1RBw7qI/4YgGgIsg6eVIkqeIZqzxAefQh8NKkKe5xFjmK7MtfR9gjmmZmRC+A+jU BI1A== 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=k/Gg0WRydNmlZAOMNvEOqBauGq0UR0LRrCy5ICaDNEA=; b=ZeaiUmbYpI7gm1Nl/w1PKQqxevUyxV+nP0O/dri7cDML5fLsSovcstzxNES/RhjxJu WgCixV5fhPQmnmicJCUj6dKia1LDyk2DHOXR0lxqlzykbtuwsOUlaQPAntYngx5SiWRW nC3cbk8XV/P/VpjLuJa7TnL5+jWQcTZBTQWz+aYKRUdEmTRZOMh7RWZT7umEnjgyToXU Vhg/ZW3HEYtEmMLtdmnc0sb2p16xRfvncRXz4GElYeL9A8wHXePgS3xEGyjQl92OD0Ti 5Bsoy9EJ15wkT9F2A60c10zgIX9GINvkM7T1+AqZP6of46q3NVksuByniI73H4A/OZrx 8NIw== X-Gm-Message-State: AD7BkJLtDQtV0jrRivfABXbIE/GKZnZvRXEeO4h4nswsAawO5rxcAK7wlYFJLzyzL+rq1OtL X-Received: by 10.98.74.144 with SMTP id c16mr39962253pfj.129.1456938669844; Wed, 02 Mar 2016 09:11:09 -0800 (PST) Received: from localhost ([2620:0:1009:3:10b2:1b3a:febc:1b15]) by smtp.gmail.com with ESMTPSA id v4sm15489107pfa.21.2016.03.02.09.11.08 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Wed, 02 Mar 2016 09:11:09 -0800 (PST) From: Peter Feiner To: kvm@vger.kernel.org, jan.kiszka@siemens.com, drjones@redhat.com, pbonzini@redhat.com Cc: pfeiner@google.com Subject: [kvm-unit-tests v3 5/6] x86: vmx: split large EPTEs in install_ept_entry Date: Wed, 2 Mar 2016 09:10:56 -0800 Message-Id: <1456938657-20850-6-git-send-email-pfeiner@google.com> X-Mailer: git-send-email 2.7.0.rc3.207.g0ac5344 In-Reply-To: <1456938657-20850-1-git-send-email-pfeiner@google.com> References: <1456860622-31251-1-git-send-email-pfeiner@google.com> <1456938657-20850-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=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 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 d3a852e..34e9be4 100644 --- a/x86/vmx.h +++ b/x86/vmx.h @@ -467,6 +467,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 GENMASK_ULL(51, 12) #define PAGE_MASK (~(PAGE_SIZE-1))