diff mbox series

[v3,3/6] xen/arm: keep track of reserved-memory regions

Message ID 20190621235608.2153-3-sstabellini@kernel.org (mailing list archive)
State Superseded
Headers show
Series [v3,1/6] xen/arm: extend device_tree_for_each_node | expand

Commit Message

Stefano Stabellini June 21, 2019, 11:56 p.m. UTC
As we parse the device tree in Xen, keep track of the reserved-memory
regions as they need special treatment (follow-up patches will make use
of the stored information.)

Reuse process_memory_node to add reserved-memory regions to the
bootinfo.reserved_mem array.

Refuse to continue once we reach the max number of reserved memory
regions to avoid accidentally mapping any portions of them into a VM.

Signed-off-by: Stefano Stabellini <stefanos@xilinx.com>

---
It is cleaner to avoid sharing the whole function process_memory_node
between the normal memory case and the reserved-memory case. I'll do it
in the next version once I understand the best way do to it.

---
Changes in v3:
- match only /reserved-memory
- put the warning back in place for reg not present on a normal memory
  region
- refuse to continue once we reach the max number of reserved memory
  regions

Changes in v2:
- call process_memory_node from process_reserved_memory_node to avoid
  duplication
---
 xen/arch/arm/bootfdt.c      | 38 +++++++++++++++++++++++++++++++------
 xen/include/asm-arm/setup.h |  1 +
 2 files changed, 33 insertions(+), 6 deletions(-)

Comments

Oleksandr Tyshchenko July 8, 2019, 7:02 p.m. UTC | #1
On 22.06.19 02:56, Stefano Stabellini wrote:

Hi Stefano

> As we parse the device tree in Xen, keep track of the reserved-memory
> regions as they need special treatment (follow-up patches will make use
> of the stored information.)
>
> Reuse process_memory_node to add reserved-memory regions to the
> bootinfo.reserved_mem array.
>
> Refuse to continue once we reach the max number of reserved memory
> regions to avoid accidentally mapping any portions of them into a VM.
>
> Signed-off-by: Stefano Stabellini <stefanos@xilinx.com>
>
> ---
> It is cleaner to avoid sharing the whole function process_memory_node
> between the normal memory case and the reserved-memory case. I'll do it
> in the next version once I understand the best way do to it.
>
> ---
> Changes in v3:
> - match only /reserved-memory
> - put the warning back in place for reg not present on a normal memory
>    region
> - refuse to continue once we reach the max number of reserved memory
>    regions
>
> Changes in v2:
> - call process_memory_node from process_reserved_memory_node to avoid
>    duplication
> ---
>   xen/arch/arm/bootfdt.c      | 38 +++++++++++++++++++++++++++++++------
>   xen/include/asm-arm/setup.h |  1 +
>   2 files changed, 33 insertions(+), 6 deletions(-)
>
> diff --git a/xen/arch/arm/bootfdt.c b/xen/arch/arm/bootfdt.c
> index 611724433b..b24ab10cb9 100644
> --- a/xen/arch/arm/bootfdt.c
> +++ b/xen/arch/arm/bootfdt.c
> @@ -135,6 +135,8 @@ static int __init process_memory_node(const void *fdt, int node,
>       const __be32 *cell;
>       paddr_t start, size;
>       u32 reg_cells = address_cells + size_cells;
> +    struct meminfo *mem;
> +    bool reserved = (bool)data;
>   
>       if ( address_cells < 1 || size_cells < 1 )
>       {
> @@ -143,29 +145,49 @@ static int __init process_memory_node(const void *fdt, int node,
>           return 0;
>       }
>   
> +    if ( reserved )
> +        mem = &bootinfo.reserved_mem;
> +    else
> +        mem = &bootinfo.mem;
> +
>       prop = fdt_get_property(fdt, node, "reg", NULL);
>       if ( !prop )
>       {
> -        printk("fdt: node `%s': missing `reg' property\n", name);
> +        if ( !reserved )
> +            printk("fdt: node `%s': missing `reg' property\n", name);
>           return 0;
>       }
>   
>       cell = (const __be32 *)prop->data;
>       banks = fdt32_to_cpu(prop->len) / (reg_cells * sizeof (u32));
>   
> -    for ( i = 0; i < banks && bootinfo.mem.nr_banks < NR_MEM_BANKS; i++ )
> +    for ( i = 0; i < banks && mem->nr_banks < NR_MEM_BANKS; i++ )
>       {
>           device_tree_get_reg(&cell, address_cells, size_cells, &start, &size);
>           if ( !size )
>               continue;
> -        bootinfo.mem.bank[bootinfo.mem.nr_banks].start = start;
> -        bootinfo.mem.bank[bootinfo.mem.nr_banks].size = size;
> -        bootinfo.mem.nr_banks++;
> +        mem->bank[mem->nr_banks].start = start;
> +        mem->bank[mem->nr_banks].size = size;
> +        mem->nr_banks++;
>       }
> +    /*
> +     * We reached the max number of supported reserved-memory regions.
> +     * Stop and refuse to continue. We don't want to risk Xen allocating
> +     * those regions as normal memory to a VM.
> +     */
> +    BUG_ON(reserved && mem->nr_banks == NR_MEM_BANKS);
>   
>       return 0;
>   }
>   
> +static int __init process_reserved_memory_node(const void *fdt, int node,
> +                                               const char *name, int depth,
> +                                               u32 address_cells, u32 size_cells)
> +{
> +    device_tree_for_each_node(fdt, node, depth, process_memory_node, (void*)true);
> +    return 0;
> +}

I have tested this series and got the same behavior as with V2 [1].

The "non-reserved-memory" node in my device-tree 
(sram@47FFF000->scp_shmem@0) is still interpreted as a "reserved-memory".
I think, this takes place because current implementation iterates over 
all device tree nodes starting
from real "reserved-memory" node up to the end of the device tree. And 
if there is at least one "non-reserved-memory" node (with a suitable 
depth and valid "reg" property)
located down the device tree, it will be mistakenly inserted to 
bootinfo.reserved_mem (as in my case).

----------

Unpacked device tree:
{
         ...

         memory@48000000 {
                 device_type = "memory";
                 reg = <0x00000000 0x48000000 0x00000000 0x78000000 
0x00000005 0x00000000 0x00000000 0x80000000 0x00000006 0x00000000 
0x00000000 0x80000000 0x00000007 0x00000000 0x00000000 0x80000000>;
         };
         reserved-memory {
                 #address-cells = <0x00000002>;
                 #size-cells = <0x00000002>;
                 ranges;
                 linux,lossy_decompress@54000000 {
                         no-map;
                         reg = <0x00000000 0x54000000 0x00000000 
0x03000000>;
                         linux,phandle = <0x000000b0>;
                         phandle = <0x000000b0>;
                 };
                 linux,adsp@57000000 {
                         compatible = "shared-dma-pool";
                         reusable;
                         reg = <0x00000000 0x57000000 0x00000000 
0x01000000>;
                 };
                 linux,cma@58000000 {
                         compatible = "shared-dma-pool";
                         reusable;
                         reg = <0x00000000 0x58000000 0x00000000 
0x18000000>;
                         linux,cma-default;
                 };
                 linux,multimedia@70000000 {
                         compatible = "shared-dma-pool";
                         reusable;
                         reg = <0x00000000 0x70000000 0x00000000 
0x10000000>;
                         linux,phandle = <0x000000af>;
                         phandle = <0x000000af>;
                 };
         };

         ...

         sram@47FFF000 {
                 compatible = "mmio-sram";
                 reg = <0x00000000 0x47fff000 0x00000000 0x00001000>;
                 #address-cells = <0x00000001>;
                 #size-cells = <0x00000001>;
                 ranges = <0x00000000 0x00000000 0x47fff000 0x00001000>;
                 scp_shmem@0 {
                         compatible = "mmio-sram";
                         reg = <0x00000000 0x00000200>;
                         linux,phandle = <0x000000b2>;
                         phandle = <0x000000b2>;
                 };
         };

         ...
};

----------

Xen log:

Starting kernel ...

- UART enabled -
- CPU 00000000 booting -
- Current EL 00000008 -
- Xen starting at EL2 -
- Zero BSS -
- Setting up control registers -
- Turning on paging -
- Ready -
(XEN) Checking for initrd in /chosen
(XEN) Initrd 0000000074000040-0000000076252c54
(XEN) node memory@48000000: insert bank 0: 0x48000000->0xc0000000 type: 
normal
(XEN) node memory@48000000: insert bank 1: 0x500000000->0x580000000 
type: normal
(XEN) node memory@48000000: insert bank 2: 0x600000000->0x680000000 
type: normal
(XEN) node memory@48000000: insert bank 3: 0x700000000->0x780000000 
type: normal
(XEN) node linux,lossy_decompress@54000000: insert bank 0: 
0x54000000->0x57000000 type: reserved
(XEN) node linux,adsp@57000000: insert bank 0: 0x57000000->0x58000000 
type: reserved
(XEN) node linux,cma@58000000: insert bank 0: 0x58000000->0x70000000 
type: reserved
(XEN) node linux,multimedia@70000000: insert bank 0: 
0x70000000->0x80000000 type: reserved
(XEN) node scp_shmem@0: insert bank 0: 0->0x200 type: reserved 
<--------------- Not a reserved memory
(XEN) RAM: 0000000048000000 - 00000000bfffffff
(XEN) RAM: 0000000500000000 - 000000057fffffff
(XEN) RAM: 0000000600000000 - 000000067fffffff
(XEN) RAM: 0000000700000000 - 000000077fffffff
(XEN)
(XEN) MODULE[0]: 0000000048000000 - 0000000048014080 Device Tree
(XEN) MODULE[1]: 0000000074000040 - 0000000076252c54 Ramdisk
(XEN) MODULE[2]: 000000007a000000 - 000000007c000000 Kernel
(XEN) MODULE[3]: 000000007c000000 - 000000007c010000 XSM
(XEN)  RESVD[0]: 0000000048000000 - 0000000048014000
(XEN)  RESVD[1]: 0000000074000040 - 0000000076252c54

----------

[1] 
https://www.mail-archive.com/xen-devel@lists.xenproject.org/msg45632.html
Julien Grall July 10, 2019, 3:40 p.m. UTC | #2
Hi,

On 6/22/19 12:56 AM, Stefano Stabellini wrote:
> As we parse the device tree in Xen, keep track of the reserved-memory
> regions as they need special treatment (follow-up patches will make use
> of the stored information.)
> 
> Reuse process_memory_node to add reserved-memory regions to the
> bootinfo.reserved_mem array.
> 
> Refuse to continue once we reach the max number of reserved memory
> regions to avoid accidentally mapping any portions of them into a VM.
> 
> Signed-off-by: Stefano Stabellini <stefanos@xilinx.com>
> 
> ---
> It is cleaner to avoid sharing the whole function process_memory_node
> between the normal memory case and the reserved-memory case. I'll do it
> in the next version once I understand the best way do to it.

parse_reg(....)
{

if (reg not present)
   return -ENOPRESENT

/* parse regs */

return (full) ? -EFULL : 0;
}

process_memory_node(....)
{
    return parse_reg(...);
}

process_reserved_region()
{
     ret = parse_reg(...);
     if ( ret == -EFULL )
       panic(....);
     else if ( ret != -ENOPRESENT )
       return ret;
     return 0;
}

> 
> ---
> Changes in v3:
> - match only /reserved-memory
> - put the warning back in place for reg not present on a normal memory
>    region
> - refuse to continue once we reach the max number of reserved memory
>    regions
> 
> Changes in v2:
> - call process_memory_node from process_reserved_memory_node to avoid
>    duplication
> ---
>   xen/arch/arm/bootfdt.c      | 38 +++++++++++++++++++++++++++++++------
>   xen/include/asm-arm/setup.h |  1 +
>   2 files changed, 33 insertions(+), 6 deletions(-)
> 
> diff --git a/xen/arch/arm/bootfdt.c b/xen/arch/arm/bootfdt.c
> index 611724433b..b24ab10cb9 100644
> --- a/xen/arch/arm/bootfdt.c
> +++ b/xen/arch/arm/bootfdt.c
> @@ -135,6 +135,8 @@ static int __init process_memory_node(const void *fdt, int node,
>       const __be32 *cell;
>       paddr_t start, size;
>       u32 reg_cells = address_cells + size_cells;
> +    struct meminfo *mem;
> +    bool reserved = (bool)data;
>   
>       if ( address_cells < 1 || size_cells < 1 )
>       {
> @@ -143,29 +145,49 @@ static int __init process_memory_node(const void *fdt, int node,
>           return 0;
>       }
>   
> +    if ( reserved )
> +        mem = &bootinfo.reserved_mem;
> +    else
> +        mem = &bootinfo.mem;

Rather than passing a bool, you could pass bootinfo.{mem, reserved_mem} 
in parameter.

> +
>       prop = fdt_get_property(fdt, node, "reg", NULL);
>       if ( !prop )
>       {
> -        printk("fdt: node `%s': missing `reg' property\n", name);
> +        if ( !reserved )
> +            printk("fdt: node `%s': missing `reg' property\n", name);

I would just get rid of this print and return an error than allow the 
caller to decide what to do.

>           return 0;
>       }
>   
>       cell = (const __be32 *)prop->data;
>       banks = fdt32_to_cpu(prop->len) / (reg_cells * sizeof (u32));
>   
> -    for ( i = 0; i < banks && bootinfo.mem.nr_banks < NR_MEM_BANKS; i++ )
> +    for ( i = 0; i < banks && mem->nr_banks < NR_MEM_BANKS; i++ )
>       {
>           device_tree_get_reg(&cell, address_cells, size_cells, &start, &size);
>           if ( !size )
>               continue;
> -        bootinfo.mem.bank[bootinfo.mem.nr_banks].start = start;
> -        bootinfo.mem.bank[bootinfo.mem.nr_banks].size = size;
> -        bootinfo.mem.nr_banks++;
> +        mem->bank[mem->nr_banks].start = start;
> +        mem->bank[mem->nr_banks].size = size;
> +        mem->nr_banks++;
>       }
> +    /*
> +     * We reached the max number of supported reserved-memory regions.
> +     * Stop and refuse to continue. We don't want to risk Xen allocating
> +     * those regions as normal memory to a VM.

The last sentence is confusing because reserved-region are normal memory 
that have been carved out for a specific usage. Also, the problem is not 
only with VM but any memory allocation.

So a better sentence would be: "We don't want to give the pages to the 
allocator".

> +     */
> +    BUG_ON(reserved && mem->nr_banks == NR_MEM_BANKS);
>   
>       return 0;
>   }
>   
> +static int __init process_reserved_memory_node(const void *fdt, int node,
> +                                               const char *name, int depth,
> +                                               u32 address_cells, u32 size_cells)
> +{
> +    device_tree_for_each_node(fdt, node, depth, process_memory_node, (void*)true);
> +    return 0;
> +}
> +
>   static void __init process_multiboot_node(const void *fdt, int node,
>                                             const char *name,
>                                             u32 address_cells, u32 size_cells)
> @@ -299,7 +321,11 @@ static int __init early_scan_node(const void *fdt,
>   
>       if ( device_tree_node_matches(fdt, node, "memory") )
>           rc = process_memory_node(fdt, node, name, depth,
> -                                 address_cells, size_cells, NULL);
> +                                 address_cells, size_cells, (void*)false);
> +    else if ( depth == 1 && !strcmp(name, "reserved-memory") &&
> +              strlen(name) == strlen("reserved-memory") )
> +        rc = process_reserved_memory_node(fdt, node, name, depth,
> +                                          address_cells, size_cells);
>       else if ( depth <= 3 && (device_tree_node_compatible(fdt, node, "xen,multiboot-module" ) ||
>                 device_tree_node_compatible(fdt, node, "multiboot,module" )))
>           process_multiboot_node(fdt, node, name, address_cells, size_cells);
> diff --git a/xen/include/asm-arm/setup.h b/xen/include/asm-arm/setup.h
> index 8bf3d5910a..efcba545c2 100644
> --- a/xen/include/asm-arm/setup.h
> +++ b/xen/include/asm-arm/setup.h
> @@ -66,6 +66,7 @@ struct bootcmdlines {
>   
>   struct bootinfo {
>       struct meminfo mem;
> +    struct meminfo reserved_mem;
>       struct bootmodules modules;
>       struct bootcmdlines cmdlines;
>   #ifdef CONFIG_ACPI
> 

Cheers,
Julien Grall July 10, 2019, 3:54 p.m. UTC | #3
Hi,

On 7/8/19 8:02 PM, Oleksandr wrote:
> On 22.06.19 02:56, Stefano Stabellini wrote:
> I have tested this series and got the same behavior as with V2 [1].
> 
> The "non-reserved-memory" node in my device-tree 
> (sram@47FFF000->scp_shmem@0) is still interpreted as a "reserved-memory".
> I think, this takes place because current implementation iterates over 
> all device tree nodes starting
> from real "reserved-memory" node up to the end of the device tree. And 
> if there is at least one "non-reserved-memory" node (with a suitable 
> depth and valid "reg" property)
> located down the device tree, it will be mistakenly inserted to 
> bootinfo.reserved_mem (as in my case).

The problem is because we are passing the depth of the parent. Because 
of that, it will look for anything at the same depth (see the check 
depth >= min_depth in device_tree_for_each_node).

The correct solution here, would be to use the depth of the child (i.e 
depth + 1) when calling device_tree_for_each_node in 
process_reserved_memory_node.

Cheers,
Stefano Stabellini Aug. 6, 2019, 7:20 p.m. UTC | #4
On Wed, 10 Jul 2019, Julien Grall wrote:
> Hi,
> 
> On 7/8/19 8:02 PM, Oleksandr wrote:
> > On 22.06.19 02:56, Stefano Stabellini wrote:
> > I have tested this series and got the same behavior as with V2 [1].
> > 
> > The "non-reserved-memory" node in my device-tree
> > (sram@47FFF000->scp_shmem@0) is still interpreted as a "reserved-memory".
> > I think, this takes place because current implementation iterates over all
> > device tree nodes starting
> > from real "reserved-memory" node up to the end of the device tree. And if
> > there is at least one "non-reserved-memory" node (with a suitable depth and
> > valid "reg" property)
> > located down the device tree, it will be mistakenly inserted to
> > bootinfo.reserved_mem (as in my case).
> 
> The problem is because we are passing the depth of the parent. Because of
> that, it will look for anything at the same depth (see the check depth >=
> min_depth in device_tree_for_each_node).
> 
> The correct solution here, would be to use the depth of the child (i.e depth +
> 1) when calling device_tree_for_each_node in process_reserved_memory_node.

Yes, that is the right thing to do, together with also passing
address_cells and size_cells of the parent to device_tree_for_each_node,
so that it can be calculate appropriately at all times, regardless of
depth.
Stefano Stabellini Aug. 6, 2019, 8:52 p.m. UTC | #5
On Wed, 10 Jul 2019, Julien Grall wrote:
> Hi,
> 
> On 6/22/19 12:56 AM, Stefano Stabellini wrote:
> > As we parse the device tree in Xen, keep track of the reserved-memory
> > regions as they need special treatment (follow-up patches will make use
> > of the stored information.)
> > 
> > Reuse process_memory_node to add reserved-memory regions to the
> > bootinfo.reserved_mem array.
> > 
> > Refuse to continue once we reach the max number of reserved memory
> > regions to avoid accidentally mapping any portions of them into a VM.
> > 
> > Signed-off-by: Stefano Stabellini <stefanos@xilinx.com>
> > 
> > ---
> > It is cleaner to avoid sharing the whole function process_memory_node
> > between the normal memory case and the reserved-memory case. I'll do it
> > in the next version once I understand the best way do to it.
> 
> parse_reg(....)
> {
> 
> if (reg not present)
>   return -ENOPRESENT
> 
> /* parse regs */
> 
> return (full) ? -EFULL : 0;
> }
> 
> process_memory_node(....)
> {
>    return parse_reg(...);
> }
> 
> process_reserved_region()
> {
>     ret = parse_reg(...);
>     if ( ret == -EFULL )
>       panic(....);
>     else if ( ret != -ENOPRESENT )
>       return ret;
>     return 0;
> }

Thank you, that clarified things a lot!


> > ---
> > Changes in v3:
> > - match only /reserved-memory
> > - put the warning back in place for reg not present on a normal memory
> >    region
> > - refuse to continue once we reach the max number of reserved memory
> >    regions
> > 
> > Changes in v2:
> > - call process_memory_node from process_reserved_memory_node to avoid
> >    duplication
> > ---
> >   xen/arch/arm/bootfdt.c      | 38 +++++++++++++++++++++++++++++++------
> >   xen/include/asm-arm/setup.h |  1 +
> >   2 files changed, 33 insertions(+), 6 deletions(-)
> > 
> > diff --git a/xen/arch/arm/bootfdt.c b/xen/arch/arm/bootfdt.c
> > index 611724433b..b24ab10cb9 100644
> > --- a/xen/arch/arm/bootfdt.c
> > +++ b/xen/arch/arm/bootfdt.c
> > @@ -135,6 +135,8 @@ static int __init process_memory_node(const void *fdt,
> > int node,
> >       const __be32 *cell;
> >       paddr_t start, size;
> >       u32 reg_cells = address_cells + size_cells;
> > +    struct meminfo *mem;
> > +    bool reserved = (bool)data;
> >         if ( address_cells < 1 || size_cells < 1 )
> >       {
> > @@ -143,29 +145,49 @@ static int __init process_memory_node(const void *fdt,
> > int node,
> >           return 0;
> >       }
> >   +    if ( reserved )
> > +        mem = &bootinfo.reserved_mem;
> > +    else
> > +        mem = &bootinfo.mem;
> 
> Rather than passing a bool, you could pass bootinfo.{mem, reserved_mem} in
> parameter.

I'll do that


> > +
> >       prop = fdt_get_property(fdt, node, "reg", NULL);
> >       if ( !prop )
> >       {
> > -        printk("fdt: node `%s': missing `reg' property\n", name);
> > +        if ( !reserved )
> > +            printk("fdt: node `%s': missing `reg' property\n", name);
> 
> I would just get rid of this print and return an error than allow the caller
> to decide what to do.

Yep


> >           return 0;
> >       }
> >         cell = (const __be32 *)prop->data;
> >       banks = fdt32_to_cpu(prop->len) / (reg_cells * sizeof (u32));
> >   -    for ( i = 0; i < banks && bootinfo.mem.nr_banks < NR_MEM_BANKS; i++ )
> > +    for ( i = 0; i < banks && mem->nr_banks < NR_MEM_BANKS; i++ )
> >       {
> >           device_tree_get_reg(&cell, address_cells, size_cells, &start,
> > &size);
> >           if ( !size )
> >               continue;
> > -        bootinfo.mem.bank[bootinfo.mem.nr_banks].start = start;
> > -        bootinfo.mem.bank[bootinfo.mem.nr_banks].size = size;
> > -        bootinfo.mem.nr_banks++;
> > +        mem->bank[mem->nr_banks].start = start;
> > +        mem->bank[mem->nr_banks].size = size;
> > +        mem->nr_banks++;
> >       }
> > +    /*
> > +     * We reached the max number of supported reserved-memory regions.
> > +     * Stop and refuse to continue. We don't want to risk Xen allocating
> > +     * those regions as normal memory to a VM.
> 
> The last sentence is confusing because reserved-region are normal memory that
> have been carved out for a specific usage. Also, the problem is not only with
> VM but any memory allocation.
> 
> So a better sentence would be: "We don't want to give the pages to the
> allocator".

Thanks, I'll make the change
diff mbox series

Patch

diff --git a/xen/arch/arm/bootfdt.c b/xen/arch/arm/bootfdt.c
index 611724433b..b24ab10cb9 100644
--- a/xen/arch/arm/bootfdt.c
+++ b/xen/arch/arm/bootfdt.c
@@ -135,6 +135,8 @@  static int __init process_memory_node(const void *fdt, int node,
     const __be32 *cell;
     paddr_t start, size;
     u32 reg_cells = address_cells + size_cells;
+    struct meminfo *mem;
+    bool reserved = (bool)data;
 
     if ( address_cells < 1 || size_cells < 1 )
     {
@@ -143,29 +145,49 @@  static int __init process_memory_node(const void *fdt, int node,
         return 0;
     }
 
+    if ( reserved )
+        mem = &bootinfo.reserved_mem;
+    else
+        mem = &bootinfo.mem;
+
     prop = fdt_get_property(fdt, node, "reg", NULL);
     if ( !prop )
     {
-        printk("fdt: node `%s': missing `reg' property\n", name);
+        if ( !reserved )
+            printk("fdt: node `%s': missing `reg' property\n", name);
         return 0;
     }
 
     cell = (const __be32 *)prop->data;
     banks = fdt32_to_cpu(prop->len) / (reg_cells * sizeof (u32));
 
-    for ( i = 0; i < banks && bootinfo.mem.nr_banks < NR_MEM_BANKS; i++ )
+    for ( i = 0; i < banks && mem->nr_banks < NR_MEM_BANKS; i++ )
     {
         device_tree_get_reg(&cell, address_cells, size_cells, &start, &size);
         if ( !size )
             continue;
-        bootinfo.mem.bank[bootinfo.mem.nr_banks].start = start;
-        bootinfo.mem.bank[bootinfo.mem.nr_banks].size = size;
-        bootinfo.mem.nr_banks++;
+        mem->bank[mem->nr_banks].start = start;
+        mem->bank[mem->nr_banks].size = size;
+        mem->nr_banks++;
     }
+    /*
+     * We reached the max number of supported reserved-memory regions.
+     * Stop and refuse to continue. We don't want to risk Xen allocating
+     * those regions as normal memory to a VM.
+     */
+    BUG_ON(reserved && mem->nr_banks == NR_MEM_BANKS);
 
     return 0;
 }
 
+static int __init process_reserved_memory_node(const void *fdt, int node,
+                                               const char *name, int depth,
+                                               u32 address_cells, u32 size_cells)
+{
+    device_tree_for_each_node(fdt, node, depth, process_memory_node, (void*)true);
+    return 0;
+}
+
 static void __init process_multiboot_node(const void *fdt, int node,
                                           const char *name,
                                           u32 address_cells, u32 size_cells)
@@ -299,7 +321,11 @@  static int __init early_scan_node(const void *fdt,
 
     if ( device_tree_node_matches(fdt, node, "memory") )
         rc = process_memory_node(fdt, node, name, depth,
-                                 address_cells, size_cells, NULL);
+                                 address_cells, size_cells, (void*)false);
+    else if ( depth == 1 && !strcmp(name, "reserved-memory") &&
+              strlen(name) == strlen("reserved-memory") )
+        rc = process_reserved_memory_node(fdt, node, name, depth,
+                                          address_cells, size_cells);
     else if ( depth <= 3 && (device_tree_node_compatible(fdt, node, "xen,multiboot-module" ) ||
               device_tree_node_compatible(fdt, node, "multiboot,module" )))
         process_multiboot_node(fdt, node, name, address_cells, size_cells);
diff --git a/xen/include/asm-arm/setup.h b/xen/include/asm-arm/setup.h
index 8bf3d5910a..efcba545c2 100644
--- a/xen/include/asm-arm/setup.h
+++ b/xen/include/asm-arm/setup.h
@@ -66,6 +66,7 @@  struct bootcmdlines {
 
 struct bootinfo {
     struct meminfo mem;
+    struct meminfo reserved_mem;
     struct bootmodules modules;
     struct bootcmdlines cmdlines;
 #ifdef CONFIG_ACPI