From patchwork Sun Apr 2 12:24:23 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yu Zhang X-Patchwork-Id: 9658447 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 649F3602BA for ; Sun, 2 Apr 2017 12:46:28 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 422C32835B for ; Sun, 2 Apr 2017 12:46:28 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 34ED9284A5; Sun, 2 Apr 2017 12:46:28 +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 D18BF2835B for ; Sun, 2 Apr 2017 12:46:26 +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 1cuerG-00033U-Jh; Sun, 02 Apr 2017 12:43:50 +0000 Received: from mail6.bemta3.messagelabs.com ([195.245.230.39]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1cuerF-00032u-Hz for xen-devel@lists.xen.org; Sun, 02 Apr 2017 12:43:49 +0000 Received: from [85.158.137.68] by server-12.bemta-3.messagelabs.com id B5/3F-12861-402F0E85; Sun, 02 Apr 2017 12:43:48 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFupkkeJIrShJLcpLzFFi42I5YG5SrMv86UG EwfNjUhZLPi5mcWD0OLr7N1MAYxRrZl5SfkUCa0bvGcuCR6cYK85PPMvawDhtEmMXIxcHi8At JonpV98xgThCAtMYJa51/WTuYuTkkBDglTiybAYrhO0nseVWPxuILSTQzijx8HkBiM0moC3xY /VvRhBbREBa4trny2BTmQWWMElc2LcOLCEskCex6elfJhCbRUBV4vyam2ALeAU8JA48X8YCsU BO4uSxyWDLOAU8JZ4u38AKscxDYsf/xewTGPkWMDKsYtQoTi0qSy3SNTTWSyrKTM8oyU3MzNE 1NDDWy00tLk5MT81JTCrWS87P3cQIDBYGINjBuG275yFGSQ4mJVHe78X3IoT4kvJTKjMSizPi i0pzUosPMcpwcChJ8HJ9fBAhJFiUmp5akZaZAwxbmLQEB4+SCG/hB6A0b3FBYm5xZjpE6hSjL sePVQfeMAmx5OXnpUqJ8zKBzBAAKcoozYMbAYuhS4yyUsK8jEBHCfEUpBblZpagyr9iFOdgVB LmZQeZwpOZVwK36RXQEUxAR1h8vQtyREkiQkqqgXHKnq8fVF40+yad+PtE0n9PQPpDsyCh0kd WYlUl0u6tK7YxVVuH9H53Oev4fA/zNjfj9S+OzTN5O9eHr838svT5ss1qC557sZ2Zq/940QPb A/nxpycvuLHtCncXS/+u3/mNnjejWg4V/n85ybkvW4lbavrM6iqO+X5Pm/I3tLOp5KeH272cP VmJpTgj0VCLuag4EQC/0hMOnAIAAA== X-Env-Sender: yu.c.zhang@linux.intel.com X-Msg-Ref: server-13.tower-31.messagelabs.com!1491137024!92931830!1 X-Originating-IP: [192.55.52.115] X-SpamReason: No, hits=0.0 required=7.0 tests= X-StarScan-Received: X-StarScan-Version: 9.2.3; banners=-,-,- X-VirusChecked: Checked Received: (qmail 48120 invoked from network); 2 Apr 2017 12:43:46 -0000 Received: from mga14.intel.com (HELO mga14.intel.com) (192.55.52.115) by server-13.tower-31.messagelabs.com with DHE-RSA-AES256-GCM-SHA384 encrypted SMTP; 2 Apr 2017 12:43:46 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=intel.com; i=@intel.com; q=dns/txt; s=intel; t=1491137026; x=1522673026; h=from:to:cc:subject:date:message-id:in-reply-to: references; bh=sGquOy7nWSJFHXBLKRwo/fY1g2gmoyJaRM6MB0HmBBc=; b=XzBkAUj2858awubX/0du1eGF5hROunyEfbKvInW9TobCz9cOPcG/DY0Q 1PLPQyHDMSIObjt1Z5ovO0wyGkBZvQ==; Received: from orsmga005.jf.intel.com ([10.7.209.41]) by fmsmga103.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 02 Apr 2017 05:43:44 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.36,263,1486454400"; d="scan'208";a="81958885" Received: from zhangyu-optiplex-9020.bj.intel.com ([10.238.135.159]) by orsmga005.jf.intel.com with ESMTP; 02 Apr 2017 05:43:42 -0700 From: Yu Zhang To: xen-devel@lists.xen.org Date: Sun, 2 Apr 2017 20:24:23 +0800 Message-Id: <1491135867-623-3-git-send-email-yu.c.zhang@linux.intel.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1491135867-623-1-git-send-email-yu.c.zhang@linux.intel.com> References: <1491135867-623-1-git-send-email-yu.c.zhang@linux.intel.com> Cc: Kevin Tian , Jun Nakajima , George Dunlap , Andrew Cooper , Tim Deegan , Paul Durrant , zhiyuan.lv@intel.com, Jan Beulich Subject: [Xen-devel] [PATCH v10 2/6] x86/ioreq server: Add DMOP to map guest ram with p2m_ioreq_server to an ioreq server. 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: , MIME-Version: 1.0 Errors-To: xen-devel-bounces@lists.xen.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP Previously, p2m_ioreq_server is used to write-protect guest ram pages, which are tracked with ioreq server's rangeset. However, number of ram pages to be tracked may exceed the upper limit of rangeset. Now, a new DMOP - XEN_DMOP_map_mem_type_to_ioreq_server, is added to let one ioreq server claim/disclaim its responsibility for the handling of guest pages with p2m type p2m_ioreq_server. Users of this DMOP can specify which kind of operation is supposed to be emulated in a parameter named flags. Currently, this DMOP only support the emulation of write operations. And it can be further extended to support the emulation of read ones if an ioreq server has such requirement in the future. For now, we only support one ioreq server for this p2m type, so once an ioreq server has claimed its ownership, subsequent calls of the XEN_DMOP_map_mem_type_to_ioreq_server will fail. Users can also disclaim the ownership of guest ram pages with p2m_ioreq_server, by triggering this new DMOP, with ioreq server id set to the current owner's and flags parameter set to 0. Note: a> both XEN_DMOP_map_mem_type_to_ioreq_server and p2m_ioreq_server are only supported for HVMs with HAP enabled. b> only after one ioreq server claims its ownership of p2m_ioreq_server, will the p2m type change to p2m_ioreq_server be allowed. c> this patch shall be accepted together with the following ones in this series. Signed-off-by: Paul Durrant Signed-off-by: Yu Zhang Acked-by: Tim Deegan Reviewed-by: Jan Beulich Reviewed-by: George Dunlap --- Cc: Jan Beulich Cc: Andrew Cooper Cc: Paul Durrant Cc: George Dunlap Cc: Jun Nakajima Cc: Kevin Tian Cc: Tim Deegan changes in v10: - According to comments from Jan: add a new patch for the libdevicemodel and libxc interface. - According to comments from Jan: remove p2m_destroy_ioreq_server(), use p2m_set_ioreq_server(d, 0, s) instead. - According to comments from Jan & Kevin: comments changes in hvmemul_do_io(). - According to comments from Jan & Kevin: commit message changes. changes in v8: - According to comments from Jan & Paul: comments changes in hvmemul_do_io(). - According to comments from Jan: remove the redundant code which would only be useful for read emulations. - According to comments from Jan: change interface which maps mem type to ioreq server, removed uint16_t pad and added an uint64_t opaque. - Address other comments from Jan, i.e. correct return values; remove stray cast. changes in v7: - Use new ioreq server interface - XEN_DMOP_map_mem_type_to_ioreq_server. - According to comments from George: removed domain_pause/unpause() in hvm_map_mem_type_to_ioreq_server(), because it's too expensive, and we can avoid the: a> deadlock issue existed in v6 patch, between p2m lock and ioreq server lock by using these locks in the same order - solved in patch 4; b> for race condition between vm exit and ioreq server unbinding, we can just retry this instruction. - According to comments from Jan and George: continue to clarify logic in hvmemul_do_io(). - According to comments from Jan: clarify comment in p2m_set_ioreq_server(). changes in v6: - Clarify logic in hvmemul_do_io(). - Use recursive lock for ioreq server lock. - Remove debug print when mapping ioreq server. - Clarify code in ept_p2m_type_to_flags() for consistency. - Remove definition of P2M_IOREQ_HANDLE_WRITE_ACCESS. - Add comments for HVMMEM_ioreq_server to note only changes to/from HVMMEM_ram_rw are permitted. - Add domain_pause/unpause() in hvm_map_mem_type_to_ioreq_server() to avoid the race condition when a vm exit happens on a write- protected page, just to find the ioreq server has been unmapped already. - Introduce a seperate patch to delay the release of p2m lock to avoid the race condition. - Introduce a seperate patch to handle the read-modify-write operations on a write protected page. changes in v5: - Simplify logic in hvmemul_do_io(). - Use natual width types instead of fixed width types when possible. - Do not grant executable permission for p2m_ioreq_server entries. - Clarify comments and commit message. - Introduce a seperate patch to recalculate the p2m types after the ioreq server unmaps the p2m_ioreq_server. changes in v4: - According to Paul's advice, add comments around the definition of HVMMEM_iore_server in hvm_op.h. - According to Wei Liu's comments, change the format of the commit message. changes in v3: - Only support write emulation in this patch; - Remove the code to handle race condition in hvmemul_do_io(), - No need to reset the p2m type after an ioreq server has disclaimed its ownership of p2m_ioreq_server; - Only allow p2m type change to p2m_ioreq_server after an ioreq server has claimed its ownership of p2m_ioreq_server; - Only allow p2m type change to p2m_ioreq_server from pages with type p2m_ram_rw, and vice versa; - HVMOP_map_mem_type_to_ioreq_server interface change - use uint16, instead of enum to specify the memory type; - Function prototype change to p2m_get_ioreq_server(); - Coding style changes; - Commit message changes; - Add Tim's Acked-by. changes in v2: - Only support HAP enabled HVMs; - Replace p2m_mem_type_changed() with p2m_change_entry_type_global() to reset the p2m type, when an ioreq server tries to claim/disclaim its ownership of p2m_ioreq_server; - Comments changes. --- xen/arch/x86/hvm/dm.c | 35 +++++++++++++++++++++-- xen/arch/x86/hvm/emulate.c | 61 ++++++++++++++++++++++++++++++++++++++--- xen/arch/x86/hvm/ioreq.c | 44 +++++++++++++++++++++++++++++ xen/arch/x86/mm/p2m-ept.c | 8 +++++- xen/arch/x86/mm/p2m-pt.c | 19 +++++++++---- xen/arch/x86/mm/p2m.c | 58 +++++++++++++++++++++++++++++++++++++++ xen/arch/x86/mm/shadow/multi.c | 3 +- xen/include/asm-x86/hvm/ioreq.h | 2 ++ xen/include/asm-x86/p2m.h | 25 +++++++++++++++-- xen/include/public/hvm/dm_op.h | 28 +++++++++++++++++++ xen/include/public/hvm/hvm_op.h | 8 +++++- 11 files changed, 272 insertions(+), 19 deletions(-) diff --git a/xen/arch/x86/hvm/dm.c b/xen/arch/x86/hvm/dm.c index 333c884..7e0da81 100644 --- a/xen/arch/x86/hvm/dm.c +++ b/xen/arch/x86/hvm/dm.c @@ -173,9 +173,14 @@ static int modified_memory(struct domain *d, static bool allow_p2m_type_change(p2m_type_t old, p2m_type_t new) { + if ( new == p2m_ioreq_server ) + return old == p2m_ram_rw; + + if ( old == p2m_ioreq_server ) + return new == p2m_ram_rw; + return p2m_is_ram(old) || - (p2m_is_hole(old) && new == p2m_mmio_dm) || - (old == p2m_ioreq_server && new == p2m_ram_rw); + (p2m_is_hole(old) && new == p2m_mmio_dm); } static int set_mem_type(struct domain *d, @@ -202,6 +207,18 @@ static int set_mem_type(struct domain *d, unlikely(data->mem_type == HVMMEM_unused) ) return -EINVAL; + if ( data->mem_type == HVMMEM_ioreq_server ) + { + unsigned int flags; + + if ( !hap_enabled(d) ) + return -EOPNOTSUPP; + + /* Do not change to HVMMEM_ioreq_server if no ioreq server mapped. */ + if ( !p2m_get_ioreq_server(d, &flags) ) + return -EINVAL; + } + while ( iter < data->nr ) { unsigned long pfn = data->first_pfn + iter; @@ -365,6 +382,20 @@ static int dm_op(domid_t domid, break; } + case XEN_DMOP_map_mem_type_to_ioreq_server: + { + const struct xen_dm_op_map_mem_type_to_ioreq_server *data = + &op.u.map_mem_type_to_ioreq_server; + + rc = -EOPNOTSUPP; + if ( !hap_enabled(d) ) + break; + + rc = hvm_map_mem_type_to_ioreq_server(d, data->id, + data->type, data->flags); + break; + } + case XEN_DMOP_set_ioreq_server_state: { const struct xen_dm_op_set_ioreq_server_state *data = diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c index 2d92957..dc6f1f2 100644 --- a/xen/arch/x86/hvm/emulate.c +++ b/xen/arch/x86/hvm/emulate.c @@ -100,6 +100,7 @@ static int hvmemul_do_io( uint8_t dir, bool_t df, bool_t data_is_addr, uintptr_t data) { struct vcpu *curr = current; + struct domain *currd = curr->domain; struct hvm_vcpu_io *vio = &curr->arch.hvm_vcpu.hvm_io; ioreq_t p = { .type = is_mmio ? IOREQ_TYPE_COPY : IOREQ_TYPE_PIO, @@ -141,7 +142,7 @@ static int hvmemul_do_io( (p.dir != dir) || (p.df != df) || (p.data_is_ptr != data_is_addr) ) - domain_crash(curr->domain); + domain_crash(currd); if ( data_is_addr ) return X86EMUL_UNHANDLEABLE; @@ -178,8 +179,60 @@ static int hvmemul_do_io( break; case X86EMUL_UNHANDLEABLE: { - struct hvm_ioreq_server *s = - hvm_select_ioreq_server(curr->domain, &p); + /* + * Xen isn't emulating the instruction internally, so see if there's + * an ioreq server that can handle it. + * + * Rules: + * A> PIO or MMIO accesses run through hvm_select_ioreq_server() to + * choose the ioreq server by range. If no server is found, the access + * is ignored. + * + * B> p2m_ioreq_server accesses are handled by the designated + * ioreq server for the domain, but there are some corner cases: + * + * - If the domain ioreq server is NULL, it's likely we suffer from + * a race with an unmap operation on the ioreq server, so re-try the + * instruction. + * + * Note: Even when an ioreq server is found, its value could become + * stale later, because it is possible that + * + * - the PIO or MMIO address is removed from the rangeset of the + * ioreq server, before the event is delivered to the device model. + * + * - the p2m_ioreq_server type is unmapped from the ioreq server, + * before the event is delivered to the device model. + * + * However, there's no cheap approach to avoid above situations in xen, + * so the device model side needs to check the incoming ioreq event. + */ + struct hvm_ioreq_server *s = NULL; + p2m_type_t p2mt = p2m_invalid; + + if ( is_mmio ) + { + unsigned long gmfn = paddr_to_pfn(addr); + + get_gfn_query_unlocked(currd, gmfn, &p2mt); + + if ( p2mt == p2m_ioreq_server ) + { + unsigned int flags; + + s = p2m_get_ioreq_server(currd, &flags); + + if ( s == NULL ) + { + rc = X86EMUL_RETRY; + vio->io_req.state = STATE_IOREQ_NONE; + break; + } + } + } + + if ( !s ) + s = hvm_select_ioreq_server(currd, &p); /* If there is no suitable backing DM, just ignore accesses */ if ( !s ) @@ -190,7 +243,7 @@ static int hvmemul_do_io( else { rc = hvm_send_ioreq(s, &p, 0); - if ( rc != X86EMUL_RETRY || curr->domain->is_shutting_down ) + if ( rc != X86EMUL_RETRY || currd->is_shutting_down ) vio->io_req.state = STATE_IOREQ_NONE; else if ( data_is_addr ) rc = X86EMUL_OKAY; diff --git a/xen/arch/x86/hvm/ioreq.c b/xen/arch/x86/hvm/ioreq.c index ad2edad..5bf3b6d 100644 --- a/xen/arch/x86/hvm/ioreq.c +++ b/xen/arch/x86/hvm/ioreq.c @@ -753,6 +753,8 @@ int hvm_destroy_ioreq_server(struct domain *d, ioservid_t id) domain_pause(d); + p2m_set_ioreq_server(d, 0, s); + hvm_ioreq_server_disable(s, 0); list_del(&s->list_entry); @@ -914,6 +916,48 @@ int hvm_unmap_io_range_from_ioreq_server(struct domain *d, ioservid_t id, return rc; } +/* + * Map or unmap an ioreq server to specific memory type. For now, only + * HVMMEM_ioreq_server is supported, and in the future new types can be + * introduced, e.g. HVMMEM_ioreq_serverX mapped to ioreq server X. And + * currently, only write operations are to be forwarded to an ioreq server. + * Support for the emulation of read operations can be added when an ioreq + * server has such requirement in the future. + */ +int hvm_map_mem_type_to_ioreq_server(struct domain *d, ioservid_t id, + uint32_t type, uint32_t flags) +{ + struct hvm_ioreq_server *s; + int rc; + + if ( type != HVMMEM_ioreq_server ) + return -EINVAL; + + if ( flags & ~XEN_DMOP_IOREQ_MEM_ACCESS_WRITE ) + return -EINVAL; + + spin_lock_recursive(&d->arch.hvm_domain.ioreq_server.lock); + + rc = -ENOENT; + list_for_each_entry ( s, + &d->arch.hvm_domain.ioreq_server.list, + list_entry ) + { + if ( s == d->arch.hvm_domain.default_ioreq_server ) + continue; + + if ( s->id == id ) + { + rc = p2m_set_ioreq_server(d, flags, s); + break; + } + } + + spin_unlock_recursive(&d->arch.hvm_domain.ioreq_server.lock); + + return rc; +} + int hvm_set_ioreq_server_state(struct domain *d, ioservid_t id, bool_t enabled) { diff --git a/xen/arch/x86/mm/p2m-ept.c b/xen/arch/x86/mm/p2m-ept.c index 568944f..cc1eb21 100644 --- a/xen/arch/x86/mm/p2m-ept.c +++ b/xen/arch/x86/mm/p2m-ept.c @@ -131,6 +131,13 @@ static void ept_p2m_type_to_flags(struct p2m_domain *p2m, ept_entry_t *entry, entry->r = entry->w = entry->x = 1; entry->a = entry->d = !!cpu_has_vmx_ept_ad; break; + case p2m_ioreq_server: + entry->r = 1; + entry->w = !(p2m->ioreq.flags & XEN_DMOP_IOREQ_MEM_ACCESS_WRITE); + entry->x = 0; + entry->a = !!cpu_has_vmx_ept_ad; + entry->d = entry->w && entry->a; + break; case p2m_mmio_direct: entry->r = entry->x = 1; entry->w = !rangeset_contains_singleton(mmio_ro_ranges, @@ -170,7 +177,6 @@ static void ept_p2m_type_to_flags(struct p2m_domain *p2m, ept_entry_t *entry, entry->a = entry->d = !!cpu_has_vmx_ept_ad; break; case p2m_grant_map_ro: - case p2m_ioreq_server: entry->r = 1; entry->w = entry->x = 0; entry->a = !!cpu_has_vmx_ept_ad; diff --git a/xen/arch/x86/mm/p2m-pt.c b/xen/arch/x86/mm/p2m-pt.c index 268b232..c0055f3 100644 --- a/xen/arch/x86/mm/p2m-pt.c +++ b/xen/arch/x86/mm/p2m-pt.c @@ -70,7 +70,9 @@ static const unsigned long pgt[] = { PGT_l3_page_table }; -static unsigned long p2m_type_to_flags(p2m_type_t t, mfn_t mfn, +static unsigned long p2m_type_to_flags(const struct p2m_domain *p2m, + p2m_type_t t, + mfn_t mfn, unsigned int level) { unsigned long flags; @@ -92,8 +94,12 @@ static unsigned long p2m_type_to_flags(p2m_type_t t, mfn_t mfn, default: return flags | _PAGE_NX_BIT; case p2m_grant_map_ro: - case p2m_ioreq_server: return flags | P2M_BASE_FLAGS | _PAGE_NX_BIT; + case p2m_ioreq_server: + flags |= P2M_BASE_FLAGS | _PAGE_RW | _PAGE_NX_BIT; + if ( p2m->ioreq.flags & XEN_DMOP_IOREQ_MEM_ACCESS_WRITE ) + return flags & ~_PAGE_RW; + return flags; case p2m_ram_ro: case p2m_ram_logdirty: case p2m_ram_shared: @@ -440,7 +446,8 @@ static int do_recalc(struct p2m_domain *p2m, unsigned long gfn) p2m_type_t p2mt = p2m_is_logdirty_range(p2m, gfn & mask, gfn | ~mask) ? p2m_ram_logdirty : p2m_ram_rw; unsigned long mfn = l1e_get_pfn(e); - unsigned long flags = p2m_type_to_flags(p2mt, _mfn(mfn), level); + unsigned long flags = p2m_type_to_flags(p2m, p2mt, + _mfn(mfn), level); if ( level ) { @@ -578,7 +585,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, ASSERT(!mfn_valid(mfn) || p2mt != p2m_mmio_direct); l3e_content = mfn_valid(mfn) || p2m_allows_invalid_mfn(p2mt) ? l3e_from_pfn(mfn_x(mfn), - p2m_type_to_flags(p2mt, mfn, 2) | _PAGE_PSE) + p2m_type_to_flags(p2m, p2mt, mfn, 2) | _PAGE_PSE) : l3e_empty(); entry_content.l1 = l3e_content.l3; @@ -615,7 +622,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, if ( mfn_valid(mfn) || p2m_allows_invalid_mfn(p2mt) ) entry_content = p2m_l1e_from_pfn(mfn_x(mfn), - p2m_type_to_flags(p2mt, mfn, 0)); + p2m_type_to_flags(p2m, p2mt, mfn, 0)); else entry_content = l1e_empty(); @@ -652,7 +659,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, unsigned long gfn, mfn_t mfn, ASSERT(!mfn_valid(mfn) || p2mt != p2m_mmio_direct); if ( mfn_valid(mfn) || p2m_allows_invalid_mfn(p2mt) ) l2e_content = l2e_from_pfn(mfn_x(mfn), - p2m_type_to_flags(p2mt, mfn, 1) | + p2m_type_to_flags(p2m, p2mt, mfn, 1) | _PAGE_PSE); else l2e_content = l2e_empty(); diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c index d38004c..b84add0 100644 --- a/xen/arch/x86/mm/p2m.c +++ b/xen/arch/x86/mm/p2m.c @@ -82,6 +82,8 @@ static int p2m_initialise(struct domain *d, struct p2m_domain *p2m) else p2m_pt_init(p2m); + spin_lock_init(&p2m->ioreq.lock); + return ret; } @@ -286,6 +288,62 @@ void p2m_memory_type_changed(struct domain *d) } } +int p2m_set_ioreq_server(struct domain *d, + unsigned int flags, + struct hvm_ioreq_server *s) +{ + struct p2m_domain *p2m = p2m_get_hostp2m(d); + int rc; + + /* + * Use lock to prevent concurrent setting attempts + * from multiple ioreq servers. + */ + spin_lock(&p2m->ioreq.lock); + + /* Unmap ioreq server from p2m type by passing flags with 0. */ + if ( flags == 0 ) + { + rc = -EINVAL; + if ( p2m->ioreq.server != s ) + goto out; + + p2m->ioreq.server = NULL; + p2m->ioreq.flags = 0; + } + else + { + rc = -EBUSY; + if ( p2m->ioreq.server != NULL ) + goto out; + + p2m->ioreq.server = s; + p2m->ioreq.flags = flags; + } + + rc = 0; + + out: + spin_unlock(&p2m->ioreq.lock); + + return rc; +} + +struct hvm_ioreq_server *p2m_get_ioreq_server(struct domain *d, + unsigned int *flags) +{ + struct p2m_domain *p2m = p2m_get_hostp2m(d); + struct hvm_ioreq_server *s; + + spin_lock(&p2m->ioreq.lock); + + s = p2m->ioreq.server; + *flags = p2m->ioreq.flags; + + spin_unlock(&p2m->ioreq.lock); + return s; +} + void p2m_enable_hardware_log_dirty(struct domain *d) { struct p2m_domain *p2m = p2m_get_hostp2m(d); diff --git a/xen/arch/x86/mm/shadow/multi.c b/xen/arch/x86/mm/shadow/multi.c index 4798c93..5195d61 100644 --- a/xen/arch/x86/mm/shadow/multi.c +++ b/xen/arch/x86/mm/shadow/multi.c @@ -3306,8 +3306,7 @@ static int sh_page_fault(struct vcpu *v, } /* Need to hand off device-model MMIO to the device model */ - if ( p2mt == p2m_mmio_dm - || (p2mt == p2m_ioreq_server && ft == ft_demand_write) ) + if ( p2mt == p2m_mmio_dm ) { gpa = guest_walk_to_gpa(&gw); goto mmio; diff --git a/xen/include/asm-x86/hvm/ioreq.h b/xen/include/asm-x86/hvm/ioreq.h index fbf2c74..b43667a 100644 --- a/xen/include/asm-x86/hvm/ioreq.h +++ b/xen/include/asm-x86/hvm/ioreq.h @@ -37,6 +37,8 @@ int hvm_map_io_range_to_ioreq_server(struct domain *d, ioservid_t id, int hvm_unmap_io_range_from_ioreq_server(struct domain *d, ioservid_t id, uint32_t type, uint64_t start, uint64_t end); +int hvm_map_mem_type_to_ioreq_server(struct domain *d, ioservid_t id, + uint32_t type, uint32_t flags); int hvm_set_ioreq_server_state(struct domain *d, ioservid_t id, bool_t enabled); diff --git a/xen/include/asm-x86/p2m.h b/xen/include/asm-x86/p2m.h index bc189d1..4521620 100644 --- a/xen/include/asm-x86/p2m.h +++ b/xen/include/asm-x86/p2m.h @@ -89,7 +89,8 @@ typedef unsigned int p2m_query_t; | p2m_to_mask(p2m_ram_paging_out) \ | p2m_to_mask(p2m_ram_paged) \ | p2m_to_mask(p2m_ram_paging_in) \ - | p2m_to_mask(p2m_ram_shared)) + | p2m_to_mask(p2m_ram_shared) \ + | p2m_to_mask(p2m_ioreq_server)) /* Types that represent a physmap hole that is ok to replace with a shared * entry */ @@ -111,8 +112,7 @@ typedef unsigned int p2m_query_t; #define P2M_RO_TYPES (p2m_to_mask(p2m_ram_logdirty) \ | p2m_to_mask(p2m_ram_ro) \ | p2m_to_mask(p2m_grant_map_ro) \ - | p2m_to_mask(p2m_ram_shared) \ - | p2m_to_mask(p2m_ioreq_server)) + | p2m_to_mask(p2m_ram_shared)) /* Write-discard types, which should discard the write operations */ #define P2M_DISCARD_WRITE_TYPES (p2m_to_mask(p2m_ram_ro) \ @@ -336,6 +336,20 @@ struct p2m_domain { struct ept_data ept; /* NPT-equivalent structure could be added here. */ }; + + struct { + spinlock_t lock; + /* + * ioreq server who's responsible for the emulation of + * gfns with specific p2m type(for now, p2m_ioreq_server). + */ + struct hvm_ioreq_server *server; + /* + * flags specifies whether read, write or both operations + * are to be emulated by an ioreq server. + */ + unsigned int flags; + } ioreq; }; /* get host p2m table */ @@ -827,6 +841,11 @@ static inline unsigned int p2m_get_iommu_flags(p2m_type_t p2mt, mfn_t mfn) return flags; } +int p2m_set_ioreq_server(struct domain *d, unsigned int flags, + struct hvm_ioreq_server *s); +struct hvm_ioreq_server *p2m_get_ioreq_server(struct domain *d, + unsigned int *flags); + #endif /* _XEN_ASM_X86_P2M_H */ /* diff --git a/xen/include/public/hvm/dm_op.h b/xen/include/public/hvm/dm_op.h index f54cece..5ea79ef 100644 --- a/xen/include/public/hvm/dm_op.h +++ b/xen/include/public/hvm/dm_op.h @@ -318,6 +318,32 @@ struct xen_dm_op_inject_msi { uint64_aligned_t addr; }; +/* + * XEN_DMOP_map_mem_type_to_ioreq_server : map or unmap the IOREQ Server + * to specific memory type + * for specific accesses + * + * For now, flags only accept the value of XEN_DMOP_IOREQ_MEM_ACCESS_WRITE, + * which means only write operations are to be forwarded to an ioreq server. + * Support for the emulation of read operations can be added when an ioreq + * server has such requirement in future. + */ +#define XEN_DMOP_map_mem_type_to_ioreq_server 15 + +struct xen_dm_op_map_mem_type_to_ioreq_server { + ioservid_t id; /* IN - ioreq server id */ + uint16_t type; /* IN - memory type */ + uint32_t flags; /* IN - types of accesses to be forwarded to the + ioreq server. flags with 0 means to unmap the + ioreq server */ + +#define XEN_DMOP_IOREQ_MEM_ACCESS_READ (1u << 0) +#define XEN_DMOP_IOREQ_MEM_ACCESS_WRITE (1u << 1) + + uint64_t opaque; /* IN/OUT - only used for hypercall continuation, + has to be set to zero by the caller */ +}; + struct xen_dm_op { uint32_t op; uint32_t pad; @@ -336,6 +362,8 @@ struct xen_dm_op { struct xen_dm_op_set_mem_type set_mem_type; struct xen_dm_op_inject_event inject_event; struct xen_dm_op_inject_msi inject_msi; + struct xen_dm_op_map_mem_type_to_ioreq_server + map_mem_type_to_ioreq_server; } u; }; diff --git a/xen/include/public/hvm/hvm_op.h b/xen/include/public/hvm/hvm_op.h index bc00ef0..0bdafdf 100644 --- a/xen/include/public/hvm/hvm_op.h +++ b/xen/include/public/hvm/hvm_op.h @@ -93,7 +93,13 @@ typedef enum { HVMMEM_unused, /* Placeholder; setting memory to this type will fail for code after 4.7.0 */ #endif - HVMMEM_ioreq_server + HVMMEM_ioreq_server /* Memory type claimed by an ioreq server; type + changes to this value are only allowed after + an ioreq server has claimed its ownership. + Only pages with HVMMEM_ram_rw are allowed to + change to this type; conversely, pages with + this type are only allowed to be changed back + to HVMMEM_ram_rw. */ } hvmmem_type_t; /* Hint from PV drivers for pagetable destruction. */