From patchwork Fri Sep 8 16:05:36 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Alexandru Stefan ISAILA X-Patchwork-Id: 9944639 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 B70F0604D4 for ; Fri, 8 Sep 2017 16:08:58 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 96F842881C for ; Fri, 8 Sep 2017 16:08:58 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8B7C128821; Fri, 8 Sep 2017 16:08:58 +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=-4.1 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id D2C6D2881C for ; Fri, 8 Sep 2017 16:08:57 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1dqLnp-00063q-9g; Fri, 08 Sep 2017 16:06:45 +0000 Received: from mail6.bemta5.messagelabs.com ([195.245.231.135]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1dqLno-00062X-84 for xen-devel@lists.xen.org; Fri, 08 Sep 2017 16:06:44 +0000 Received: from [85.158.139.211] by server-7.bemta-5.messagelabs.com id 04/C4-06889-310C2B95; Fri, 08 Sep 2017 16:06:43 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrLIsWRWlGSWpSXmKPExsUSfTyjVVfowKZ Ig49d1hZLPi5mcWD0OLr7N1MAYxRrZl5SfkUCa0bP7gtsBb0eFc+3XGJtYFxr3sXIycEsYC3R +6+ZsYuRi4NF4ByLRP/NW8wQzjEWiQs33zKCVAkJuEtMfbCIFSQhJDCfUWLbr19sMInOpYcZI RJLGSV2fzkNlmATMJB49fUbWLeIgLTEtc+XwYqYBf4zS1x7+husSFjAT2LxosXsIDaLgKrEhN 0bwBp4BdwkXjdtB7MlBOQkbp7rZAaxOYG2rb7wHqieA2ibm8T8BjWIckGJkzOfsICEmQXUJdb PE4J4TV6ieets5gmMwrOQVM1CqJqFpGoBI/MqRo3i1KKy1CJdI0u9pKLM9IyS3MTMHF1DA1O9 3NTi4sT01JzEpGK95PzcTYzAQK9nYGDcwXh5i98hRkkOJiVRXpmeTZFCfEn5KZUZicUZ8UWlO anFhxhlODiUJHjt9gPlBItS01Mr0jJzgDEHk5bg4FES4X2zDyjNW1yQmFucmQ6ROsVozHFs0+ U/TBwdN+/+YRJiycvPS5US530MUioAUppRmgc3CJYKLjHKSgnzMjIwMAjxFKQW5WaWoMq/YhT nYFQS5nUAuYcnM68Ebt8roFOYgE4peb4B5JSSRISUVAPj3CzblgrGxLRph5JYLgesiVs3l/Hc M60ihdI7IimxT17tlTE3te+L7EpPOjPjr/Lby5xr37Bwiuj25nUdTv+ZmJS55sKtmIQDnQdVv modj9BfpMTHubnyxGw7ueJm1Q2ns35Wss35vu13sv4d3ivW9it9yp+ft1BKFZhUHFbIYSpm65 S8O1KJpTgj0VCLuag4EQCn15cwAAMAAA== X-Env-Sender: aisaila@bitdefender.com X-Msg-Ref: server-3.tower-206.messagelabs.com!1504886802!107178675!1 X-Originating-IP: [91.199.104.133] X-SpamReason: No, hits=0.0 required=7.0 tests= X-StarScan-Received: X-StarScan-Version: 9.4.45; banners=-,-,- X-VirusChecked: Checked Received: (qmail 9511 invoked from network); 8 Sep 2017 16:06:42 -0000 Received: from mx02.bbu.dsd.mx.bitdefender.com (HELO mx02.buh.bitdefender.com) (91.199.104.133) by server-3.tower-206.messagelabs.com with DHE-RSA-AES128-GCM-SHA256 encrypted SMTP; 8 Sep 2017 16:06:42 -0000 Comment: DomainKeys? See http://domainkeys.sourceforge.net/ DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=default; d=bitdefender.com; b=Np+8z1XH8Ap5RU2C64iU571smJqhOo6abPxI3ReiOZp9O0b3nPA8A5iqaUJXIwcUvaa5iKtGkEOJwLPDI35HQfEp2Vz/vyOcZoyz7WBttNIUNhLeOEWg1wIkJ900wIPPGGffROcvjfz8HhoCIZN/E8Hvzq7U6ocv6u4N2ZbPaP7/+7ldUBA4RofQIwXw5kJMiO98oXz1NlBpVkvVdazjjE1krth3qwGLjw38XbnRufX1oUFPKjpT6BumT8yX2xMUBBNDozza0Pmi98c6z3j0iE6QldQyw341cOPRSsDMFzdp+JlApz0ZWTu5Z8Wyw/Qbg3nQFF6/+3+ll07tcaYxDQ==; h=Received:Received:Received:Received:From:To:Cc:Subject:Date:Message-Id:X-Mailer:In-Reply-To:References:MIME-Version:Content-Type:Content-Transfer-Encoding; DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=bitdefender.com; h=from:to :cc:subject:date:message-id:in-reply-to:references:mime-version :content-type:content-transfer-encoding; s=default; bh=BCG2eooKK wCt9aZYcqIX8gsDB7I=; b=JLfC3xJPABqAi6MISlauy4Qajry/YrCoLbnnpFUxJ DIRq+6MjbGmEq3BKkTe8cTyhGSUFLzPS6k2B5F74X+++qydrjWaUQw4P4NBvwhZP J+MUYv36vJAg1ZKdIN9G5Cq79Z1MyfwDXb95kkJaSaPb23vwKX2nC5V3YjOk4D5o 575Wsr4dCiuR0vYXXgfbSlkQojFIDLsD02T9RueQtZr2iKvbVXS2DnxPVO3vBnqc g0YEaFsGHCkGkyt8uixLPYf23qOL0mhlzihvGu3i9ISblR9nV+zHD56kMR5Gn9nS iW3D7dCSjW2wLWxK7U2lO/GIzLcWc5qYTPh8kIfSoH+xg== Received: (qmail 25113 invoked from network); 8 Sep 2017 19:06:39 +0300 Received: from mx01robo.bbu.dsd.mx.bitdefender.com (10.17.80.60) by mx02.buh.bitdefender.com with AES128-GCM-SHA256 encrypted SMTP; 8 Sep 2017 19:06:39 +0300 Received: (qmail 10205 invoked from network); 8 Sep 2017 19:06:39 +0300 Received: from unknown (HELO aisaila-Latitude-E5570.dsd.bitdefender.biz) (10.10.195.54) by mx01robo.bbu.dsd.mx.bitdefender.com with SMTP; 8 Sep 2017 19:06:39 +0300 From: Alexandru Isaila To: xen-devel@lists.xen.org Date: Fri, 8 Sep 2017 19:05:36 +0300 Message-Id: <1504886736-1823-5-git-send-email-aisaila@bitdefender.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1504886736-1823-1-git-send-email-aisaila@bitdefender.com> References: <1504886736-1823-1-git-send-email-aisaila@bitdefender.com> MIME-Version: 1.0 Cc: jun.nakajima@intel.com, kevin.tian@intel.com, sstabellini@kernel.org, wei.liu2@citrix.com, suravee.suthikulpanit@amd.com, Razvan Cojocaru , george.dunlap@eu.citrix.com, andrew.cooper3@citrix.com, =?UTF-8?q?Mihai=20Don=C8=9Bu?= , tim@xen.org, paul.durrant@citrix.com, Jan Beulich , Alexandru Isaila , boris.ostrovsky@oracle.com, ian.jackson@eu.citrix.com Subject: [Xen-devel] [PATCH v2 4/4] x86/hvm: Implement hvmemul_write() using real mappings X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xen.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP From: Andrew Cooper An access which crosses a page boundary is performed atomically by x86 hardware, albeit with a severe performance penalty. An important corner case is when a straddled access hits two pages which differ in whether a translation exists, or in net access rights. The use of hvm_copy*() in hvmemul_write() is problematic, because it performs a translation then completes the partial write, before moving onto the next translation. If an individual emulated write straddles two pages, the first of which is writable, and the second of which is not, the first half of the write will complete before #PF is raised from the second half. This results in guest state corruption as a side effect of emulation, which has been observed to cause windows to crash while under introspection. Introduce the hvmemul_{,un}map_linear_addr() helpers, which translate an entire contents of a linear access, and vmap() the underlying frames to provide a contiguous virtual mapping for the emulator to use. This is the same mechanism as used by the shadow emulation code. This will catch any translation issues and abort the emulation before any modifications occur. Signed-off-by: Andrew Cooper Signed-off-by: Alexandru Isaila --- CC: Jan Beulich CC: Paul Durrant CC: Razvan Cojocaru CC: Mihai Donțu Changes since V1: - Moved ASSERT to the begining of the loop - Corrected the decrement on mfn int the while statement - Modified the comment to PAGE_SIZE+1 While the maximum size of linear mapping is capped at 1 page, the logic in the helpers is written to work properly as hvmemul_ctxt->mfn[] gets longer, specifically with XSAVE instruction emulation in mind. This has only had light testing so far. --- xen/arch/x86/hvm/emulate.c | 179 ++++++++++++++++++++++++++++++++++---- xen/include/asm-x86/hvm/emulate.h | 7 ++ 2 files changed, 169 insertions(+), 17 deletions(-) diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c index c871cb3..196a77c 100644 --- a/xen/arch/x86/hvm/emulate.c +++ b/xen/arch/x86/hvm/emulate.c @@ -498,6 +498,159 @@ static int hvmemul_do_mmio_addr(paddr_t mmio_gpa, } /* + * Map the frame(s) covering an individual linear access, for writeable + * access. May return NULL for MMIO, or ERR_PTR(~X86EMUL_*) for other errors + * including ERR_PTR(~X86EMUL_OKAY) for write-discard mappings. + * + * In debug builds, map() checks that each slot in hvmemul_ctxt->mfn[] is + * clean before use, and poisions unused slots with INVALID_MFN. + */ +static void *hvmemul_map_linear_addr( + unsigned long linear, unsigned int bytes, uint32_t pfec, + struct hvm_emulate_ctxt *hvmemul_ctxt) +{ + struct vcpu *curr = current; + void *err, *mapping; + + /* First and final gfns which need mapping. */ + unsigned long frame = linear >> PAGE_SHIFT, first = frame; + unsigned long final = (linear + bytes - !!bytes) >> PAGE_SHIFT; + + /* + * mfn points to the next free slot. All used slots have a page reference + * held on them. + */ + mfn_t *mfn = &hvmemul_ctxt->mfn[0]; + + /* + * The caller has no legitimate reason for trying a zero-byte write, but + * final is calculate to fail safe in release builds. + * + * The maximum write size depends on the number of adjacent mfns[] which + * can be vmap()'d, accouting for possible misalignment within the region. + * The higher level emulation callers are responsible for ensuring that + * mfns[] is large enough for the requested write size. + */ + if ( bytes == 0 || + final - first > ARRAY_SIZE(hvmemul_ctxt->mfn) - 1 ) + { + ASSERT_UNREACHABLE(); + goto unhandleable; + } + + do { + enum hvm_translation_result res; + struct page_info *page; + pagefault_info_t pfinfo; + p2m_type_t p2mt; + + /* Error checking. Confirm that the current slot is clean. */ + ASSERT(mfn_x(*mfn) == 0); + + res = hvm_translate_get_page(curr, frame << PAGE_SHIFT, true, pfec, + &pfinfo, &page, NULL, &p2mt); + + switch ( res ) + { + case HVMTRANS_okay: + break; + + case HVMTRANS_bad_linear_to_gfn: + x86_emul_pagefault(pfinfo.ec, pfinfo.linear, &hvmemul_ctxt->ctxt); + err = ERR_PTR(~(long)X86EMUL_EXCEPTION); + goto out; + + case HVMTRANS_bad_gfn_to_mfn: + err = NULL; + goto out; + + case HVMTRANS_gfn_paged_out: + case HVMTRANS_gfn_shared: + err = ERR_PTR(~(long)X86EMUL_RETRY); + goto out; + + default: + goto unhandleable; + } + + *mfn++ = _mfn(page_to_mfn(page)); + frame++; + + if ( p2m_is_discard_write(p2mt) ) + { + err = ERR_PTR(~(long)X86EMUL_OKAY); + goto out; + } + + } while ( frame < final ); + + /* Entire access within a single frame? */ + if ( first == final ) + mapping = map_domain_page(hvmemul_ctxt->mfn[0]) + (linear & ~PAGE_MASK); + /* Multiple frames? Need to vmap(). */ + else if ( (mapping = vmap(hvmemul_ctxt->mfn, + mfn - hvmemul_ctxt->mfn)) == NULL ) + goto unhandleable; + +#ifndef NDEBUG /* Poision unused mfn[]s with INVALID_MFN. */ + while ( mfn < hvmemul_ctxt->mfn + ARRAY_SIZE(hvmemul_ctxt->mfn) ) + { + ASSERT(mfn_x(*mfn) == 0); + *mfn++ = INVALID_MFN; + } +#endif + + return mapping; + + unhandleable: + err = ERR_PTR(~(long)X86EMUL_UNHANDLEABLE); + + out: + /* Drop all held references. */ + while ( mfn-- > hvmemul_ctxt->mfn ) + put_page(mfn_to_page(mfn_x(*mfn))); + + return err; +} + +static void hvmemul_unmap_linear_addr( + void *mapping, unsigned long linear, unsigned int bytes, + struct hvm_emulate_ctxt *hvmemul_ctxt) +{ + struct domain *currd = current->domain; + unsigned long frame = linear >> PAGE_SHIFT; + unsigned long final = (linear + bytes - !!bytes) >> PAGE_SHIFT; + mfn_t *mfn = &hvmemul_ctxt->mfn[0]; + + ASSERT(bytes > 0); + + if ( frame == final ) + unmap_domain_page(mapping); + else + vunmap(mapping); + + do + { + ASSERT(mfn_valid(*mfn)); + paging_mark_dirty(currd, *mfn); + put_page(mfn_to_page(mfn_x(*mfn))); + + frame++; + *mfn++ = _mfn(0); /* Clean slot for map()'s error checking. */ + + } while ( frame < final ); + + +#ifndef NDEBUG /* Check (and clean) all unused mfns. */ + while ( mfn < hvmemul_ctxt->mfn + ARRAY_SIZE(hvmemul_ctxt->mfn) ) + { + ASSERT(mfn_eq(*mfn, INVALID_MFN)); + *mfn++ = _mfn(0); + } +#endif +} + +/* * Convert addr from linear to physical form, valid over the range * [addr, addr + *reps * bytes_per_rep]. *reps is adjusted according to * the valid computed range. It is always >0 when X86EMUL_OKAY is returned. @@ -987,11 +1140,11 @@ static int hvmemul_write( struct hvm_emulate_ctxt *hvmemul_ctxt = container_of(ctxt, struct hvm_emulate_ctxt, ctxt); struct vcpu *curr = current; - pagefault_info_t pfinfo; unsigned long addr, reps = 1; uint32_t pfec = PFEC_page_present | PFEC_write_access; struct hvm_vcpu_io *vio = &curr->arch.hvm_vcpu.hvm_io; int rc; + void *mapping; if ( is_x86_system_segment(seg) ) pfec |= PFEC_implicit; @@ -1007,23 +1160,15 @@ static int hvmemul_write( (vio->mmio_gla == (addr & PAGE_MASK)) ) return hvmemul_linear_mmio_write(addr, bytes, p_data, pfec, hvmemul_ctxt, 1); - rc = hvm_copy_to_guest_linear(addr, p_data, bytes, pfec, &pfinfo); - - switch ( rc ) - { - case HVMTRANS_okay: - break; - case HVMTRANS_bad_linear_to_gfn: - x86_emul_pagefault(pfinfo.ec, pfinfo.linear, &hvmemul_ctxt->ctxt); - return X86EMUL_EXCEPTION; - case HVMTRANS_bad_gfn_to_mfn: + mapping = hvmemul_map_linear_addr(addr, bytes, pfec, hvmemul_ctxt); + if ( IS_ERR(mapping) ) + return ~PTR_ERR(mapping); + else if ( !mapping ) return hvmemul_linear_mmio_write(addr, bytes, p_data, pfec, hvmemul_ctxt, 0); - case HVMTRANS_gfn_paged_out: - case HVMTRANS_gfn_shared: - return X86EMUL_RETRY; - default: - return X86EMUL_UNHANDLEABLE; - } + + memcpy(mapping, p_data, bytes); + + hvmemul_unmap_linear_addr(mapping, addr, bytes, hvmemul_ctxt); return X86EMUL_OKAY; } diff --git a/xen/include/asm-x86/hvm/emulate.h b/xen/include/asm-x86/hvm/emulate.h index 8864775..d379a4a 100644 --- a/xen/include/asm-x86/hvm/emulate.h +++ b/xen/include/asm-x86/hvm/emulate.h @@ -37,6 +37,13 @@ struct hvm_emulate_ctxt { unsigned long seg_reg_accessed; unsigned long seg_reg_dirty; + /* + * MFNs behind temporary mappings in the write callback. The length is + * arbitrary, and can be increased if writes longer than PAGE_SIZE+1 are + * needed. + */ + mfn_t mfn[2]; + uint32_t intr_shadow; bool_t set_context;