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

[xen staging] device-tree: Move Arm's static-shmem feature to common



commit 72c5fa220804520839fd431ed19cd2b6eb04015a
Author:     Michal Orzel <michal.orzel@xxxxxxx>
AuthorDate: Tue Jun 3 12:03:29 2025 +0200
Commit:     Michal Orzel <michal.orzel@xxxxxxx>
CommitDate: Wed Jun 4 09:04:24 2025 +0200

    device-tree: Move Arm's static-shmem feature to common
    
    This feature is arch agnostic, thus move it to common.
    
    Signed-off-by: Michal Orzel <michal.orzel@xxxxxxx>
    Acked-by: Julien Grall <jgrall@xxxxxxxxxx>
---
 xen/arch/arm/Kconfig                    |   6 -
 xen/arch/arm/Makefile                   |   1 -
 xen/arch/arm/arm32/mmu/mm.c             |   2 +-
 xen/arch/arm/arm64/mmu/mm.c             |   2 +-
 xen/arch/arm/dom0less-build.c           |   2 +-
 xen/arch/arm/domain_build.c             |   2 +-
 xen/arch/arm/include/asm/static-shmem.h |  94 ----
 xen/arch/arm/static-shmem.c             | 854 --------------------------------
 xen/common/Kconfig                      |   6 +
 xen/common/device-tree/Makefile         |   1 +
 xen/common/device-tree/bootfdt.c        |   4 +-
 xen/common/device-tree/dom0less-build.c |   9 +-
 xen/common/device-tree/static-shmem.c   | 854 ++++++++++++++++++++++++++++++++
 xen/include/xen/static-shmem.h          |  94 ++++
 14 files changed, 961 insertions(+), 970 deletions(-)

diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
index 57919d8b3a..3f25da3ca5 100644
--- a/xen/arch/arm/Kconfig
+++ b/xen/arch/arm/Kconfig
@@ -247,12 +247,6 @@ config ARM64_BTI
 
 source "arch/arm/tee/Kconfig"
 
-config STATIC_SHM
-       bool "Statically shared memory on a dom0less system" if UNSUPPORTED
-       depends on STATIC_MEMORY
-       help
-         This option enables statically shared memory on a dom0less system.
-
 config PARTIAL_EMULATION
        bool "Enable partial emulation of system/coprocessor registers"
        default y
diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
index 4f08014547..ab0a0c2be6 100644
--- a/xen/arch/arm/Makefile
+++ b/xen/arch/arm/Makefile
@@ -51,7 +51,6 @@ obj-y += setup.o
 obj-y += shutdown.o
 obj-y += smp.o
 obj-y += smpboot.o
-obj-$(CONFIG_STATIC_SHM) += static-shmem.init.o
 obj-y += sysctl.o
 obj-y += time.o
 obj-y += traps.o
diff --git a/xen/arch/arm/arm32/mmu/mm.c b/xen/arch/arm/arm32/mmu/mm.c
index f3305e28e9..4d22f35618 100644
--- a/xen/arch/arm/arm32/mmu/mm.c
+++ b/xen/arch/arm/arm32/mmu/mm.c
@@ -7,9 +7,9 @@
 #include <xen/param.h>
 #include <xen/pfn.h>
 #include <xen/static-memory.h>
+#include <xen/static-shmem.h>
 #include <asm/fixmap.h>
 #include <asm/setup.h>
-#include <asm/static-shmem.h>
 
 static unsigned long opt_xenheap_megabytes __initdata;
 integer_param("xenheap_megabytes", opt_xenheap_megabytes);
diff --git a/xen/arch/arm/arm64/mmu/mm.c b/xen/arch/arm/arm64/mmu/mm.c
index cded8f2787..a0a2dd8cc7 100644
--- a/xen/arch/arm/arm64/mmu/mm.c
+++ b/xen/arch/arm/arm64/mmu/mm.c
@@ -5,9 +5,9 @@
 #include <xen/mm.h>
 #include <xen/pfn.h>
 #include <xen/static-memory.h>
+#include <xen/static-shmem.h>
 
 #include <asm/setup.h>
-#include <asm/static-shmem.h>
 
 /* Override macros from asm/page.h to make them work with mfn_t */
 #undef virt_to_mfn
diff --git a/xen/arch/arm/dom0less-build.c b/xen/arch/arm/dom0less-build.c
index bcfd686a8b..20aabf6be5 100644
--- a/xen/arch/arm/dom0less-build.c
+++ b/xen/arch/arm/dom0less-build.c
@@ -13,6 +13,7 @@
 #include <xen/serial.h>
 #include <xen/sizes.h>
 #include <xen/static-memory.h>
+#include <xen/static-shmem.h>
 #include <xen/vmap.h>
 
 #include <public/bootfdt.h>
@@ -23,7 +24,6 @@
 #include <asm/domain_build.h>
 #include <asm/grant_table.h>
 #include <asm/setup.h>
-#include <asm/static-shmem.h>
 
 #ifdef CONFIG_VGICV2
 static int __init make_gicv2_domU_node(struct kernel_info *kinfo)
diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
index 068af31a62..590f38e520 100644
--- a/xen/arch/arm/domain_build.c
+++ b/xen/arch/arm/domain_build.c
@@ -21,6 +21,7 @@
 #include <xen/acpi.h>
 #include <xen/vmap.h>
 #include <xen/warning.h>
+#include <xen/static-shmem.h>
 #include <asm/device.h>
 #include <asm/setup.h>
 #include <asm/tee/tee.h>
@@ -32,7 +33,6 @@
 #include <asm/cpufeature.h>
 #include <asm/dom0less-build.h>
 #include <asm/domain_build.h>
-#include <asm/static-shmem.h>
 #include <xen/event.h>
 
 #include <xen/irq.h>
diff --git a/xen/arch/arm/include/asm/static-shmem.h 
b/xen/arch/arm/include/asm/static-shmem.h
deleted file mode 100644
index 6a4c33cca8..0000000000
--- a/xen/arch/arm/include/asm/static-shmem.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-
-#ifndef __ASM_STATIC_SHMEM_H_
-#define __ASM_STATIC_SHMEM_H_
-
-#include <xen/fdt-kernel.h>
-#include <xen/types.h>
-
-#ifdef CONFIG_STATIC_SHM
-
-/* Worst case /memory node reg element: (addrcells + sizecells) */
-#define DT_MEM_NODE_REG_RANGE_SIZE ((NR_MEM_BANKS + NR_SHMEM_BANKS) * 4)
-
-int make_resv_memory_node(const struct kernel_info *kinfo, int addrcells,
-                          int sizecells);
-
-int process_shm(struct domain *d, struct kernel_info *kinfo,
-                const struct dt_device_node *node);
-
-int process_shm_node(const void *fdt, int node, uint32_t address_cells,
-                     uint32_t size_cells);
-
-void early_print_info_shmem(void);
-
-void init_sharedmem_pages(void);
-
-int remove_shm_from_rangeset(const struct kernel_info *kinfo,
-                             struct rangeset *rangeset);
-
-int make_shm_resv_memory_node(const struct kernel_info *kinfo, int addrcells,
-                              int sizecells);
-
-void shm_mem_node_fill_reg_range(const struct kernel_info *kinfo, __be32 *reg,
-                                 int *nr_cells, int addrcells, int sizecells);
-
-static inline struct membanks *
-kernel_info_get_shm_mem(struct kernel_info *kinfo)
-{
-    return container_of(&kinfo->shm_mem.common, struct membanks, common);
-}
-
-static inline const struct membanks *
-kernel_info_get_shm_mem_const(const struct kernel_info *kinfo)
-{
-    return container_of(&kinfo->shm_mem.common, const struct membanks, common);
-}
-
-#else /* !CONFIG_STATIC_SHM */
-
-/* Worst case /memory node reg element: (addrcells + sizecells) */
-#define DT_MEM_NODE_REG_RANGE_SIZE (NR_MEM_BANKS * 4)
-
-static inline int make_resv_memory_node(const struct kernel_info *kinfo,
-                                        int addrcells, int sizecells)
-{
-    return 0;
-}
-
-static inline int process_shm(struct domain *d, struct kernel_info *kinfo,
-                              const struct dt_device_node *node)
-{
-    return 0;
-}
-
-static inline void init_sharedmem_pages(void) {};
-
-static inline int remove_shm_from_rangeset(const struct kernel_info *kinfo,
-                                           struct rangeset *rangeset)
-{
-    return 0;
-}
-
-static inline int make_shm_resv_memory_node(const struct kernel_info *kinfo,
-                                            int addrcells, int sizecells)
-{
-    return 0;
-}
-
-static inline void shm_mem_node_fill_reg_range(const struct kernel_info *kinfo,
-                                               __be32 *reg, int *nr_cells,
-                                               int addrcells, int sizecells) 
{};
-
-#endif /* CONFIG_STATIC_SHM */
-
-#endif /* __ASM_STATIC_SHMEM_H_ */
-
-/*
- * Local variables:
- * mode: C
- * c-file-style: "BSD"
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/xen/arch/arm/static-shmem.c b/xen/arch/arm/static-shmem.c
deleted file mode 100644
index 2055b7be0f..0000000000
--- a/xen/arch/arm/static-shmem.c
+++ /dev/null
@@ -1,854 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-
-#include <xen/device_tree.h>
-#include <xen/fdt-domain-build.h>
-#include <xen/libfdt/libfdt.h>
-#include <xen/rangeset.h>
-#include <xen/sched.h>
-#include <xen/static-memory.h>
-
-#include <asm/setup.h>
-#include <asm/static-shmem.h>
-
-typedef struct {
-    struct domain *d;
-    const char *role_str;
-    paddr_t gbase;
-    struct shmem_membank_extra *bank_extra_info;
-} alloc_heap_pages_cb_extra;
-
-static struct {
-    struct membanks_hdr common;
-    struct membank bank[NR_SHMEM_BANKS];
-} shm_heap_banks __initdata = {
-    .common.max_banks = NR_SHMEM_BANKS,
-    .common.type = STATIC_SHARED_MEMORY
-};
-
-static inline struct membanks *get_shmem_heap_banks(void)
-{
-    return container_of(&shm_heap_banks.common, struct membanks, common);
-}
-
-static void __init __maybe_unused build_assertions(void)
-{
-    /*
-     * Check that no padding is between struct membanks "bank" flexible array
-     * member and struct shared_meminfo "bank" member
-     */
-    BUILD_BUG_ON((offsetof(struct membanks, bank) !=
-                 offsetof(struct shared_meminfo, bank)));
-}
-
-static const struct membank __init *
-find_shm_bank_by_id(const struct membanks *shmem, const char *shm_id)
-{
-    unsigned int bank;
-
-    for ( bank = 0 ; bank < shmem->nr_banks; bank++ )
-    {
-        if ( strcmp(shm_id, shmem->bank[bank].shmem_extra->shm_id) == 0 )
-            break;
-    }
-
-    if ( bank == shmem->nr_banks )
-        return NULL;
-
-    return &shmem->bank[bank];
-}
-
-/*
- * This function checks whether the static shared memory region is
- * already allocated to dom_io.
- */
-static bool __init is_shm_allocated_to_domio(paddr_t pbase)
-{
-    struct page_info *page;
-    struct domain *d;
-
-    page = maddr_to_page(pbase);
-    d = page_get_owner_and_reference(page);
-    if ( d == NULL )
-        return false;
-    put_page(page);
-
-    if ( d != dom_io )
-    {
-        printk(XENLOG_ERR
-               "shm memory node has already been allocated to a specific owner 
%pd, Please check your configuration\n",
-               d);
-        return false;
-    }
-
-    return true;
-}
-
-static mfn_t __init acquire_shared_memory_bank(struct domain *d,
-                                               paddr_t pbase, paddr_t psize,
-                                               bool bank_from_heap)
-{
-    mfn_t smfn;
-    unsigned long nr_pfns;
-    int res;
-
-    /*
-     * Pages of statically shared memory shall be included
-     * into domain_tot_pages().
-     */
-    nr_pfns = PFN_DOWN(psize);
-    if ( (UINT_MAX - d->max_pages) < nr_pfns )
-    {
-        printk(XENLOG_ERR "%pd: Over-allocation for d->max_pages: %lu.\n",
-               d, nr_pfns);
-        return INVALID_MFN;
-    }
-    d->max_pages += nr_pfns;
-
-    smfn = maddr_to_mfn(pbase);
-    if ( bank_from_heap )
-        /*
-         * When host address is not provided, static shared memory is
-         * allocated from heap and shall be assigned to owner domain.
-         */
-        res = assign_pages(maddr_to_page(pbase), nr_pfns, d, 0);
-    else
-        res = acquire_domstatic_pages(d, smfn, nr_pfns, 0);
-
-    if ( res )
-    {
-        printk(XENLOG_ERR "%pd: failed to %s static memory: %d.\n", d,
-               bank_from_heap ? "assign" : "acquire", res);
-        goto fail;
-    }
-
-    return smfn;
-
- fail:
-    d->max_pages -= nr_pfns;
-    return INVALID_MFN;
-}
-
-static int __init assign_shared_memory(struct domain *d, paddr_t gbase,
-                                       bool bank_from_heap,
-                                       const struct membank *shm_bank)
-{
-    mfn_t smfn;
-    int ret = 0;
-    unsigned long nr_pages, nr_borrowers, i;
-    struct page_info *page;
-    paddr_t pbase, psize;
-
-    pbase = shm_bank->start;
-    psize = shm_bank->size;
-    nr_borrowers = shm_bank->shmem_extra->nr_shm_borrowers;
-
-    smfn = acquire_shared_memory_bank(d, pbase, psize, bank_from_heap);
-    if ( mfn_eq(smfn, INVALID_MFN) )
-        return -EINVAL;
-
-    /*
-     * DOMID_IO is not auto-translated (i.e. it sees RAM 1:1). So we do not 
need
-     * to create mapping in the P2M.
-     */
-    nr_pages = PFN_DOWN(psize);
-    if ( d != dom_io )
-    {
-        ret = guest_physmap_add_pages(d, gaddr_to_gfn(gbase), smfn,
-                                      PFN_DOWN(psize));
-        if ( ret )
-        {
-            printk(XENLOG_ERR "Failed to map shared memory to %pd.\n", d);
-            return ret;
-        }
-    }
-
-    /*
-     * Instead of letting borrower domain get a page ref, we add as many
-     * additional reference as the number of borrowers when the owner
-     * is allocated, since there is a chance that owner is created
-     * after borrower.
-     * So if the borrower is created first, it will cause adding pages
-     * in the P2M without reference.
-     */
-    page = mfn_to_page(smfn);
-    for ( i = 0; i < nr_pages; i++ )
-    {
-        if ( !get_page_nr(page + i, d, nr_borrowers) )
-        {
-            printk(XENLOG_ERR
-                   "Failed to add %lu references to page %"PRI_mfn".\n",
-                   nr_borrowers, mfn_x(smfn) + i);
-            goto fail;
-        }
-    }
-
-    return 0;
-
- fail:
-    while ( --i >= 0 )
-        put_page_nr(page + i, nr_borrowers);
-    return ret;
-}
-
-static int __init
-append_shm_bank_to_domain(struct kernel_info *kinfo, paddr_t start,
-                          paddr_t size, const char *shm_id)
-{
-    struct membanks *shm_mem = kernel_info_get_shm_mem(kinfo);
-    struct shmem_membank_extra *shm_mem_extra;
-
-    if ( shm_mem->nr_banks >= shm_mem->max_banks )
-        return -ENOMEM;
-
-    shm_mem_extra = &kinfo->shm_mem.extra[shm_mem->nr_banks];
-
-    shm_mem->bank[shm_mem->nr_banks].start = start;
-    shm_mem->bank[shm_mem->nr_banks].size = size;
-    safe_strcpy(shm_mem_extra->shm_id, shm_id);
-    shm_mem->bank[shm_mem->nr_banks].shmem_extra = shm_mem_extra;
-    shm_mem->nr_banks++;
-
-    return 0;
-}
-
-static int __init handle_shared_mem_bank(struct domain *d, paddr_t gbase,
-                                         const char *role_str,
-                                         bool bank_from_heap,
-                                         const struct membank *shm_bank)
-{
-    bool owner_dom_io = true;
-    paddr_t pbase, psize;
-    int ret;
-
-    pbase = shm_bank->start;
-    psize = shm_bank->size;
-
-    /*
-     * "role" property is optional and if it is defined explicitly,
-     * then the owner domain is not the default "dom_io" domain.
-     */
-    if ( role_str != NULL )
-        owner_dom_io = false;
-
-    /*
-     * DOMID_IO is a fake domain and is not described in the Device-Tree.
-     * Therefore when the owner of the shared region is DOMID_IO, we will
-     * only find the borrowers.
-     */
-    if ( (owner_dom_io && !is_shm_allocated_to_domio(pbase)) ||
-         (!owner_dom_io && strcmp(role_str, "owner") == 0) )
-    {
-        /*
-         * We found the first borrower of the region, the owner was not
-         * specified, so they should be assigned to dom_io.
-         */
-        ret = assign_shared_memory(owner_dom_io ? dom_io : d, gbase,
-                                   bank_from_heap, shm_bank);
-        if ( ret )
-            return ret;
-    }
-
-    if ( owner_dom_io || (strcmp(role_str, "borrower") == 0) )
-    {
-        /* Set up P2M foreign mapping for borrower domain. */
-        ret = map_regions_p2mt(d, _gfn(PFN_UP(gbase)), PFN_DOWN(psize),
-                               _mfn(PFN_UP(pbase)), p2m_map_foreign_rw);
-        if ( ret )
-            return ret;
-    }
-
-    return 0;
-}
-
-static bool __init save_map_heap_pages(struct domain *d, struct page_info *pg,
-                                       unsigned int order, void *extra)
-{
-    alloc_heap_pages_cb_extra *b_extra = (alloc_heap_pages_cb_extra *)extra;
-    int idx = shm_heap_banks.common.nr_banks;
-    int ret = -ENOSPC;
-
-    BUG_ON(!b_extra);
-
-    if ( idx < shm_heap_banks.common.max_banks )
-    {
-        shm_heap_banks.bank[idx].start = page_to_maddr(pg);
-        shm_heap_banks.bank[idx].size = (1ULL << (PAGE_SHIFT + order));
-        shm_heap_banks.bank[idx].shmem_extra = b_extra->bank_extra_info;
-        shm_heap_banks.common.nr_banks++;
-
-        ret = handle_shared_mem_bank(b_extra->d, b_extra->gbase,
-                                     b_extra->role_str, true,
-                                     &shm_heap_banks.bank[idx]);
-        if ( !ret )
-        {
-            /* Increment guest physical address for next mapping */
-            b_extra->gbase += shm_heap_banks.bank[idx].size;
-            return true;
-        }
-    }
-
-    printk("Failed to allocate static shared memory from Xen heap: (%d)\n",
-           ret);
-
-    return false;
-}
-
-int __init process_shm(struct domain *d, struct kernel_info *kinfo,
-                       const struct dt_device_node *node)
-{
-    struct dt_device_node *shm_node;
-
-    /* Hwdom case - shm node under /chosen */
-    if ( !node )
-    {
-        node = dt_find_node_by_path("/chosen");
-        BUG_ON(!node);
-    }
-
-    dt_for_each_child_node(node, shm_node)
-    {
-        const struct membank *boot_shm_bank;
-        const struct dt_property *prop;
-        const __be32 *cells;
-        uint32_t addr_cells;
-        paddr_t gbase, pbase, psize;
-        int ret = 0;
-        unsigned int i;
-        const char *role_str;
-        const char *shm_id;
-
-        if ( !dt_device_is_compatible(shm_node, "xen,domain-shared-memory-v1") 
)
-            continue;
-
-        if ( dt_property_read_string(shm_node, "xen,shm-id", &shm_id) )
-        {
-            printk("%pd: invalid \"xen,shm-id\" property", d);
-            return -EINVAL;
-        }
-        BUG_ON((strlen(shm_id) <= 0) || (strlen(shm_id) >= MAX_SHM_ID_LENGTH));
-
-        boot_shm_bank = find_shm_bank_by_id(bootinfo_get_shmem(), shm_id);
-        if ( !boot_shm_bank )
-        {
-            printk("%pd: static shared memory bank not found: '%s'", d, 
shm_id);
-            return -ENOENT;
-        }
-
-        pbase = boot_shm_bank->start;
-        psize = boot_shm_bank->size;
-
-        /* "role" property is optional */
-        if ( dt_property_read_string(shm_node, "role", &role_str) != 0 )
-            role_str = NULL;
-
-        /*
-         * xen,shared-mem = <[pbase,] gbase, size>;
-         * pbase is optional.
-         */
-        addr_cells = dt_n_addr_cells(shm_node);
-        prop = dt_find_property(shm_node, "xen,shared-mem", NULL);
-        BUG_ON(!prop);
-        cells = (const __be32 *)prop->value;
-
-        if ( pbase != INVALID_PADDR )
-        {
-            /* guest phys address is after host phys address */
-            gbase = dt_read_paddr(cells + addr_cells, addr_cells);
-
-            if ( is_domain_direct_mapped(d) && (pbase != gbase) )
-            {
-                printk("%pd: physical address 0x%"PRIpaddr" and guest address 
0x%"PRIpaddr" are not direct-mapped.\n",
-                       d, pbase, gbase);
-                return -EINVAL;
-            }
-
-            for ( i = 0; i < PFN_DOWN(psize); i++ )
-                if ( !mfn_valid(mfn_add(maddr_to_mfn(pbase), i)) )
-                {
-                    printk("%pd: invalid physical address 0x%"PRI_mfn"\n",
-                        d, mfn_x(mfn_add(maddr_to_mfn(pbase), i)));
-                    return -EINVAL;
-                }
-
-            /* The host physical address is supplied by the user */
-            ret = handle_shared_mem_bank(d, gbase, role_str, false,
-                                         boot_shm_bank);
-            if ( ret )
-                return ret;
-        }
-        else
-        {
-            /*
-             * The host physical address is not supplied by the user, so it
-             * means that the banks needs to be allocated from the Xen heap,
-             * look into the already allocated banks from the heap.
-             */
-            const struct membank *alloc_bank =
-                find_shm_bank_by_id(get_shmem_heap_banks(), shm_id);
-
-            if ( is_domain_direct_mapped(d) )
-            {
-                printk("%pd: host and guest physical address must be supplied 
for direct-mapped domains\n",
-                       d);
-                return -EINVAL;
-            }
-
-            /* guest phys address is right at the beginning */
-            gbase = dt_read_paddr(cells, addr_cells);
-
-            if ( !alloc_bank )
-            {
-                alloc_heap_pages_cb_extra cb_arg = { d, role_str, gbase,
-                    boot_shm_bank->shmem_extra };
-
-                /* shm_id identified bank is not yet allocated */
-                if ( !allocate_domheap_memory(NULL, psize, save_map_heap_pages,
-                                              &cb_arg) )
-                {
-                    printk(XENLOG_ERR
-                           "Failed to allocate (%"PRIpaddr"KB) pages as static 
shared memory from heap\n",
-                           psize >> 10);
-                    return -EINVAL;
-                }
-            }
-            else
-            {
-                /* shm_id identified bank is already allocated */
-                const struct membank *end_bank =
-                        &shm_heap_banks.bank[shm_heap_banks.common.nr_banks];
-                paddr_t gbase_bank = gbase;
-
-                /*
-                 * Static shared memory banks that are taken from the Xen heap
-                 * are allocated sequentially in shm_heap_banks, so starting
-                 * from the first bank found identified by shm_id, the code can
-                 * just advance by one bank at the time until it reaches the 
end
-                 * of the array or it finds another bank NOT identified by
-                 * shm_id
-                 */
-                for ( ; alloc_bank < end_bank; alloc_bank++ )
-                {
-                    if ( strcmp(shm_id, alloc_bank->shmem_extra->shm_id) != 0 )
-                        break;
-
-                    ret = handle_shared_mem_bank(d, gbase_bank, role_str, true,
-                                                 alloc_bank);
-                    if ( ret )
-                        return ret;
-
-                    /* Increment guest physical address for next mapping */
-                    gbase_bank += alloc_bank->size;
-                }
-            }
-        }
-
-        /*
-         * Record static shared memory region info for later setting
-         * up shm-node in guest device tree.
-         */
-        ret = append_shm_bank_to_domain(kinfo, gbase, psize, shm_id);
-        if ( ret )
-            return ret;
-    }
-
-    return 0;
-}
-
-int __init make_shm_resv_memory_node(const struct kernel_info *kinfo,
-                                     int addrcells, int sizecells)
-{
-    const struct membanks *mem = kernel_info_get_shm_mem_const(kinfo);
-    void *fdt = kinfo->fdt;
-    unsigned int i = 0;
-    int res = 0;
-
-    if ( mem->nr_banks == 0 )
-        return 0;
-
-    /*
-     * For each shared memory region, a range is exposed under
-     * the /reserved-memory node as a child node. Each range sub-node is
-     * named xen-shmem@<address>.
-     */
-    dt_dprintk("Create xen-shmem node\n");
-
-    for ( ; i < mem->nr_banks; i++ )
-    {
-        uint64_t start = mem->bank[i].start;
-        uint64_t size = mem->bank[i].size;
-        const char compat[] = "xen,shared-memory-v1";
-        /* Worst case addrcells + sizecells */
-        __be32 reg[GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS];
-        __be32 *cells;
-        unsigned int len = (addrcells + sizecells) * sizeof(__be32);
-
-        res = domain_fdt_begin_node(fdt, "xen-shmem", mem->bank[i].start);
-        if ( res )
-            return res;
-
-        res = fdt_property(fdt, "compatible", compat, sizeof(compat));
-        if ( res )
-            return res;
-
-        cells = reg;
-        dt_child_set_range(&cells, addrcells, sizecells, start, size);
-
-        res = fdt_property(fdt, "reg", reg, len);
-        if ( res )
-            return res;
-
-        dt_dprintk("Shared memory bank %u: %#"PRIx64"->%#"PRIx64"\n",
-                   i, start, start + size);
-
-        res = fdt_property_string(fdt, "xen,id",
-                                  mem->bank[i].shmem_extra->shm_id);
-        if ( res )
-            return res;
-
-        /*
-         * TODO:
-         * - xen,offset: (borrower VMs only)
-         *   64 bit integer offset within the owner virtual machine's shared
-         *   memory region used for the mapping in the borrower VM
-         */
-        res = fdt_property_u64(fdt, "xen,offset", 0);
-        if ( res )
-            return res;
-
-        res = fdt_end_node(fdt);
-        if ( res )
-            return res;
-    }
-
-    return res;
-}
-
-int __init process_shm_node(const void *fdt, int node, uint32_t address_cells,
-                            uint32_t size_cells)
-{
-    const struct fdt_property *prop, *prop_id, *prop_role;
-    const __be32 *cell;
-    paddr_t paddr = INVALID_PADDR;
-    paddr_t gaddr, size, end;
-    struct membanks *mem = bootinfo_get_shmem();
-    struct shmem_membank_extra *shmem_extra = bootinfo_get_shmem_extra();
-    unsigned int i;
-    int len;
-    bool owner = false;
-    const char *shm_id;
-
-    if ( address_cells < 1 || size_cells < 1 )
-    {
-        printk("fdt: invalid #address-cells or #size-cells for static shared 
memory node.\n");
-        return -EINVAL;
-    }
-
-    /*
-     * "xen,shm-id" property holds an arbitrary string with a strict limit
-     * on the number of characters, MAX_SHM_ID_LENGTH
-     */
-    prop_id = fdt_get_property(fdt, node, "xen,shm-id", NULL);
-    if ( !prop_id )
-        return -ENOENT;
-    shm_id = (const char *)prop_id->data;
-    if ( strnlen(shm_id, MAX_SHM_ID_LENGTH) == MAX_SHM_ID_LENGTH )
-    {
-        printk("fdt: invalid xen,shm-id %s, it must be limited to %u 
characters\n",
-               shm_id, MAX_SHM_ID_LENGTH);
-        return -EINVAL;
-    }
-
-    /*
-     * "role" property is optional and if it is defined explicitly,
-     * it must be either `owner` or `borrower`.
-     */
-    prop_role = fdt_get_property(fdt, node, "role", NULL);
-    if ( prop_role )
-    {
-        if ( !strcmp(prop_role->data, "owner") )
-            owner = true;
-        else if ( strcmp(prop_role->data, "borrower") )
-        {
-            printk("fdt: invalid `role` property for static shared memory 
node.\n");
-            return -EINVAL;
-        }
-    }
-
-    /*
-     * xen,shared-mem = <paddr, gaddr, size>;
-     * Memory region starting from physical address #paddr of #size shall
-     * be mapped to guest physical address #gaddr as static shared memory
-     * region.
-     */
-    prop = fdt_get_property(fdt, node, "xen,shared-mem", &len);
-    if ( !prop )
-        return -ENOENT;
-
-    cell = (const __be32 *)prop->data;
-    if ( len != dt_cells_to_size(address_cells + size_cells + address_cells) )
-    {
-        if ( len == dt_cells_to_size(address_cells + size_cells) )
-            device_tree_get_reg(&cell, address_cells, size_cells, &gaddr,
-                                &size);
-        else
-        {
-            printk("fdt: invalid `xen,shared-mem` property.\n");
-            return -EINVAL;
-        }
-    }
-    else
-    {
-        device_tree_get_reg(&cell, address_cells, address_cells, &paddr,
-                            &gaddr);
-        size = dt_next_cell(size_cells, &cell);
-
-        if ( !IS_ALIGNED(paddr, PAGE_SIZE) )
-        {
-            printk("fdt: physical address 0x%"PRIpaddr" is not suitably 
aligned.\n",
-                paddr);
-            return -EINVAL;
-        }
-
-        end = paddr + size;
-        if ( end <= paddr )
-        {
-            printk("fdt: static shared memory region %s overflow\n", shm_id);
-            return -EINVAL;
-        }
-    }
-
-    if ( !IS_ALIGNED(gaddr, PAGE_SIZE) )
-    {
-        printk("fdt: guest address 0x%"PRIpaddr" is not suitably aligned.\n",
-               gaddr);
-        return -EINVAL;
-    }
-
-    if ( !size )
-    {
-        printk("fdt: the size for static shared memory region can not be 
zero\n");
-        return -EINVAL;
-    }
-
-    if ( !IS_ALIGNED(size, PAGE_SIZE) )
-    {
-        printk("fdt: size 0x%"PRIpaddr" is not suitably aligned\n", size);
-        return -EINVAL;
-    }
-
-    for ( i = 0; i < mem->nr_banks; i++ )
-    {
-        /*
-         * Meet the following check:
-         * - when host address is provided:
-         *   1) The shm ID matches and the region exactly match
-         *   2) The shm ID doesn't match and the region doesn't overlap
-         *      with an existing one
-         * - when host address is not provided:
-         *   1) The shm ID matches and the region size exactly match
-         */
-        bool paddr_assigned = (INVALID_PADDR != paddr);
-
-        if ( strncmp(shm_id, shmem_extra[i].shm_id, MAX_SHM_ID_LENGTH) == 0 )
-        {
-            /*
-             * Regions have same shm_id (cases):
-             * 1) physical host address is supplied:
-             *    - OK:   paddr is equal and size is equal (same region)
-             *    - Fail: paddr doesn't match or size doesn't match (there
-             *            cannot exists two shmem regions with same shm_id)
-             * 2) physical host address is NOT supplied:
-             *    - OK:   size is equal (same region)
-             *    - Fail: size is not equal (same shm_id must identify only one
-             *            region, there can't be two different regions with 
same
-             *            shm_id)
-             */
-            bool start_match = paddr_assigned ? (paddr == mem->bank[i].start) :
-                                                true;
-
-            if ( start_match && (size == mem->bank[i].size) )
-                break;
-            else
-            {
-                printk("fdt: different shared memory region could not share 
the same shm ID %s\n",
-                       shm_id);
-                return -EINVAL;
-            }
-        }
-
-        /*
-         * Regions have different shm_id (cases):
-         * 1) physical host address is supplied:
-         *    - OK:   paddr different, or size different (case where paddr
-         *            is equal but psize is different are wrong, but they
-         *            are handled later when checking for overlapping)
-         *    - Fail: paddr equal and size equal (the same region can't be
-         *            identified with different shm_id)
-         * 2) physical host address is NOT supplied:
-         *    - OK:   Both have different shm_id so even with same size they
-         *            can exists
-         */
-        if ( !paddr_assigned || (paddr != mem->bank[i].start) ||
-             (size != mem->bank[i].size) )
-            continue;
-        else
-        {
-            printk("fdt: xen,shm-id %s does not match for all the nodes using 
the same region\n",
-                   shm_id);
-            return -EINVAL;
-        }
-    }
-
-    if ( i == mem->nr_banks )
-    {
-        if (i < mem->max_banks)
-        {
-            if ( (paddr != INVALID_PADDR) &&
-                 check_reserved_regions_overlap(paddr, size, false) )
-                return -EINVAL;
-
-            /* Static shared memory shall be reserved from any other use. */
-            safe_strcpy(shmem_extra[mem->nr_banks].shm_id, shm_id);
-            mem->bank[mem->nr_banks].start = paddr;
-            mem->bank[mem->nr_banks].size = size;
-            mem->bank[mem->nr_banks].shmem_extra = &shmem_extra[mem->nr_banks];
-            mem->nr_banks++;
-        }
-        else
-        {
-            printk("Warning: Max number of supported memory regions 
reached.\n");
-            return -ENOSPC;
-        }
-    }
-    /*
-     * keep a count of the number of borrowers, which later may be used
-     * to calculate the reference count.
-     */
-    if ( !owner )
-        shmem_extra[i].nr_shm_borrowers++;
-
-    return 0;
-}
-
-int __init make_resv_memory_node(const struct kernel_info *kinfo, int 
addrcells,
-                                 int sizecells)
-{
-    const struct membanks *mem = kernel_info_get_shm_mem_const(kinfo);
-    void *fdt = kinfo->fdt;
-    int res = 0;
-    /* Placeholder for reserved-memory\0 */
-    const char resvbuf[16] = "reserved-memory";
-
-    if ( mem->nr_banks == 0 )
-        /* No shared memory provided. */
-        return 0;
-
-    dt_dprintk("Create reserved-memory node\n");
-
-    res = fdt_begin_node(fdt, resvbuf);
-    if ( res )
-        return res;
-
-    res = fdt_property(fdt, "ranges", NULL, 0);
-    if ( res )
-        return res;
-
-    res = fdt_property_cell(fdt, "#address-cells", addrcells);
-    if ( res )
-        return res;
-
-    res = fdt_property_cell(fdt, "#size-cells", sizecells);
-    if ( res )
-        return res;
-
-    res = make_shm_resv_memory_node(kinfo, addrcells, sizecells);
-    if ( res )
-        return res;
-
-    res = fdt_end_node(fdt);
-
-    return res;
-}
-
-void __init early_print_info_shmem(void)
-{
-    const struct membanks *shmem = bootinfo_get_shmem();
-    unsigned int bank;
-    unsigned int printed = 0;
-
-    for ( bank = 0; bank < shmem->nr_banks; bank++, printed++ )
-        if ( shmem->bank[bank].start != INVALID_PADDR )
-            printk(" SHMEM[%u]: %"PRIpaddr" - %"PRIpaddr"\n", printed,
-                shmem->bank[bank].start,
-                shmem->bank[bank].start + shmem->bank[bank].size - 1);
-}
-
-void __init init_sharedmem_pages(void)
-{
-    const struct membanks *shmem = bootinfo_get_shmem();
-    unsigned int bank;
-
-    for ( bank = 0 ; bank < shmem->nr_banks; bank++ )
-        if ( shmem->bank[bank].start != INVALID_PADDR )
-            init_staticmem_bank(&shmem->bank[bank]);
-}
-
-int __init remove_shm_from_rangeset(const struct kernel_info *kinfo,
-                                    struct rangeset *rangeset)
-{
-    const struct membanks *shm_mem = kernel_info_get_shm_mem_const(kinfo);
-    unsigned int i;
-
-    /* Remove static shared memory regions */
-    for ( i = 0; i < shm_mem->nr_banks; i++ )
-    {
-        paddr_t start, end;
-        int res;
-
-        start = shm_mem->bank[i].start;
-        end = shm_mem->bank[i].start + shm_mem->bank[i].size;
-        res = rangeset_remove_range(rangeset, PFN_DOWN(start),
-                                    PFN_DOWN(end - 1));
-        if ( res )
-        {
-            printk(XENLOG_ERR
-                   "Failed to remove: %#"PRIpaddr"->%#"PRIpaddr", error: %d\n",
-                   start, end, res);
-            return -EINVAL;
-        }
-    }
-
-    return 0;
-}
-
-void __init shm_mem_node_fill_reg_range(const struct kernel_info *kinfo,
-                                        __be32 *reg, int *nr_cells,
-                                        int addrcells, int sizecells)
-{
-    const struct membanks *mem = kernel_info_get_shm_mem_const(kinfo);
-    unsigned int i;
-    __be32 *cells;
-
-    BUG_ON(!nr_cells || !reg);
-
-    cells = &reg[*nr_cells];
-    for ( i = 0; i < mem->nr_banks; i++ )
-    {
-        paddr_t start = mem->bank[i].start;
-        paddr_t size = mem->bank[i].size;
-
-        *nr_cells += addrcells + sizecells;
-        BUG_ON(*nr_cells >= DT_MEM_NODE_REG_RANGE_SIZE);
-        dt_child_set_range(&cells, addrcells, sizecells, start, size);
-    }
-}
-
-/*
- * Local variables:
- * mode: C
- * c-file-style: "BSD"
- * c-basic-offset: 4
- * tab-width: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/xen/common/Kconfig b/xen/common/Kconfig
index 7ecf5a8031..eece1370a3 100644
--- a/xen/common/Kconfig
+++ b/xen/common/Kconfig
@@ -162,6 +162,12 @@ config STATIC_MEMORY
 
          If unsure, say N.
 
+config STATIC_SHM
+       bool "Statically shared memory on a dom0less system" if UNSUPPORTED
+       depends on STATIC_MEMORY
+       help
+         This option enables statically shared memory on a dom0less system.
+
 config STATIC_EVTCHN
        bool "Static event channel support on a dom0less system"
        depends on DOM0LESS_BOOT
diff --git a/xen/common/device-tree/Makefile b/xen/common/device-tree/Makefile
index ed11f2c3b4..13127296cb 100644
--- a/xen/common/device-tree/Makefile
+++ b/xen/common/device-tree/Makefile
@@ -8,3 +8,4 @@ obj-y += intc.o
 obj-$(CONFIG_DOMAIN_BUILD_HELPERS) += kernel.o
 obj-$(CONFIG_STATIC_EVTCHN) += static-evtchn.init.o
 obj-$(CONFIG_STATIC_MEMORY) += static-memory.init.o
+obj-$(CONFIG_STATIC_SHM) += static-shmem.init.o
diff --git a/xen/common/device-tree/bootfdt.c b/xen/common/device-tree/bootfdt.c
index aa44f5a67c..9df80291b1 100644
--- a/xen/common/device-tree/bootfdt.c
+++ b/xen/common/device-tree/bootfdt.c
@@ -13,11 +13,9 @@
 #include <xen/lib.h>
 #include <xen/libfdt/libfdt-xen.h>
 #include <xen/sort.h>
+#include <xen/static-shmem.h>
 #include <xsm/xsm.h>
 #include <asm/setup.h>
-#ifdef CONFIG_STATIC_SHM
-#include <asm/static-shmem.h>
-#endif
 
 static void __init __maybe_unused build_assertions(void)
 {
diff --git a/xen/common/device-tree/dom0less-build.c 
b/xen/common/device-tree/dom0less-build.c
index c8c5a04f24..3d503c6973 100644
--- a/xen/common/device-tree/dom0less-build.c
+++ b/xen/common/device-tree/dom0less-build.c
@@ -29,10 +29,7 @@
 #include <asm/setup.h>
 
 #include <xen/static-memory.h>
-
-#if __has_include(<asm/static-shmem.h>)
-#   include <asm/static-shmem.h>
-#endif
+#include <xen/static-shmem.h>
 
 #define XENSTORE_PFN_LATE_ALLOC UINT64_MAX
 
@@ -505,11 +502,9 @@ static int __init prepare_dtb_domU(struct domain *d, 
struct kernel_info *kinfo)
     if ( ret )
         goto err;
 
-#ifdef CONFIG_STATIC_SHM
     ret = make_resv_memory_node(kinfo, addrcells, sizecells);
     if ( ret )
         goto err;
-#endif
 
     /*
      * domain_handle_dtb_bootmodule has to be called before the rest of
@@ -802,11 +797,9 @@ static int __init construct_domU(struct domain *d,
         else
             assign_static_memory_11(d, &kinfo, node);
 
-#ifdef CONFIG_STATIC_SHM
         rc = process_shm(d, &kinfo, node);
         if ( rc < 0 )
             return rc;
-#endif
 
         rc = init_vuart(d, &kinfo, node);
         if ( rc < 0 )
diff --git a/xen/common/device-tree/static-shmem.c 
b/xen/common/device-tree/static-shmem.c
new file mode 100644
index 0000000000..8023c0a484
--- /dev/null
+++ b/xen/common/device-tree/static-shmem.c
@@ -0,0 +1,854 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <xen/device_tree.h>
+#include <xen/fdt-domain-build.h>
+#include <xen/libfdt/libfdt.h>
+#include <xen/rangeset.h>
+#include <xen/sched.h>
+#include <xen/static-memory.h>
+#include <xen/static-shmem.h>
+
+#include <asm/setup.h>
+
+typedef struct {
+    struct domain *d;
+    const char *role_str;
+    paddr_t gbase;
+    struct shmem_membank_extra *bank_extra_info;
+} alloc_heap_pages_cb_extra;
+
+static struct {
+    struct membanks_hdr common;
+    struct membank bank[NR_SHMEM_BANKS];
+} shm_heap_banks __initdata = {
+    .common.max_banks = NR_SHMEM_BANKS,
+    .common.type = STATIC_SHARED_MEMORY
+};
+
+static inline struct membanks *get_shmem_heap_banks(void)
+{
+    return container_of(&shm_heap_banks.common, struct membanks, common);
+}
+
+static void __init __maybe_unused build_assertions(void)
+{
+    /*
+     * Check that no padding is between struct membanks "bank" flexible array
+     * member and struct shared_meminfo "bank" member
+     */
+    BUILD_BUG_ON((offsetof(struct membanks, bank) !=
+                 offsetof(struct shared_meminfo, bank)));
+}
+
+static const struct membank __init *
+find_shm_bank_by_id(const struct membanks *shmem, const char *shm_id)
+{
+    unsigned int bank;
+
+    for ( bank = 0 ; bank < shmem->nr_banks; bank++ )
+    {
+        if ( strcmp(shm_id, shmem->bank[bank].shmem_extra->shm_id) == 0 )
+            break;
+    }
+
+    if ( bank == shmem->nr_banks )
+        return NULL;
+
+    return &shmem->bank[bank];
+}
+
+/*
+ * This function checks whether the static shared memory region is
+ * already allocated to dom_io.
+ */
+static bool __init is_shm_allocated_to_domio(paddr_t pbase)
+{
+    struct page_info *page;
+    struct domain *d;
+
+    page = maddr_to_page(pbase);
+    d = page_get_owner_and_reference(page);
+    if ( d == NULL )
+        return false;
+    put_page(page);
+
+    if ( d != dom_io )
+    {
+        printk(XENLOG_ERR
+               "shm memory node has already been allocated to a specific owner 
%pd, Please check your configuration\n",
+               d);
+        return false;
+    }
+
+    return true;
+}
+
+static mfn_t __init acquire_shared_memory_bank(struct domain *d,
+                                               paddr_t pbase, paddr_t psize,
+                                               bool bank_from_heap)
+{
+    mfn_t smfn;
+    unsigned long nr_pfns;
+    int res;
+
+    /*
+     * Pages of statically shared memory shall be included
+     * into domain_tot_pages().
+     */
+    nr_pfns = PFN_DOWN(psize);
+    if ( (UINT_MAX - d->max_pages) < nr_pfns )
+    {
+        printk(XENLOG_ERR "%pd: Over-allocation for d->max_pages: %lu.\n",
+               d, nr_pfns);
+        return INVALID_MFN;
+    }
+    d->max_pages += nr_pfns;
+
+    smfn = maddr_to_mfn(pbase);
+    if ( bank_from_heap )
+        /*
+         * When host address is not provided, static shared memory is
+         * allocated from heap and shall be assigned to owner domain.
+         */
+        res = assign_pages(maddr_to_page(pbase), nr_pfns, d, 0);
+    else
+        res = acquire_domstatic_pages(d, smfn, nr_pfns, 0);
+
+    if ( res )
+    {
+        printk(XENLOG_ERR "%pd: failed to %s static memory: %d.\n", d,
+               bank_from_heap ? "assign" : "acquire", res);
+        goto fail;
+    }
+
+    return smfn;
+
+ fail:
+    d->max_pages -= nr_pfns;
+    return INVALID_MFN;
+}
+
+static int __init assign_shared_memory(struct domain *d, paddr_t gbase,
+                                       bool bank_from_heap,
+                                       const struct membank *shm_bank)
+{
+    mfn_t smfn;
+    int ret = 0;
+    unsigned long nr_pages, nr_borrowers, i;
+    struct page_info *page;
+    paddr_t pbase, psize;
+
+    pbase = shm_bank->start;
+    psize = shm_bank->size;
+    nr_borrowers = shm_bank->shmem_extra->nr_shm_borrowers;
+
+    smfn = acquire_shared_memory_bank(d, pbase, psize, bank_from_heap);
+    if ( mfn_eq(smfn, INVALID_MFN) )
+        return -EINVAL;
+
+    /*
+     * DOMID_IO is not auto-translated (i.e. it sees RAM 1:1). So we do not 
need
+     * to create mapping in the P2M.
+     */
+    nr_pages = PFN_DOWN(psize);
+    if ( d != dom_io )
+    {
+        ret = guest_physmap_add_pages(d, gaddr_to_gfn(gbase), smfn,
+                                      PFN_DOWN(psize));
+        if ( ret )
+        {
+            printk(XENLOG_ERR "Failed to map shared memory to %pd.\n", d);
+            return ret;
+        }
+    }
+
+    /*
+     * Instead of letting borrower domain get a page ref, we add as many
+     * additional reference as the number of borrowers when the owner
+     * is allocated, since there is a chance that owner is created
+     * after borrower.
+     * So if the borrower is created first, it will cause adding pages
+     * in the P2M without reference.
+     */
+    page = mfn_to_page(smfn);
+    for ( i = 0; i < nr_pages; i++ )
+    {
+        if ( !get_page_nr(page + i, d, nr_borrowers) )
+        {
+            printk(XENLOG_ERR
+                   "Failed to add %lu references to page %"PRI_mfn".\n",
+                   nr_borrowers, mfn_x(smfn) + i);
+            goto fail;
+        }
+    }
+
+    return 0;
+
+ fail:
+    while ( --i >= 0 )
+        put_page_nr(page + i, nr_borrowers);
+    return ret;
+}
+
+static int __init
+append_shm_bank_to_domain(struct kernel_info *kinfo, paddr_t start,
+                          paddr_t size, const char *shm_id)
+{
+    struct membanks *shm_mem = kernel_info_get_shm_mem(kinfo);
+    struct shmem_membank_extra *shm_mem_extra;
+
+    if ( shm_mem->nr_banks >= shm_mem->max_banks )
+        return -ENOMEM;
+
+    shm_mem_extra = &kinfo->shm_mem.extra[shm_mem->nr_banks];
+
+    shm_mem->bank[shm_mem->nr_banks].start = start;
+    shm_mem->bank[shm_mem->nr_banks].size = size;
+    safe_strcpy(shm_mem_extra->shm_id, shm_id);
+    shm_mem->bank[shm_mem->nr_banks].shmem_extra = shm_mem_extra;
+    shm_mem->nr_banks++;
+
+    return 0;
+}
+
+static int __init handle_shared_mem_bank(struct domain *d, paddr_t gbase,
+                                         const char *role_str,
+                                         bool bank_from_heap,
+                                         const struct membank *shm_bank)
+{
+    bool owner_dom_io = true;
+    paddr_t pbase, psize;
+    int ret;
+
+    pbase = shm_bank->start;
+    psize = shm_bank->size;
+
+    /*
+     * "role" property is optional and if it is defined explicitly,
+     * then the owner domain is not the default "dom_io" domain.
+     */
+    if ( role_str != NULL )
+        owner_dom_io = false;
+
+    /*
+     * DOMID_IO is a fake domain and is not described in the Device-Tree.
+     * Therefore when the owner of the shared region is DOMID_IO, we will
+     * only find the borrowers.
+     */
+    if ( (owner_dom_io && !is_shm_allocated_to_domio(pbase)) ||
+         (!owner_dom_io && strcmp(role_str, "owner") == 0) )
+    {
+        /*
+         * We found the first borrower of the region, the owner was not
+         * specified, so they should be assigned to dom_io.
+         */
+        ret = assign_shared_memory(owner_dom_io ? dom_io : d, gbase,
+                                   bank_from_heap, shm_bank);
+        if ( ret )
+            return ret;
+    }
+
+    if ( owner_dom_io || (strcmp(role_str, "borrower") == 0) )
+    {
+        /* Set up P2M foreign mapping for borrower domain. */
+        ret = map_regions_p2mt(d, _gfn(PFN_UP(gbase)), PFN_DOWN(psize),
+                               _mfn(PFN_UP(pbase)), p2m_map_foreign_rw);
+        if ( ret )
+            return ret;
+    }
+
+    return 0;
+}
+
+static bool __init save_map_heap_pages(struct domain *d, struct page_info *pg,
+                                       unsigned int order, void *extra)
+{
+    alloc_heap_pages_cb_extra *b_extra = (alloc_heap_pages_cb_extra *)extra;
+    int idx = shm_heap_banks.common.nr_banks;
+    int ret = -ENOSPC;
+
+    BUG_ON(!b_extra);
+
+    if ( idx < shm_heap_banks.common.max_banks )
+    {
+        shm_heap_banks.bank[idx].start = page_to_maddr(pg);
+        shm_heap_banks.bank[idx].size = (1ULL << (PAGE_SHIFT + order));
+        shm_heap_banks.bank[idx].shmem_extra = b_extra->bank_extra_info;
+        shm_heap_banks.common.nr_banks++;
+
+        ret = handle_shared_mem_bank(b_extra->d, b_extra->gbase,
+                                     b_extra->role_str, true,
+                                     &shm_heap_banks.bank[idx]);
+        if ( !ret )
+        {
+            /* Increment guest physical address for next mapping */
+            b_extra->gbase += shm_heap_banks.bank[idx].size;
+            return true;
+        }
+    }
+
+    printk("Failed to allocate static shared memory from Xen heap: (%d)\n",
+           ret);
+
+    return false;
+}
+
+int __init process_shm(struct domain *d, struct kernel_info *kinfo,
+                       const struct dt_device_node *node)
+{
+    struct dt_device_node *shm_node;
+
+    /* Hwdom case - shm node under /chosen */
+    if ( !node )
+    {
+        node = dt_find_node_by_path("/chosen");
+        BUG_ON(!node);
+    }
+
+    dt_for_each_child_node(node, shm_node)
+    {
+        const struct membank *boot_shm_bank;
+        const struct dt_property *prop;
+        const __be32 *cells;
+        uint32_t addr_cells;
+        paddr_t gbase, pbase, psize;
+        int ret = 0;
+        unsigned int i;
+        const char *role_str;
+        const char *shm_id;
+
+        if ( !dt_device_is_compatible(shm_node, "xen,domain-shared-memory-v1") 
)
+            continue;
+
+        if ( dt_property_read_string(shm_node, "xen,shm-id", &shm_id) )
+        {
+            printk("%pd: invalid \"xen,shm-id\" property", d);
+            return -EINVAL;
+        }
+        BUG_ON((strlen(shm_id) <= 0) || (strlen(shm_id) >= MAX_SHM_ID_LENGTH));
+
+        boot_shm_bank = find_shm_bank_by_id(bootinfo_get_shmem(), shm_id);
+        if ( !boot_shm_bank )
+        {
+            printk("%pd: static shared memory bank not found: '%s'", d, 
shm_id);
+            return -ENOENT;
+        }
+
+        pbase = boot_shm_bank->start;
+        psize = boot_shm_bank->size;
+
+        /* "role" property is optional */
+        if ( dt_property_read_string(shm_node, "role", &role_str) != 0 )
+            role_str = NULL;
+
+        /*
+         * xen,shared-mem = <[pbase,] gbase, size>;
+         * pbase is optional.
+         */
+        addr_cells = dt_n_addr_cells(shm_node);
+        prop = dt_find_property(shm_node, "xen,shared-mem", NULL);
+        BUG_ON(!prop);
+        cells = (const __be32 *)prop->value;
+
+        if ( pbase != INVALID_PADDR )
+        {
+            /* guest phys address is after host phys address */
+            gbase = dt_read_paddr(cells + addr_cells, addr_cells);
+
+            if ( is_domain_direct_mapped(d) && (pbase != gbase) )
+            {
+                printk("%pd: physical address 0x%"PRIpaddr" and guest address 
0x%"PRIpaddr" are not direct-mapped.\n",
+                       d, pbase, gbase);
+                return -EINVAL;
+            }
+
+            for ( i = 0; i < PFN_DOWN(psize); i++ )
+                if ( !mfn_valid(mfn_add(maddr_to_mfn(pbase), i)) )
+                {
+                    printk("%pd: invalid physical address 0x%"PRI_mfn"\n",
+                        d, mfn_x(mfn_add(maddr_to_mfn(pbase), i)));
+                    return -EINVAL;
+                }
+
+            /* The host physical address is supplied by the user */
+            ret = handle_shared_mem_bank(d, gbase, role_str, false,
+                                         boot_shm_bank);
+            if ( ret )
+                return ret;
+        }
+        else
+        {
+            /*
+             * The host physical address is not supplied by the user, so it
+             * means that the banks needs to be allocated from the Xen heap,
+             * look into the already allocated banks from the heap.
+             */
+            const struct membank *alloc_bank =
+                find_shm_bank_by_id(get_shmem_heap_banks(), shm_id);
+
+            if ( is_domain_direct_mapped(d) )
+            {
+                printk("%pd: host and guest physical address must be supplied 
for direct-mapped domains\n",
+                       d);
+                return -EINVAL;
+            }
+
+            /* guest phys address is right at the beginning */
+            gbase = dt_read_paddr(cells, addr_cells);
+
+            if ( !alloc_bank )
+            {
+                alloc_heap_pages_cb_extra cb_arg = { d, role_str, gbase,
+                    boot_shm_bank->shmem_extra };
+
+                /* shm_id identified bank is not yet allocated */
+                if ( !allocate_domheap_memory(NULL, psize, save_map_heap_pages,
+                                              &cb_arg) )
+                {
+                    printk(XENLOG_ERR
+                           "Failed to allocate (%"PRIpaddr"KB) pages as static 
shared memory from heap\n",
+                           psize >> 10);
+                    return -EINVAL;
+                }
+            }
+            else
+            {
+                /* shm_id identified bank is already allocated */
+                const struct membank *end_bank =
+                        &shm_heap_banks.bank[shm_heap_banks.common.nr_banks];
+                paddr_t gbase_bank = gbase;
+
+                /*
+                 * Static shared memory banks that are taken from the Xen heap
+                 * are allocated sequentially in shm_heap_banks, so starting
+                 * from the first bank found identified by shm_id, the code can
+                 * just advance by one bank at the time until it reaches the 
end
+                 * of the array or it finds another bank NOT identified by
+                 * shm_id
+                 */
+                for ( ; alloc_bank < end_bank; alloc_bank++ )
+                {
+                    if ( strcmp(shm_id, alloc_bank->shmem_extra->shm_id) != 0 )
+                        break;
+
+                    ret = handle_shared_mem_bank(d, gbase_bank, role_str, true,
+                                                 alloc_bank);
+                    if ( ret )
+                        return ret;
+
+                    /* Increment guest physical address for next mapping */
+                    gbase_bank += alloc_bank->size;
+                }
+            }
+        }
+
+        /*
+         * Record static shared memory region info for later setting
+         * up shm-node in guest device tree.
+         */
+        ret = append_shm_bank_to_domain(kinfo, gbase, psize, shm_id);
+        if ( ret )
+            return ret;
+    }
+
+    return 0;
+}
+
+int __init make_shm_resv_memory_node(const struct kernel_info *kinfo,
+                                     int addrcells, int sizecells)
+{
+    const struct membanks *mem = kernel_info_get_shm_mem_const(kinfo);
+    void *fdt = kinfo->fdt;
+    unsigned int i = 0;
+    int res = 0;
+
+    if ( mem->nr_banks == 0 )
+        return 0;
+
+    /*
+     * For each shared memory region, a range is exposed under
+     * the /reserved-memory node as a child node. Each range sub-node is
+     * named xen-shmem@<address>.
+     */
+    dt_dprintk("Create xen-shmem node\n");
+
+    for ( ; i < mem->nr_banks; i++ )
+    {
+        uint64_t start = mem->bank[i].start;
+        uint64_t size = mem->bank[i].size;
+        const char compat[] = "xen,shared-memory-v1";
+        /* Worst case addrcells + sizecells */
+        __be32 reg[GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS];
+        __be32 *cells;
+        unsigned int len = (addrcells + sizecells) * sizeof(__be32);
+
+        res = domain_fdt_begin_node(fdt, "xen-shmem", mem->bank[i].start);
+        if ( res )
+            return res;
+
+        res = fdt_property(fdt, "compatible", compat, sizeof(compat));
+        if ( res )
+            return res;
+
+        cells = reg;
+        dt_child_set_range(&cells, addrcells, sizecells, start, size);
+
+        res = fdt_property(fdt, "reg", reg, len);
+        if ( res )
+            return res;
+
+        dt_dprintk("Shared memory bank %u: %#"PRIx64"->%#"PRIx64"\n",
+                   i, start, start + size);
+
+        res = fdt_property_string(fdt, "xen,id",
+                                  mem->bank[i].shmem_extra->shm_id);
+        if ( res )
+            return res;
+
+        /*
+         * TODO:
+         * - xen,offset: (borrower VMs only)
+         *   64 bit integer offset within the owner virtual machine's shared
+         *   memory region used for the mapping in the borrower VM
+         */
+        res = fdt_property_u64(fdt, "xen,offset", 0);
+        if ( res )
+            return res;
+
+        res = fdt_end_node(fdt);
+        if ( res )
+            return res;
+    }
+
+    return res;
+}
+
+int __init process_shm_node(const void *fdt, int node, uint32_t address_cells,
+                            uint32_t size_cells)
+{
+    const struct fdt_property *prop, *prop_id, *prop_role;
+    const __be32 *cell;
+    paddr_t paddr = INVALID_PADDR;
+    paddr_t gaddr, size, end;
+    struct membanks *mem = bootinfo_get_shmem();
+    struct shmem_membank_extra *shmem_extra = bootinfo_get_shmem_extra();
+    unsigned int i;
+    int len;
+    bool owner = false;
+    const char *shm_id;
+
+    if ( address_cells < 1 || size_cells < 1 )
+    {
+        printk("fdt: invalid #address-cells or #size-cells for static shared 
memory node.\n");
+        return -EINVAL;
+    }
+
+    /*
+     * "xen,shm-id" property holds an arbitrary string with a strict limit
+     * on the number of characters, MAX_SHM_ID_LENGTH
+     */
+    prop_id = fdt_get_property(fdt, node, "xen,shm-id", NULL);
+    if ( !prop_id )
+        return -ENOENT;
+    shm_id = (const char *)prop_id->data;
+    if ( strnlen(shm_id, MAX_SHM_ID_LENGTH) == MAX_SHM_ID_LENGTH )
+    {
+        printk("fdt: invalid xen,shm-id %s, it must be limited to %u 
characters\n",
+               shm_id, MAX_SHM_ID_LENGTH);
+        return -EINVAL;
+    }
+
+    /*
+     * "role" property is optional and if it is defined explicitly,
+     * it must be either `owner` or `borrower`.
+     */
+    prop_role = fdt_get_property(fdt, node, "role", NULL);
+    if ( prop_role )
+    {
+        if ( !strcmp(prop_role->data, "owner") )
+            owner = true;
+        else if ( strcmp(prop_role->data, "borrower") )
+        {
+            printk("fdt: invalid `role` property for static shared memory 
node.\n");
+            return -EINVAL;
+        }
+    }
+
+    /*
+     * xen,shared-mem = <paddr, gaddr, size>;
+     * Memory region starting from physical address #paddr of #size shall
+     * be mapped to guest physical address #gaddr as static shared memory
+     * region.
+     */
+    prop = fdt_get_property(fdt, node, "xen,shared-mem", &len);
+    if ( !prop )
+        return -ENOENT;
+
+    cell = (const __be32 *)prop->data;
+    if ( len != dt_cells_to_size(address_cells + size_cells + address_cells) )
+    {
+        if ( len == dt_cells_to_size(address_cells + size_cells) )
+            device_tree_get_reg(&cell, address_cells, size_cells, &gaddr,
+                                &size);
+        else
+        {
+            printk("fdt: invalid `xen,shared-mem` property.\n");
+            return -EINVAL;
+        }
+    }
+    else
+    {
+        device_tree_get_reg(&cell, address_cells, address_cells, &paddr,
+                            &gaddr);
+        size = dt_next_cell(size_cells, &cell);
+
+        if ( !IS_ALIGNED(paddr, PAGE_SIZE) )
+        {
+            printk("fdt: physical address 0x%"PRIpaddr" is not suitably 
aligned.\n",
+                paddr);
+            return -EINVAL;
+        }
+
+        end = paddr + size;
+        if ( end <= paddr )
+        {
+            printk("fdt: static shared memory region %s overflow\n", shm_id);
+            return -EINVAL;
+        }
+    }
+
+    if ( !IS_ALIGNED(gaddr, PAGE_SIZE) )
+    {
+        printk("fdt: guest address 0x%"PRIpaddr" is not suitably aligned.\n",
+               gaddr);
+        return -EINVAL;
+    }
+
+    if ( !size )
+    {
+        printk("fdt: the size for static shared memory region can not be 
zero\n");
+        return -EINVAL;
+    }
+
+    if ( !IS_ALIGNED(size, PAGE_SIZE) )
+    {
+        printk("fdt: size 0x%"PRIpaddr" is not suitably aligned\n", size);
+        return -EINVAL;
+    }
+
+    for ( i = 0; i < mem->nr_banks; i++ )
+    {
+        /*
+         * Meet the following check:
+         * - when host address is provided:
+         *   1) The shm ID matches and the region exactly match
+         *   2) The shm ID doesn't match and the region doesn't overlap
+         *      with an existing one
+         * - when host address is not provided:
+         *   1) The shm ID matches and the region size exactly match
+         */
+        bool paddr_assigned = (INVALID_PADDR != paddr);
+
+        if ( strncmp(shm_id, shmem_extra[i].shm_id, MAX_SHM_ID_LENGTH) == 0 )
+        {
+            /*
+             * Regions have same shm_id (cases):
+             * 1) physical host address is supplied:
+             *    - OK:   paddr is equal and size is equal (same region)
+             *    - Fail: paddr doesn't match or size doesn't match (there
+             *            cannot exists two shmem regions with same shm_id)
+             * 2) physical host address is NOT supplied:
+             *    - OK:   size is equal (same region)
+             *    - Fail: size is not equal (same shm_id must identify only one
+             *            region, there can't be two different regions with 
same
+             *            shm_id)
+             */
+            bool start_match = paddr_assigned ? (paddr == mem->bank[i].start) :
+                                                true;
+
+            if ( start_match && (size == mem->bank[i].size) )
+                break;
+            else
+            {
+                printk("fdt: different shared memory region could not share 
the same shm ID %s\n",
+                       shm_id);
+                return -EINVAL;
+            }
+        }
+
+        /*
+         * Regions have different shm_id (cases):
+         * 1) physical host address is supplied:
+         *    - OK:   paddr different, or size different (case where paddr
+         *            is equal but psize is different are wrong, but they
+         *            are handled later when checking for overlapping)
+         *    - Fail: paddr equal and size equal (the same region can't be
+         *            identified with different shm_id)
+         * 2) physical host address is NOT supplied:
+         *    - OK:   Both have different shm_id so even with same size they
+         *            can exists
+         */
+        if ( !paddr_assigned || (paddr != mem->bank[i].start) ||
+             (size != mem->bank[i].size) )
+            continue;
+        else
+        {
+            printk("fdt: xen,shm-id %s does not match for all the nodes using 
the same region\n",
+                   shm_id);
+            return -EINVAL;
+        }
+    }
+
+    if ( i == mem->nr_banks )
+    {
+        if (i < mem->max_banks)
+        {
+            if ( (paddr != INVALID_PADDR) &&
+                 check_reserved_regions_overlap(paddr, size, false) )
+                return -EINVAL;
+
+            /* Static shared memory shall be reserved from any other use. */
+            safe_strcpy(shmem_extra[mem->nr_banks].shm_id, shm_id);
+            mem->bank[mem->nr_banks].start = paddr;
+            mem->bank[mem->nr_banks].size = size;
+            mem->bank[mem->nr_banks].shmem_extra = &shmem_extra[mem->nr_banks];
+            mem->nr_banks++;
+        }
+        else
+        {
+            printk("Warning: Max number of supported memory regions 
reached.\n");
+            return -ENOSPC;
+        }
+    }
+    /*
+     * keep a count of the number of borrowers, which later may be used
+     * to calculate the reference count.
+     */
+    if ( !owner )
+        shmem_extra[i].nr_shm_borrowers++;
+
+    return 0;
+}
+
+int __init make_resv_memory_node(const struct kernel_info *kinfo, int 
addrcells,
+                                 int sizecells)
+{
+    const struct membanks *mem = kernel_info_get_shm_mem_const(kinfo);
+    void *fdt = kinfo->fdt;
+    int res = 0;
+    /* Placeholder for reserved-memory\0 */
+    const char resvbuf[16] = "reserved-memory";
+
+    if ( mem->nr_banks == 0 )
+        /* No shared memory provided. */
+        return 0;
+
+    dt_dprintk("Create reserved-memory node\n");
+
+    res = fdt_begin_node(fdt, resvbuf);
+    if ( res )
+        return res;
+
+    res = fdt_property(fdt, "ranges", NULL, 0);
+    if ( res )
+        return res;
+
+    res = fdt_property_cell(fdt, "#address-cells", addrcells);
+    if ( res )
+        return res;
+
+    res = fdt_property_cell(fdt, "#size-cells", sizecells);
+    if ( res )
+        return res;
+
+    res = make_shm_resv_memory_node(kinfo, addrcells, sizecells);
+    if ( res )
+        return res;
+
+    res = fdt_end_node(fdt);
+
+    return res;
+}
+
+void __init early_print_info_shmem(void)
+{
+    const struct membanks *shmem = bootinfo_get_shmem();
+    unsigned int bank;
+    unsigned int printed = 0;
+
+    for ( bank = 0; bank < shmem->nr_banks; bank++, printed++ )
+        if ( shmem->bank[bank].start != INVALID_PADDR )
+            printk(" SHMEM[%u]: %"PRIpaddr" - %"PRIpaddr"\n", printed,
+                shmem->bank[bank].start,
+                shmem->bank[bank].start + shmem->bank[bank].size - 1);
+}
+
+void __init init_sharedmem_pages(void)
+{
+    const struct membanks *shmem = bootinfo_get_shmem();
+    unsigned int bank;
+
+    for ( bank = 0 ; bank < shmem->nr_banks; bank++ )
+        if ( shmem->bank[bank].start != INVALID_PADDR )
+            init_staticmem_bank(&shmem->bank[bank]);
+}
+
+int __init remove_shm_from_rangeset(const struct kernel_info *kinfo,
+                                    struct rangeset *rangeset)
+{
+    const struct membanks *shm_mem = kernel_info_get_shm_mem_const(kinfo);
+    unsigned int i;
+
+    /* Remove static shared memory regions */
+    for ( i = 0; i < shm_mem->nr_banks; i++ )
+    {
+        paddr_t start, end;
+        int res;
+
+        start = shm_mem->bank[i].start;
+        end = shm_mem->bank[i].start + shm_mem->bank[i].size;
+        res = rangeset_remove_range(rangeset, PFN_DOWN(start),
+                                    PFN_DOWN(end - 1));
+        if ( res )
+        {
+            printk(XENLOG_ERR
+                   "Failed to remove: %#"PRIpaddr"->%#"PRIpaddr", error: %d\n",
+                   start, end, res);
+            return -EINVAL;
+        }
+    }
+
+    return 0;
+}
+
+void __init shm_mem_node_fill_reg_range(const struct kernel_info *kinfo,
+                                        __be32 *reg, int *nr_cells,
+                                        int addrcells, int sizecells)
+{
+    const struct membanks *mem = kernel_info_get_shm_mem_const(kinfo);
+    unsigned int i;
+    __be32 *cells;
+
+    BUG_ON(!nr_cells || !reg);
+
+    cells = &reg[*nr_cells];
+    for ( i = 0; i < mem->nr_banks; i++ )
+    {
+        paddr_t start = mem->bank[i].start;
+        paddr_t size = mem->bank[i].size;
+
+        *nr_cells += addrcells + sizecells;
+        BUG_ON(*nr_cells >= DT_MEM_NODE_REG_RANGE_SIZE);
+        dt_child_set_range(&cells, addrcells, sizecells, start, size);
+    }
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/include/xen/static-shmem.h b/xen/include/xen/static-shmem.h
new file mode 100644
index 0000000000..76a4986912
--- /dev/null
+++ b/xen/include/xen/static-shmem.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef XEN_STATIC_SHMEM_H
+#define XEN_STATIC_SHMEM_H
+
+#include <xen/fdt-kernel.h>
+#include <xen/types.h>
+
+#ifdef CONFIG_STATIC_SHM
+
+/* Worst case /memory node reg element: (addrcells + sizecells) */
+#define DT_MEM_NODE_REG_RANGE_SIZE ((NR_MEM_BANKS + NR_SHMEM_BANKS) * 4)
+
+int make_resv_memory_node(const struct kernel_info *kinfo, int addrcells,
+                          int sizecells);
+
+int process_shm(struct domain *d, struct kernel_info *kinfo,
+                const struct dt_device_node *node);
+
+int process_shm_node(const void *fdt, int node, uint32_t address_cells,
+                     uint32_t size_cells);
+
+void early_print_info_shmem(void);
+
+void init_sharedmem_pages(void);
+
+int remove_shm_from_rangeset(const struct kernel_info *kinfo,
+                             struct rangeset *rangeset);
+
+int make_shm_resv_memory_node(const struct kernel_info *kinfo, int addrcells,
+                              int sizecells);
+
+void shm_mem_node_fill_reg_range(const struct kernel_info *kinfo, __be32 *reg,
+                                 int *nr_cells, int addrcells, int sizecells);
+
+static inline struct membanks *
+kernel_info_get_shm_mem(struct kernel_info *kinfo)
+{
+    return container_of(&kinfo->shm_mem.common, struct membanks, common);
+}
+
+static inline const struct membanks *
+kernel_info_get_shm_mem_const(const struct kernel_info *kinfo)
+{
+    return container_of(&kinfo->shm_mem.common, const struct membanks, common);
+}
+
+#else /* !CONFIG_STATIC_SHM */
+
+/* Worst case /memory node reg element: (addrcells + sizecells) */
+#define DT_MEM_NODE_REG_RANGE_SIZE (NR_MEM_BANKS * 4)
+
+static inline int make_resv_memory_node(const struct kernel_info *kinfo,
+                                        int addrcells, int sizecells)
+{
+    return 0;
+}
+
+static inline int process_shm(struct domain *d, struct kernel_info *kinfo,
+                              const struct dt_device_node *node)
+{
+    return 0;
+}
+
+static inline void init_sharedmem_pages(void) {};
+
+static inline int remove_shm_from_rangeset(const struct kernel_info *kinfo,
+                                           struct rangeset *rangeset)
+{
+    return 0;
+}
+
+static inline int make_shm_resv_memory_node(const struct kernel_info *kinfo,
+                                            int addrcells, int sizecells)
+{
+    return 0;
+}
+
+static inline void shm_mem_node_fill_reg_range(const struct kernel_info *kinfo,
+                                               __be32 *reg, int *nr_cells,
+                                               int addrcells, int sizecells) 
{};
+
+#endif /* CONFIG_STATIC_SHM */
+
+#endif /* XEN_STATIC_SHMEM_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--
generated by git-patchbot for /home/xen/git/xen.git#staging



 


Rackspace

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