Hyperlaunch mandates either a reg or module-index DT prop on nodes that
contain `multiboot,module" under their "compatible" prop. This patch
introduces a helper to generically find such index, appending the module
to the list of modules if it wasn't already (i.e: because it's given via
the "reg" prop).

Signed-off-by: Jason Andryuk <jason.andr...@amd.com>
Signed-off-by: Alejandro Vallejo <agarc...@amd.com>
---
v3:
    * New on v3.
    * Subsumes much of the dup code between kernel/initrd patches.
    * Changes previous behaviour in v2 to look into "reg" and
      "module-index" props, rather than just index.
    * Use addr_cells/size_cells rather than size_size
---
 xen/arch/x86/domain-builder/fdt.c   | 142 ++++++++++++++++++++++++++++
 xen/arch/x86/domain-builder/fdt.h   |   2 +
 xen/include/xen/libfdt/libfdt-xen.h |  57 +++++++++++
 3 files changed, 201 insertions(+)

diff --git a/xen/arch/x86/domain-builder/fdt.c 
b/xen/arch/x86/domain-builder/fdt.c
index 4c5b7747f5..9ebc8fd0e4 100644
--- a/xen/arch/x86/domain-builder/fdt.c
+++ b/xen/arch/x86/domain-builder/fdt.c
@@ -13,6 +13,148 @@
 
 #include "fdt.h"
 
+/*
+ * Unpacks a "reg" property into its address and size constituents.
+ *
+ * @param prop          Pointer to an FDT "reg" property.
+ * @param address_cells Number of 4-octet cells that make up an "address".
+ * @param size_cells    Number of 4-octet cells that make up a "size".
+ * @param p_addr[out]   Address encoded in the property.
+ * @param p_size[out]   Size encoded in the property.
+ * @returns             -EINVAL on malformed property, 0 otherwise.
+ */
+static int __init read_fdt_prop_as_reg(const struct fdt_property *prop,
+                                       int address_cells, int size_cells,
+                                       uint64_t *p_addr, uint64_t *p_size)
+{
+    const fdt32_t *cell = (const fdt32_t *)prop->data;
+    uint64_t addr, size;
+
+    if ( fdt32_to_cpu(prop->len) !=
+         (address_cells + size_cells) * sizeof(*cell) )
+    {
+        printk("  Cannot read reg %lu+%lu from prop len %u\n",
+            address_cells * sizeof(*cell), size_cells * sizeof(*cell),
+            fdt32_to_cpu(prop->len));
+        return -EINVAL;
+    }
+
+    switch ( address_cells ) {
+    case 1:
+        addr = fdt32_to_cpu(*cell);
+        break;
+    case 2:
+        addr = fdt64_to_cpu(*(const fdt64_t *)cell);
+        break;
+    default:
+        printk("  unsupported sized address_cells\n");
+        return -EINVAL;
+    }
+
+    cell += address_cells;
+    switch ( size_cells ) {
+    case 1:
+        size = fdt32_to_cpu(*cell);
+        break;
+    case 2:
+        size = fdt64_to_cpu(*(const fdt64_t *)cell);
+        break;
+    default:
+        printk("  unsupported sized size_cells\n");
+        return -EINVAL;
+    }
+
+    *p_addr = addr;
+    *p_size = size;
+
+    return 0;
+}
+
+/*
+ * Locate a multiboot module given its node offset in the FDT.
+ *
+ * The module location may be given via either FDT property:
+ *     * reg = <address, size>
+ *         * Mutates `bi` to append the module.
+ *     * module-index = <idx>
+ *         * Leaves `bi` unchanged.
+ *
+ * @param fdt           Pointer to the full FDT.
+ * @param node          Offset for the module node.
+ * @param address_cells Number of 4-octet cells that make up an "address".
+ * @param size_cells    Number of 4-octet cells that make up a "size".
+ * @param bi[inout]     Xen's representation of the boot parameters.
+ * @return              -EINVAL on malformed nodes, otherwise
+ *                      index inside `bi->mods`
+ */
+int __init fdt_read_multiboot_module(const void *fdt, int node,
+                                     int address_cells, int size_cells,
+                                     struct boot_info *bi)
+{
+    const struct fdt_property *prop;
+    uint64_t addr, size;
+    int ret;
+    int idx;
+
+    ASSERT(!fdt_node_check_compatible(fdt, node, "multiboot,module"));
+
+    /* Location given as a `module-index` property. */
+    prop = fdt_get_property(fdt, node, "module-index", NULL);
+
+    if ( prop )
+    {
+        if ( fdt_get_property(fdt, node, "reg", NULL) )
+        {
+            printk("  Location of multiboot,module defined multiple times\n");
+            return -EINVAL;
+        }
+        return fdt_cell_as_u32((const fdt32_t *)prop->data);
+    }
+
+    /* Otherwise location given as a `reg` property. */
+    prop = fdt_get_property(fdt, node, "reg", NULL);
+
+    if ( !prop )
+    {
+        printk("  No location for multiboot,module\n");
+        return -EINVAL;
+    }
+    if ( fdt_get_property(fdt, node, "module-index", NULL) )
+    {
+        printk("  Location of multiboot,module defined multiple times\n");
+        return -EINVAL;
+    }
+
+    ret = read_fdt_prop_as_reg(prop, address_cells, size_cells, &addr, &size);
+
+    if ( ret < 0 )
+    {
+        printk("  Failed reading reg for multiboot,module\n");
+        return -EINVAL;
+    }
+
+    idx = bi->nr_modules + 1;
+    if ( idx > MAX_NR_BOOTMODS )
+    {
+        /*
+         * MAX_NR_BOOTMODS cannot exceed the max for MB1, represented by 
32bits,
+         * thus the cast down to a u32 will be safe due to the prior check.
+         */
+        BUILD_BUG_ON(MAX_NR_BOOTMODS >= (uint64_t)UINT32_MAX);
+        printk("  idx %d exceeds maximum boot modules\n", idx);
+        return -EINVAL;
+    }
+
+    /* Append new module to the existing list */
+
+    bi->nr_modules = idx;
+    bi->mods[idx].start = addr;
+    bi->mods[idx].size = size;
+    printk("  module[%d]: addr %lx size %lx\n", idx, addr, size);
+
+    return idx;
+}
+
 static int __init find_hyperlaunch_node(const void *fdt)
 {
     int hv_node = fdt_path_offset(fdt, "/chosen/hypervisor");
diff --git a/xen/arch/x86/domain-builder/fdt.h 
b/xen/arch/x86/domain-builder/fdt.h
index 1849656571..e8769dc51c 100644
--- a/xen/arch/x86/domain-builder/fdt.h
+++ b/xen/arch/x86/domain-builder/fdt.h
@@ -3,6 +3,8 @@
 #define __XEN_X86_FDT_H__
 
 #include <xen/init.h>
+#include <xen/libfdt/libfdt.h>
+#include <xen/libfdt/libfdt-xen.h>
 
 struct boot_info;
 
diff --git a/xen/include/xen/libfdt/libfdt-xen.h 
b/xen/include/xen/libfdt/libfdt-xen.h
index a5340bc9f4..2259c09a6a 100644
--- a/xen/include/xen/libfdt/libfdt-xen.h
+++ b/xen/include/xen/libfdt/libfdt-xen.h
@@ -13,6 +13,63 @@
 
 #include <xen/libfdt/libfdt.h>
 
+static inline int __init fdt_cell_as_u32(const fdt32_t *cell)
+{
+    return fdt32_to_cpu(*cell);
+}
+
+static inline uint64_t  __init fdt_cell_as_u64(const fdt32_t *cell)
+{
+    return ((uint64_t)fdt32_to_cpu(cell[0]) << 32) | fdt32_to_cpu(cell[1]);
+}
+
+/*
+ * Property: reg
+ *
+ * Defined in Section 2.3.6 of the Device Tree Specification is the "reg"
+ * standard property. The property is a prop-encoded-array that is encoded as
+ * an arbitrary number of (address, size) pairs.  We only extract a single
+ * pair since that is what is used in practice.
+ */
+static inline int __init fdt_get_reg_prop(
+    const void *fdt, int node, unsigned int addr_cells, unsigned int 
size_cells,
+    uint64_t *addr, uint64_t *size)
+{
+    int ret;
+    const struct fdt_property *prop;
+    fdt32_t *cell;
+
+    /* FDT spec max size is 4 (128bit int), but largest arch int size is 64 */
+    if ( size_cells > 2 || addr_cells > 2 )
+        return -EINVAL;
+
+    prop = fdt_get_property(fdt, node, "reg", &ret);
+    if ( !prop || ret < sizeof(u32) )
+        return ret < 0 ? ret : -EINVAL;
+
+    if ( fdt32_to_cpu(prop->len) !=
+        ((size_cells + addr_cells) * sizeof(*cell)) )
+        return -EINVAL;
+
+    cell = (fdt32_t *)prop->data;
+
+    /* read address field */
+    if ( addr_cells == 1 )
+        *addr = fdt_cell_as_u32(cell);
+    else
+        *addr = fdt_cell_as_u64(cell);
+
+    cell += addr_cells;
+
+    /* read size field */
+    if ( size_cells == 1 )
+        *size = fdt_cell_as_u32(cell);
+    else
+        *size = fdt_cell_as_u64(cell);
+
+    return 0;
+}
+
 static inline int fdt_get_mem_rsv_paddr(const void *fdt, int n,
                                         paddr_t *address,
                                         paddr_t *size)
-- 
2.43.0


Reply via email to