diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
index cd585f05ca..c71b9f2889 100644
--- a/xen/arch/arm/domain_build.c
+++ b/xen/arch/arm/domain_build.c
@@ -14,6 +14,7 @@
 #include <xen/guest_access.h>
 #include <xen/iocap.h>
 #include <xen/acpi.h>
+#include <xen/vmap.h>
 #include <xen/warning.h>
 #include <acpi/actables.h>
 #include <asm/device.h>
@@ -1713,6 +1714,111 @@ static int __init make_vpl011_uart_node(struct kernel_info *kinfo)
 }
 #endif
 
+static int __init handle_prop_pfdt(struct kernel_info *kinfo,
+                                   const void *pfdt, int nodeoff,
+                                   uint32_t address_cells, uint32_t size_cells)
+{
+    void *fdt = kinfo->fdt;
+    int propoff, nameoff, res;
+    const struct fdt_property *prop;
+
+    for ( propoff = fdt_first_property_offset(pfdt, nodeoff);
+          propoff >= 0;
+          propoff = fdt_next_property_offset(pfdt, propoff) )
+    {
+        if ( !(prop = fdt_get_property_by_offset(pfdt, propoff, NULL)) )
+            return -FDT_ERR_INTERNAL;
+
+        nameoff = fdt32_to_cpu(prop->nameoff);
+        res = fdt_property(fdt, fdt_string(pfdt, nameoff),
+                           prop->data, fdt32_to_cpu(prop->len));
+        if ( res )
+            return res;
+    }
+
+    /* FDT_ERR_NOTFOUND => There is no more properties for this node */
+    return ( propoff != -FDT_ERR_NOTFOUND ) ? propoff : 0;
+}
+
+static int __init scan_pfdt_node(struct kernel_info *kinfo, const void *pfdt,
+                                 int nodeoff, int depth,
+                                 uint32_t address_cells, uint32_t size_cells)
+{
+    int rc = 0;
+    void *fdt = kinfo->fdt;
+    int node_next;
+    const char *name = fdt_get_name(pfdt, nodeoff, NULL);
+
+    /*
+     * Take the GIC phandle value from the special /gic node in the DTB
+     * fragment.
+     */
+    if ( depth == 1 && dt_node_cmp(name, "gic") == 0 )
+    {
+        kinfo->guest_phandle_gic = fdt_get_phandle(pfdt, nodeoff);
+        return 0;
+    }
+
+    rc = fdt_begin_node(fdt, fdt_get_name(pfdt, nodeoff, NULL));
+    if ( rc )
+        return rc;
+
+    rc = handle_prop_pfdt(kinfo, pfdt, nodeoff, address_cells, size_cells);
+    if ( rc )
+        return rc;
+
+    address_cells = device_tree_get_u32(pfdt, nodeoff, "#address-cells",
+                                        address_cells);
+    size_cells = device_tree_get_u32(pfdt, nodeoff, "#size-cells",
+                                     size_cells);
+
+    node_next = fdt_first_subnode(pfdt, nodeoff);
+    while ( node_next > 0 )
+    {
+        scan_pfdt_node(kinfo, pfdt, node_next, depth + 1, address_cells, size_cells);
+        node_next = fdt_next_subnode(pfdt, node_next);
+    }
+
+    return fdt_end_node(fdt);
+}
+
+static int __init domain_handle_dtb_bootmodule(struct domain *d,
+                                               struct kernel_info *kinfo)
+{
+    void *pfdt;
+    int res, node_next;
+
+    pfdt = ioremap_cache(kinfo->dtb_bootmodule->start,
+                         kinfo->dtb_bootmodule->size);
+    if ( pfdt == NULL )
+        return -EFAULT;
+
+    node_next = fdt_first_subnode(pfdt, 0);
+    while ( node_next > 0 )
+    {
+        const char *name = fdt_get_name(pfdt, node_next, NULL);
+
+        /* only scan /gic /aliases /passthrough, ignore the rest */
+        if ( name != NULL &&
+             (dt_node_cmp(name, "passthrough") == 0 ||
+              dt_node_cmp(name, "aliases") == 0 ||
+              dt_node_cmp(name, "gic") == 0) )
+        {
+            res = scan_pfdt_node(kinfo, pfdt, node_next, 1,
+                    DT_ROOT_NODE_ADDR_CELLS_DEFAULT,
+                    DT_ROOT_NODE_SIZE_CELLS_DEFAULT);
+            if ( res )
+                return res;
+        }
+
+        node_next = fdt_next_subnode(pfdt, node_next);
+    }
+
+    iounmap(pfdt);
+
+    return res;
+}
+
 /*
  * The max size for DT is 2MB. However, the generated DT is small, 4KB
  * are enough for now, but we might have to increase it in the future.
@@ -1768,6 +1874,12 @@ static int __init prepare_dtb_domU(struct domain *d, struct kernel_info *kinfo)
     if ( ret )
         goto err;
 
+    if ( kinfo->dtb_bootmodule ) {
+        ret = domain_handle_dtb_bootmodule(d, kinfo);
+        if ( ret )
+            return ret;
+    }
+
     ret = make_gic_domU_node(kinfo);
     if ( ret )
         goto err;
diff --git a/xen/include/asm-arm/kernel.h b/xen/include/asm-arm/kernel.h
index 760434369b..7f5e659561 100644
--- a/xen/include/asm-arm/kernel.h
+++ b/xen/include/asm-arm/kernel.h
@@ -28,7 +28,7 @@ struct kernel_info {
     paddr_t gnttab_size;
 
     /* boot blob load addresses */
-    const struct bootmodule *kernel_bootmodule, *initrd_bootmodule;
+    const struct bootmodule *kernel_bootmodule, *initrd_bootmodule, *dtb_bootmodule;
     const char* cmdline;
     paddr_t dtb_paddr;
     paddr_t initrd_paddr;
