From nobody Sat Jan 10 09:59:09 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1767803615; cv=none; d=zohomail.com; s=zohoarc; b=ijwZR/0zKRpeXp2wUrvNc22i5YqBSrLSu50tt3+7pER9NcJkyna3CqyjB0Rhi/lWL46+UAPK/lY19593xOSmpmXeS2WpuR/a8Rbpoqb1kp9eNn9UN/GNc7nvZMztxjNi8NnurYO4YJXevAzoHHy1P+6TgrLVTieyETzgQIAYu/I= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767803615; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=6wih4LTb2ED43OLZRmlKN38NY3IqU/8TNYjFjkPFOhQ=; b=MrvWBy/6FCezjr9v9VUNOWfSz7JdZ2Fq5AwX5Q39uBnv6quWkvJlA9aDKCiL8zk8sSIWeCN/5+CqX4n0itZSMfk1ypBrvnFkMzlw2Ix9PIyz3utBobI+NMkkv1FCyeW2+0l8s4OAND0fnIIc5cT/FP/OPqQkWcn+7gK02pkrI7I= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1767803614996159.7557049468228; Wed, 7 Jan 2026 08:33:34 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.1196926.1514647 (Exim 4.92) (envelope-from ) id 1vdWTF-0003Il-8C; Wed, 07 Jan 2026 16:33:17 +0000 Received: by outflank-mailman (output) from mailman id 1196926.1514647; Wed, 07 Jan 2026 16:33:17 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1vdWTF-0003IW-5e; Wed, 07 Jan 2026 16:33:17 +0000 Received: by outflank-mailman (input) for mailman id 1196926; Wed, 07 Jan 2026 16:33:15 +0000 Received: from se1-gles-flk1-in.inumbo.com ([94.247.172.50] helo=se1-gles-flk1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1vdWTD-0003H4-9N for xen-devel@lists.xenproject.org; Wed, 07 Jan 2026 16:33:15 +0000 Received: from mail-ej1-x635.google.com (mail-ej1-x635.google.com [2a00:1450:4864:20::635]) by se1-gles-flk1.inumbo.com (Halon) with ESMTPS id 8f12eac7-ebe6-11f0-9ccf-f158ae23cfc8; Wed, 07 Jan 2026 17:33:12 +0100 (CET) Received: by mail-ej1-x635.google.com with SMTP id a640c23a62f3a-b802d5e9f06so316793266b.1 for ; Wed, 07 Jan 2026 08:33:12 -0800 (PST) Received: from fedora (user-109-243-67-101.play-internet.pl. [109.243.67.101]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b842a27c755sm561592366b.18.2026.01.07.08.33.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 07 Jan 2026 08:33:11 -0800 (PST) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 8f12eac7-ebe6-11f0-9ccf-f158ae23cfc8 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1767803592; x=1768408392; darn=lists.xenproject.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=6wih4LTb2ED43OLZRmlKN38NY3IqU/8TNYjFjkPFOhQ=; b=BBd0WdNc8s2IcHuEFcL88XKASvJ490zdvqxqnbwHvs8ZrCibSFraeaNvBCtJ5zoMZi t64G/l5uTbjy7vIMfPEDn+A3cEsNGiHzD2cSm4H9veksFuwcQt5/15MnBf8JhC2Q+hDY BFfbSVfK3dVsD7cE9TvEMPGNxmHaUFiRrfbL8PzOjwuUHDWKDPYLTM3ItoqrwAtZwauC DbTMTgcDguTQQV5Woadp14ADCtRxCXlLVP06310xlTcDOflOvz98F8HQbfJ2/QCI3uQR gkzvHg6yhY1zT06wy1B71WrUmeJW+YKLky4h3FzkmpMUleq2xMi0sYOqRHU7HfmYR304 dsvw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767803592; x=1768408392; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=6wih4LTb2ED43OLZRmlKN38NY3IqU/8TNYjFjkPFOhQ=; b=wuJsK//HToQjxls+vX2ROpyL5A9loY0DD60ki6mKXaw2ntt9T2Q2/FuA6HOcicei94 5L3l8l+uJG2/cv70zhFWpL8MbAIjDDBW2fWPPaQVTLvQDa0GXOWiXlO6yQjpkN2vBZto ihJitO+1WkpU/ZQhWGYFxXA7pF5266SlJafIFbJFIpJ1fe/t275i8g7ypibzREe+8lgm Qj/Z7MRUFKNqvbZcxp1Q06VWPtL6ZQuBnSGRIDj1zJdsWjrPK/eiu66BYfU9vssTo/5b +eL9VsP2IfVu1tVzFuZrQXuBSE6SE7aHGh5YXs2NIKBP3Px0sxfFSGnQud6pjiclFvxF 9FNA== X-Gm-Message-State: AOJu0YzhyelOvn77SXdsRSCuABS4pMw5BMU894GYJfA+UV/M1RPrUV0P Z+yG/z7jp48N0lCqiIK+CS5ehGx+PelB/+r1W6yd64XwSohefy55ZLZtIJGOTQ== X-Gm-Gg: AY/fxX4zMMq+i5dKib2FLIyiP7qfkSqQ5eEf3yR64QxzSLdb1z2mwBIYzE1WgkUyG6R cMZ/tZtfnaoLEps6orT5V0CJK8+MijAUaPPYR14wYb668S2m6ZSGfVIeo41ehP6e7/1KJJGrLSn EuD+C86v4RWBGnQCXqXalZEcyjWQBZq+zzRDZLn0hrMn9yYf/sJoFPGH5OAtyDJ1Nw+5jE/AsLc IAgYmShrPvN4Fev2IP5gnS0387Fp/nKdjSrjzgqYALrIu6mbXHV7ERVn/aw8+EBRlQZqXCBdor/ ODxbw3FBKBoKcQShpYc8YyC9FiTZC6qVJIG83Kbo89P59xl7G0g6FRWC56t7Mgjqk/yAtXf31Lw WkRStWNSOhoOOTnFmb2u6ZJqciY06w6HdN5s/FJrZpZ9oCG9tyk0F6AWm9xeYQB76jjXWimNiiH gLk1/9ID0VT2s1FH8t8rhwfMyGVbZB5c5Tv6KUXUpcByEU9G5JcVUvS96CSQ== X-Google-Smtp-Source: AGHT+IFwdQobeN1dR5jdK2Rqc6S6Hjj/xOA8rwgLm6hY0d9udmD5D6jP9nqYd8xKER3Fk5ezFIMNew== X-Received: by 2002:a17:907:3d02:b0:b83:c81a:197e with SMTP id a640c23a62f3a-b84451c5fc6mr317327666b.24.1767803591561; Wed, 07 Jan 2026 08:33:11 -0800 (PST) From: Oleksii Kurochko To: xen-devel@lists.xenproject.org Cc: Oleksii Kurochko , Alistair Francis , Connor Davis , Andrew Cooper , Anthony PERARD , Michal Orzel , Jan Beulich , Julien Grall , =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= , Stefano Stabellini Subject: [PATCH v9 1/3] xen/riscv: add support of page lookup by GFN Date: Wed, 7 Jan 2026 17:32:57 +0100 Message-ID: X-Mailer: git-send-email 2.52.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1767803616931158500 Content-Type: text/plain; charset="utf-8" Introduce helper functions for safely querying the P2M (physical-to-machine) mapping: - add p2m_read_lock(), p2m_read_unlock(), and p2m_is_locked() for managing P2M lock state. - Implement p2m_get_entry() to retrieve mapping details for a given GFN, including MFN, page order, and validity. - Introduce p2m_get_page_from_gfn() to convert a GFN into a page_info pointer, acquiring a reference to the page if valid. - Introduce get_page(). Implementations are based on Arm's functions with some minor modifications: - p2m_get_entry(): - Reverse traversal of page tables, as RISC-V uses the opposite level numbering compared to Arm. - Removed the return of p2m_access_t from p2m_get_entry() since mem_access_settings is not introduced for RISC-V. - Updated BUILD_BUG_ON() to check using the level 0 mask, which correspon= ds to Arm's THIRD_MASK. - Replaced open-coded bit shifts with the BIT() macro. Signed-off-by: Oleksii Kurochko Acked-by: Jan Beulich --- Changes in V9: - Update check_outside_boundary() to return (P2M_MAX_ROOT_LEVEL + 1) in th= e case if gfn is inside range. --- Changes in V8: - Drop the local variable masked_gfn inside check_outside_boundary() and fo= ld the is_lower conditionals into the for loop. - Initialize the local variable level in p2m_get_entry() to the root level and drop the explicit assignment when root page table wasn't found, as it now defaults to the root level. - Introduce gfn_limit_bits and use it to calculate the maximum GFN for the MMU second stage, and return the appropriate page_order when the GFN exce= eds this limit. --- Changes in V7: - Refactor check_outside_boundary(). - Reword the comment above p2m_get_entry(). - As at the moment p2m_get_entry() doesn't pass `t` as NULL we could drop "if ( t )" checks inside it to not have dead code now. - Add the check inside p2m_get_entry() that requested gfn is correct. - Add "if ( t )" check inside p2m_get_page_from_gfn() as it is going to be some callers with t =3D NULL. --- Changes in V6: - Move if-condition with initialization up in p2m_get_page_from_gfn(). - Pass p2mt to the call of p2m_get_entry() inside p2m_get_page_from_gfn() to avoid an issue when 't' is passed NULL. With p2mt passed to p2m_get_e= ntry() we will recieve a proper type and so the rest of the function will able = to continue use a proper type. - In check_outside_boundary() in the case when is_lower =3D=3D true fill th= e bottom bits of masked_gfn with all 1s. - Update code of check_outside_boundary() to return proper level in the cas= e when `level` is equal to 0. - Add ASSERT(p2m) in check_outside_boundary() to be sure that p2m isn't NUL= L as P2M_LEVEL_MASK() depends on p2m value. --- Changes in V5: - Use introduced in earlier patches P2M_DECLARE_OFFSETS() instead of DECLARE_OFFSETS(). - Drop blank line before check_outside_boundary(). - Use more readable version of if statements inside check_outside_boundary= (). - Accumulate mask in check_outside_boundary() instead of re-writing it for each page table level to have correct gfns for comparison. - Set argument `t` of p2m_get_entry() to p2m_invalid by default. - Drop checking of (rc =3D=3D P2M_TABLE_MAP_NOMEM ) when p2m_next_level(..= .,false,...) is called. - Add ASSERT(mfn & (BIT(P2M_LEVEL_ORDER(level), UL) - 1)); in p2m_get_entr= y() to be sure that recieved `mfn` has cleared lowest bits. - Drop `valid` argument from p2m_get_entry(), it is not needed anymore. - Drop p2m_lookup(), use p2m_get_entry() explicitly inside p2m_get_page_fr= om_gfn(). - Update the commit message. --- Changes in V4: - Update prototype of p2m_is_locked() to return bool and accept pointer-to= -const. - Correct the comment above p2m_get_entry(). - Drop the check "BUILD_BUG_ON(XEN_PT_LEVEL_MAP_MASK(0) !=3D PAGE_MASK);" = inside p2m_get_entry() as it is stale and it was needed to sure that 4k page(s)= are used on L3 (in Arm terms) what is true for RISC-V. (if not special exten= sion are used). It was another reason for Arm to have it (and I copied it to = RISC-V), but it isn't true for RISC-V. (some details could be found in response t= o the patch). - Style fixes. - Add explanatory comment what the loop inside "gfn is higher then the hig= hest p2m mapping" does. Move this loop to separate function check_outside_bou= ndary() to cover both boundaries (lower_mapped_gfn and max_mapped_gfn). - There is not need to allocate a page table as it is expected that p2m_get_entry() normally would be called after a corresponding p2m_set_e= ntry() was called. So change 'true' to 'false' in a page table walking loop ins= ide p2m_get_entry(). - Correct handling of p2m_is_foreign case inside p2m_get_page_from_gfn(). - Introduce and use P2M_LEVEL_MASK instead of XEN_PT_LEVEL_MASK as it isn'= t take into account two extra bits for root table in case of P2M. - Drop stale item from "change in v3" - Add is_p2m_foreign() macro and con= nected stuff. - Add p2m_read_(un)lock(). --- Changes in V3: - Change struct domain *d argument of p2m_get_page_from_gfn() to struct p2m_domain. - Update the comment above p2m_get_entry(). - s/_t/p2mt for local variable in p2m_get_entry(). - Drop local variable addr in p2m_get_entry() and use gfn_to_gaddr(gfn) to define offsets array. - Code style fixes. - Update a check of rc code from p2m_next_level() in p2m_get_entry() and drop "else" case. - Do not call p2m_get_type() if p2m_get_entry()'s t argument is NULL. - Use struct p2m_domain instead of struct domain for p2m_lookup() and p2m_get_page_from_gfn(). - Move defintion of get_page() from "xen/riscv: implement mfn_valid() and = page reference, ownership handling helpers" --- Changes in V2: - New patch. --- xen/arch/riscv/include/asm/p2m.h | 21 ++++ xen/arch/riscv/mm.c | 13 +++ xen/arch/riscv/p2m.c | 185 +++++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+) diff --git a/xen/arch/riscv/include/asm/p2m.h b/xen/arch/riscv/include/asm/= p2m.h index b48693a2b41c..f63b5dec99b1 100644 --- a/xen/arch/riscv/include/asm/p2m.h +++ b/xen/arch/riscv/include/asm/p2m.h @@ -41,6 +41,9 @@ =20 #define P2M_GFN_LEVEL_SHIFT(lvl) (P2M_LEVEL_ORDER(lvl) + PAGE_SHIFT) =20 +#define P2M_LEVEL_MASK(p2m, lvl) \ + (P2M_TABLE_OFFSET(p2m, lvl) << P2M_GFN_LEVEL_SHIFT(lvl)) + #define paddr_bits PADDR_BITS =20 /* Get host p2m table */ @@ -234,6 +237,24 @@ static inline bool p2m_is_write_locked(struct p2m_doma= in *p2m) =20 unsigned long construct_hgatp(const struct p2m_domain *p2m, uint16_t vmid); =20 +static inline void p2m_read_lock(struct p2m_domain *p2m) +{ + read_lock(&p2m->lock); +} + +static inline void p2m_read_unlock(struct p2m_domain *p2m) +{ + read_unlock(&p2m->lock); +} + +static inline bool p2m_is_locked(const struct p2m_domain *p2m) +{ + return rw_is_locked(&p2m->lock); +} + +struct page_info *p2m_get_page_from_gfn(struct p2m_domain *p2m, gfn_t gfn, + p2m_type_t *t); + #endif /* ASM__RISCV__P2M_H */ =20 /* diff --git a/xen/arch/riscv/mm.c b/xen/arch/riscv/mm.c index e25f995b727f..e9ce182d066c 100644 --- a/xen/arch/riscv/mm.c +++ b/xen/arch/riscv/mm.c @@ -673,3 +673,16 @@ struct domain *page_get_owner_and_reference(struct pag= e_info *page) =20 return owner; } + +bool get_page(struct page_info *page, const struct domain *domain) +{ + const struct domain *owner =3D page_get_owner_and_reference(page); + + if ( likely(owner =3D=3D domain) ) + return true; + + if ( owner !=3D NULL ) + put_page(page); + + return false; +} diff --git a/xen/arch/riscv/p2m.c b/xen/arch/riscv/p2m.c index 8d572f838fc3..c6de785e4c4b 100644 --- a/xen/arch/riscv/p2m.c +++ b/xen/arch/riscv/p2m.c @@ -1057,3 +1057,188 @@ int map_regions_p2mt(struct domain *d, =20 return rc; } + +/* + * p2m_get_entry() should always return the correct order value, even if an + * entry is not present (i.e. the GFN is outside the range): + * [p2m->lowest_mapped_gfn, p2m->max_mapped_gfn] (1) + * + * This ensures that callers of p2m_get_entry() can determine what range of + * address space would be altered by a corresponding p2m_set_entry(). + * Also, it would help to avoid costly page walks for GFNs outside range (= 1). + * + * Therefore, this function returns true for GFNs outside range (1), and in + * that case the corresponding level is returned via the level_out argumen= t. + * Otherwise, it returns false and p2m_get_entry() performs a page walk to + * find the proper entry. + */ +static bool check_outside_boundary(const struct p2m_domain *p2m, gfn_t gfn, + gfn_t boundary, bool is_lower, + unsigned int *level_out) +{ + unsigned int level =3D P2M_MAX_ROOT_LEVEL + 1; + bool ret =3D false; + + ASSERT(p2m); + + if ( is_lower ? gfn_x(gfn) < gfn_x(boundary) + : gfn_x(gfn) > gfn_x(boundary) ) + { + for ( level =3D P2M_ROOT_LEVEL(p2m) ; level; level-- ) + { + unsigned long mask =3D BIT(P2M_GFN_LEVEL_SHIFT(level), UL) - 1; + + if ( is_lower ? (gfn_x(gfn) | mask) < gfn_x(boundary) + : (gfn_x(gfn) & ~mask) > gfn_x(boundary) ) + break; + } + + ret =3D true; + } + + if ( level_out ) + *level_out =3D level; + + return ret; +} + +/* + * Get the details of a given gfn. + * + * If the entry is present, the associated MFN, the p2m type of the mappin= g, + * and the page order of the mapping in the page table (i.e., it could be a + * superpage) will be returned. + * + * If the entry is not present, INVALID_MFN will be returned, page_order w= ill + * be set according to the order of the invalid range, and the type will be + * p2m_invalid. + */ +static mfn_t p2m_get_entry(struct p2m_domain *p2m, gfn_t gfn, + p2m_type_t *t, + unsigned int *page_order) +{ + unsigned int level =3D P2M_ROOT_LEVEL(p2m); + unsigned int gfn_limit_bits =3D + P2M_LEVEL_ORDER(level + 1) + P2M_ROOT_EXTRA_BITS(p2m, level); + pte_t entry, *table; + int rc; + mfn_t mfn =3D INVALID_MFN; + + P2M_BUILD_LEVEL_OFFSETS(p2m, offsets, gfn_to_gaddr(gfn)); + + ASSERT(p2m_is_locked(p2m)); + + *t =3D p2m_invalid; + + if ( gfn_x(gfn) > (BIT(gfn_limit_bits, UL) - 1) ) + { + if ( page_order ) + *page_order =3D gfn_limit_bits; + + return mfn; + } + + if ( check_outside_boundary(p2m, gfn, p2m->lowest_mapped_gfn, true, + &level) ) + goto out; + + if ( check_outside_boundary(p2m, gfn, p2m->max_mapped_gfn, false, &lev= el) ) + goto out; + + table =3D 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. + */ + if ( !table ) + { + ASSERT_UNREACHABLE(); + goto out; + } + + for ( level =3D P2M_ROOT_LEVEL(p2m); level; level-- ) + { + rc =3D p2m_next_level(p2m, false, level, &table, offsets[level]); + if ( rc =3D=3D P2M_TABLE_MAP_NONE ) + goto out_unmap; + + if ( rc !=3D P2M_TABLE_NORMAL ) + break; + } + + entry =3D table[offsets[level]]; + + if ( pte_is_valid(entry) ) + { + *t =3D p2m_get_type(entry); + + mfn =3D pte_get_mfn(entry); + + ASSERT(!(mfn_x(mfn) & (BIT(P2M_LEVEL_ORDER(level), UL) - 1))); + + /* + * The entry may point to a superpage. Find the MFN associated + * to the GFN. + */ + mfn =3D mfn_add(mfn, + gfn_x(gfn) & (BIT(P2M_LEVEL_ORDER(level), UL) - 1)); + } + + out_unmap: + unmap_domain_page(table); + + out: + if ( page_order ) + *page_order =3D P2M_LEVEL_ORDER(level); + + return mfn; +} + +struct page_info *p2m_get_page_from_gfn(struct p2m_domain *p2m, gfn_t gfn, + p2m_type_t *t) +{ + struct page_info *page; + p2m_type_t p2mt; + mfn_t mfn; + + p2m_read_lock(p2m); + mfn =3D p2m_get_entry(p2m, gfn, &p2mt, NULL); + + if ( t ) + *t =3D p2mt; + + if ( !mfn_valid(mfn) ) + { + p2m_read_unlock(p2m); + return NULL; + } + + page =3D mfn_to_page(mfn); + + /* + * get_page won't work on foreign mapping because the page doesn't + * belong to the current domain. + */ + if ( unlikely(p2m_is_foreign(p2mt)) ) + { + const struct domain *fdom =3D page_get_owner_and_reference(page); + + p2m_read_unlock(p2m); + + if ( fdom ) + { + if ( likely(fdom !=3D p2m->domain) ) + return page; + + ASSERT_UNREACHABLE(); + put_page(page); + } + + return NULL; + } + + p2m_read_unlock(p2m); + + return get_page(page, p2m->domain) ? page : NULL; +} --=20 2.52.0