From patchwork Tue Dec 4 20:26:40 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Julien Grall X-Patchwork-Id: 152860 Delivered-To: patch@linaro.org Received: by 2002:a2e:299d:0:0:0:0:0 with SMTP id p29-v6csp8503530ljp; Tue, 4 Dec 2018 12:29:36 -0800 (PST) X-Google-Smtp-Source: AFSGD/U3NByi0QizGKXpxe+OV7jLL1XbkVECbXkaxBGCN8M8FM+rBGHLKCynyxb92sIgSUpkUutJ X-Received: by 2002:a25:ddc3:: with SMTP id u186-v6mr20455485ybg.440.1543955376265; Tue, 04 Dec 2018 12:29:36 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1543955376; cv=none; d=google.com; s=arc-20160816; b=JdPi7pmxlmM1XMe0jnVdpYkQMHC6vygQk3MNTbfppPIYEZ3AvOabUe3cvRjw/0s8bl 81i3UzO38DaLdEagyI/sn9rcWRSwTYlXAOI8ckdvriFRQ+2j0OZhZWdJidNj5CzMVMqT bSkX4OZB57Zn7qezEVzHcGuxeiOWS495Dq3DEXI8NNmPAJfDR67fYF6sMt4sP77s3Yds 86NbFUSTpsjw7jGxr0RRMoYmEar9imdFMRUel5lmCtmsVQ4QReMqDBQ1BIfgv2PAZSOA l4uDpcn/mYClgMVvH5ooCFUpHXlbbe02vOcLzWRmvqFZYzdm7DnbkzDI/UdjLAw88G/l ifaw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:cc :list-subscribe:list-help:list-post:list-unsubscribe:list-id :precedence:subject:references:in-reply-to:message-id:date:to:from; bh=Bb/mCPfKxBJn52xwHMEUwR3F138ZgTRqdSQetFHOD1Y=; b=NCEYfrvXlSh7AimqnwkhMrEW2IGM2nqQrEadOh/iD1ItijkAytITkRhWbbFXccXRuu dSTvVhMxH9nLt/SirwWAcdT7hfNANC+ARRE7FSXVS/iNHoEr0ctTF/OgmxBUXl/4b0AR pr7RqzuaJ0skDR8zJjN6nG7ht5+RfWq/+ChaiQsT8wcbL83Y6erj3YAa9W6hK4G6bEmg EldW0HeNooeVGxjAKsUx8xe7RKvecaZIqIoFht9i1vcZNMA1jcNUblskGh5Z5v6+JCIE C3yITFUvicya0i+QehhHlqIhaC4HH5knY3Ug60C8wUsdc3OMg+XX+UFujlu3kgKEWOel pBwA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of xen-devel-bounces@lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org Return-Path: Received: from lists.xenproject.org (lists.xenproject.org. [192.237.175.120]) by mx.google.com with ESMTPS id l8-v6si9951978ybp.221.2018.12.04.12.29.36 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Tue, 04 Dec 2018 12:29:36 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of xen-devel-bounces@lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of xen-devel-bounces@lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.89) (envelope-from ) id 1gUHHy-00086x-1Y; Tue, 04 Dec 2018 20:27:26 +0000 Received: from all-amaz-eas1.inumbo.com ([34.197.232.57] helo=us1-amaz-eas2.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.89) (envelope-from ) id 1gUHHw-00086b-Lb for xen-devel@lists.xenproject.org; Tue, 04 Dec 2018 20:27:24 +0000 X-Inumbo-ID: 02382768-f803-11e8-a67f-2bd172bc02dd Received: from foss.arm.com (unknown [217.140.101.70]) by us1-amaz-eas2.inumbo.com (Halon) with ESMTP id 02382768-f803-11e8-a67f-2bd172bc02dd; Tue, 04 Dec 2018 20:27:24 +0000 (UTC) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id E7E51165C; Tue, 4 Dec 2018 12:27:23 -0800 (PST) Received: from e108454-lin.cambridge.arm.com (e108454-lin.cambridge.arm.com [10.1.196.50]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 0813A3F614; Tue, 4 Dec 2018 12:27:22 -0800 (PST) From: Julien Grall To: xen-devel@lists.xenproject.org Date: Tue, 4 Dec 2018 20:26:40 +0000 Message-Id: <20181204202651.8836-7-julien.grall@arm.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20181204202651.8836-1-julien.grall@arm.com> References: <20181204202651.8836-1-julien.grall@arm.com> Subject: [Xen-devel] [PATCH for-4.12 v2 06/17] xen/arm: p2m: Introduce a function to resolve translation fault X-BeenThere: xen-devel@lists.xenproject.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Cc: Julien Grall , sstabellini@kernel.org MIME-Version: 1.0 Errors-To: xen-devel-bounces@lists.xenproject.org Sender: "Xen-devel" Currently a Stage-2 translation fault could happen: 1) MMIO emulation 2) Another pCPU was modifying the P2M using Break-Before-Make 3) Guest Physical address is not mapped A follow-up patch will re-purpose the valid bit in an entry to generate translation fault. This would be used to do an action on each entry to track pages used for a given period. When receiving the translation fault, we would need to walk the pages table to find the faulting entry and then toggle valid bit. We can't use p2m_lookup() for this purpose as it only tells us the mapping exists. So this patch adds a new function to walk the page-tables and updates the entry. This function will also handle 2) as it also requires walking the page-table. The function is able to cope with both table and block entry having the validate bit unset. This gives flexibility to the function clearing the valid bits. To keep the algorithm simple, the fault will be propating one-level down. This will be repeated until a block entry has been reached. At the moment, there are no action done when reaching a block/page entry but setting the valid bit to 1. Signed-off-by: Julien Grall Acked-by: Stefano Stabellini --- Changes in v2: - Typoes - Add more comment - Skip clearing valid bit if it was already done - Move the prototype in p2m.h - Expand commit message --- xen/arch/arm/p2m.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++ xen/arch/arm/traps.c | 10 ++-- xen/include/asm-arm/p2m.h | 2 + 3 files changed, 148 insertions(+), 6 deletions(-) diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c index 39680eeb6e..2706db3e67 100644 --- a/xen/arch/arm/p2m.c +++ b/xen/arch/arm/p2m.c @@ -1035,6 +1035,148 @@ int p2m_set_entry(struct p2m_domain *p2m, return rc; } +/* Invalidate all entries in the table. The p2m should be write locked. */ +static void p2m_invalidate_table(struct p2m_domain *p2m, mfn_t mfn) +{ + lpae_t *table; + unsigned int i; + + ASSERT(p2m_is_write_locked(p2m)); + + table = map_domain_page(mfn); + + for ( i = 0; i < LPAE_ENTRIES; i++ ) + { + lpae_t pte = table[i]; + + /* + * Writing an entry can be expensive because it may involve + * cleaning the cache. So avoid updating the entry if the valid + * bit is already cleared. + */ + if ( !pte.p2m.valid ) + continue; + + pte.p2m.valid = 0; + + p2m_write_pte(&table[i], pte, p2m->clean_pte); + } + + unmap_domain_page(table); + + p2m->need_flush = true; +} + +/* + * Resolve any translation fault due to change in the p2m. This + * includes break-before-make and valid bit cleared. + */ +bool p2m_resolve_translation_fault(struct domain *d, gfn_t gfn) +{ + struct p2m_domain *p2m = p2m_get_hostp2m(d); + unsigned int level = 0; + bool resolved = false; + lpae_t entry, *table; + paddr_t addr = gfn_to_gaddr(gfn); + + /* Convenience aliases */ + const unsigned int offsets[4] = { + zeroeth_table_offset(addr), + first_table_offset(addr), + second_table_offset(addr), + third_table_offset(addr) + }; + + p2m_write_lock(p2m); + + /* This gfn is higher than the highest the p2m map currently holds */ + if ( gfn_x(gfn) > gfn_x(p2m->max_mapped_gfn) ) + goto out; + + table = p2m_get_root_pointer(p2m, gfn); + /* + * The table should always be non-NULL because the gfn is below + * p2m->max_mapped_gfn and the root table pages are always present. + */ + BUG_ON(table == NULL); + + /* + * Go down the page-tables until an entry has the valid bit unset or + * a block/page entry has been hit. + */ + for ( level = P2M_ROOT_LEVEL; level <= 3; level++ ) + { + int rc; + + entry = table[offsets[level]]; + + if ( level == 3 ) + break; + + /* Stop as soon as we hit an entry with the valid bit unset. */ + if ( !lpae_is_valid(entry) ) + break; + + rc = p2m_next_level(p2m, true, level, &table, offsets[level]); + if ( rc == GUEST_TABLE_MAP_FAILED ) + goto out_unmap; + else if ( rc != GUEST_TABLE_NORMAL_PAGE ) + break; + } + + /* + * If the valid bit of the entry is set, it means someone was playing with + * the Stage-2 page table. Nothing to do and mark the fault as resolved. + */ + if ( lpae_is_valid(entry) ) + { + resolved = true; + goto out_unmap; + } + + /* + * The valid bit is unset. If the entry is still not valid then the fault + * cannot be resolved, exit and report it. + */ + if ( !p2m_is_valid(entry) ) + goto out_unmap; + + /* + * Now we have an entry with valid bit unset, but still valid from + * the P2M point of view. + * + * If an entry is pointing to a table, each entry of the table will + * have there valid bit cleared. This allows a function to clear the + * full p2m with just a couple of write. The valid bit will then be + * propagated on the fault. + * If an entry is pointing to a block/page, no work to do for now. + */ + if ( lpae_is_table(entry, level) ) + p2m_invalidate_table(p2m, lpae_get_mfn(entry)); + + /* + * Now that the work on the entry is done, set the valid bit to prevent + * another fault on that entry. + */ + resolved = true; + entry.p2m.valid = 1; + + p2m_write_pte(table + offsets[level], entry, p2m->clean_pte); + + /* + * No need to flush the TLBs as the modified entry had the valid bit + * unset. + */ + +out_unmap: + unmap_domain_page(table); + +out: + p2m_write_unlock(p2m); + + return resolved; +} + static inline int p2m_insert_mapping(struct domain *d, gfn_t start_gfn, unsigned long nr, diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c index 94fe1a6da7..b00d0b8e1e 100644 --- a/xen/arch/arm/traps.c +++ b/xen/arch/arm/traps.c @@ -1893,7 +1893,6 @@ static void do_trap_stage2_abort_guest(struct cpu_user_regs *regs, vaddr_t gva; paddr_t gpa; uint8_t fsc = xabt.fsc & ~FSC_LL_MASK; - mfn_t mfn; bool is_data = (hsr.ec == HSR_EC_DATA_ABORT_LOWER_EL); /* @@ -1972,12 +1971,11 @@ static void do_trap_stage2_abort_guest(struct cpu_user_regs *regs, } /* - * The PT walk may have failed because someone was playing - * with the Stage-2 page table. Walk the Stage-2 PT to check - * if the entry exists. If it's the case, return to the guest + * First check if the translation fault can be resolved by the + * P2M subsystem. If that's the case nothing else to do. */ - mfn = gfn_to_mfn(current->domain, gaddr_to_gfn(gpa)); - if ( !mfn_eq(mfn, INVALID_MFN) ) + if ( p2m_resolve_translation_fault(current->domain, + gaddr_to_gfn(gpa)) ) return; if ( is_data && try_map_mmio(gaddr_to_gfn(gpa)) ) diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h index 4fe78d39a5..13f7a27c38 100644 --- a/xen/include/asm-arm/p2m.h +++ b/xen/include/asm-arm/p2m.h @@ -226,6 +226,8 @@ int p2m_set_entry(struct p2m_domain *p2m, p2m_type_t t, p2m_access_t a); +bool p2m_resolve_translation_fault(struct domain *d, gfn_t gfn); + /* Clean & invalidate caches corresponding to a region of guest address space */ int p2m_cache_flush(struct domain *d, gfn_t start, unsigned long nr);