diff mbox series

xen/device-tree: Allow exact match for overlapping regions

Message ID 20241106134132.2185492-1-luca.fancellu@arm.com (mailing list archive)
State New
Headers show
Series xen/device-tree: Allow exact match for overlapping regions | expand

Commit Message

Luca Fancellu Nov. 6, 2024, 1:41 p.m. UTC
There are some cases where the device tree exposes a memory range
in both /memreserve/ and reserved-memory node, in this case the
current code will stop Xen to boot since it will find that the
latter range is clashing with the already recorded /memreserve/
ranges.

Furthermore, u-boot lists boot modules ranges, such as ramdisk,
in the /memreserve/ part and even in this case this will prevent
Xen to boot since it will see that the module memory range that
it is going to add in 'add_boot_module' clashes with a /memreserve/
range.

When Xen populate the data structure that tracks the memory ranges,
it also adds a memory type described in 'enum membank_type', so
in order to fix this behavior, allow the 'check_reserved_regions_overlap'
function to check for exact memory range match given a specific memory
type; allowing reserved-memory node ranges and boot modules to have an
exact match with ranges from /memreserve/.

While there, set a type for the memory recorded during ACPI boot.

Fixes: 53dc37829c31 ("xen/arm: Add DT reserve map regions to bootinfo.reserved_mem")
Reported-by: Shawn Anastasio <sanastasio@raptorengineering.com>
Reported-by: Grygorii Strashko <grygorii_strashko@epam.com>
Signed-off-by: Luca Fancellu <luca.fancellu@arm.com>
---
I tested this patch adding the same range in a /memreserve/ entry and
/reserved-memory node, and by letting u-boot pass a ramdisk.
I've also tested that a configuration running static shared memory still works
fine.
---
 xen/arch/arm/efi/efi-boot.h       |  3 +-
 xen/arch/arm/static-shmem.c       |  2 +-
 xen/common/device-tree/bootfdt.c  |  9 +++++-
 xen/common/device-tree/bootinfo.c | 48 ++++++++++++++++++++++++-------
 xen/include/xen/bootfdt.h         |  9 +++++-
 5 files changed, 57 insertions(+), 14 deletions(-)

Comments

Grygorii Strashko Nov. 6, 2024, 2:19 p.m. UTC | #1
On 06.11.24 15:41, Luca Fancellu wrote:
> There are some cases where the device tree exposes a memory range
> in both /memreserve/ and reserved-memory node, in this case the
> current code will stop Xen to boot since it will find that the
> latter range is clashing with the already recorded /memreserve/
> ranges.
> 
> Furthermore, u-boot lists boot modules ranges, such as ramdisk,
> in the /memreserve/ part and even in this case this will prevent
> Xen to boot since it will see that the module memory range that
> it is going to add in 'add_boot_module' clashes with a /memreserve/
> range.
> 
> When Xen populate the data structure that tracks the memory ranges,
> it also adds a memory type described in 'enum membank_type', so
> in order to fix this behavior, allow the 'check_reserved_regions_overlap'
> function to check for exact memory range match given a specific memory
> type; allowing reserved-memory node ranges and boot modules to have an
> exact match with ranges from /memreserve/.
> 
> While there, set a type for the memory recorded during ACPI boot.
> 
> Fixes: 53dc37829c31 ("xen/arm: Add DT reserve map regions to bootinfo.reserved_mem")
> Reported-by: Shawn Anastasio <sanastasio@raptorengineering.com>
> Reported-by: Grygorii Strashko <grygorii_strashko@epam.com>
> Signed-off-by: Luca Fancellu <luca.fancellu@arm.com>
> ---
> I tested this patch adding the same range in a /memreserve/ entry and
> /reserved-memory node, and by letting u-boot pass a ramdisk.
> I've also tested that a configuration running static shared memory still works
> fine.

[...]

Thank you, Dom0 started successfully with Initrd.

Tested-by: Grygorii Strashko <grygorii_strashko@epam.com>

Meminfo from boot log is below:

(XEN) Checking for initrd in /chosen
(XEN) Initrd 0000000084000040-00000000860864fd
(XEN) RAM: 0000000048000000 - 00000000bfffffff
(XEN) RAM: 0000000480000000 - 00000004ffffffff
(XEN) RAM: 0000000600000000 - 00000006ffffffff
(XEN)
(XEN) MODULE[0]: 0000000048080000 - 00000000481ec000 Xen
(XEN) MODULE[1]: 0000000048000000 - 000000004801e080 Device Tree
(XEN) MODULE[2]: 0000000084000040 - 00000000860864fd Ramdisk
(XEN) MODULE[3]: 0000000048300000 - 000000004a300000 Kernel
(XEN) MODULE[4]: 0000000048070000 - 0000000048080000 XSM
(XEN)  RESVD[0]: 0000000084000040 - 00000000860864fc
(XEN)  RESVD[1]: 0000000060000000 - 000000007fffffff
(XEN)  RESVD[2]: 00000000b0000000 - 00000000bfffffff
(XEN)  RESVD[3]: 00000000a0000000 - 00000000afffffff
...

BR,
-grygorii
Michal Orzel Nov. 6, 2024, 3:07 p.m. UTC | #2
On 06/11/2024 14:41, Luca Fancellu wrote:
> 
> 
> There are some cases where the device tree exposes a memory range
> in both /memreserve/ and reserved-memory node, in this case the
> current code will stop Xen to boot since it will find that the
> latter range is clashing with the already recorded /memreserve/
> ranges.
> 
> Furthermore, u-boot lists boot modules ranges, such as ramdisk,
> in the /memreserve/ part and even in this case this will prevent
> Xen to boot since it will see that the module memory range that
> it is going to add in 'add_boot_module' clashes with a /memreserve/
> range.
> 
> When Xen populate the data structure that tracks the memory ranges,
> it also adds a memory type described in 'enum membank_type', so
> in order to fix this behavior, allow the 'check_reserved_regions_overlap'
> function to check for exact memory range match given a specific memory
> type; allowing reserved-memory node ranges and boot modules to have an
> exact match with ranges from /memreserve/.
> 
> While there, set a type for the memory recorded during ACPI boot.
> 
> Fixes: 53dc37829c31 ("xen/arm: Add DT reserve map regions to bootinfo.reserved_mem")
> Reported-by: Shawn Anastasio <sanastasio@raptorengineering.com>
> Reported-by: Grygorii Strashko <grygorii_strashko@epam.com>
> Signed-off-by: Luca Fancellu <luca.fancellu@arm.com>
> ---
> I tested this patch adding the same range in a /memreserve/ entry and
> /reserved-memory node, and by letting u-boot pass a ramdisk.
> I've also tested that a configuration running static shared memory still works
> fine.
> ---
So we have 2 separate issues. I don't particularly like the concept of introducing MEMBANK_NONE
and the changes below look a bit too much for me, given that for boot modules we can only have
/memreserve/ matching initrd.

Shawn patch fixes the first issue. AFAICT the second issue can be fixed by below simple patch:
diff --git a/xen/common/device-tree/bootfdt.c b/xen/common/device-tree/bootfdt.c
index 927f59c64b0d..d8bd8c44bd35 100644
--- a/xen/common/device-tree/bootfdt.c
+++ b/xen/common/device-tree/bootfdt.c
@@ -586,6 +586,10 @@ size_t __init boot_fdt_info(const void *fdt, paddr_t paddr)

     add_boot_module(BOOTMOD_FDT, paddr, fdt_totalsize(fdt), false);

+    ret = device_tree_for_each_node(fdt, 0, early_scan_node, NULL);
+    if ( ret )
+        panic("Early FDT parsing failed (%d)\n", ret);
+
     nr_rsvd = fdt_num_mem_rsv(fdt);
     if ( nr_rsvd < 0 )
         panic("Parsing FDT memory reserve map failed (%d)\n", nr_rsvd);
@@ -594,10 +598,14 @@ size_t __init boot_fdt_info(const void *fdt, paddr_t paddr)
     {
         struct membank *bank;
         paddr_t s, sz;
+        const struct bootmodule *mod = boot_module_find_by_kind(BOOTMOD_RAMDISK);

         if ( fdt_get_mem_rsv_paddr(device_tree_flattened, i, &s, &sz) < 0 )
             continue;

+        if ( mod && (mod->start == s) && (mod->size == sz) )
+            continue;
+
         if ( reserved_mem->nr_banks < reserved_mem->max_banks )
         {
             bank = &reserved_mem->bank[reserved_mem->nr_banks];
@@ -610,10 +618,6 @@ size_t __init boot_fdt_info(const void *fdt, paddr_t paddr)
             panic("Cannot allocate reserved memory bank\n");
     }

-    ret = device_tree_for_each_node(fdt, 0, early_scan_node, NULL);
-    if ( ret )
-        panic("Early FDT parsing failed (%d)\n", ret);
-
     /*
      * On Arm64 setup_directmap_mappings() expects to be called with the lowest
      * bank in memory first. There is no requirement that the DT will provide

I'll let other DT maintainers voice their opinion.

~Michal
Luca Fancellu Nov. 6, 2024, 3:16 p.m. UTC | #3
Hi Michal,

> So we have 2 separate issues. I don't particularly like the concept of introducing MEMBANK_NONE
> and the changes below look a bit too much for me, given that for boot modules we can only have
> /memreserve/ matching initrd.
> 
> Shawn patch fixes the first issue. AFAICT the second issue can be fixed by below simple patch:
> diff --git a/xen/common/device-tree/bootfdt.c b/xen/common/device-tree/bootfdt.c
> index 927f59c64b0d..d8bd8c44bd35 100644
> --- a/xen/common/device-tree/bootfdt.c
> +++ b/xen/common/device-tree/bootfdt.c
> @@ -586,6 +586,10 @@ size_t __init boot_fdt_info(const void *fdt, paddr_t paddr)
> 
>     add_boot_module(BOOTMOD_FDT, paddr, fdt_totalsize(fdt), false);
> 
> +    ret = device_tree_for_each_node(fdt, 0, early_scan_node, NULL);
> +    if ( ret )
> +        panic("Early FDT parsing failed (%d)\n", ret);
> +
>     nr_rsvd = fdt_num_mem_rsv(fdt);
>     if ( nr_rsvd < 0 )
>         panic("Parsing FDT memory reserve map failed (%d)\n", nr_rsvd);
> @@ -594,10 +598,14 @@ size_t __init boot_fdt_info(const void *fdt, paddr_t paddr)
>     {
>         struct membank *bank;
>         paddr_t s, sz;
> +        const struct bootmodule *mod = boot_module_find_by_kind(BOOTMOD_RAMDISK);
> 
>         if ( fdt_get_mem_rsv_paddr(device_tree_flattened, i, &s, &sz) < 0 )
>             continue;
> 
> +        if ( mod && (mod->start == s) && (mod->size == sz) )
> +            continue;

Ok I see, we skip the /memreserve/ entry if it matches the ramdisk, fair enough, I don’t have
a strong opinion on how we do that, the important thing is just to unblock the users experiencing
this issue.

Cheers,
Luca
diff mbox series

Patch

diff --git a/xen/arch/arm/efi/efi-boot.h b/xen/arch/arm/efi/efi-boot.h
index 199f5260229d..d35c991c856f 100644
--- a/xen/arch/arm/efi/efi-boot.h
+++ b/xen/arch/arm/efi/efi-boot.h
@@ -167,13 +167,14 @@  static bool __init meminfo_add_bank(struct membanks *mem,
     if ( mem->nr_banks >= mem->max_banks )
         return false;
 #ifdef CONFIG_ACPI
-    if ( check_reserved_regions_overlap(start, size) )
+    if ( check_reserved_regions_overlap(start, size, MEMBANK_NONE) )
         return false;
 #endif
 
     bank = &mem->bank[mem->nr_banks];
     bank->start = start;
     bank->size = size;
+    bank->type = MEMBANK_DEFAULT;
 
     mem->nr_banks++;
 
diff --git a/xen/arch/arm/static-shmem.c b/xen/arch/arm/static-shmem.c
index aa80756c3ca5..149ed4b0a5ba 100644
--- a/xen/arch/arm/static-shmem.c
+++ b/xen/arch/arm/static-shmem.c
@@ -696,7 +696,7 @@  int __init process_shm_node(const void *fdt, int node, uint32_t address_cells,
         if (i < mem->max_banks)
         {
             if ( (paddr != INVALID_PADDR) &&
-                 check_reserved_regions_overlap(paddr, size) )
+                 check_reserved_regions_overlap(paddr, size, MEMBANK_NONE) )
                 return -EINVAL;
 
             /* Static shared memory shall be reserved from any other use. */
diff --git a/xen/common/device-tree/bootfdt.c b/xen/common/device-tree/bootfdt.c
index 927f59c64b0d..fb3a6ab95a22 100644
--- a/xen/common/device-tree/bootfdt.c
+++ b/xen/common/device-tree/bootfdt.c
@@ -176,8 +176,15 @@  static int __init device_tree_get_meminfo(const void *fdt, int node,
     for ( i = 0; i < banks && mem->nr_banks < mem->max_banks; i++ )
     {
         device_tree_get_reg(&cell, address_cells, size_cells, &start, &size);
+        /*
+         * Some valid device trees, such as those generated by OpenPOWER
+         * skiboot firmware, expose all reserved memory regions in the
+         * FDT memory reservation block AND in the reserved-memory node which
+         * has already been parsed. Thus, any matching overlaps in the
+         * reserved_mem banks should be ignored.
+         */
         if ( mem == bootinfo_get_reserved_mem() &&
-             check_reserved_regions_overlap(start, size) )
+             check_reserved_regions_overlap(start, size, MEMBANK_FDT_RESVMEM) )
             return -EINVAL;
         /* Some DT may describe empty bank, ignore them */
         if ( !size )
diff --git a/xen/common/device-tree/bootinfo.c b/xen/common/device-tree/bootinfo.c
index f2e6a1145b7c..05038075e835 100644
--- a/xen/common/device-tree/bootinfo.c
+++ b/xen/common/device-tree/bootinfo.c
@@ -99,7 +99,8 @@  static void __init dt_unreserved_regions(paddr_t s, paddr_t e,
  */
 static bool __init meminfo_overlap_check(const struct membanks *mem,
                                          paddr_t region_start,
-                                         paddr_t region_size)
+                                         paddr_t region_size,
+                                         enum membank_type allow_match_type)
 {
     paddr_t bank_start = INVALID_PADDR, bank_end = 0;
     paddr_t region_end = region_start + region_size;
@@ -113,12 +114,16 @@  static bool __init meminfo_overlap_check(const struct membanks *mem,
         if ( INVALID_PADDR == bank_start || region_end <= bank_start ||
              region_start >= bank_end )
             continue;
-        else
-        {
-            printk("Region: [%#"PRIpaddr", %#"PRIpaddr") overlapping with bank[%u]: [%#"PRIpaddr", %#"PRIpaddr")\n",
-                   region_start, region_end, i, bank_start, bank_end);
-            return true;
-        }
+
+        if ( (allow_match_type != MEMBANK_NONE) &&
+             (region_start == bank_start) && (region_end == bank_end) &&
+             (allow_match_type == mem->bank[i].type) )
+            continue;
+
+        printk("Region: [%#"PRIpaddr", %#"PRIpaddr") overlapping with bank[%u]: [%#"PRIpaddr", %#"PRIpaddr")\n",
+                region_start, region_end, i, bank_start, bank_end);
+        return true;
+
     }
 
     return false;
@@ -169,9 +174,14 @@  void __init fw_unreserved_regions(paddr_t s, paddr_t e,
  * with the existing reserved memory regions defined in bootinfo.
  * Return true if the input physical address range is overlapping with any
  * existing reserved memory regions, otherwise false.
+ * The 'allow_match_type' parameter can be used to allow exact match of a
+ * region with another memory region already recorded, but it needs to be used
+ * only on memory regions that allows a type (reserved_mem, acpi). For all the
+ * other cases, passing 'MEMBANK_NONE' will disable the exact match.
  */
 bool __init check_reserved_regions_overlap(paddr_t region_start,
-                                           paddr_t region_size)
+                                           paddr_t region_size,
+                                           enum membank_type allow_match_type)
 {
     const struct membanks *mem_banks[] = {
         bootinfo_get_reserved_mem(),
@@ -190,8 +200,21 @@  bool __init check_reserved_regions_overlap(paddr_t region_start,
      * shared memory banks (when static shared memory feature is enabled)
      */
     for ( i = 0; i < ARRAY_SIZE(mem_banks); i++ )
-        if ( meminfo_overlap_check(mem_banks[i], region_start, region_size) )
+    {
+        enum membank_type type = allow_match_type;
+
+#ifdef CONFIG_STATIC_SHM
+        /*
+         * Static shared memory banks don't have a type, so for them disable
+         * the exact match check.
+         */
+        if (mem_banks[i] == bootinfo_get_shmem())
+            type = MEMBANK_NONE;
+#endif
+        if ( meminfo_overlap_check(mem_banks[i], region_start, region_size,
+                                   type) )
             return true;
+    }
 
     /* Check if input region is overlapping with bootmodules */
     if ( bootmodules_overlap_check(&bootinfo.modules,
@@ -216,7 +239,12 @@  struct bootmodule __init *add_boot_module(bootmodule_kind kind,
         return NULL;
     }
 
-    if ( check_reserved_regions_overlap(start, size) )
+    /*
+     * u-boot adds boot module such as ramdisk to the /memreserve/, since these
+     * ranges are saved in reserved_mem at this stage, allow an eventual exact
+     * match with MEMBANK_FDT_RESVMEM banks.
+     */
+    if ( check_reserved_regions_overlap(start, size, MEMBANK_FDT_RESVMEM) )
         return NULL;
 
     for ( i = 0 ; i < mods->nr_mods ; i++ )
diff --git a/xen/include/xen/bootfdt.h b/xen/include/xen/bootfdt.h
index 16fa05f38f38..8f8a7ad882a4 100644
--- a/xen/include/xen/bootfdt.h
+++ b/xen/include/xen/bootfdt.h
@@ -23,6 +23,12 @@  typedef enum {
 }  bootmodule_kind;
 
 enum membank_type {
+    /*
+     * The MEMBANK_NONE type is a special placeholder that should not be applied
+     * to a memory bank, it is used as a special value in some function in order
+     * to disable some feature.
+     */
+    MEMBANK_NONE = -1,
     /*
      * The MEMBANK_DEFAULT type refers to either reserved memory for the
      * device/firmware (when the bank is in 'reserved_mem') or any RAM (when
@@ -158,7 +164,8 @@  struct bootinfo {
 extern struct bootinfo bootinfo;
 
 bool check_reserved_regions_overlap(paddr_t region_start,
-                                    paddr_t region_size);
+                                    paddr_t region_size,
+                                    enum membank_type allow_match_type);
 
 struct bootmodule *add_boot_module(bootmodule_kind kind,
                                    paddr_t start, paddr_t size, bool domU);