[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH 1/8] xen/arm: Implement p2m_set_entry for MPU systems


  • To: Luca Fancellu <luca.fancellu@xxxxxxx>, <xen-devel@xxxxxxxxxxxxxxxxxxxx>
  • From: "Halder, Ayan Kumar" <ayankuma@xxxxxxx>
  • Date: Fri, 10 Jul 2026 16:03:33 +0100
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 165.204.84.17) smtp.rcpttodomain=arm.com smtp.mailfrom=amd.com; dmarc=pass (p=quarantine sp=quarantine pct=100) action=none header.from=amd.com; dkim=none (message not signed); arc=none (0)
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=K+GCN0uCPhbYpjkLakFcOrKmJerj6Qan4aAw7P14occ=; b=YYiVmYjxHgpHM9e0Z5qJDb9ICAl11lH6BU8xb5NZNBWAB97F4FXJtDTLpM21MxAP/xizoXV7mlyINcgcU4SbRXxvxDGdOnAUzFQKnKB5RFRF6HGqLNVGK/myrijuDThzOJrHYnb0NOeJjQuRD2NFv/KRu/DcIX6bWzsr+EV+9w/THlGEfkXcp510TmJOLM1kctbU44ZzGjCITqjQWDD8i0WHVIgrbkFbAPRyE0lsmt1LH6DoICklU9Aq+QSRmV6hGDjAZrhW0B2UMWv5L/cUbOke2CengVAB2aYC1rjK0Qy2gV5LF3JC9/j0a8l1fMwvXD+lIoNsSS5OPjGV4xi4rQ==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=V1SsdhKNvAJQI7QPSLpgo4OxH/tERtjr0YuynPbV8Ye/XJrAQHLvQSXo0N1AWSlTwJeVqo2cQPQ/UJkkgn9w6t1/kZcpY3EPd4YD+rZXGmzHajag7625iiN/1QmG76WR07JP0/oNCHp1RzXVFmBRgVOESEeYV5p6pPVEPWTm4XIVN+iDoNBKxyYnbVAd10xvYD0pc2gycJi+UT8kz1VPzYY5+MUKFIYUV5MeIAzz34E4x3o1/IhPpg5Kw2P5pWT4AFwo3yBKvzavuUljgh9fZn2YI4MfV90MpNUJXZuPC9oAhDgE3JV3mrj86ar/1PoADwwMc2neqZWy81EwsbL0cw==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=selector1 header.d=amd.com header.i="@amd.com" header.h="From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck"
  • Cc: Penny Zheng <Penny.Zheng@xxxxxxx>, Stefano Stabellini <sstabellini@xxxxxxxxxx>, Julien Grall <julien@xxxxxxx>, Bertrand Marquis <bertrand.marquis@xxxxxxx>, Michal Orzel <michal.orzel@xxxxxxx>, "Volodymyr Babchuk" <Volodymyr_Babchuk@xxxxxxxx>, Wei Chen <wei.chen@xxxxxxx>, "Hari Limaye" <hari.limaye@xxxxxxx>, Harry Ramsey <harry.ramsey@xxxxxxx>
  • Delivery-date: Fri, 10 Jul 2026 15:04:46 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

Hi Luca,

On 20/04/2026 15:25, Luca Fancellu wrote:

From: Penny Zheng <Penny.Zheng@xxxxxxx>

Implement the function `p2m_set_entry`, which is responsible for
inserting a new entry into the p2m tables, for MPU systems.

Signed-off-by: Penny Zheng <penny.zheng@xxxxxxx>
Signed-off-by: Wei Chen <wei.chen@xxxxxxx>
Signed-off-by: Luca Fancellu <luca.fancellu@xxxxxxx>
Signed-off-by: Hari Limaye <hari.limaye@xxxxxxx>
Signed-off-by: Harry Ramsey <harry.ramsey@xxxxxxx>
---
  xen/arch/arm/include/asm/arm32/mpu.h |   3 +-
  xen/arch/arm/include/asm/arm64/mpu.h |   3 +-
  xen/arch/arm/include/asm/mpu/mm.h    |   3 +-
  xen/arch/arm/include/asm/mpu/p2m.h   |  10 +++
  xen/arch/arm/include/asm/p2m.h       |   3 +
  xen/arch/arm/mpu/mm.c                |  43 ++++++----
  xen/arch/arm/mpu/p2m.c               | 123 ++++++++++++++++++++++++++-
  7 files changed, 167 insertions(+), 21 deletions(-)

diff --git a/xen/arch/arm/include/asm/arm32/mpu.h 
b/xen/arch/arm/include/asm/arm32/mpu.h
index d565230f84ee..ab58df079920 100644
--- a/xen/arch/arm/include/asm/arm32/mpu.h
+++ b/xen/arch/arm/include/asm/arm32/mpu.h
@@ -42,7 +42,8 @@ typedef struct {
      prbar_t prbar;
      prlar_t prlar;
      uint8_t refcount;
-    uint8_t pad[7];     /* Pad structure to 16 Bytes */
+    uint8_t p2m_type;
+    uint8_t pad[6];     /* Pad structure to 16 Bytes */
  } pr_t;
#endif /* __ASSEMBLER__ */
diff --git a/xen/arch/arm/include/asm/arm64/mpu.h 
b/xen/arch/arm/include/asm/arm64/mpu.h
index 8b86a03fee44..c82624f0f2cf 100644
--- a/xen/arch/arm/include/asm/arm64/mpu.h
+++ b/xen/arch/arm/include/asm/arm64/mpu.h
@@ -41,7 +41,8 @@ typedef struct {
      prbar_t prbar;
      prlar_t prlar;
      uint8_t refcount;
-    uint8_t pad[15];    /* Pad structure to 32 Bytes */
+    uint8_t p2m_type;
+    uint8_t pad[14];    /* Pad structure to 32 Bytes */
  } pr_t;
#endif /* __ASSEMBLER__ */
diff --git a/xen/arch/arm/include/asm/mpu/mm.h 
b/xen/arch/arm/include/asm/mpu/mm.h
index 1b5ffa5b644d..24bffdee4fb6 100644
--- a/xen/arch/arm/include/asm/mpu/mm.h
+++ b/xen/arch/arm/include/asm/mpu/mm.h
@@ -75,9 +75,10 @@ void write_protection_region(const pr_t *pr_write, uint8_t 
sel);
   * @param base      Base address of the range to map (inclusive).
   * @param limit     Limit address of the range to map (exclusive).
   * @param flags     Flags for the memory range to map.
+ * @param p2m       True for a stage 2 mapping, otherwise False.
   * @return          0 on success, negative on error.
   */
-int xen_mpumap_update(paddr_t base, paddr_t limit, unsigned int flags);
+int xen_mpumap_update(paddr_t base, paddr_t limit, unsigned int flags, bool 
p2m);
/*
   * Creates a pr_t structure describing a protection region.
diff --git a/xen/arch/arm/include/asm/mpu/p2m.h 
b/xen/arch/arm/include/asm/mpu/p2m.h
index 39fc0c944916..b9c7be2d9dcc 100644
--- a/xen/arch/arm/include/asm/mpu/p2m.h
+++ b/xen/arch/arm/include/asm/mpu/p2m.h
@@ -21,6 +21,16 @@ static inline void p2m_clear_root_pages(struct p2m_domain 
*p2m) {}
static inline void p2m_tlb_flush_sync(struct p2m_domain *p2m) {} +static inline void region_set_p2m(pr_t *pr, p2m_type_t p2m_type)
+{
+    pr->p2m_type = p2m_type;
+}
+
+static inline p2m_type_t region_get_p2m(pr_t *pr)
The pointer is read-only here; take const pr_t *. Same for region_set_p2m's
sibling accessors added in this series
+{
+    return pr->p2m_type;
+}
+
  #endif /* __ARM_MPU_P2M_H__ */
/*
diff --git a/xen/arch/arm/include/asm/p2m.h b/xen/arch/arm/include/asm/p2m.h
index ed1b6dd40f40..43b383885da0 100644
--- a/xen/arch/arm/include/asm/p2m.h
+++ b/xen/arch/arm/include/asm/p2m.h
@@ -54,6 +54,9 @@ struct p2m_domain {
  #else
      /* Current Virtualization System Control Register for the p2m */
      register_t vsctlr;
+
+    /* Number of MPU memory regions in P2M MPU memory mapping table. */
+    uint8_t nr_regions;
  #endif
/* Highest guest frame that's ever been mapped in the p2m */
diff --git a/xen/arch/arm/mpu/mm.c b/xen/arch/arm/mpu/mm.c
index aff88bd3a9c1..4ee58ded5ad6 100644
--- a/xen/arch/arm/mpu/mm.c
+++ b/xen/arch/arm/mpu/mm.c
@@ -317,13 +317,14 @@ static int xen_mpumap_free_entry(uint8_t idx, int 
region_found_type)
   * Update the entry in the MPU memory region mapping table (xen_mpumap) for 
the
   * given memory range and flags, creating one if none exists.
   *
- * @param base  Base address (inclusive).
- * @param limit Limit address (exclusive).
- * @param flags Region attributes (a combination of PAGE_HYPERVISOR_XXX)
+ * @param base      Base address (inclusive).
+ * @param limit     Limit address (exclusive).
+ * @param flags     Region attributes (a combination of PAGE_HYPERVISOR_XXX)
+ * @param p2m       True for a stage 2 mapping, otherwise False.
   * @return      0 on success, otherwise negative on error.
   */
  static int xen_mpumap_update_entry(paddr_t base, paddr_t limit,
-                                   unsigned int flags)
+                                   unsigned int flags, bool p2m)
  {
      bool flags_has_page_present;
      uint8_t idx;
@@ -399,6 +400,8 @@ static int xen_mpumap_update_entry(paddr_t base, paddr_t 
limit,
              return -ENOENT;
xen_mpumap[idx] = pr_of_addr(base, limit, flags);
+        /* AP[0] always 1 for stage 2 */
+        xen_mpumap[idx].prbar.reg.ap_0 = (p2m ? 1 : 0);
write_protection_region(&xen_mpumap[idx], idx);
      }
@@ -418,33 +421,41 @@ static int xen_mpumap_update_entry(paddr_t base, paddr_t 
limit,
      return 0;
  }
-int xen_mpumap_update(paddr_t base, paddr_t limit, unsigned int flags)
+int check_mpu_mapping(paddr_t base, paddr_t limit, unsigned int flags)
  {
-    int rc;
-
      if ( flags_has_rwx(flags) )
      {
          printk("Mappings should not be both Writeable and Executable\n");
-        return -EINVAL;
+        return false;
This function returns bool but is typed int, and has external linkage with
no prototype. It becomes static bool in patch 4 - please make it static bool
here so it is correct when introduced, and drop the churn in 4.
      }
if ( base >= limit )
      {
          printk("Base address %#"PRIpaddr" must be smaller than limit address 
%#"PRIpaddr"\n",
                 base, limit);
-        return -EINVAL;
+        return false;
      }
if ( !IS_ALIGNED(base, PAGE_SIZE) || !IS_ALIGNED(limit, PAGE_SIZE) )
      {
          printk("base address %#"PRIpaddr", or limit address %#"PRIpaddr" is not 
page aligned\n",
                 base, limit);
-        return -EINVAL;
+        return false;
      }
+ return true;
+}
+
+int xen_mpumap_update(paddr_t base, paddr_t limit, unsigned int flags, bool 
p2m)
+{
+    int rc;
+
+    if ( !check_mpu_mapping(base, limit, flags) )
+        return -EINVAL;
+
      spin_lock(&xen_mpumap_lock);
- rc = xen_mpumap_update_entry(base, limit, flags);
+    rc = xen_mpumap_update_entry(base, limit, flags, p2m);
      if ( !rc )
          context_sync_mpu();
@@ -459,7 +470,7 @@ int destroy_xen_mappings(unsigned long s, unsigned long e)
      ASSERT(IS_ALIGNED(e, PAGE_SIZE));
      ASSERT(s < e);
- return xen_mpumap_update(s, e, 0);
+    return xen_mpumap_update(s, e, 0, false);
  }
int destroy_xen_mapping_containing(paddr_t s)
@@ -499,7 +510,7 @@ int map_pages_to_xen(unsigned long virt, mfn_t mfn, 
unsigned long nr_mfns,
                       unsigned int flags)
  {
      /* MPU systems have no translation, ma == va, so pass virt directly */
-    return xen_mpumap_update(virt, mfn_to_maddr(mfn_add(mfn, nr_mfns)), flags);
+    return xen_mpumap_update(virt, mfn_to_maddr(mfn_add(mfn, nr_mfns)), flags, 
false);
  }
/*
@@ -520,7 +531,7 @@ void __init setup_mm_helper(void)
              paddr_t bank_end = bank_start + bank_size;
/* Map static heap with one MPU protection region */
-            if ( xen_mpumap_update(bank_start, bank_end, PAGE_HYPERVISOR) )
+            if ( xen_mpumap_update(bank_start, bank_end, PAGE_HYPERVISOR, 
false) )
                  panic("Failed to map static heap\n");
break;
@@ -533,7 +544,7 @@ void __init setup_mm_helper(void)
int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf)
  {
-    return xen_mpumap_update(s, e, nf);
+    return xen_mpumap_update(s, e, nf, false);
  }
void dump_hyp_walk(vaddr_t addr)
@@ -598,7 +609,7 @@ void __iomem *ioremap_attr(paddr_t start, size_t len, 
unsigned int flags)
      paddr_t start_pg = round_pgdown(start);
      paddr_t end_pg = round_pgup(start + len);
- if ( xen_mpumap_update(start_pg, end_pg, flags) )
+    if ( xen_mpumap_update(start_pg, end_pg, flags, false) )
          return NULL;
/* Mapped or already mapped */
diff --git a/xen/arch/arm/mpu/p2m.c b/xen/arch/arm/mpu/p2m.c
index ec8f630acd90..4a8595b1b25e 100644
--- a/xen/arch/arm/mpu/p2m.c
+++ b/xen/arch/arm/mpu/p2m.c
@@ -8,12 +8,131 @@
  #include <xen/sched.h>
  #include <xen/types.h>
  #include <asm/p2m.h>
+#include <asm/mpu.h>
+
+static inline unsigned int build_p2m_flags(p2m_type_t t)
+{
+    unsigned int flags = 0;
+
+    BUILD_BUG_ON(p2m_max_real_type > (1 << 4));
+
+    switch ( t )
+    {
+    case p2m_ram_rw:
+        /* Nothing to do, XN=0, RO=0 */
+        break;
+
+    case p2m_ram_ro:
+        flags |= _PAGE_RO;
+        break;
+
+    case p2m_invalid:
+        flags |= _PAGE_XN | _PAGE_RO;
+        break;
+
+    case p2m_max_real_type:
+        BUG();
+        break;
+
+    case p2m_mmio_direct_dev:
+    case p2m_mmio_direct_nc:
+    case p2m_mmio_direct_c:
+    case p2m_iommu_map_ro:
+    case p2m_iommu_map_rw:
+    case p2m_map_foreign_ro:
+    case p2m_map_foreign_rw:
+    case p2m_grant_map_ro:
+    case p2m_grant_map_rw:
+        panic(XENLOG_G_ERR "p2m: UNIMPLEMENTED p2m permission in MPU 
system\n");
+        break;
+    }
+
+    flags |= MT_NORMAL;
+
+    return flags;
+}
+
+/*
+ * Check whether guest memory region [`sgfn`, `sgfn` + `nr_gfns`) is mapped in
+ * mpumap `table`.
+ *
+ * If the memory region is mapped, `idx` is set to the index of the associated
+ * MPU memory region and 0 is returned.
+ * If the memory region is not mapped, -ENOENT is returned.
+ */
+static int is_gfns_mapped(pr_t *table, uint8_t nr_regions, gfn_t sgfn,
+                          unsigned long nr_gfns, uint8_t *idx)
+{
+    paddr_t gbase = gfn_to_gaddr(sgfn);
+    paddr_t glimit = gfn_to_gaddr(gfn_add(sgfn, nr_gfns));
+    int rc;
+
+    rc = mpumap_contains_region(table, nr_regions, gbase, glimit, idx);
+    if ( MPUMAP_REGION_OVERLAP == rc )
+        return -EINVAL;
+
+    if ( MPUMAP_REGION_NOTFOUND == rc )
+        return -ENOENT;
+
+    return 0;
+}
+
+static int __p2m_set_entry(struct p2m_domain *p2m, gfn_t sgfn, unsigned int nr,
p2m_set_entry takes unsigned long nr and passes it here as unsigned int, so
the count is silently truncated on the way in. Keep the type consistent.
+                    mfn_t smfn, p2m_type_t t, p2m_access_t a)
+{
+    pr_t *table;
+    mfn_t emfn = mfn_add(smfn, nr);
+    unsigned int flags;
+    uint8_t idx = INVALID_REGION_IDX;
+
+    /*
+     * In all cases other than when removing a mapping (mfn == MFN_INVALID),
+     * gfn == mfn on MPU systems.
+     */
+    if ( !mfn_eq(smfn, INVALID_MFN) && gfn_x(sgfn) != mfn_x(smfn) )
+    {
+        printk(XENLOG_G_ERR "Unable to map MFN %#"PRI_mfn" at %#"PRI_mfn"\n",
+               mfn_x(smfn), gfn_x(sgfn));
+        return -EINVAL;
+    }
+
+    table = (pr_t *)page_to_virt(p2m->root);
+    if ( !table )
+        return -EINVAL;
+
+    /* Already mapped */
+    if ( is_gfns_mapped(table, p2m->nr_regions, sgfn, nr, &idx) != -ENOENT )
+    {
+        printk(XENLOG_G_ERR "Unable to insert P2M MPU memory region 
%#"PRIpaddr"-%#"PRIpaddr"\n",
+               gfn_to_gaddr(sgfn), gfn_to_gaddr(gfn_add(sgfn, nr)));
+        return -EINVAL;
+    }
+
+    flags = build_p2m_flags(t);
+    table[p2m->nr_regions] = pr_of_addr(mfn_to_maddr(smfn),
+                                        mfn_to_maddr(mfn_add(smfn, nr)), 
flags);
+    region_set_p2m(&table[p2m->nr_regions], t);
+    p2m->nr_regions++;
Should we check that against the upperbound MAX_MPU_REGION_NR. And panic when it reaches the max , so that an overflow is prevented.
+
+    p2m->max_mapped_gfn = gfn_max(p2m->max_mapped_gfn, _gfn(mfn_x(emfn)));
+    p2m->lowest_mapped_gfn = gfn_min(p2m->lowest_mapped_gfn, 
_gfn(mfn_x(smfn)));
+
+    return 0;
+}
int p2m_set_entry(struct p2m_domain *p2m, gfn_t sgfn, unsigned long nr,
                    mfn_t smfn, p2m_type_t t, p2m_access_t a)
  {
-    BUG_ON("unimplemented");
-    return -EINVAL;
+    /*
+     * Any reference taken by the P2M mappings (e.g. foreign mapping) will
+     * be dropped in relinquish_p2m_mapping(). As the P2M will still
+     * be accessible after, we need to prevent mapping to be added when the
+     * domain is dying.
+     */
+    if ( unlikely(p2m->domain->is_dying) )
+        return -ENOMEM;
+
+    return __p2m_set_entry(p2m, sgfn, nr, smfn, t, a);
  }
mfn_t p2m_get_entry(struct p2m_domain *p2m, gfn_t gfn, p2m_type_t *t,
- Ayan



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.