Change function type of following function to access during runtime:
1. map_irq_to_domain()
2. handle_device_interrupt()
3. map_range_to_domain()
4. unflatten_dt_node()
5. unflatten_device_tree()
Move map_irq_to_domain(), handle_device_interrupt() and map_range_to_domain() to
device.c.
unflatten_device_tree(): Add handling of memory allocation failure.
These changes are done to support the dynamic programming of a nodes where an
overlay node will be added to fdt and unflattened node will be added to dt_host.
Furthermore, IRQ and mmio mapping will be done for the added node.
Signed-off-by: Vikram Garhwal <vikram.garhwal@amd.com>
---
xen/arch/arm/device.c | 145 +++++++++++++++++++++++++++++++
xen/arch/arm/domain_build.c | 142 ------------------------------
xen/arch/arm/include/asm/setup.h | 3 +
xen/common/device_tree.c | 27 +++---
xen/include/xen/device_tree.h | 5 ++
5 files changed, 170 insertions(+), 152 deletions(-)
diff --git a/xen/arch/arm/device.c b/xen/arch/arm/device.c
index 70cd6c1a19..d299c04e62 100644
--- a/xen/arch/arm/device.c
+++ b/xen/arch/arm/device.c
@@ -21,6 +21,9 @@
#include <xen/errno.h>
#include <xen/init.h>
#include <xen/lib.h>
+#include <xen/iocap.h>
+#include <asm/domain_build.h>
+#include <asm/setup.h>
extern const struct device_desc _sdevice[], _edevice[];
extern const struct acpi_device_desc _asdevice[], _aedevice[];
@@ -84,6 +87,148 @@ enum device_class device_get_class(const struct dt_device_node *dev)
return DEVICE_UNKNOWN;
}
+int map_irq_to_domain(struct domain *d, unsigned int irq,
+ bool need_mapping, const char *devname)
+{
+ int res;
+
+ res = irq_permit_access(d, irq);
+ if ( res )
+ {
+ printk(XENLOG_ERR "Unable to permit to dom%u access to IRQ %u\n",
+ d->domain_id, irq);
+ return res;
+ }
+
+ if ( need_mapping )
+ {
+ /*
+ * Checking the return of vgic_reserve_virq is not
+ * necessary. It should not fail except when we try to map
+ * the IRQ twice. This can legitimately happen if the IRQ is shared
+ */
+ vgic_reserve_virq(d, irq);
+
+ res = route_irq_to_guest(d, irq, irq, devname);
+ if ( res < 0 )
+ {
+ printk(XENLOG_ERR "Unable to map IRQ%"PRId32" to dom%d\n",
+ irq, d->domain_id);
+ return res;
+ }
+ }
+
+ dt_dprintk(" - IRQ: %u\n", irq);
+ return 0;
+}
+
+int map_range_to_domain(const struct dt_device_node *dev,
+ u64 addr, u64 len, void *data)
+{
+ struct map_range_data *mr_data = data;
+ struct domain *d = mr_data->d;
+ int res;
+
+ /*
+ * reserved-memory regions are RAM carved out for a special purpose.
+ * They are not MMIO and therefore a domain should not be able to
+ * manage them via the IOMEM interface.
+ */
+ if ( strncasecmp(dt_node_full_name(dev), "/reserved-memory/",
+ strlen("/reserved-memory/")) != 0 )
+ {
+ res = iomem_permit_access(d, paddr_to_pfn(addr),
+ paddr_to_pfn(PAGE_ALIGN(addr + len - 1)));
+ if ( res )
+ {
+ printk(XENLOG_ERR "Unable to permit to dom%d access to"
+ " 0x%"PRIx64" - 0x%"PRIx64"\n",
+ d->domain_id,
+ addr & PAGE_MASK, PAGE_ALIGN(addr + len) - 1);
+ return res;
+ }
+ }
+
+ if ( !mr_data->skip_mapping )
+ {
+ res = map_regions_p2mt(d,
+ gaddr_to_gfn(addr),
+ PFN_UP(len),
+ maddr_to_mfn(addr),
+ mr_data->p2mt);
+
+ if ( res < 0 )
+ {
+ printk(XENLOG_ERR "Unable to map 0x%"PRIx64
+ " - 0x%"PRIx64" in domain %d\n",
+ addr & PAGE_MASK, PAGE_ALIGN(addr + len) - 1,
+ d->domain_id);
+ return res;
+ }
+ }
+
+ dt_dprintk(" - MMIO: %010"PRIx64" - %010"PRIx64" P2MType=%x\n",
+ addr, addr + len, mr_data->p2mt);
+
+ return 0;
+}
+
+/*
+ * handle_device_interrupts retrieves the interrupts configuration from
+ * a device tree node and maps those interrupts to the target domain.
+ *
+ * Returns:
+ * < 0 error
+ * 0 success
+ */
+int handle_device_interrupts(struct domain *d,
+ struct dt_device_node *dev,
+ bool need_mapping)
+{
+ unsigned int i, nirq;
+ int res;
+ struct dt_raw_irq rirq;
+
+ nirq = dt_number_of_irq(dev);
+
+ /* Give permission and map IRQs */
+ for ( i = 0; i < nirq; i++ )
+ {
+ res = dt_device_get_raw_irq(dev, i, &rirq);
+ if ( res )
+ {
+ printk(XENLOG_ERR "Unable to retrieve irq %u for %s\n",
+ i, dt_node_full_name(dev));
+ return res;
+ }
+
+ /*
+ * Don't map IRQ that have no physical meaning
+ * ie: IRQ whose controller is not the GIC
+ */
+ if ( rirq.controller != dt_interrupt_controller )
+ {
+ dt_dprintk("irq %u not connected to primary controller. Connected to %s\n",
+ i, dt_node_full_name(rirq.controller));
+ continue;
+ }
+
+ res = platform_get_irq(dev, i);
+ if ( res < 0 )
+ {
+ printk(XENLOG_ERR "Unable to get irq %u for %s\n",
+ i, dt_node_full_name(dev));
+ return res;
+ }
+
+ res = map_irq_to_domain(d, res, need_mapping, dt_node_name(dev));
+ if ( res )
+ return res;
+ }
+
+ return 0;
+}
+
/*
* Local variables:
* mode: C
diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
index 4fb5c20b13..acde8e714e 100644
--- a/xen/arch/arm/domain_build.c
+++ b/xen/arch/arm/domain_build.c
@@ -2229,41 +2229,6 @@ int __init make_chosen_node(const struct kernel_info *kinfo)
return res;
}
-int __init map_irq_to_domain(struct domain *d, unsigned int irq,
- bool need_mapping, const char *devname)
-{
- int res;
-
- res = irq_permit_access(d, irq);
- if ( res )
- {
- printk(XENLOG_ERR "Unable to permit to dom%u access to IRQ %u\n",
- d->domain_id, irq);
- return res;
- }
-
- if ( need_mapping )
- {
- /*
- * Checking the return of vgic_reserve_virq is not
- * necessary. It should not fail except when we try to map
- * the IRQ twice. This can legitimately happen if the IRQ is shared
- */
- vgic_reserve_virq(d, irq);
-
- res = route_irq_to_guest(d, irq, irq, devname);
- if ( res < 0 )
- {
- printk(XENLOG_ERR "Unable to map IRQ%"PRId32" to dom%d\n",
- irq, d->domain_id);
- return res;
- }
- }
-
- dt_dprintk(" - IRQ: %u\n", irq);
- return 0;
-}
-
static int __init map_dt_irq_to_domain(const struct dt_device_node *dev,
const struct dt_irq *dt_irq,
void *data)
@@ -2295,57 +2260,6 @@ static int __init map_dt_irq_to_domain(const struct dt_device_node *dev,
return 0;
}
-int __init map_range_to_domain(const struct dt_device_node *dev,
- u64 addr, u64 len, void *data)
-{
- struct map_range_data *mr_data = data;
- struct domain *d = mr_data->d;
- int res;
-
- /*
- * reserved-memory regions are RAM carved out for a special purpose.
- * They are not MMIO and therefore a domain should not be able to
- * manage them via the IOMEM interface.
- */
- if ( strncasecmp(dt_node_full_name(dev), "/reserved-memory/",
- strlen("/reserved-memory/")) != 0 )
- {
- res = iomem_permit_access(d, paddr_to_pfn(addr),
- paddr_to_pfn(PAGE_ALIGN(addr + len - 1)));
- if ( res )
- {
- printk(XENLOG_ERR "Unable to permit to dom%d access to"
- " 0x%"PRIx64" - 0x%"PRIx64"\n",
- d->domain_id,
- addr & PAGE_MASK, PAGE_ALIGN(addr + len) - 1);
- return res;
- }
- }
-
- if ( !mr_data->skip_mapping )
- {
- res = map_regions_p2mt(d,
- gaddr_to_gfn(addr),
- PFN_UP(len),
- maddr_to_mfn(addr),
- mr_data->p2mt);
-
- if ( res < 0 )
- {
- printk(XENLOG_ERR "Unable to map 0x%"PRIx64
- " - 0x%"PRIx64" in domain %d\n",
- addr & PAGE_MASK, PAGE_ALIGN(addr + len) - 1,
- d->domain_id);
- return res;
- }
- }
-
- dt_dprintk(" - MMIO: %010"PRIx64" - %010"PRIx64" P2MType=%x\n",
- addr, addr + len, mr_data->p2mt);
-
- return 0;
-}
-
/*
* For a node which describes a discoverable bus (such as a PCI bus)
* then we may need to perform additional mappings in order to make
@@ -2373,62 +2287,6 @@ static int __init map_device_children(const struct dt_device_node *dev,
return 0;
}
-/*
- * handle_device_interrupts retrieves the interrupts configuration from
- * a device tree node and maps those interrupts to the target domain.
- *
- * Returns:
- * < 0 error
- * 0 success
- */
-static int __init handle_device_interrupts(struct domain *d,
- struct dt_device_node *dev,
- bool need_mapping)
-{
- unsigned int i, nirq;
- int res;
- struct dt_raw_irq rirq;
-
- nirq = dt_number_of_irq(dev);
-
- /* Give permission and map IRQs */
- for ( i = 0; i < nirq; i++ )
- {
- res = dt_device_get_raw_irq(dev, i, &rirq);
- if ( res )
- {
- printk(XENLOG_ERR "Unable to retrieve irq %u for %s\n",
- i, dt_node_full_name(dev));
- return res;
- }
-
- /*
- * Don't map IRQ that have no physical meaning
- * ie: IRQ whose controller is not the GIC
- */
- if ( rirq.controller != dt_interrupt_controller )
- {
- dt_dprintk("irq %u not connected to primary controller. Connected to %s\n",
- i, dt_node_full_name(rirq.controller));
- continue;
- }
-
- res = platform_get_irq(dev, i);
- if ( res < 0 )
- {
- printk(XENLOG_ERR "Unable to get irq %u for %s\n",
- i, dt_node_full_name(dev));
- return res;
- }
-
- res = map_irq_to_domain(d, res, need_mapping, dt_node_name(dev));
- if ( res )
- return res;
- }
-
- return 0;
-}
-
/*
* For a given device node:
* - Give permission to the guest to manage IRQ and MMIO range
diff --git a/xen/arch/arm/include/asm/setup.h b/xen/arch/arm/include/asm/setup.h
index fdbf68aadc..ec050848aa 100644
--- a/xen/arch/arm/include/asm/setup.h
+++ b/xen/arch/arm/include/asm/setup.h
@@ -163,6 +163,9 @@ void device_tree_get_reg(const __be32 **cell, u32 address_cells,
u32 device_tree_get_u32(const void *fdt, int node,
const char *prop_name, u32 dflt);
+int handle_device_interrupts(struct domain *d, struct dt_device_node *dev,
+ bool need_mapping);
+
int map_range_to_domain(const struct dt_device_node *dev,
u64 addr, u64 len, void *data);
diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c
index 6c9712ab7b..6518eff9b0 100644
--- a/xen/common/device_tree.c
+++ b/xen/common/device_tree.c
@@ -1811,12 +1811,12 @@ int dt_count_phandle_with_args(const struct dt_device_node *np,
* @allnextpp: pointer to ->allnext from last allocated device_node
* @fpsize: Size of the node path up at the current depth.
*/
-static unsigned long __init unflatten_dt_node(const void *fdt,
- unsigned long mem,
- unsigned long *p,
- struct dt_device_node *dad,
- struct dt_device_node ***allnextpp,
- unsigned long fpsize)
+static unsigned long unflatten_dt_node(const void *fdt,
+ unsigned long mem,
+ unsigned long *p,
+ struct dt_device_node *dad,
+ struct dt_device_node ***allnextpp,
+ unsigned long fpsize)
{
struct dt_device_node *np;
struct dt_property *pp, **prev_pp = NULL;
@@ -2047,7 +2047,7 @@ static unsigned long __init unflatten_dt_node(const void *fdt,
}
/**
- * __unflatten_device_tree - create tree of device_nodes from flat blob
+ * unflatten_device_tree - create tree of device_nodes from flat blob
*
* unflattens a device-tree, creating the
* tree of struct device_node. It also fills the "name" and "type"
@@ -2056,8 +2056,7 @@ static unsigned long __init unflatten_dt_node(const void *fdt,
* @fdt: The fdt to expand
* @mynodes: The device_node tree created by the call
*/
-static void __init __unflatten_device_tree(const void *fdt,
- struct dt_device_node **mynodes)
+int unflatten_device_tree(const void *fdt, struct dt_device_node **mynodes)
{
unsigned long start, mem, size;
struct dt_device_node **allnextp = mynodes;
@@ -2079,6 +2078,12 @@ static void __init __unflatten_device_tree(const void *fdt,
/* Allocate memory for the expanded device tree */
mem = (unsigned long)_xmalloc (size + 4, __alignof__(struct dt_device_node));
+ if ( mem == 0 )
+ {
+ printk(XENLOG_ERR "Cannot allocate memory for unflatten device tree\n");
+ return -ENOMEM;
+ }
+
((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef);
dt_dprintk(" unflattening %lx...\n", mem);
@@ -2095,6 +2100,8 @@ static void __init __unflatten_device_tree(const void *fdt,
*allnextp = NULL;
dt_dprintk(" <- unflatten_device_tree()\n");
+
+ return 0;
}
static void dt_alias_add(struct dt_alias_prop *ap,
@@ -2179,7 +2186,7 @@ dt_find_interrupt_controller(const struct dt_device_match *matches)
void __init dt_unflatten_host_device_tree(void)
{
- __unflatten_device_tree(device_tree_flattened, &dt_host);
+ unflatten_device_tree(device_tree_flattened, &dt_host);
dt_alias_scan();
}
diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h
index a28937d12a..bde46d7120 100644
--- a/xen/include/xen/device_tree.h
+++ b/xen/include/xen/device_tree.h
@@ -181,6 +181,11 @@ int device_tree_for_each_node(const void *fdt, int node,
*/
void dt_unflatten_host_device_tree(void);
+/**
+ * unflatten any device tree.
+ */
+int unflatten_device_tree(const void *fdt, struct dt_device_node **mynodes);
+
/**
* IRQ translation callback
* TODO: For the moment we assume that we only have ONE
--
2.17.1
Hi Vikram,
On 07/12/2022 07:15, Vikram Garhwal wrote:
>
>
> Change function type of following function to access during runtime:
> 1. map_irq_to_domain()
> 2. handle_device_interrupt()
> 3. map_range_to_domain()
> 4. unflatten_dt_node()
> 5. unflatten_device_tree()
If you do not want to do this at first use, then this should be a separate patch.
>
> Move map_irq_to_domain(), handle_device_interrupt() and map_range_to_domain() to
> device.c.
This should be a separate patch (without removing __init) to make the comparison easier.
>
> unflatten_device_tree(): Add handling of memory allocation failure.
Apart from that you also renamed __unflatten_device_tree to unflatten_device_tree
and you did not mention it.
>
> These changes are done to support the dynamic programming of a nodes where an
> overlay node will be added to fdt and unflattened node will be added to dt_host.
> Furthermore, IRQ and mmio mapping will be done for the added node.
>
> Signed-off-by: Vikram Garhwal <vikram.garhwal@amd.com>
> ---
> xen/arch/arm/device.c | 145 +++++++++++++++++++++++++++++++
> xen/arch/arm/domain_build.c | 142 ------------------------------
> xen/arch/arm/include/asm/setup.h | 3 +
> xen/common/device_tree.c | 27 +++---
> xen/include/xen/device_tree.h | 5 ++
> 5 files changed, 170 insertions(+), 152 deletions(-)
>
> diff --git a/xen/arch/arm/device.c b/xen/arch/arm/device.c
> index 70cd6c1a19..d299c04e62 100644
> --- a/xen/arch/arm/device.c
> +++ b/xen/arch/arm/device.c
> @@ -21,6 +21,9 @@
> #include <xen/errno.h>
> #include <xen/init.h>
> #include <xen/lib.h>
> +#include <xen/iocap.h>
> +#include <asm/domain_build.h>
> +#include <asm/setup.h>
>
> extern const struct device_desc _sdevice[], _edevice[];
> extern const struct acpi_device_desc _asdevice[], _aedevice[];
> @@ -84,6 +87,148 @@ enum device_class device_get_class(const struct dt_device_node *dev)
> return DEVICE_UNKNOWN;
> }
>
> +int map_irq_to_domain(struct domain *d, unsigned int irq,
> + bool need_mapping, const char *devname)
> +{
> + int res;
> +
> + res = irq_permit_access(d, irq);
> + if ( res )
> + {
> + printk(XENLOG_ERR "Unable to permit to dom%u access to IRQ %u\n",
> + d->domain_id, irq);
> + return res;
> + }
> +
> + if ( need_mapping )
> + {
> + /*
> + * Checking the return of vgic_reserve_virq is not
> + * necessary. It should not fail except when we try to map
> + * the IRQ twice. This can legitimately happen if the IRQ is shared
> + */
> + vgic_reserve_virq(d, irq);
> +
> + res = route_irq_to_guest(d, irq, irq, devname);
> + if ( res < 0 )
> + {
> + printk(XENLOG_ERR "Unable to map IRQ%"PRId32" to dom%d\n",
> + irq, d->domain_id);
> + return res;
> + }
> + }
> +
> + dt_dprintk(" - IRQ: %u\n", irq);
> + return 0;
> +}
> +
> +int map_range_to_domain(const struct dt_device_node *dev,
> + u64 addr, u64 len, void *data)
> +{
> + struct map_range_data *mr_data = data;
> + struct domain *d = mr_data->d;
> + int res;
> +
> + /*
> + * reserved-memory regions are RAM carved out for a special purpose.
> + * They are not MMIO and therefore a domain should not be able to
> + * manage them via the IOMEM interface.
> + */
> + if ( strncasecmp(dt_node_full_name(dev), "/reserved-memory/",
> + strlen("/reserved-memory/")) != 0 )
> + {
> + res = iomem_permit_access(d, paddr_to_pfn(addr),
> + paddr_to_pfn(PAGE_ALIGN(addr + len - 1)));
> + if ( res )
> + {
> + printk(XENLOG_ERR "Unable to permit to dom%d access to"
> + " 0x%"PRIx64" - 0x%"PRIx64"\n",
> + d->domain_id,
> + addr & PAGE_MASK, PAGE_ALIGN(addr + len) - 1);
> + return res;
> + }
> + }
> +
> + if ( !mr_data->skip_mapping )
> + {
> + res = map_regions_p2mt(d,
> + gaddr_to_gfn(addr),
> + PFN_UP(len),
> + maddr_to_mfn(addr),
> + mr_data->p2mt);
> +
> + if ( res < 0 )
> + {
> + printk(XENLOG_ERR "Unable to map 0x%"PRIx64
> + " - 0x%"PRIx64" in domain %d\n",
> + addr & PAGE_MASK, PAGE_ALIGN(addr + len) - 1,
> + d->domain_id);
> + return res;
> + }
> + }
> +
> + dt_dprintk(" - MMIO: %010"PRIx64" - %010"PRIx64" P2MType=%x\n",
> + addr, addr + len, mr_data->p2mt);
> +
> + return 0;
> +}
> +
> +/*
> + * handle_device_interrupts retrieves the interrupts configuration from
> + * a device tree node and maps those interrupts to the target domain.
> + *
> + * Returns:
> + * < 0 error
> + * 0 success
> + */
> +int handle_device_interrupts(struct domain *d,
> + struct dt_device_node *dev,
> + bool need_mapping)
> +{
> + unsigned int i, nirq;
> + int res;
> + struct dt_raw_irq rirq;
> +
> + nirq = dt_number_of_irq(dev);
> +
> + /* Give permission and map IRQs */
> + for ( i = 0; i < nirq; i++ )
> + {
> + res = dt_device_get_raw_irq(dev, i, &rirq);
> + if ( res )
> + {
> + printk(XENLOG_ERR "Unable to retrieve irq %u for %s\n",
> + i, dt_node_full_name(dev));
> + return res;
> + }
> +
> + /*
> + * Don't map IRQ that have no physical meaning
> + * ie: IRQ whose controller is not the GIC
> + */
> + if ( rirq.controller != dt_interrupt_controller )
> + {
> + dt_dprintk("irq %u not connected to primary controller. Connected to %s\n",
> + i, dt_node_full_name(rirq.controller));
> + continue;
> + }
> +
> + res = platform_get_irq(dev, i);
> + if ( res < 0 )
> + {
> + printk(XENLOG_ERR "Unable to get irq %u for %s\n",
> + i, dt_node_full_name(dev));
> + return res;
> + }
> +
> + res = map_irq_to_domain(d, res, need_mapping, dt_node_name(dev));
> + if ( res )
> + return res;
> + }
> +
> + return 0;
> +}
> +
> /*
> * Local variables:
> * mode: C
> diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
> index 4fb5c20b13..acde8e714e 100644
> --- a/xen/arch/arm/domain_build.c
> +++ b/xen/arch/arm/domain_build.c
> @@ -2229,41 +2229,6 @@ int __init make_chosen_node(const struct kernel_info *kinfo)
> return res;
> }
>
> -int __init map_irq_to_domain(struct domain *d, unsigned int irq,
> - bool need_mapping, const char *devname)
> -{
> - int res;
> -
> - res = irq_permit_access(d, irq);
> - if ( res )
> - {
> - printk(XENLOG_ERR "Unable to permit to dom%u access to IRQ %u\n",
> - d->domain_id, irq);
> - return res;
> - }
> -
> - if ( need_mapping )
> - {
> - /*
> - * Checking the return of vgic_reserve_virq is not
> - * necessary. It should not fail except when we try to map
> - * the IRQ twice. This can legitimately happen if the IRQ is shared
> - */
> - vgic_reserve_virq(d, irq);
> -
> - res = route_irq_to_guest(d, irq, irq, devname);
> - if ( res < 0 )
> - {
> - printk(XENLOG_ERR "Unable to map IRQ%"PRId32" to dom%d\n",
> - irq, d->domain_id);
> - return res;
> - }
> - }
> -
> - dt_dprintk(" - IRQ: %u\n", irq);
> - return 0;
> -}
If you move map_irq_to_domain from domain_build.c to device.c, then the prototype needs to also
be moved from domain_build.h to setup.h
> -
> static int __init map_dt_irq_to_domain(const struct dt_device_node *dev,
> const struct dt_irq *dt_irq,
> void *data)
> @@ -2295,57 +2260,6 @@ static int __init map_dt_irq_to_domain(const struct dt_device_node *dev,
> return 0;
> }
>
> -int __init map_range_to_domain(const struct dt_device_node *dev,
> - u64 addr, u64 len, void *data)
> -{
> - struct map_range_data *mr_data = data;
> - struct domain *d = mr_data->d;
> - int res;
> -
> - /*
> - * reserved-memory regions are RAM carved out for a special purpose.
> - * They are not MMIO and therefore a domain should not be able to
> - * manage them via the IOMEM interface.
> - */
> - if ( strncasecmp(dt_node_full_name(dev), "/reserved-memory/",
> - strlen("/reserved-memory/")) != 0 )
> - {
> - res = iomem_permit_access(d, paddr_to_pfn(addr),
> - paddr_to_pfn(PAGE_ALIGN(addr + len - 1)));
> - if ( res )
> - {
> - printk(XENLOG_ERR "Unable to permit to dom%d access to"
> - " 0x%"PRIx64" - 0x%"PRIx64"\n",
> - d->domain_id,
> - addr & PAGE_MASK, PAGE_ALIGN(addr + len) - 1);
> - return res;
> - }
> - }
> -
> - if ( !mr_data->skip_mapping )
> - {
> - res = map_regions_p2mt(d,
> - gaddr_to_gfn(addr),
> - PFN_UP(len),
> - maddr_to_mfn(addr),
> - mr_data->p2mt);
> -
> - if ( res < 0 )
> - {
> - printk(XENLOG_ERR "Unable to map 0x%"PRIx64
> - " - 0x%"PRIx64" in domain %d\n",
> - addr & PAGE_MASK, PAGE_ALIGN(addr + len) - 1,
> - d->domain_id);
> - return res;
> - }
> - }
> -
> - dt_dprintk(" - MMIO: %010"PRIx64" - %010"PRIx64" P2MType=%x\n",
> - addr, addr + len, mr_data->p2mt);
> -
> - return 0;
> -}
> -
> /*
> * For a node which describes a discoverable bus (such as a PCI bus)
> * then we may need to perform additional mappings in order to make
> @@ -2373,62 +2287,6 @@ static int __init map_device_children(const struct dt_device_node *dev,
> return 0;
> }
>
> -/*
> - * handle_device_interrupts retrieves the interrupts configuration from
> - * a device tree node and maps those interrupts to the target domain.
> - *
> - * Returns:
> - * < 0 error
> - * 0 success
> - */
> -static int __init handle_device_interrupts(struct domain *d,
> - struct dt_device_node *dev,
> - bool need_mapping)
> -{
> - unsigned int i, nirq;
> - int res;
> - struct dt_raw_irq rirq;
> -
> - nirq = dt_number_of_irq(dev);
> -
> - /* Give permission and map IRQs */
> - for ( i = 0; i < nirq; i++ )
> - {
> - res = dt_device_get_raw_irq(dev, i, &rirq);
> - if ( res )
> - {
> - printk(XENLOG_ERR "Unable to retrieve irq %u for %s\n",
> - i, dt_node_full_name(dev));
> - return res;
> - }
> -
> - /*
> - * Don't map IRQ that have no physical meaning
> - * ie: IRQ whose controller is not the GIC
> - */
> - if ( rirq.controller != dt_interrupt_controller )
> - {
> - dt_dprintk("irq %u not connected to primary controller. Connected to %s\n",
> - i, dt_node_full_name(rirq.controller));
> - continue;
> - }
> -
> - res = platform_get_irq(dev, i);
> - if ( res < 0 )
> - {
> - printk(XENLOG_ERR "Unable to get irq %u for %s\n",
> - i, dt_node_full_name(dev));
> - return res;
> - }
> -
> - res = map_irq_to_domain(d, res, need_mapping, dt_node_name(dev));
> - if ( res )
> - return res;
> - }
> -
> - return 0;
> -}
> -
> /*
> * For a given device node:
> * - Give permission to the guest to manage IRQ and MMIO range
> diff --git a/xen/arch/arm/include/asm/setup.h b/xen/arch/arm/include/asm/setup.h
> index fdbf68aadc..ec050848aa 100644
> --- a/xen/arch/arm/include/asm/setup.h
> +++ b/xen/arch/arm/include/asm/setup.h
> @@ -163,6 +163,9 @@ void device_tree_get_reg(const __be32 **cell, u32 address_cells,
> u32 device_tree_get_u32(const void *fdt, int node,
> const char *prop_name, u32 dflt);
>
> +int handle_device_interrupts(struct domain *d, struct dt_device_node *dev,
> + bool need_mapping);
> +
> int map_range_to_domain(const struct dt_device_node *dev,
> u64 addr, u64 len, void *data);
>
> diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c
> index 6c9712ab7b..6518eff9b0 100644
> --- a/xen/common/device_tree.c
> +++ b/xen/common/device_tree.c
> @@ -1811,12 +1811,12 @@ int dt_count_phandle_with_args(const struct dt_device_node *np,
> * @allnextpp: pointer to ->allnext from last allocated device_node
> * @fpsize: Size of the node path up at the current depth.
> */
> -static unsigned long __init unflatten_dt_node(const void *fdt,
> - unsigned long mem,
> - unsigned long *p,
> - struct dt_device_node *dad,
> - struct dt_device_node ***allnextpp,
> - unsigned long fpsize)
> +static unsigned long unflatten_dt_node(const void *fdt,
> + unsigned long mem,
> + unsigned long *p,
> + struct dt_device_node *dad,
> + struct dt_device_node ***allnextpp,
> + unsigned long fpsize)
> {
> struct dt_device_node *np;
> struct dt_property *pp, **prev_pp = NULL;
> @@ -2047,7 +2047,7 @@ static unsigned long __init unflatten_dt_node(const void *fdt,
> }
>
> /**
> - * __unflatten_device_tree - create tree of device_nodes from flat blob
> + * unflatten_device_tree - create tree of device_nodes from flat blob
> *
> * unflattens a device-tree, creating the
> * tree of struct device_node. It also fills the "name" and "type"
> @@ -2056,8 +2056,7 @@ static unsigned long __init unflatten_dt_node(const void *fdt,
> * @fdt: The fdt to expand
> * @mynodes: The device_node tree created by the call
> */
> -static void __init __unflatten_device_tree(const void *fdt,
> - struct dt_device_node **mynodes)
> +int unflatten_device_tree(const void *fdt, struct dt_device_node **mynodes)
> {
> unsigned long start, mem, size;
> struct dt_device_node **allnextp = mynodes;
> @@ -2079,6 +2078,12 @@ static void __init __unflatten_device_tree(const void *fdt,
> /* Allocate memory for the expanded device tree */
> mem = (unsigned long)_xmalloc (size + 4, __alignof__(struct dt_device_node));
>
> + if ( mem == 0 )
NIT: !mem would be preffered.
> + {
> + printk(XENLOG_ERR "Cannot allocate memory for unflatten device tree\n");
> + return -ENOMEM;
What is the point of modifying the function to return a value if ...
> + }
> +
> ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef);
>
> dt_dprintk(" unflattening %lx...\n", mem);
> @@ -2095,6 +2100,8 @@ static void __init __unflatten_device_tree(const void *fdt,
> *allnextp = NULL;
>
> dt_dprintk(" <- unflatten_device_tree()\n");
> +
> + return 0;
> }
>
> static void dt_alias_add(struct dt_alias_prop *ap,
> @@ -2179,7 +2186,7 @@ dt_find_interrupt_controller(const struct dt_device_match *matches)
>
> void __init dt_unflatten_host_device_tree(void)
> {
> - __unflatten_device_tree(device_tree_flattened, &dt_host);
> + unflatten_device_tree(device_tree_flattened, &dt_host);
... you do not check it anyway?
> dt_alias_scan();
> }
>
> diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h
> index a28937d12a..bde46d7120 100644
> --- a/xen/include/xen/device_tree.h
> +++ b/xen/include/xen/device_tree.h
> @@ -181,6 +181,11 @@ int device_tree_for_each_node(const void *fdt, int node,
> */
> void dt_unflatten_host_device_tree(void);
>
> +/**
> + * unflatten any device tree.
> + */
> +int unflatten_device_tree(const void *fdt, struct dt_device_node **mynodes);
> +
> /**
> * IRQ translation callback
> * TODO: For the moment we assume that we only have ONE
> --
> 2.17.1
>
>
~Michal
Hi Michal,
Thanks for reviewing this. Please see my comments below.
On 1/20/23 4:39 AM, Michal Orzel wrote:
> Hi Vikram,
>
> On 07/12/2022 07:15, Vikram Garhwal wrote:
>>
>> Change function type of following function to access during runtime:
>> 1. map_irq_to_domain()
>> 2. handle_device_interrupt()
>> 3. map_range_to_domain()
>> 4. unflatten_dt_node()
>> 5. unflatten_device_tree()
> If you do not want to do this at first use, then this should be a separate patch.
>> Move map_irq_to_domain(), handle_device_interrupt() and map_range_to_domain() to
>> device.c.
> This should be a separate patch (without removing __init) to make the comparison easier.
Okay
>
>> unflatten_device_tree(): Add handling of memory allocation failure.
> Apart from that you also renamed __unflatten_device_tree to unflatten_device_tree
> and you did not mention it.
I will add this in commit.
>
>> These changes are done to support the dynamic programming of a nodes where an
>> overlay node will be added to fdt and unflattened node will be added to dt_host.
>> Furthermore, IRQ and mmio mapping will be done for the added node.
>>
>> Signed-off-by: Vikram Garhwal <vikram.garhwal@amd.com>
>> ---
>> xen/arch/arm/device.c | 145 +++++++++++++++++++++++++++++++
>> xen/arch/arm/domain_build.c | 142 ------------------------------
>> xen/arch/arm/include/asm/setup.h | 3 +
>> xen/common/device_tree.c | 27 +++---
>> xen/include/xen/device_tree.h | 5 ++
>> 5 files changed, 170 insertions(+), 152 deletions(-)
>>
>> diff --git a/xen/arch/arm/device.c b/xen/arch/arm/device.c
>> index 70cd6c1a19..d299c04e62 100644
>> --- a/xen/arch/arm/device.c
>> +++ b/xen/arch/arm/device.c
>> @@ -21,6 +21,9 @@
>> #include <xen/errno.h>
>> #include <xen/init.h>
>> #include <xen/lib.h>
>> +#include <xen/iocap.h>
>> +#include <asm/domain_build.h>
>> +#include <asm/setup.h>
>>
>> extern const struct device_desc _sdevice[], _edevice[];
>> extern const struct acpi_device_desc _asdevice[], _aedevice[];
>> @@ -84,6 +87,148 @@ enum device_class device_get_class(const struct dt_device_node *dev)
>> return DEVICE_UNKNOWN;
>> }
>>
>> +int map_irq_to_domain(struct domain *d, unsigned int irq,
>> + bool need_mapping, const char *devname)
>> +{
>> + int res;
>> +
>> + res = irq_permit_access(d, irq);
>> + if ( res )
>> + {
>> + printk(XENLOG_ERR "Unable to permit to dom%u access to IRQ %u\n",
>> + d->domain_id, irq);
>> + return res;
>> + }
>> +
>> + if ( need_mapping )
>> + {
>> + /*
>> + * Checking the return of vgic_reserve_virq is not
>> + * necessary. It should not fail except when we try to map
>> + * the IRQ twice. This can legitimately happen if the IRQ is shared
>> + */
>> + vgic_reserve_virq(d, irq);
>> +
>> + res = route_irq_to_guest(d, irq, irq, devname);
>> + if ( res < 0 )
>> + {
>> + printk(XENLOG_ERR "Unable to map IRQ%"PRId32" to dom%d\n",
>> + irq, d->domain_id);
>> + return res;
>> + }
>> + }
>> +
>> + dt_dprintk(" - IRQ: %u\n", irq);
>> + return 0;
>> +}
>> +
>> +int map_range_to_domain(const struct dt_device_node *dev,
>> + u64 addr, u64 len, void *data)
>> +{
>> + struct map_range_data *mr_data = data;
>> + struct domain *d = mr_data->d;
>> + int res;
>> +
>> + /*
>> + * reserved-memory regions are RAM carved out for a special purpose.
>> + * They are not MMIO and therefore a domain should not be able to
>> + * manage them via the IOMEM interface.
>> + */
>> + if ( strncasecmp(dt_node_full_name(dev), "/reserved-memory/",
>> + strlen("/reserved-memory/")) != 0 )
>> + {
>> + res = iomem_permit_access(d, paddr_to_pfn(addr),
>> + paddr_to_pfn(PAGE_ALIGN(addr + len - 1)));
>> + if ( res )
>> + {
>> + printk(XENLOG_ERR "Unable to permit to dom%d access to"
>> + " 0x%"PRIx64" - 0x%"PRIx64"\n",
>> + d->domain_id,
>> + addr & PAGE_MASK, PAGE_ALIGN(addr + len) - 1);
>> + return res;
>> + }
>> + }
>> +
>> + if ( !mr_data->skip_mapping )
>> + {
>> + res = map_regions_p2mt(d,
>> + gaddr_to_gfn(addr),
>> + PFN_UP(len),
>> + maddr_to_mfn(addr),
>> + mr_data->p2mt);
>> +
>> + if ( res < 0 )
>> + {
>> + printk(XENLOG_ERR "Unable to map 0x%"PRIx64
>> + " - 0x%"PRIx64" in domain %d\n",
>> + addr & PAGE_MASK, PAGE_ALIGN(addr + len) - 1,
>> + d->domain_id);
>> + return res;
>> + }
>> + }
>> +
>> + dt_dprintk(" - MMIO: %010"PRIx64" - %010"PRIx64" P2MType=%x\n",
>> + addr, addr + len, mr_data->p2mt);
>> +
>> + return 0;
>> +}
>> +
>> +/*
>> + * handle_device_interrupts retrieves the interrupts configuration from
>> + * a device tree node and maps those interrupts to the target domain.
>> + *
>> + * Returns:
>> + * < 0 error
>> + * 0 success
>> + */
>> +int handle_device_interrupts(struct domain *d,
>> + struct dt_device_node *dev,
>> + bool need_mapping)
>> +{
>> + unsigned int i, nirq;
>> + int res;
>> + struct dt_raw_irq rirq;
>> +
>> + nirq = dt_number_of_irq(dev);
>> +
>> + /* Give permission and map IRQs */
>> + for ( i = 0; i < nirq; i++ )
>> + {
>> + res = dt_device_get_raw_irq(dev, i, &rirq);
>> + if ( res )
>> + {
>> + printk(XENLOG_ERR "Unable to retrieve irq %u for %s\n",
>> + i, dt_node_full_name(dev));
>> + return res;
>> + }
>> +
>> + /*
>> + * Don't map IRQ that have no physical meaning
>> + * ie: IRQ whose controller is not the GIC
>> + */
>> + if ( rirq.controller != dt_interrupt_controller )
>> + {
>> + dt_dprintk("irq %u not connected to primary controller. Connected to %s\n",
>> + i, dt_node_full_name(rirq.controller));
>> + continue;
>> + }
>> +
>> + res = platform_get_irq(dev, i);
>> + if ( res < 0 )
>> + {
>> + printk(XENLOG_ERR "Unable to get irq %u for %s\n",
>> + i, dt_node_full_name(dev));
>> + return res;
>> + }
>> +
>> + res = map_irq_to_domain(d, res, need_mapping, dt_node_name(dev));
>> + if ( res )
>> + return res;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> /*
>> * Local variables:
>> * mode: C
>> diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
>> index 4fb5c20b13..acde8e714e 100644
>> --- a/xen/arch/arm/domain_build.c
>> +++ b/xen/arch/arm/domain_build.c
>> @@ -2229,41 +2229,6 @@ int __init make_chosen_node(const struct kernel_info *kinfo)
>> return res;
>> }
>>
>> -int __init map_irq_to_domain(struct domain *d, unsigned int irq,
>> - bool need_mapping, const char *devname)
>> -{
>> - int res;
>> -
>> - res = irq_permit_access(d, irq);
>> - if ( res )
>> - {
>> - printk(XENLOG_ERR "Unable to permit to dom%u access to IRQ %u\n",
>> - d->domain_id, irq);
>> - return res;
>> - }
>> -
>> - if ( need_mapping )
>> - {
>> - /*
>> - * Checking the return of vgic_reserve_virq is not
>> - * necessary. It should not fail except when we try to map
>> - * the IRQ twice. This can legitimately happen if the IRQ is shared
>> - */
>> - vgic_reserve_virq(d, irq);
>> -
>> - res = route_irq_to_guest(d, irq, irq, devname);
>> - if ( res < 0 )
>> - {
>> - printk(XENLOG_ERR "Unable to map IRQ%"PRId32" to dom%d\n",
>> - irq, d->domain_id);
>> - return res;
>> - }
>> - }
>> -
>> - dt_dprintk(" - IRQ: %u\n", irq);
>> - return 0;
>> -}
> If you move map_irq_to_domain from domain_build.c to device.c, then the prototype needs to also
> be moved from domain_build.h to setup.h
>
>> -
>> static int __init map_dt_irq_to_domain(const struct dt_device_node *dev,
>> const struct dt_irq *dt_irq,
>> void *data)
>> @@ -2295,57 +2260,6 @@ static int __init map_dt_irq_to_domain(const struct dt_device_node *dev,
>> return 0;
>> }
>>
>> -int __init map_range_to_domain(const struct dt_device_node *dev,
>> - u64 addr, u64 len, void *data)
>> -{
>> - struct map_range_data *mr_data = data;
>> - struct domain *d = mr_data->d;
>> - int res;
>> -
>> - /*
>> - * reserved-memory regions are RAM carved out for a special purpose.
>> - * They are not MMIO and therefore a domain should not be able to
>> - * manage them via the IOMEM interface.
>> - */
>> - if ( strncasecmp(dt_node_full_name(dev), "/reserved-memory/",
>> - strlen("/reserved-memory/")) != 0 )
>> - {
>> - res = iomem_permit_access(d, paddr_to_pfn(addr),
>> - paddr_to_pfn(PAGE_ALIGN(addr + len - 1)));
>> - if ( res )
>> - {
>> - printk(XENLOG_ERR "Unable to permit to dom%d access to"
>> - " 0x%"PRIx64" - 0x%"PRIx64"\n",
>> - d->domain_id,
>> - addr & PAGE_MASK, PAGE_ALIGN(addr + len) - 1);
>> - return res;
>> - }
>> - }
>> -
>> - if ( !mr_data->skip_mapping )
>> - {
>> - res = map_regions_p2mt(d,
>> - gaddr_to_gfn(addr),
>> - PFN_UP(len),
>> - maddr_to_mfn(addr),
>> - mr_data->p2mt);
>> -
>> - if ( res < 0 )
>> - {
>> - printk(XENLOG_ERR "Unable to map 0x%"PRIx64
>> - " - 0x%"PRIx64" in domain %d\n",
>> - addr & PAGE_MASK, PAGE_ALIGN(addr + len) - 1,
>> - d->domain_id);
>> - return res;
>> - }
>> - }
>> -
>> - dt_dprintk(" - MMIO: %010"PRIx64" - %010"PRIx64" P2MType=%x\n",
>> - addr, addr + len, mr_data->p2mt);
>> -
>> - return 0;
>> -}
>> -
>> /*
>> * For a node which describes a discoverable bus (such as a PCI bus)
>> * then we may need to perform additional mappings in order to make
>> @@ -2373,62 +2287,6 @@ static int __init map_device_children(const struct dt_device_node *dev,
>> return 0;
>> }
>>
>> -/*
>> - * handle_device_interrupts retrieves the interrupts configuration from
>> - * a device tree node and maps those interrupts to the target domain.
>> - *
>> - * Returns:
>> - * < 0 error
>> - * 0 success
>> - */
>> -static int __init handle_device_interrupts(struct domain *d,
>> - struct dt_device_node *dev,
>> - bool need_mapping)
>> -{
>> - unsigned int i, nirq;
>> - int res;
>> - struct dt_raw_irq rirq;
>> -
>> - nirq = dt_number_of_irq(dev);
>> -
>> - /* Give permission and map IRQs */
>> - for ( i = 0; i < nirq; i++ )
>> - {
>> - res = dt_device_get_raw_irq(dev, i, &rirq);
>> - if ( res )
>> - {
>> - printk(XENLOG_ERR "Unable to retrieve irq %u for %s\n",
>> - i, dt_node_full_name(dev));
>> - return res;
>> - }
>> -
>> - /*
>> - * Don't map IRQ that have no physical meaning
>> - * ie: IRQ whose controller is not the GIC
>> - */
>> - if ( rirq.controller != dt_interrupt_controller )
>> - {
>> - dt_dprintk("irq %u not connected to primary controller. Connected to %s\n",
>> - i, dt_node_full_name(rirq.controller));
>> - continue;
>> - }
>> -
>> - res = platform_get_irq(dev, i);
>> - if ( res < 0 )
>> - {
>> - printk(XENLOG_ERR "Unable to get irq %u for %s\n",
>> - i, dt_node_full_name(dev));
>> - return res;
>> - }
>> -
>> - res = map_irq_to_domain(d, res, need_mapping, dt_node_name(dev));
>> - if ( res )
>> - return res;
>> - }
>> -
>> - return 0;
>> -}
>> -
>> /*
>> * For a given device node:
>> * - Give permission to the guest to manage IRQ and MMIO range
>> diff --git a/xen/arch/arm/include/asm/setup.h b/xen/arch/arm/include/asm/setup.h
>> index fdbf68aadc..ec050848aa 100644
>> --- a/xen/arch/arm/include/asm/setup.h
>> +++ b/xen/arch/arm/include/asm/setup.h
>> @@ -163,6 +163,9 @@ void device_tree_get_reg(const __be32 **cell, u32 address_cells,
>> u32 device_tree_get_u32(const void *fdt, int node,
>> const char *prop_name, u32 dflt);
>>
>> +int handle_device_interrupts(struct domain *d, struct dt_device_node *dev,
>> + bool need_mapping);
>> +
>> int map_range_to_domain(const struct dt_device_node *dev,
>> u64 addr, u64 len, void *data);
>>
>> diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c
>> index 6c9712ab7b..6518eff9b0 100644
>> --- a/xen/common/device_tree.c
>> +++ b/xen/common/device_tree.c
>> @@ -1811,12 +1811,12 @@ int dt_count_phandle_with_args(const struct dt_device_node *np,
>> * @allnextpp: pointer to ->allnext from last allocated device_node
>> * @fpsize: Size of the node path up at the current depth.
>> */
>> -static unsigned long __init unflatten_dt_node(const void *fdt,
>> - unsigned long mem,
>> - unsigned long *p,
>> - struct dt_device_node *dad,
>> - struct dt_device_node ***allnextpp,
>> - unsigned long fpsize)
>> +static unsigned long unflatten_dt_node(const void *fdt,
>> + unsigned long mem,
>> + unsigned long *p,
>> + struct dt_device_node *dad,
>> + struct dt_device_node ***allnextpp,
>> + unsigned long fpsize)
>> {
>> struct dt_device_node *np;
>> struct dt_property *pp, **prev_pp = NULL;
>> @@ -2047,7 +2047,7 @@ static unsigned long __init unflatten_dt_node(const void *fdt,
>> }
>>
>> /**
>> - * __unflatten_device_tree - create tree of device_nodes from flat blob
>> + * unflatten_device_tree - create tree of device_nodes from flat blob
>> *
>> * unflattens a device-tree, creating the
>> * tree of struct device_node. It also fills the "name" and "type"
>> @@ -2056,8 +2056,7 @@ static unsigned long __init unflatten_dt_node(const void *fdt,
>> * @fdt: The fdt to expand
>> * @mynodes: The device_node tree created by the call
>> */
>> -static void __init __unflatten_device_tree(const void *fdt,
>> - struct dt_device_node **mynodes)
>> +int unflatten_device_tree(const void *fdt, struct dt_device_node **mynodes)
>> {
>> unsigned long start, mem, size;
>> struct dt_device_node **allnextp = mynodes;
>> @@ -2079,6 +2078,12 @@ static void __init __unflatten_device_tree(const void *fdt,
>> /* Allocate memory for the expanded device tree */
>> mem = (unsigned long)_xmalloc (size + 4, __alignof__(struct dt_device_node));
>>
>> + if ( mem == 0 )
> NIT: !mem would be preffered.
>
>> + {
>> + printk(XENLOG_ERR "Cannot allocate memory for unflatten device tree\n");
>> + return -ENOMEM;
> What is the point of modifying the function to return a value if ...
>> + }
>> +
>> ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef);
>>
>> dt_dprintk(" unflattening %lx...\n", mem);
>> @@ -2095,6 +2100,8 @@ static void __init __unflatten_device_tree(const void *fdt,
>> *allnextp = NULL;
>>
>> dt_dprintk(" <- unflatten_device_tree()\n");
>> +
>> + return 0;
>> }
>>
>> static void dt_alias_add(struct dt_alias_prop *ap,
>> @@ -2179,7 +2186,7 @@ dt_find_interrupt_controller(const struct dt_device_match *matches)
>>
>> void __init dt_unflatten_host_device_tree(void)
>> {
>> - __unflatten_device_tree(device_tree_flattened, &dt_host);
>> + unflatten_device_tree(device_tree_flattened, &dt_host);
> ... you do not check it anyway?
So, here we kept it same but unflatten_device_tree() return is checked
in dt_overlay.c. Return of this function will be useful in dt_overlay as
we can run out of mem during runtime. Perhaps I will add another comment
about the reasoning for adding this.
>
>> dt_alias_scan();
>> }
>>
>> diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h
>> index a28937d12a..bde46d7120 100644
>> --- a/xen/include/xen/device_tree.h
>> +++ b/xen/include/xen/device_tree.h
>> @@ -181,6 +181,11 @@ int device_tree_for_each_node(const void *fdt, int node,
>> */
>> void dt_unflatten_host_device_tree(void);
>>
>> +/**
>> + * unflatten any device tree.
>> + */
>> +int unflatten_device_tree(const void *fdt, struct dt_device_node **mynodes);
>> +
>> /**
>> * IRQ translation callback
>> * TODO: For the moment we assume that we only have ONE
>> --
>> 2.17.1
>>
>>
> ~Michal
>
On 09/02/2023 20:11, Vikram Garhwal wrote:
> Hi Michal,
>
> Thanks for reviewing this. Please see my comments below.
>
> On 1/20/23 4:39 AM, Michal Orzel wrote:
>> Hi Vikram,
>>
>> On 07/12/2022 07:15, Vikram Garhwal wrote:
>>>
>>> Change function type of following function to access during runtime:
>>> 1. map_irq_to_domain()
>>> 2. handle_device_interrupt()
>>> 3. map_range_to_domain()
>>> 4. unflatten_dt_node()
>>> 5. unflatten_device_tree()
>> If you do not want to do this at first use, then this should be a
>> separate patch.
>>> Move map_irq_to_domain(), handle_device_interrupt() and
>>> map_range_to_domain() to
>>> device.c.
>> This should be a separate patch (without removing __init) to make the
>> comparison easier.
> Okay
>>
>>> unflatten_device_tree(): Add handling of memory allocation failure.
>> Apart from that you also renamed __unflatten_device_tree to
>> unflatten_device_tree
>> and you did not mention it.
> I will add this in commit.
>>
>>> These changes are done to support the dynamic programming of a nodes
>>> where an
>>> overlay node will be added to fdt and unflattened node will be added
>>> to dt_host.
>>> Furthermore, IRQ and mmio mapping will be done for the added node.
>>>
>>> Signed-off-by: Vikram Garhwal <vikram.garhwal@amd.com>
>>> ---
>>> xen/arch/arm/device.c | 145 +++++++++++++++++++++++++++++++
>>> xen/arch/arm/domain_build.c | 142 ------------------------------
>>> xen/arch/arm/include/asm/setup.h | 3 +
>>> xen/common/device_tree.c | 27 +++---
>>> xen/include/xen/device_tree.h | 5 ++
>>> 5 files changed, 170 insertions(+), 152 deletions(-)
>>>
>>> diff --git a/xen/arch/arm/device.c b/xen/arch/arm/device.c
>>> index 70cd6c1a19..d299c04e62 100644
>>> --- a/xen/arch/arm/device.c
>>> +++ b/xen/arch/arm/device.c
>>> @@ -21,6 +21,9 @@
>>> #include <xen/errno.h>
>>> #include <xen/init.h>
>>> #include <xen/lib.h>
>>> +#include <xen/iocap.h>
>>> +#include <asm/domain_build.h>
>>> +#include <asm/setup.h>
>>>
>>> extern const struct device_desc _sdevice[], _edevice[];
>>> extern const struct acpi_device_desc _asdevice[], _aedevice[];
>>> @@ -84,6 +87,148 @@ enum device_class device_get_class(const struct
>>> dt_device_node *dev)
>>> return DEVICE_UNKNOWN;
>>> }
>>>
>>> +int map_irq_to_domain(struct domain *d, unsigned int irq,
>>> + bool need_mapping, const char *devname)
>>> +{
>>> + int res;
>>> +
>>> + res = irq_permit_access(d, irq);
>>> + if ( res )
>>> + {
>>> + printk(XENLOG_ERR "Unable to permit to dom%u access to IRQ
>>> %u\n",
>>> + d->domain_id, irq);
>>> + return res;
>>> + }
>>> +
>>> + if ( need_mapping )
>>> + {
>>> + /*
>>> + * Checking the return of vgic_reserve_virq is not
>>> + * necessary. It should not fail except when we try to map
>>> + * the IRQ twice. This can legitimately happen if the IRQ is
>>> shared
>>> + */
>>> + vgic_reserve_virq(d, irq);
>>> +
>>> + res = route_irq_to_guest(d, irq, irq, devname);
>>> + if ( res < 0 )
>>> + {
>>> + printk(XENLOG_ERR "Unable to map IRQ%"PRId32" to dom%d\n",
>>> + irq, d->domain_id);
>>> + return res;
>>> + }
>>> + }
>>> +
>>> + dt_dprintk(" - IRQ: %u\n", irq);
>>> + return 0;
>>> +}
>>> +
>>> +int map_range_to_domain(const struct dt_device_node *dev,
>>> + u64 addr, u64 len, void *data)
>>> +{
>>> + struct map_range_data *mr_data = data;
>>> + struct domain *d = mr_data->d;
>>> + int res;
>>> +
>>> + /*
>>> + * reserved-memory regions are RAM carved out for a special
>>> purpose.
>>> + * They are not MMIO and therefore a domain should not be able to
>>> + * manage them via the IOMEM interface.
>>> + */
>>> + if ( strncasecmp(dt_node_full_name(dev), "/reserved-memory/",
>>> + strlen("/reserved-memory/")) != 0 )
>>> + {
>>> + res = iomem_permit_access(d, paddr_to_pfn(addr),
>>> + paddr_to_pfn(PAGE_ALIGN(addr + len - 1)));
>>> + if ( res )
>>> + {
>>> + printk(XENLOG_ERR "Unable to permit to dom%d access to"
>>> + " 0x%"PRIx64" - 0x%"PRIx64"\n",
>>> + d->domain_id,
>>> + addr & PAGE_MASK, PAGE_ALIGN(addr + len) - 1);
>>> + return res;
>>> + }
>>> + }
>>> +
>>> + if ( !mr_data->skip_mapping )
>>> + {
>>> + res = map_regions_p2mt(d,
>>> + gaddr_to_gfn(addr),
>>> + PFN_UP(len),
>>> + maddr_to_mfn(addr),
>>> + mr_data->p2mt);
>>> +
>>> + if ( res < 0 )
>>> + {
>>> + printk(XENLOG_ERR "Unable to map 0x%"PRIx64
>>> + " - 0x%"PRIx64" in domain %d\n",
>>> + addr & PAGE_MASK, PAGE_ALIGN(addr + len) - 1,
>>> + d->domain_id);
>>> + return res;
>>> + }
>>> + }
>>> +
>>> + dt_dprintk(" - MMIO: %010"PRIx64" - %010"PRIx64" P2MType=%x\n",
>>> + addr, addr + len, mr_data->p2mt);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +/*
>>> + * handle_device_interrupts retrieves the interrupts configuration from
>>> + * a device tree node and maps those interrupts to the target domain.
>>> + *
>>> + * Returns:
>>> + * < 0 error
>>> + * 0 success
>>> + */
>>> +int handle_device_interrupts(struct domain *d,
>>> + struct dt_device_node *dev,
>>> + bool need_mapping)
>>> +{
>>> + unsigned int i, nirq;
>>> + int res;
>>> + struct dt_raw_irq rirq;
>>> +
>>> + nirq = dt_number_of_irq(dev);
>>> +
>>> + /* Give permission and map IRQs */
>>> + for ( i = 0; i < nirq; i++ )
>>> + {
>>> + res = dt_device_get_raw_irq(dev, i, &rirq);
>>> + if ( res )
>>> + {
>>> + printk(XENLOG_ERR "Unable to retrieve irq %u for %s\n",
>>> + i, dt_node_full_name(dev));
>>> + return res;
>>> + }
>>> +
>>> + /*
>>> + * Don't map IRQ that have no physical meaning
>>> + * ie: IRQ whose controller is not the GIC
>>> + */
>>> + if ( rirq.controller != dt_interrupt_controller )
>>> + {
>>> + dt_dprintk("irq %u not connected to primary controller.
>>> Connected to %s\n",
>>> + i, dt_node_full_name(rirq.controller));
>>> + continue;
>>> + }
>>> +
>>> + res = platform_get_irq(dev, i);
>>> + if ( res < 0 )
>>> + {
>>> + printk(XENLOG_ERR "Unable to get irq %u for %s\n",
>>> + i, dt_node_full_name(dev));
>>> + return res;
>>> + }
>>> +
>>> + res = map_irq_to_domain(d, res, need_mapping,
>>> dt_node_name(dev));
>>> + if ( res )
>>> + return res;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> /*
>>> * Local variables:
>>> * mode: C
>>> diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
>>> index 4fb5c20b13..acde8e714e 100644
>>> --- a/xen/arch/arm/domain_build.c
>>> +++ b/xen/arch/arm/domain_build.c
>>> @@ -2229,41 +2229,6 @@ int __init make_chosen_node(const struct
>>> kernel_info *kinfo)
>>> return res;
>>> }
>>>
>>> -int __init map_irq_to_domain(struct domain *d, unsigned int irq,
>>> - bool need_mapping, const char *devname)
>>> -{
>>> - int res;
>>> -
>>> - res = irq_permit_access(d, irq);
>>> - if ( res )
>>> - {
>>> - printk(XENLOG_ERR "Unable to permit to dom%u access to IRQ
>>> %u\n",
>>> - d->domain_id, irq);
>>> - return res;
>>> - }
>>> -
>>> - if ( need_mapping )
>>> - {
>>> - /*
>>> - * Checking the return of vgic_reserve_virq is not
>>> - * necessary. It should not fail except when we try to map
>>> - * the IRQ twice. This can legitimately happen if the IRQ is
>>> shared
>>> - */
>>> - vgic_reserve_virq(d, irq);
>>> -
>>> - res = route_irq_to_guest(d, irq, irq, devname);
>>> - if ( res < 0 )
>>> - {
>>> - printk(XENLOG_ERR "Unable to map IRQ%"PRId32" to dom%d\n",
>>> - irq, d->domain_id);
>>> - return res;
>>> - }
>>> - }
>>> -
>>> - dt_dprintk(" - IRQ: %u\n", irq);
>>> - return 0;
>>> -}
>> If you move map_irq_to_domain from domain_build.c to device.c, then
>> the prototype needs to also
>> be moved from domain_build.h to setup.h
>>
>>> -
>>> static int __init map_dt_irq_to_domain(const struct dt_device_node
>>> *dev,
>>> const struct dt_irq *dt_irq,
>>> void *data)
>>> @@ -2295,57 +2260,6 @@ static int __init map_dt_irq_to_domain(const
>>> struct dt_device_node *dev,
>>> return 0;
>>> }
>>>
>>> -int __init map_range_to_domain(const struct dt_device_node *dev,
>>> - u64 addr, u64 len, void *data)
>>> -{
>>> - struct map_range_data *mr_data = data;
>>> - struct domain *d = mr_data->d;
>>> - int res;
>>> -
>>> - /*
>>> - * reserved-memory regions are RAM carved out for a special
>>> purpose.
>>> - * They are not MMIO and therefore a domain should not be able to
>>> - * manage them via the IOMEM interface.
>>> - */
>>> - if ( strncasecmp(dt_node_full_name(dev), "/reserved-memory/",
>>> - strlen("/reserved-memory/")) != 0 )
>>> - {
>>> - res = iomem_permit_access(d, paddr_to_pfn(addr),
>>> - paddr_to_pfn(PAGE_ALIGN(addr + len - 1)));
>>> - if ( res )
>>> - {
>>> - printk(XENLOG_ERR "Unable to permit to dom%d access to"
>>> - " 0x%"PRIx64" - 0x%"PRIx64"\n",
>>> - d->domain_id,
>>> - addr & PAGE_MASK, PAGE_ALIGN(addr + len) - 1);
>>> - return res;
>>> - }
>>> - }
>>> -
>>> - if ( !mr_data->skip_mapping )
>>> - {
>>> - res = map_regions_p2mt(d,
>>> - gaddr_to_gfn(addr),
>>> - PFN_UP(len),
>>> - maddr_to_mfn(addr),
>>> - mr_data->p2mt);
>>> -
>>> - if ( res < 0 )
>>> - {
>>> - printk(XENLOG_ERR "Unable to map 0x%"PRIx64
>>> - " - 0x%"PRIx64" in domain %d\n",
>>> - addr & PAGE_MASK, PAGE_ALIGN(addr + len) - 1,
>>> - d->domain_id);
>>> - return res;
>>> - }
>>> - }
>>> -
>>> - dt_dprintk(" - MMIO: %010"PRIx64" - %010"PRIx64" P2MType=%x\n",
>>> - addr, addr + len, mr_data->p2mt);
>>> -
>>> - return 0;
>>> -}
>>> -
>>> /*
>>> * For a node which describes a discoverable bus (such as a PCI bus)
>>> * then we may need to perform additional mappings in order to make
>>> @@ -2373,62 +2287,6 @@ static int __init map_device_children(const
>>> struct dt_device_node *dev,
>>> return 0;
>>> }
>>>
>>> -/*
>>> - * handle_device_interrupts retrieves the interrupts configuration from
>>> - * a device tree node and maps those interrupts to the target domain.
>>> - *
>>> - * Returns:
>>> - * < 0 error
>>> - * 0 success
>>> - */
>>> -static int __init handle_device_interrupts(struct domain *d,
>>> - struct dt_device_node *dev,
>>> - bool need_mapping)
>>> -{
>>> - unsigned int i, nirq;
>>> - int res;
>>> - struct dt_raw_irq rirq;
>>> -
>>> - nirq = dt_number_of_irq(dev);
>>> -
>>> - /* Give permission and map IRQs */
>>> - for ( i = 0; i < nirq; i++ )
>>> - {
>>> - res = dt_device_get_raw_irq(dev, i, &rirq);
>>> - if ( res )
>>> - {
>>> - printk(XENLOG_ERR "Unable to retrieve irq %u for %s\n",
>>> - i, dt_node_full_name(dev));
>>> - return res;
>>> - }
>>> -
>>> - /*
>>> - * Don't map IRQ that have no physical meaning
>>> - * ie: IRQ whose controller is not the GIC
>>> - */
>>> - if ( rirq.controller != dt_interrupt_controller )
>>> - {
>>> - dt_dprintk("irq %u not connected to primary controller.
>>> Connected to %s\n",
>>> - i, dt_node_full_name(rirq.controller));
>>> - continue;
>>> - }
>>> -
>>> - res = platform_get_irq(dev, i);
>>> - if ( res < 0 )
>>> - {
>>> - printk(XENLOG_ERR "Unable to get irq %u for %s\n",
>>> - i, dt_node_full_name(dev));
>>> - return res;
>>> - }
>>> -
>>> - res = map_irq_to_domain(d, res, need_mapping,
>>> dt_node_name(dev));
>>> - if ( res )
>>> - return res;
>>> - }
>>> -
>>> - return 0;
>>> -}
>>> -
>>> /*
>>> * For a given device node:
>>> * - Give permission to the guest to manage IRQ and MMIO range
>>> diff --git a/xen/arch/arm/include/asm/setup.h
>>> b/xen/arch/arm/include/asm/setup.h
>>> index fdbf68aadc..ec050848aa 100644
>>> --- a/xen/arch/arm/include/asm/setup.h
>>> +++ b/xen/arch/arm/include/asm/setup.h
>>> @@ -163,6 +163,9 @@ void device_tree_get_reg(const __be32 **cell, u32
>>> address_cells,
>>> u32 device_tree_get_u32(const void *fdt, int node,
>>> const char *prop_name, u32 dflt);
>>>
>>> +int handle_device_interrupts(struct domain *d, struct dt_device_node
>>> *dev,
>>> + bool need_mapping);
>>> +
>>> int map_range_to_domain(const struct dt_device_node *dev,
>>> u64 addr, u64 len, void *data);
>>>
>>> diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c
>>> index 6c9712ab7b..6518eff9b0 100644
>>> --- a/xen/common/device_tree.c
>>> +++ b/xen/common/device_tree.c
>>> @@ -1811,12 +1811,12 @@ int dt_count_phandle_with_args(const struct
>>> dt_device_node *np,
>>> * @allnextpp: pointer to ->allnext from last allocated device_node
>>> * @fpsize: Size of the node path up at the current depth.
>>> */
>>> -static unsigned long __init unflatten_dt_node(const void *fdt,
>>> - unsigned long mem,
>>> - unsigned long *p,
>>> - struct dt_device_node
>>> *dad,
>>> - struct dt_device_node
>>> ***allnextpp,
>>> - unsigned long fpsize)
>>> +static unsigned long unflatten_dt_node(const void *fdt,
>>> + unsigned long mem,
>>> + unsigned long *p,
>>> + struct dt_device_node *dad,
>>> + struct dt_device_node
>>> ***allnextpp,
>>> + unsigned long fpsize)
>>> {
>>> struct dt_device_node *np;
>>> struct dt_property *pp, **prev_pp = NULL;
>>> @@ -2047,7 +2047,7 @@ static unsigned long __init
>>> unflatten_dt_node(const void *fdt,
>>> }
>>>
>>> /**
>>> - * __unflatten_device_tree - create tree of device_nodes from flat blob
>>> + * unflatten_device_tree - create tree of device_nodes from flat blob
>>> *
>>> * unflattens a device-tree, creating the
>>> * tree of struct device_node. It also fills the "name" and "type"
>>> @@ -2056,8 +2056,7 @@ static unsigned long __init
>>> unflatten_dt_node(const void *fdt,
>>> * @fdt: The fdt to expand
>>> * @mynodes: The device_node tree created by the call
>>> */
>>> -static void __init __unflatten_device_tree(const void *fdt,
>>> - struct dt_device_node
>>> **mynodes)
>>> +int unflatten_device_tree(const void *fdt, struct dt_device_node
>>> **mynodes)
>>> {
>>> unsigned long start, mem, size;
>>> struct dt_device_node **allnextp = mynodes;
>>> @@ -2079,6 +2078,12 @@ static void __init
>>> __unflatten_device_tree(const void *fdt,
>>> /* Allocate memory for the expanded device tree */
>>> mem = (unsigned long)_xmalloc (size + 4, __alignof__(struct
>>> dt_device_node));
>>>
>>> + if ( mem == 0 )
>> NIT: !mem would be preffered.
>>
>>> + {
>>> + printk(XENLOG_ERR "Cannot allocate memory for unflatten
>>> device tree\n");
>>> + return -ENOMEM;
>> What is the point of modifying the function to return a value if ...
>>> + }
>>> +
>>> ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef);
>>>
>>> dt_dprintk(" unflattening %lx...\n", mem);
>>> @@ -2095,6 +2100,8 @@ static void __init
>>> __unflatten_device_tree(const void *fdt,
>>> *allnextp = NULL;
>>>
>>> dt_dprintk(" <- unflatten_device_tree()\n");
>>> +
>>> + return 0;
>>> }
>>>
>>> static void dt_alias_add(struct dt_alias_prop *ap,
>>> @@ -2179,7 +2186,7 @@ dt_find_interrupt_controller(const struct
>>> dt_device_match *matches)
>>>
>>> void __init dt_unflatten_host_device_tree(void)
>>> {
>>> - __unflatten_device_tree(device_tree_flattened, &dt_host);
>>> + unflatten_device_tree(device_tree_flattened, &dt_host);
>> ... you do not check it anyway?
> So, here we kept it same but unflatten_device_tree() return is checked
> in dt_overlay.c. Return of this function will be useful in dt_overlay as
> we can run out of mem during runtime. Perhaps I will add another comment
> about the reasoning for adding this.
It is a good practice to check the error return. In this case, I would
call panic() when there is an error.
With that said, the hardening of __unflatten_device_tree() deserve its
own patch.
Cheers,
--
Julien Grall
© 2016 - 2026 Red Hat, Inc.