From patchwork Tue Mar 1 22:34:53 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Feiner X-Patchwork-Id: 8471121 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 7DF449F2F0 for ; Tue, 1 Mar 2016 22:35:33 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id A78C82034E for ; Tue, 1 Mar 2016 22:35:32 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id A476420304 for ; Tue, 1 Mar 2016 22:35:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752013AbcCAWf1 (ORCPT ); Tue, 1 Mar 2016 17:35:27 -0500 Received: from mail-pa0-f44.google.com ([209.85.220.44]:36838 "EHLO mail-pa0-f44.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751787AbcCAWfZ (ORCPT ); Tue, 1 Mar 2016 17:35:25 -0500 Received: by mail-pa0-f44.google.com with SMTP id yy13so119741361pab.3 for ; Tue, 01 Mar 2016 14:35:25 -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=hIawOZymq1dNvrKpN0ydE9oVD44hHPqDDaYxN1VIe/w=; b=V3TJ0aCX+GPSlKCFgDoIuPUYolLJBOXONJ6sYTXx4t2smxPJrKs46qjWf50nHBjjcz Bgf1ZaWbBZrsDJb8G3sfl2iLZ4CWrtbkz2GWEfPyr7mysGNEvOjxyMDRBubEUoxW7Trs KQhMPOwqEsKg4/P5S8arCWYDzQ+gr412cXiYCPwRd/GGjdPTBwg+JyGr5PmVx2Xx9TEd W/MOtEe93WpIrlkylZ6W82zBzXVabq4PsvsQiW9lqsXiKZAj2jOxlYf+h452D3OmwJiv phkogQ9e+Lw0WXdvIOs1nzcSwXfqmUsVJyRLk7hTa+Q5EoJAHwL84CjY71kKKxMxLhWF 5EhQ== 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=hIawOZymq1dNvrKpN0ydE9oVD44hHPqDDaYxN1VIe/w=; b=Qtusm7JYLkE3pRZgHo2y+xI1QfAxShSBcN2+gwT2WiloKo/6xnLMV9osQbCCPvsqUS PL1ku6xpRIE44WkUWmcQhR0KoJJDQ0EeNsEZtXkatIxq4lUpPy9fn5tkNP+q3MQoHRdJ xpWcjQu8jK4zZ7PLl1md09HBDtNunQQeECACO8X04zuu9rD5tDlFSG+QHaFbb3jl9oYz bsvPGJkEDU5yz/A05tBQ71/kBK2rnlMLD2Ils6a4GXZsC3TB7stGPdSRTIWiKBU4luOl AtSqVPjov2bOTruG9OWprH2w73Jg93MPFcTnOFgI/4vuxRGLUh9nh/nY6xQm3AtGFdnB U18g== X-Gm-Message-State: AD7BkJIdZguUuUmC2oRWpMWMHYvF1Nz2U/QFGqym2WGgqyTy3e8V48v11NYRv9+RBsv9tzgg X-Received: by 10.66.237.1 with SMTP id uy1mr24392768pac.114.1456871724931; Tue, 01 Mar 2016 14:35:24 -0800 (PST) Received: from localhost ([2620:0:1009:3:b1e9:1d95:1ce5:e70c]) by smtp.gmail.com with ESMTPSA id 27sm47809326pfh.48.2016.03.01.14.35.22 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Tue, 01 Mar 2016 14:35:23 -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 v2 5/6] x86: vmx: split large EPTEs in install_ept_entry Date: Tue, 1 Mar 2016 14:34:53 -0800 Message-Id: <1456871694-23042-6-git-send-email-pfeiner@google.com> X-Mailer: git-send-email 2.7.0.rc3.207.g0ac5344 In-Reply-To: <1456871694-23042-1-git-send-email-pfeiner@google.com> References: <1456860622-31251-1-git-send-email-pfeiner@google.com> <1456871694-23042-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 018051a..35b5431 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(52, 11) #define PAGE_MASK (~(PAGE_SIZE-1))