[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Minios-devel] [UNIKRAFT PATCHv4 5/9] plat/common: Add fdt address translation support



The ranges property provides a means of defining a mapping or
translation between the address space of the bus (the child
address space) and the address space of the bus node's parent
(the parent address space).

Currently only 1:1 mapping between parent and child address is
supported.

Signed-off-by: Jia He <justin.he@xxxxxxx>
---
 plat/common/fdt.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 136 insertions(+)

diff --git a/plat/common/fdt.c b/plat/common/fdt.c
index 6feec0a..1246dec 100644
--- a/plat/common/fdt.c
+++ b/plat/common/fdt.c
@@ -40,6 +40,11 @@
 #include "libfdt_internal.h"
 #include "uk/print.h"
 
+#define FDT_BAD_ADDR (uint64_t)(-1)
+#define FDT_MAX_ADDR_CELLS FDT_MAX_NCELLS
+#define FDT_CHECK_COUNTS(na, ns)  ((na) > 0 && (na) <= FDT_MAX_ADDR_CELLS && \
+                       (ns) > 0)
+
 int fdt_getprop_u32_by_offset(const void *fdt, int offset,
                const char *name, uint32_t *out)
 {
@@ -156,3 +161,134 @@ int fdt_size_cells_or_parent(const void *fdt, int 
nodeoffset)
 
        return -FDT_ERR_NOTFOUND;
 }
+
+static uint64_t fdt_reg_read_number(const fdt32_t *regs, uint32_t size)
+{
+       uint64_t number = 0;
+
+       if (size >= 3 || size <= 0)
+               return -FDT_ERR_BADNCELLS;
+
+       for (uint32_t i = 0; i < size; i++) {
+               number <<= 32;
+               number |= fdt32_to_cpu(*regs);
+               regs++;
+       }
+
+       return number;
+}
+
+/* Default translator (generic bus) */
+static void fdt_default_count_cells(const void *fdt, int parentoffset,
+                                              int *addrc, int *sizec)
+{
+       if (addrc)
+               *addrc = fdt_address_cells(fdt, parentoffset);
+
+       if (sizec)
+               *sizec = fdt_size_cells(fdt, parentoffset);
+}
+
+static int fdt_default_translate(fdt32_t *addr, uint64_t offset, int na)
+{
+       uint64_t a = fdt_reg_read_number(addr, na);
+
+       memset(addr, 0, na * 4);
+       a += offset;
+       if (na > 1)
+               addr[na - 2] = cpu_to_fdt32(a >> 32);
+       addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu);
+
+       return 0;
+}
+
+static int fdt_translate_one(const void *fdt, int parent, fdt32_t *addr,
+                                   int na, int pna, const char *rprop)
+{
+       const fdt32_t *ranges;
+       int rlen;
+       uint64_t offset = FDT_BAD_ADDR;
+
+       ranges = fdt_getprop(fdt, parent, rprop, &rlen);
+       if (!ranges)
+               return 1;
+       if (rlen == 0) {
+               offset = fdt_reg_read_number(addr, na);
+               memset(addr, 0, pna * 4);
+               uk_pr_debug("empty ranges, 1:1 translation\n");
+               goto finish;
+       }
+
+       uk_pr_err("Error, only 1:1 translation is supported...\n");
+       return 1;
+ finish:
+       uk_pr_debug("with offset: 0x%lx\n", offset);
+
+       /* Translate it into parent bus space */
+       return fdt_default_translate(addr, offset, pna);
+}
+
+/*
+ * Translate an address from the device-tree into a CPU physical address,
+ * this walks up the tree and applies the various bus mappings on the
+ * way.
+ */
+static uint64_t fdt_translate_address_by_ranges(const void *fdt,
+                                       int node_offset, const fdt32_t *regs)
+{
+       int parent;
+       fdt32_t addr[FDT_MAX_ADDR_CELLS];
+       int na, ns, pna, pns;
+       uint64_t result = FDT_BAD_ADDR;
+
+       /* Get parent */
+       parent = fdt_parent_offset(fdt, node_offset);
+       if (parent < 0)
+               goto bail;
+
+       /* Count address cells & copy address locally */
+       fdt_default_count_cells(fdt, parent, &na, &ns);
+       if (!FDT_CHECK_COUNTS(na, ns)) {
+               uk_pr_err("Bad cell count for %s\n",
+                      fdt_get_name(fdt, node_offset, NULL));
+               goto bail;
+       }
+       memcpy(addr, regs, na * 4);
+
+       /* Translate */
+       for (;;) {
+               /* Switch to parent bus */
+               node_offset = parent;
+               parent = fdt_parent_offset(fdt, node_offset);
+
+               /* If root, we have finished */
+               if (parent < 0) {
+                       uk_pr_debug("reached root node\n");
+                       result = fdt_reg_read_number(addr, na);
+                       break;
+               }
+
+               /* Get new parent bus and counts */
+               fdt_default_count_cells(fdt, parent, &pna, &pns);
+               if (!FDT_CHECK_COUNTS(pna, pns)) {
+                       uk_pr_err("Bad cell count for %s\n",
+                               fdt_get_name(fdt, node_offset, NULL));
+                       break;
+               }
+
+               uk_pr_debug("parent bus (na=%d, ns=%d) on %s\n",
+                        pna, pns, fdt_get_name(fdt, parent, NULL));
+
+               /* Apply bus translation */
+               if (fdt_translate_one(fdt, node_offset,
+                                       addr, na, pna, "ranges"))
+                       break;
+
+               /* Complete the move up one level */
+               na = pna;
+               ns = pns;
+       }
+bail:
+       return result;
+}
+
-- 
2.17.1


_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.