aboutsummaryrefslogtreecommitdiffstats
path: root/roms/opensbi/lib/utils/fdt
diff options
context:
space:
mode:
Diffstat (limited to 'roms/opensbi/lib/utils/fdt')
-rw-r--r--roms/opensbi/lib/utils/fdt/fdt_domain.c455
-rw-r--r--roms/opensbi/lib/utils/fdt/fdt_fixup.c265
-rw-r--r--roms/opensbi/lib/utils/fdt/fdt_helper.c465
-rw-r--r--roms/opensbi/lib/utils/fdt/objects.mk9
4 files changed, 1194 insertions, 0 deletions
diff --git a/roms/opensbi/lib/utils/fdt/fdt_domain.c b/roms/opensbi/lib/utils/fdt/fdt_domain.c
new file mode 100644
index 000000000..09615e53e
--- /dev/null
+++ b/roms/opensbi/lib/utils/fdt/fdt_domain.c
@@ -0,0 +1,455 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * fdt_domain.c - Flat Device Tree Domain helper routines
+ *
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ */
+
+#include <libfdt.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_hartmask.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi_utils/fdt/fdt_domain.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+
+int fdt_iterate_each_domain(void *fdt, void *opaque,
+ int (*fn)(void *fdt, int domain_offset,
+ void *opaque))
+{
+ int rc, doffset, poffset;
+
+ if (!fdt || !fn)
+ return SBI_EINVAL;
+
+ poffset = fdt_path_offset(fdt, "/chosen");
+ if (poffset < 0)
+ return 0;
+ poffset = fdt_node_offset_by_compatible(fdt, poffset,
+ "opensbi,domain,config");
+ if (poffset < 0)
+ return 0;
+
+ fdt_for_each_subnode(doffset, fdt, poffset) {
+ if (fdt_node_check_compatible(fdt, doffset,
+ "opensbi,domain,instance"))
+ continue;
+
+ rc = fn(fdt, doffset, opaque);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+int fdt_iterate_each_memregion(void *fdt, int domain_offset, void *opaque,
+ int (*fn)(void *fdt, int domain_offset,
+ int region_offset, u32 region_access,
+ void *opaque))
+{
+ u32 i, rcount;
+ int rc, len, region_offset;
+ const u32 *regions;
+
+ if (!fdt || (domain_offset < 0) || !fn)
+ return SBI_EINVAL;
+
+ if (fdt_node_check_compatible(fdt, domain_offset,
+ "opensbi,domain,instance"))
+ return SBI_EINVAL;
+
+ regions = fdt_getprop(fdt, domain_offset, "regions", &len);
+ if (!regions)
+ return 0;
+
+ rcount = (u32)len / (sizeof(u32) * 2);
+ for (i = 0; i < rcount; i++) {
+ region_offset = fdt_node_offset_by_phandle(fdt,
+ fdt32_to_cpu(regions[2 * i]));
+ if (region_offset < 0)
+ return region_offset;
+
+ if (fdt_node_check_compatible(fdt, region_offset,
+ "opensbi,domain,memregion"))
+ return SBI_EINVAL;
+
+ rc = fn(fdt, domain_offset, region_offset,
+ fdt32_to_cpu(regions[(2 * i) + 1]), opaque);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+struct __fixup_find_domain_offset_info {
+ const char *name;
+ int *doffset;
+};
+
+static int __fixup_find_domain_offset(void *fdt, int doff, void *p)
+{
+ struct __fixup_find_domain_offset_info *fdo = p;
+
+ if (!sbi_strcmp(fdo->name, fdt_get_name(fdt, doff, NULL)))
+ *fdo->doffset = doff;
+
+ return 0;
+}
+
+#define DISABLE_DEVICES_MASK (SBI_DOMAIN_MEMREGION_READABLE | \
+ SBI_DOMAIN_MEMREGION_WRITEABLE | \
+ SBI_DOMAIN_MEMREGION_EXECUTABLE)
+
+static int __fixup_count_disable_devices(void *fdt, int doff, int roff,
+ u32 perm, void *p)
+{
+ int len;
+ u32 *dcount = p;
+
+ if (perm & DISABLE_DEVICES_MASK)
+ return 0;
+
+ len = 0;
+ if (fdt_getprop(fdt, roff, "devices", &len))
+ *dcount += len / sizeof(u32);
+
+ return 0;
+}
+
+static int __fixup_disable_devices(void *fdt, int doff, int roff,
+ u32 raccess, void *p)
+{
+ int i, len, coff;
+ const u32 *devices;
+
+ if (raccess & DISABLE_DEVICES_MASK)
+ return 0;
+
+ len = 0;
+ devices = fdt_getprop(fdt, roff, "devices", &len);
+ if (!devices)
+ return 0;
+ len = len / sizeof(u32);
+
+ for (i = 0; i < len; i++) {
+ coff = fdt_node_offset_by_phandle(fdt,
+ fdt32_to_cpu(devices[i]));
+ if (coff < 0)
+ return coff;
+
+ fdt_setprop_string(fdt, coff, "status", "disabled");
+ }
+
+ return 0;
+}
+
+void fdt_domain_fixup(void *fdt)
+{
+ u32 i, dcount;
+ int err, poffset, doffset;
+ struct sbi_domain *dom = sbi_domain_thishart_ptr();
+ struct __fixup_find_domain_offset_info fdo;
+
+ /* Remove the domain assignment DT property from CPU DT nodes */
+ poffset = fdt_path_offset(fdt, "/cpus");
+ if (poffset < 0)
+ return;
+ fdt_for_each_subnode(doffset, fdt, poffset) {
+ err = fdt_parse_hart_id(fdt, doffset, &i);
+ if (err)
+ continue;
+
+ fdt_nop_property(fdt, doffset, "opensbi-domain");
+ }
+
+ /* Skip device disable for root domain */
+ if (!dom->index)
+ goto skip_device_disable;
+
+ /* Find current domain DT node */
+ doffset = -1;
+ fdo.name = dom->name;
+ fdo.doffset = &doffset;
+ fdt_iterate_each_domain(fdt, &fdo, __fixup_find_domain_offset);
+ if (doffset < 0)
+ goto skip_device_disable;
+
+ /* Count current domain device DT nodes to be disabled */
+ dcount = 0;
+ fdt_iterate_each_memregion(fdt, doffset, &dcount,
+ __fixup_count_disable_devices);
+ if (!dcount)
+ goto skip_device_disable;
+
+ /* Expand FDT based on device DT nodes to be disabled */
+ err = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + dcount * 32);
+ if (err < 0)
+ return;
+
+ /* Again find current domain DT node */
+ doffset = -1;
+ fdo.name = dom->name;
+ fdo.doffset = &doffset;
+ fdt_iterate_each_domain(fdt, &fdo, __fixup_find_domain_offset);
+ if (doffset < 0)
+ goto skip_device_disable;
+
+ /* Disable device DT nodes for current domain */
+ fdt_iterate_each_memregion(fdt, doffset, NULL,
+ __fixup_disable_devices);
+skip_device_disable:
+
+ /* Remove the OpenSBI domain config DT node */
+ poffset = fdt_path_offset(fdt, "/chosen");
+ if (poffset < 0)
+ return;
+ poffset = fdt_node_offset_by_compatible(fdt, poffset,
+ "opensbi,domain,config");
+ if (poffset < 0)
+ return;
+ fdt_nop_node(fdt, poffset);
+}
+
+#define FDT_DOMAIN_MAX_COUNT 8
+#define FDT_DOMAIN_REGION_MAX_COUNT 16
+
+static u32 fdt_domains_count;
+static struct sbi_domain fdt_domains[FDT_DOMAIN_MAX_COUNT];
+static struct sbi_hartmask fdt_masks[FDT_DOMAIN_MAX_COUNT];
+static struct sbi_domain_memregion
+ fdt_regions[FDT_DOMAIN_MAX_COUNT][FDT_DOMAIN_REGION_MAX_COUNT + 2];
+
+static int __fdt_parse_region(void *fdt, int domain_offset,
+ int region_offset, u32 region_access,
+ void *opaque)
+{
+ int len;
+ u32 val32;
+ u64 val64;
+ const u32 *val;
+ u32 *region_count = opaque;
+ struct sbi_domain_memregion *region;
+
+ /* Find next region of the domain */
+ if (FDT_DOMAIN_REGION_MAX_COUNT <= *region_count)
+ return SBI_EINVAL;
+ region = &fdt_regions[fdt_domains_count][*region_count];
+
+ /* Read "base" DT property */
+ val = fdt_getprop(fdt, region_offset, "base", &len);
+ if (!val && len >= 8)
+ return SBI_EINVAL;
+ val64 = fdt32_to_cpu(val[0]);
+ val64 = (val64 << 32) | fdt32_to_cpu(val[1]);
+ region->base = val64;
+
+ /* Read "order" DT property */
+ val = fdt_getprop(fdt, region_offset, "order", &len);
+ if (!val && len >= 4)
+ return SBI_EINVAL;
+ val32 = fdt32_to_cpu(*val);
+ if (val32 < 3 || __riscv_xlen < val32)
+ return SBI_EINVAL;
+ region->order = val32;
+
+ /* Read "mmio" DT property */
+ region->flags = region_access & SBI_DOMAIN_MEMREGION_ACCESS_MASK;
+ if (fdt_get_property(fdt, region_offset, "mmio", NULL))
+ region->flags |= SBI_DOMAIN_MEMREGION_MMIO;
+
+ (*region_count)++;
+
+ return 0;
+}
+
+static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
+{
+ u32 val32;
+ u64 val64;
+ const u32 *val;
+ struct sbi_domain *dom;
+ struct sbi_hartmask *mask;
+ struct sbi_hartmask assign_mask;
+ int *cold_domain_offset = opaque;
+ struct sbi_domain_memregion *regions;
+ int i, err, len, cpus_offset, cpu_offset, doffset;
+
+ /* Sanity check on maximum domains we can handle */
+ if (FDT_DOMAIN_MAX_COUNT <= fdt_domains_count)
+ return SBI_EINVAL;
+ dom = &fdt_domains[fdt_domains_count];
+ mask = &fdt_masks[fdt_domains_count];
+ regions = &fdt_regions[fdt_domains_count][0];
+
+ /* Read DT node name */
+ sbi_strncpy(dom->name, fdt_get_name(fdt, domain_offset, NULL),
+ sizeof(dom->name));
+ dom->name[sizeof(dom->name) - 1] = '\0';
+
+ /* Setup possible HARTs mask */
+ SBI_HARTMASK_INIT(mask);
+ dom->possible_harts = mask;
+ val = fdt_getprop(fdt, domain_offset, "possible-harts", &len);
+ len = len / sizeof(u32);
+ if (val && len) {
+ for (i = 0; i < len; i++) {
+ cpu_offset = fdt_node_offset_by_phandle(fdt,
+ fdt32_to_cpu(val[i]));
+ if (cpu_offset < 0)
+ return cpu_offset;
+
+ err = fdt_parse_hart_id(fdt, cpu_offset, &val32);
+ if (err)
+ return err;
+
+ sbi_hartmask_set_hart(val32, mask);
+ }
+ }
+
+ /* Setup memregions from DT */
+ val32 = 0;
+ sbi_memset(regions, 0,
+ sizeof(*regions) * (FDT_DOMAIN_REGION_MAX_COUNT + 2));
+ dom->regions = regions;
+ err = fdt_iterate_each_memregion(fdt, domain_offset, &val32,
+ __fdt_parse_region);
+ if (err)
+ return err;
+ sbi_domain_memregion_initfw(&regions[val32]);
+
+ /* Read "boot-hart" DT property */
+ val32 = -1U;
+ val = fdt_getprop(fdt, domain_offset, "boot-hart", &len);
+ if (val && len >= 4) {
+ cpu_offset = fdt_node_offset_by_phandle(fdt,
+ fdt32_to_cpu(*val));
+ if (cpu_offset >= 0)
+ fdt_parse_hart_id(fdt, cpu_offset, &val32);
+ } else {
+ if (domain_offset == *cold_domain_offset)
+ val32 = current_hartid();
+ }
+ dom->boot_hartid = val32;
+
+ /* Read "next-arg1" DT property */
+ val64 = 0;
+ val = fdt_getprop(fdt, domain_offset, "next-arg1", &len);
+ if (val && len >= 8) {
+ val64 = fdt32_to_cpu(val[0]);
+ val64 = (val64 << 32) | fdt32_to_cpu(val[1]);
+ } else {
+ if (domain_offset == *cold_domain_offset)
+ val64 = sbi_scratch_thishart_ptr()->next_arg1;
+ }
+ dom->next_arg1 = val64;
+
+ /* Read "next-addr" DT property */
+ val64 = 0;
+ val = fdt_getprop(fdt, domain_offset, "next-addr", &len);
+ if (val && len >= 8) {
+ val64 = fdt32_to_cpu(val[0]);
+ val64 = (val64 << 32) | fdt32_to_cpu(val[1]);
+ } else {
+ if (domain_offset == *cold_domain_offset)
+ val64 = sbi_scratch_thishart_ptr()->next_addr;
+ }
+ dom->next_addr = val64;
+
+ /* Read "next-mode" DT property */
+ val32 = 0x1;
+ val = fdt_getprop(fdt, domain_offset, "next-mode", &len);
+ if (val && len >= 4) {
+ val32 = fdt32_to_cpu(val[0]);
+ if (val32 != 0x0 && val32 != 0x1)
+ val32 = 0x1;
+ } else {
+ if (domain_offset == *cold_domain_offset)
+ val32 = sbi_scratch_thishart_ptr()->next_mode;
+ }
+ dom->next_mode = val32;
+
+ /* Read "system-reset-allowed" DT property */
+ if (fdt_get_property(fdt, domain_offset,
+ "system-reset-allowed", NULL))
+ dom->system_reset_allowed = TRUE;
+ else
+ dom->system_reset_allowed = FALSE;
+
+ /* Find /cpus DT node */
+ cpus_offset = fdt_path_offset(fdt, "/cpus");
+ if (cpus_offset < 0)
+ return cpus_offset;
+
+ /* HART to domain assignment mask based on CPU DT nodes */
+ sbi_hartmask_clear_all(&assign_mask);
+ fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) {
+ err = fdt_parse_hart_id(fdt, cpu_offset, &val32);
+ if (err)
+ continue;
+
+ if (SBI_HARTMASK_MAX_BITS <= val32)
+ continue;
+
+ val = fdt_getprop(fdt, cpu_offset, "opensbi-domain", &len);
+ if (!val || len < 4)
+ return SBI_EINVAL;
+
+ doffset = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*val));
+ if (doffset < 0)
+ return doffset;
+
+ if (doffset == domain_offset)
+ sbi_hartmask_set_hart(val32, &assign_mask);
+ }
+
+ /* Increment domains count */
+ fdt_domains_count++;
+
+ /* Register the domain */
+ return sbi_domain_register(dom, &assign_mask);
+}
+
+int fdt_domains_populate(void *fdt)
+{
+ const u32 *val;
+ int cold_domain_offset;
+ u32 hartid, cold_hartid;
+ int err, len, cpus_offset, cpu_offset;
+
+ /* Sanity checks */
+ if (!fdt)
+ return SBI_EINVAL;
+
+ /* Find /cpus DT node */
+ cpus_offset = fdt_path_offset(fdt, "/cpus");
+ if (cpus_offset < 0)
+ return cpus_offset;
+
+ /* Find coldboot HART domain DT node offset */
+ cold_domain_offset = -1;
+ cold_hartid = current_hartid();
+ fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) {
+ err = fdt_parse_hart_id(fdt, cpu_offset, &hartid);
+ if (err)
+ continue;
+
+ if (hartid != cold_hartid)
+ continue;
+
+ val = fdt_getprop(fdt, cpu_offset, "opensbi-domain", &len);
+ if (val && len >= 4)
+ cold_domain_offset = fdt_node_offset_by_phandle(fdt,
+ fdt32_to_cpu(*val));
+
+ break;
+ }
+
+ /* Iterate over each domain in FDT and populate details */
+ return fdt_iterate_each_domain(fdt, &cold_domain_offset,
+ __fdt_parse_domain);
+}
diff --git a/roms/opensbi/lib/utils/fdt/fdt_fixup.c b/roms/opensbi/lib/utils/fdt/fdt_fixup.c
new file mode 100644
index 000000000..eea450d80
--- /dev/null
+++ b/roms/opensbi/lib/utils/fdt/fdt_fixup.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * fdt_fixup.c - Flat Device Tree parsing helper routines
+ * Implement helper routines to parse FDT nodes on top of
+ * libfdt for OpenSBI usage
+ *
+ * Copyright (C) 2020 Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <libfdt.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_math.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_string.h>
+#include <sbi_utils/fdt/fdt_fixup.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+
+void fdt_cpu_fixup(void *fdt)
+{
+ struct sbi_domain *dom = sbi_domain_thishart_ptr();
+ int err, cpu_offset, cpus_offset, len;
+ const char *mmu_type;
+ u32 hartid;
+
+ err = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + 32);
+ if (err < 0)
+ return;
+
+ cpus_offset = fdt_path_offset(fdt, "/cpus");
+ if (cpus_offset < 0)
+ return;
+
+ fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) {
+ err = fdt_parse_hart_id(fdt, cpu_offset, &hartid);
+ if (err)
+ continue;
+
+ /*
+ * Disable a HART DT node if one of the following is true:
+ * 1. The HART is not assigned to the current domain
+ * 2. MMU is not available for the HART
+ */
+
+ mmu_type = fdt_getprop(fdt, cpu_offset, "mmu-type", &len);
+ if (!sbi_domain_is_assigned_hart(dom, hartid) ||
+ !mmu_type || !len)
+ fdt_setprop_string(fdt, cpu_offset, "status",
+ "disabled");
+ }
+}
+
+void fdt_plic_fixup(void *fdt, const char *compat)
+{
+ u32 *cells;
+ int i, cells_count;
+ int plic_off;
+
+ plic_off = fdt_node_offset_by_compatible(fdt, 0, compat);
+ if (plic_off < 0)
+ return;
+
+ cells = (u32 *)fdt_getprop(fdt, plic_off,
+ "interrupts-extended", &cells_count);
+ if (!cells)
+ return;
+
+ cells_count = cells_count / sizeof(u32);
+ if (!cells_count)
+ return;
+
+ for (i = 0; i < (cells_count / 2); i++) {
+ if (fdt32_to_cpu(cells[2 * i + 1]) == IRQ_M_EXT)
+ cells[2 * i + 1] = cpu_to_fdt32(0xffffffff);
+ }
+}
+
+static int fdt_resv_memory_update_node(void *fdt, unsigned long addr,
+ unsigned long size, int index,
+ int parent, bool no_map)
+{
+ int na = fdt_address_cells(fdt, 0);
+ int ns = fdt_size_cells(fdt, 0);
+ fdt32_t addr_high, addr_low;
+ fdt32_t size_high, size_low;
+ int subnode, err;
+ fdt32_t reg[4];
+ fdt32_t *val;
+ char name[32];
+
+ addr_high = (u64)addr >> 32;
+ addr_low = addr;
+ size_high = (u64)size >> 32;
+ size_low = size;
+
+ if (na > 1 && addr_high)
+ sbi_snprintf(name, sizeof(name),
+ "mmode_resv%d@%x,%x", index,
+ addr_high, addr_low);
+ else
+ sbi_snprintf(name, sizeof(name),
+ "mmode_resv%d@%x", index,
+ addr_low);
+
+ subnode = fdt_add_subnode(fdt, parent, name);
+ if (subnode < 0)
+ return subnode;
+
+ if (no_map) {
+ /*
+ * Tell operating system not to create a virtual
+ * mapping of the region as part of its standard
+ * mapping of system memory.
+ */
+ err = fdt_setprop_empty(fdt, subnode, "no-map");
+ if (err < 0)
+ return err;
+ }
+
+ /* encode the <reg> property value */
+ val = reg;
+ if (na > 1)
+ *val++ = cpu_to_fdt32(addr_high);
+ *val++ = cpu_to_fdt32(addr_low);
+ if (ns > 1)
+ *val++ = cpu_to_fdt32(size_high);
+ *val++ = cpu_to_fdt32(size_low);
+
+ err = fdt_setprop(fdt, subnode, "reg", reg,
+ (na + ns) * sizeof(fdt32_t));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/**
+ * We use PMP to protect OpenSBI firmware to safe-guard it from buggy S-mode
+ * software, see pmp_init() in lib/sbi/sbi_hart.c. The protected memory region
+ * information needs to be conveyed to S-mode software (e.g.: operating system)
+ * via some well-known method.
+ *
+ * With device tree, this can be done by inserting a child node of the reserved
+ * memory node which is used to specify one or more regions of reserved memory.
+ *
+ * For the reserved memory node bindings, see Linux kernel documentation at
+ * Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
+ *
+ * Some additional memory spaces may be protected by platform codes via PMP as
+ * well, and corresponding child nodes will be inserted.
+ */
+int fdt_reserved_memory_fixup(void *fdt)
+{
+ struct sbi_domain_memregion *reg;
+ struct sbi_domain *dom = sbi_domain_thishart_ptr();
+ struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
+ unsigned long addr, size;
+ int err, parent, i;
+ int na = fdt_address_cells(fdt, 0);
+ int ns = fdt_size_cells(fdt, 0);
+
+ /*
+ * Expand the device tree to accommodate new node
+ * by the following estimated size:
+ *
+ * Each PMP memory region entry occupies 64 bytes.
+ * With 16 PMP memory regions we need 64 * 16 = 1024 bytes.
+ */
+ err = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + 1024);
+ if (err < 0)
+ return err;
+
+ /* try to locate the reserved memory node */
+ parent = fdt_path_offset(fdt, "/reserved-memory");
+ if (parent < 0) {
+ /* if such node does not exist, create one */
+ parent = fdt_add_subnode(fdt, 0, "reserved-memory");
+ if (parent < 0)
+ return parent;
+
+ /*
+ * reserved-memory node has 3 required properties:
+ * - #address-cells: the same value as the root node
+ * - #size-cells: the same value as the root node
+ * - ranges: should be empty
+ */
+
+ err = fdt_setprop_empty(fdt, parent, "ranges");
+ if (err < 0)
+ return err;
+
+ err = fdt_setprop_u32(fdt, parent, "#size-cells", ns);
+ if (err < 0)
+ return err;
+
+ err = fdt_setprop_u32(fdt, parent, "#address-cells", na);
+ if (err < 0)
+ return err;
+ }
+
+ /*
+ * We assume the given device tree does not contain any memory region
+ * child node protected by PMP. Normally PMP programming happens at
+ * M-mode firmware. The memory space used by OpenSBI is protected.
+ * Some additional memory spaces may be protected by domain memory
+ * regions.
+ *
+ * With above assumption, we create child nodes directly.
+ */
+
+ i = 0;
+ sbi_domain_for_each_memregion(dom, reg) {
+ /* Ignore MMIO or READABLE or WRITABLE or EXECUTABLE regions */
+ if (reg->flags & SBI_DOMAIN_MEMREGION_MMIO)
+ continue;
+ if (reg->flags & SBI_DOMAIN_MEMREGION_READABLE)
+ continue;
+ if (reg->flags & SBI_DOMAIN_MEMREGION_WRITEABLE)
+ continue;
+ if (reg->flags & SBI_DOMAIN_MEMREGION_EXECUTABLE)
+ continue;
+
+ addr = reg->base;
+ size = 1UL << reg->order;
+ fdt_resv_memory_update_node(fdt, addr, size, i, parent,
+ (sbi_hart_pmp_count(scratch)) ? false : true);
+ i++;
+ }
+
+ return 0;
+}
+
+int fdt_reserved_memory_nomap_fixup(void *fdt)
+{
+ int parent, subnode;
+ int err;
+
+ /* Locate the reserved memory node */
+ parent = fdt_path_offset(fdt, "/reserved-memory");
+ if (parent < 0)
+ return parent;
+
+ fdt_for_each_subnode(subnode, fdt, parent) {
+ /*
+ * Tell operating system not to create a virtual
+ * mapping of the region as part of its standard
+ * mapping of system memory.
+ */
+ err = fdt_setprop_empty(fdt, subnode, "no-map");
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+void fdt_fixups(void *fdt)
+{
+ fdt_plic_fixup(fdt, "riscv,plic0");
+
+ fdt_reserved_memory_fixup(fdt);
+}
+
+
diff --git a/roms/opensbi/lib/utils/fdt/fdt_helper.c b/roms/opensbi/lib/utils/fdt/fdt_helper.c
new file mode 100644
index 000000000..bf19ff92d
--- /dev/null
+++ b/roms/opensbi/lib/utils/fdt/fdt_helper.c
@@ -0,0 +1,465 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * fdt_helper.c - Flat Device Tree manipulation helper routines
+ * Implement helper routines on top of libfdt for OpenSBI usage
+ *
+ * Copyright (C) 2020 Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <libfdt.h>
+#include <sbi/riscv_asm.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_hartmask.h>
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+#include <sbi_utils/irqchip/plic.h>
+#include <sbi_utils/sys/clint.h>
+
+#define DEFAULT_UART_FREQ 0
+#define DEFAULT_UART_BAUD 115200
+#define DEFAULT_UART_REG_SHIFT 0
+#define DEFAULT_UART_REG_IO_WIDTH 1
+
+#define DEFAULT_SIFIVE_UART_FREQ 0
+#define DEFAULT_SIFIVE_UART_BAUD 115200
+#define DEFAULT_SIFIVE_UART_REG_SHIFT 0
+#define DEFAULT_SIFIVE_UART_REG_IO_WIDTH 4
+
+#define DEFAULT_SHAKTI_UART_FREQ 50000000
+#define DEFAULT_SHAKTI_UART_BAUD 115200
+
+const struct fdt_match *fdt_match_node(void *fdt, int nodeoff,
+ const struct fdt_match *match_table)
+{
+ int ret;
+
+ if (!fdt || nodeoff < 0 || !match_table)
+ return NULL;
+
+ while (match_table->compatible) {
+ ret = fdt_node_check_compatible(fdt, nodeoff,
+ match_table->compatible);
+ if (!ret)
+ return match_table;
+ match_table++;
+ }
+
+ return NULL;
+}
+
+int fdt_find_match(void *fdt, int startoff,
+ const struct fdt_match *match_table,
+ const struct fdt_match **out_match)
+{
+ int nodeoff;
+
+ if (!fdt || !match_table)
+ return SBI_ENODEV;
+
+ while (match_table->compatible) {
+ nodeoff = fdt_node_offset_by_compatible(fdt, startoff,
+ match_table->compatible);
+ if (nodeoff >= 0) {
+ if (out_match)
+ *out_match = match_table;
+ return nodeoff;
+ }
+ match_table++;
+ }
+
+ return SBI_ENODEV;
+}
+
+static int fdt_translate_address(void *fdt, uint64_t reg, int parent,
+ unsigned long *addr)
+{
+ int i, rlen;
+ int cell_addr, cell_size;
+ const fdt32_t *ranges;
+ uint64_t offset = 0, caddr = 0, paddr = 0, rsize = 0;
+
+ cell_addr = fdt_address_cells(fdt, parent);
+ if (cell_addr < 1)
+ return SBI_ENODEV;
+
+ cell_size = fdt_size_cells(fdt, parent);
+ if (cell_size < 0)
+ return SBI_ENODEV;
+
+ ranges = fdt_getprop(fdt, parent, "ranges", &rlen);
+ if (ranges && rlen > 0) {
+ for (i = 0; i < cell_addr; i++)
+ caddr = (caddr << 32) | fdt32_to_cpu(*ranges++);
+ for (i = 0; i < cell_addr; i++)
+ paddr = (paddr << 32) | fdt32_to_cpu(*ranges++);
+ for (i = 0; i < cell_size; i++)
+ rsize = (rsize << 32) | fdt32_to_cpu(*ranges++);
+ if (reg < caddr || caddr >= (reg + rsize )) {
+ sbi_printf("invalid address translation\n");
+ return SBI_ENODEV;
+ }
+ offset = reg - caddr;
+ *addr = paddr + offset;
+ } else {
+ /* No translation required */
+ *addr = reg;
+ }
+
+ return 0;
+}
+
+int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr,
+ unsigned long *size)
+{
+ int parent, len, i, rc;
+ int cell_addr, cell_size;
+ const fdt32_t *prop_addr, *prop_size;
+ uint64_t temp = 0;
+
+ parent = fdt_parent_offset(fdt, node);
+ if (parent < 0)
+ return parent;
+ cell_addr = fdt_address_cells(fdt, parent);
+ if (cell_addr < 1)
+ return SBI_ENODEV;
+
+ cell_size = fdt_size_cells(fdt, parent);
+ if (cell_size < 0)
+ return SBI_ENODEV;
+
+ prop_addr = fdt_getprop(fdt, node, "reg", &len);
+ if (!prop_addr)
+ return SBI_ENODEV;
+ prop_size = prop_addr + cell_addr;
+
+ if (addr) {
+ for (i = 0; i < cell_addr; i++)
+ temp = (temp << 32) | fdt32_to_cpu(*prop_addr++);
+ do {
+ if (parent < 0)
+ break;
+ rc = fdt_translate_address(fdt, temp, parent, addr);
+ if (rc)
+ break;
+ parent = fdt_parent_offset(fdt, parent);
+ temp = *addr;
+ } while (1);
+ }
+ temp = 0;
+
+ if (size) {
+ for (i = 0; i < cell_size; i++)
+ temp = (temp << 32) | fdt32_to_cpu(*prop_size++);
+ *size = temp;
+ }
+
+ return 0;
+}
+
+int fdt_parse_hart_id(void *fdt, int cpu_offset, u32 *hartid)
+{
+ int len;
+ const void *prop;
+ const fdt32_t *val;
+
+ if (!fdt || cpu_offset < 0)
+ return SBI_EINVAL;
+
+ prop = fdt_getprop(fdt, cpu_offset, "device_type", &len);
+ if (!prop || !len)
+ return SBI_EINVAL;
+ if (strncmp (prop, "cpu", strlen ("cpu")))
+ return SBI_EINVAL;
+
+ val = fdt_getprop(fdt, cpu_offset, "reg", &len);
+ if (!val || len < sizeof(fdt32_t))
+ return SBI_EINVAL;
+
+ if (len > sizeof(fdt32_t))
+ val++;
+
+ if (hartid)
+ *hartid = fdt32_to_cpu(*val);
+
+ return 0;
+}
+
+int fdt_parse_max_hart_id(void *fdt, u32 *max_hartid)
+{
+ u32 hartid;
+ int err, cpu_offset, cpus_offset;
+
+ if (!fdt)
+ return SBI_EINVAL;
+ if (!max_hartid)
+ return 0;
+
+ *max_hartid = 0;
+
+ cpus_offset = fdt_path_offset(fdt, "/cpus");
+ if (cpus_offset < 0)
+ return cpus_offset;
+
+ fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) {
+ err = fdt_parse_hart_id(fdt, cpu_offset, &hartid);
+ if (err)
+ continue;
+
+ if (hartid > *max_hartid)
+ *max_hartid = hartid;
+ }
+
+ return 0;
+}
+
+int fdt_parse_shakti_uart_node(void *fdt, int nodeoffset,
+ struct platform_uart_data *uart)
+{
+ int len, rc;
+ const fdt32_t *val;
+ unsigned long reg_addr, reg_size;
+
+ if (nodeoffset < 0 || !uart || !fdt)
+ return SBI_ENODEV;
+
+ rc = fdt_get_node_addr_size(fdt, nodeoffset, &reg_addr, &reg_size);
+ if (rc < 0 || !reg_addr || !reg_size)
+ return SBI_ENODEV;
+ uart->addr = reg_addr;
+
+ /**
+ * UART address is mandaotry. clock-frequency and current-speed
+ * may not be present. Don't return error.
+ */
+ val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
+ if (len > 0 && val)
+ uart->freq = fdt32_to_cpu(*val);
+ else
+ uart->freq = DEFAULT_SHAKTI_UART_FREQ;
+
+ val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
+ if (len > 0 && val)
+ uart->baud = fdt32_to_cpu(*val);
+ else
+ uart->baud = DEFAULT_SHAKTI_UART_BAUD;
+
+ return 0;
+}
+
+int fdt_parse_sifive_uart_node(void *fdt, int nodeoffset,
+ struct platform_uart_data *uart)
+{
+ int len, rc;
+ const fdt32_t *val;
+ unsigned long reg_addr, reg_size;
+
+ if (nodeoffset < 0 || !uart || !fdt)
+ return SBI_ENODEV;
+
+ rc = fdt_get_node_addr_size(fdt, nodeoffset, &reg_addr, &reg_size);
+ if (rc < 0 || !reg_addr || !reg_size)
+ return SBI_ENODEV;
+ uart->addr = reg_addr;
+
+ /**
+ * UART address is mandaotry. clock-frequency and current-speed
+ * may not be present. Don't return error.
+ */
+ val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
+ if (len > 0 && val)
+ uart->freq = fdt32_to_cpu(*val);
+ else
+ uart->freq = DEFAULT_SIFIVE_UART_FREQ;
+
+ val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
+ if (len > 0 && val)
+ uart->baud = fdt32_to_cpu(*val);
+ else
+ uart->baud = DEFAULT_SIFIVE_UART_BAUD;
+
+ /* For SiFive UART, the reg-shift and reg-io-width are fixed .*/
+ uart->reg_shift = DEFAULT_SIFIVE_UART_REG_SHIFT;
+ uart->reg_io_width = DEFAULT_SIFIVE_UART_REG_IO_WIDTH;
+
+ return 0;
+}
+
+int fdt_parse_uart8250_node(void *fdt, int nodeoffset,
+ struct platform_uart_data *uart)
+{
+ int len, rc;
+ const fdt32_t *val;
+ unsigned long reg_addr, reg_size;
+
+ if (nodeoffset < 0 || !uart || !fdt)
+ return SBI_ENODEV;
+
+ rc = fdt_get_node_addr_size(fdt, nodeoffset, &reg_addr, &reg_size);
+ if (rc < 0 || !reg_addr || !reg_size)
+ return SBI_ENODEV;
+ uart->addr = reg_addr;
+
+ /**
+ * UART address is mandaotry. clock-frequency and current-speed
+ * may not be present. Don't return error.
+ */
+ val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
+ if (len > 0 && val)
+ uart->freq = fdt32_to_cpu(*val);
+ else
+ uart->freq = DEFAULT_UART_FREQ;
+
+ val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
+ if (len > 0 && val)
+ uart->baud = fdt32_to_cpu(*val);
+ else
+ uart->baud = DEFAULT_UART_BAUD;
+
+ val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "reg-shift", &len);
+ if (len > 0 && val)
+ uart->reg_shift = fdt32_to_cpu(*val);
+ else
+ uart->reg_shift = DEFAULT_UART_REG_SHIFT;
+
+ val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "reg-io-width", &len);
+ if (len > 0 && val)
+ uart->reg_io_width = fdt32_to_cpu(*val);
+ else
+ uart->reg_io_width = DEFAULT_UART_REG_IO_WIDTH;
+
+ return 0;
+}
+
+int fdt_parse_uart8250(void *fdt, struct platform_uart_data *uart,
+ const char *compatible)
+{
+ int nodeoffset;
+
+ if (!compatible || !uart || !fdt)
+ return SBI_ENODEV;
+
+ nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible);
+ if (nodeoffset < 0)
+ return nodeoffset;
+
+ return fdt_parse_uart8250_node(fdt, nodeoffset, uart);
+}
+
+int fdt_parse_plic_node(void *fdt, int nodeoffset, struct plic_data *plic)
+{
+ int len, rc;
+ const fdt32_t *val;
+ unsigned long reg_addr, reg_size;
+
+ if (nodeoffset < 0 || !plic || !fdt)
+ return SBI_ENODEV;
+
+ rc = fdt_get_node_addr_size(fdt, nodeoffset, &reg_addr, &reg_size);
+ if (rc < 0 || !reg_addr || !reg_size)
+ return SBI_ENODEV;
+ plic->addr = reg_addr;
+
+ val = fdt_getprop(fdt, nodeoffset, "riscv,ndev", &len);
+ if (len > 0)
+ plic->num_src = fdt32_to_cpu(*val);
+
+ return 0;
+}
+
+int fdt_parse_plic(void *fdt, struct plic_data *plic, const char *compat)
+{
+ int nodeoffset;
+
+ if (!compat || !plic || !fdt)
+ return SBI_ENODEV;
+
+ nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compat);
+ if (nodeoffset < 0)
+ return nodeoffset;
+
+ return fdt_parse_plic_node(fdt, nodeoffset, plic);
+}
+
+int fdt_parse_clint_node(void *fdt, int nodeoffset, bool for_timer,
+ struct clint_data *clint)
+{
+ const fdt32_t *val;
+ unsigned long reg_addr, reg_size;
+ int i, rc, count, cpu_offset, cpu_intc_offset;
+ u32 phandle, hwirq, hartid, first_hartid, last_hartid;
+ u32 match_hwirq = (for_timer) ? IRQ_M_TIMER : IRQ_M_SOFT;
+
+ if (nodeoffset < 0 || !clint || !fdt)
+ return SBI_ENODEV;
+
+ rc = fdt_get_node_addr_size(fdt, nodeoffset, &reg_addr, &reg_size);
+ if (rc < 0 || !reg_addr || !reg_size)
+ return SBI_ENODEV;
+ clint->addr = reg_addr;
+
+ val = fdt_getprop(fdt, nodeoffset, "interrupts-extended", &count);
+ if (!val || count < sizeof(fdt32_t))
+ return SBI_EINVAL;
+ count = count / sizeof(fdt32_t);
+
+ first_hartid = -1U;
+ last_hartid = 0;
+ clint->hart_count = 0;
+ for (i = 0; i < count; i += 2) {
+ phandle = fdt32_to_cpu(val[i]);
+ hwirq = fdt32_to_cpu(val[i + 1]);
+
+ cpu_intc_offset = fdt_node_offset_by_phandle(fdt, phandle);
+ if (cpu_intc_offset < 0)
+ continue;
+
+ cpu_offset = fdt_parent_offset(fdt, cpu_intc_offset);
+ if (cpu_intc_offset < 0)
+ continue;
+
+ rc = fdt_parse_hart_id(fdt, cpu_offset, &hartid);
+ if (rc)
+ continue;
+
+ if (SBI_HARTMASK_MAX_BITS <= hartid)
+ continue;
+
+ if (match_hwirq == hwirq) {
+ if (hartid < first_hartid)
+ first_hartid = hartid;
+ if (hartid > last_hartid)
+ last_hartid = hartid;
+ clint->hart_count++;
+ }
+ }
+
+ if ((last_hartid < first_hartid) || first_hartid == -1U)
+ return SBI_ENODEV;
+
+ clint->first_hartid = first_hartid;
+ count = last_hartid - first_hartid + 1;
+ if (clint->hart_count < count)
+ clint->hart_count = count;
+
+ /* TODO: We should figure-out CLINT has_64bit_mmio from DT node */
+ clint->has_64bit_mmio = TRUE;
+
+ return 0;
+}
+
+int fdt_parse_compat_addr(void *fdt, unsigned long *addr,
+ const char *compatible)
+{
+ int nodeoffset, rc;
+
+ nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible);
+ if (nodeoffset < 0)
+ return nodeoffset;
+
+ rc = fdt_get_node_addr_size(fdt, nodeoffset, addr, NULL);
+ if (rc < 0 || !addr)
+ return SBI_ENODEV;
+
+ return 0;
+}
diff --git a/roms/opensbi/lib/utils/fdt/objects.mk b/roms/opensbi/lib/utils/fdt/objects.mk
new file mode 100644
index 000000000..d9f1eae19
--- /dev/null
+++ b/roms/opensbi/lib/utils/fdt/objects.mk
@@ -0,0 +1,9 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (C) 2020 Bin Meng <bmeng.cn@gmail.com>
+#
+
+libsbiutils-objs-y += fdt/fdt_domain.o
+libsbiutils-objs-y += fdt/fdt_helper.o
+libsbiutils-objs-y += fdt/fdt_fixup.o