aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/core/fdt.c
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/core/fdt.c
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/core/fdt.c')
-rw-r--r--roms/skiboot/core/fdt.c258
1 files changed, 258 insertions, 0 deletions
diff --git a/roms/skiboot/core/fdt.c b/roms/skiboot/core/fdt.c
new file mode 100644
index 000000000..463dc6912
--- /dev/null
+++ b/roms/skiboot/core/fdt.c
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Produce and consume flattened device trees
+ *
+ * Copyright 2013-2019 IBM Corp.
+ */
+
+#include <skiboot.h>
+#include <stdarg.h>
+#include <libfdt.h>
+#include <device.h>
+#include <chip.h>
+#include <cpu.h>
+#include <opal.h>
+#include <interrupts.h>
+#include <fsp.h>
+#include <cec.h>
+#include <vpd.h>
+#include <ccan/str/str.h>
+
+static int fdt_error;
+
+#undef DEBUG_FDT
+#ifdef DEBUG_FDT
+#define FDT_DBG(fmt, a...) prlog(PR_DEBUG, "FDT: " fmt, ##a)
+#else
+#define FDT_DBG(fmt, a...)
+#endif
+
+static void __save_err(int err, const char *str)
+{
+ FDT_DBG("rc: %d from \"%s\"\n", err, str);
+ if (err && !fdt_error) {
+ prerror("FDT: Error %d from \"%s\"\n", err, str);
+ fdt_error = err;
+ }
+}
+
+#define save_err(...) __save_err(__VA_ARGS__, #__VA_ARGS__)
+
+static void dt_property_cell(void *fdt, const char *name, u32 cell)
+{
+ save_err(fdt_property_cell(fdt, name, cell));
+}
+
+static void dt_begin_node(void *fdt, const struct dt_node *dn)
+{
+ save_err(fdt_begin_node(fdt, dn->name));
+
+ dt_property_cell(fdt, "phandle", dn->phandle);
+}
+
+static void dt_property(void *fdt, const struct dt_property *p)
+{
+ save_err(fdt_property(fdt, p->name, p->prop, p->len));
+}
+
+static void dt_end_node(void *fdt)
+{
+ save_err(fdt_end_node(fdt));
+}
+
+#ifdef DEBUG_FDT
+static void dump_fdt(void *fdt)
+{
+ int i, off, depth, err;
+
+ prlog(PR_INFO, "Device tree %u@%p\n", fdt_totalsize(fdt), fdt);
+ err = fdt_check_header(fdt);
+ if (err) {
+ prerror("fdt_check_header: %s\n", fdt_strerror(err));
+ return;
+ }
+ prlog(PR_INFO, "fdt_check_header passed\n");
+
+ prlog(PR_INFO, "fdt_num_mem_rsv = %u\n", fdt_num_mem_rsv(fdt));
+ for (i = 0; i < fdt_num_mem_rsv(fdt); i++) {
+ u64 addr, size;
+
+ err = fdt_get_mem_rsv(fdt, i, &addr, &size);
+ if (err) {
+ prlog(PR_INFO, " ERR %s\n", fdt_strerror(err));
+ return;
+ }
+ prlog(PR_INFO, " mem_rsv[%i] = %lu@%#lx\n",
+ i, (long)addr, (long)size);
+ }
+
+ for (off = fdt_next_node(fdt, 0, &depth);
+ off > 0;
+ off = fdt_next_node(fdt, off, &depth)) {
+ int len;
+ const char *name;
+
+ name = fdt_get_name(fdt, off, &len);
+ if (!name) {
+ prerror("fdt: offset %i no name!\n", off);
+ return;
+ }
+ prlog(PR_INFO, "name: %s [%u]\n", name, off);
+ }
+}
+#endif
+
+static void flatten_dt_properties(void *fdt, const struct dt_node *dn)
+{
+ const struct dt_property *p;
+
+ list_for_each(&dn->properties, p, list) {
+ if (strstarts(p->name, DT_PRIVATE))
+ continue;
+
+ FDT_DBG(" prop: %s size: %ld\n", p->name, p->len);
+ dt_property(fdt, p);
+ }
+}
+
+static void flatten_dt_node(void *fdt, const struct dt_node *root,
+ bool exclusive)
+{
+ const struct dt_node *i;
+
+ if (!exclusive) {
+ FDT_DBG("node: %s\n", root->name);
+ dt_begin_node(fdt, root);
+ flatten_dt_properties(fdt, root);
+ }
+
+ list_for_each(&root->children, i, list)
+ flatten_dt_node(fdt, i, false);
+
+ if (!exclusive)
+ dt_end_node(fdt);
+}
+
+static void create_dtb_reservemap(void *fdt, const struct dt_node *root)
+{
+ uint64_t base, size;
+ const __be64 *ranges;
+ const struct dt_property *prop;
+ int i;
+
+ /* Duplicate the reserved-ranges property into the fdt reservemap */
+ prop = dt_find_property(root, "reserved-ranges");
+ if (prop) {
+ ranges = (const void *)prop->prop;
+
+ for (i = 0; i < prop->len / (sizeof(uint64_t) * 2); i++) {
+ base = be64_to_cpu(*(ranges++));
+ size = be64_to_cpu(*(ranges++));
+ save_err(fdt_add_reservemap_entry(fdt, base, size));
+ }
+ }
+
+ save_err(fdt_finish_reservemap(fdt));
+}
+
+static int __create_dtb(void *fdt, size_t len,
+ const struct dt_node *root,
+ bool exclusive)
+{
+ if (chip_quirk(QUIRK_SLOW_SIM))
+ save_err(fdt_create_with_flags(fdt, len, FDT_CREATE_FLAG_NO_NAME_DEDUP));
+ else
+ save_err(fdt_create_with_flags(fdt, len, 0));
+ if (fdt_error)
+ goto err;
+
+ if (root == dt_root && !exclusive)
+ create_dtb_reservemap(fdt, root);
+ else
+ save_err(fdt_finish_reservemap(fdt));
+
+ flatten_dt_node(fdt, root, exclusive);
+
+ save_err(fdt_finish(fdt));
+ if (fdt_error) {
+err:
+ prerror("dtb: error %s\n", fdt_strerror(fdt_error));
+ return fdt_error;
+ }
+
+#ifdef DEBUG_FDT
+ dump_fdt(fdt);
+#endif
+ return 0;
+}
+
+void *create_dtb(const struct dt_node *root, bool exclusive)
+{
+ void *fdt = NULL;
+ size_t len = DEVICE_TREE_MAX_SIZE;
+ uint32_t old_last_phandle = get_last_phandle();
+ int ret;
+
+ do {
+ set_last_phandle(old_last_phandle);
+ fdt_error = 0;
+ fdt = malloc(len);
+ if (!fdt) {
+ prerror("dtb: could not malloc %lu\n", (long)len);
+ return NULL;
+ }
+
+ ret = __create_dtb(fdt, len, root, exclusive);
+ if (ret) {
+ free(fdt);
+ fdt = NULL;
+ }
+
+ len *= 2;
+ } while (ret == -FDT_ERR_NOSPACE);
+
+ return fdt;
+}
+
+static int64_t opal_get_device_tree(uint32_t phandle,
+ uint64_t buf, uint64_t len)
+{
+ struct dt_node *root;
+ void *fdt = (void *)buf;
+ uint32_t old_last_phandle;
+ int64_t totalsize;
+ int ret;
+
+ if (!opal_addr_valid(fdt))
+ return OPAL_PARAMETER;
+
+ root = dt_find_by_phandle(dt_root, phandle);
+ if (!root)
+ return OPAL_PARAMETER;
+
+ if (!fdt) {
+ fdt = create_dtb(root, true);
+ if (!fdt)
+ return OPAL_INTERNAL_ERROR;
+ totalsize = fdt_totalsize(fdt);
+ free(fdt);
+ return totalsize;
+ }
+
+ if (!len)
+ return OPAL_PARAMETER;
+
+ fdt_error = 0;
+ old_last_phandle = get_last_phandle();
+ ret = __create_dtb(fdt, len, root, true);
+ if (ret) {
+ set_last_phandle(old_last_phandle);
+ if (ret == -FDT_ERR_NOSPACE)
+ return OPAL_NO_MEM;
+
+ return OPAL_EMPTY;
+ }
+
+ return OPAL_SUCCESS;
+}
+opal_call(OPAL_GET_DEVICE_TREE, opal_get_device_tree, 3);