[Skiboot] [PATCH v7 05/17] core/fdt: Refactor FDT

Gavin Shan gwshan at linux.vnet.ibm.com
Thu Jun 4 16:18:57 AEST 2015


The patch refactors FDT in order to support PCI hot plugging, which
requires capability of creating FDT blob for the subordinate device
nodes of the specified one.

 * Current implementation assumes that the FDT blob is pointed by
   global variable "fdt", which won't be true when create FDT blob
   for device subtree during PCI hot plugging. Adding additional
   parameter "void *fdt" to individual functions for the operated
   FDT blob.
 * Refactoring dt_flatten_node() so that it can be used to build
   FDT blob from root device node at bootup time, or the indicated
   one during PCI hot plugging time.
 * One property "ibm,parent-phandle" added to each node to indicate
   its parent node.
 * New OPAL API: opal_get_overlay_dt().

Signed-off-by: Gavin Shan <gwshan at linux.vnet.ibm.com>
---
 core/fdt.c            | 164 +++++++++++++++++++++++++++++++++-----------------
 core/init.c           |   2 +-
 include/device_tree.h |   5 --
 include/opal-api.h    |   1 +
 include/skiboot.h     |   3 +-
 5 files changed, 113 insertions(+), 62 deletions(-)

diff --git a/core/fdt.c b/core/fdt.c
index 7abebba..f28d65a 100644
--- a/core/fdt.c
+++ b/core/fdt.c
@@ -27,7 +27,6 @@
 #include <ccan/str/str.h>
 
 static int fdt_error;
-static void *fdt;
 
 #undef DEBUG_FDT
 
@@ -44,36 +43,40 @@ static void __save_err(int err, const char *str)
 
 #define save_err(...) __save_err(__VA_ARGS__, #__VA_ARGS__)
 
-static void dt_property_cell(const char *name, u32 cell)
+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(const char *name, uint32_t phandle)
+static void dt_begin_node(void *fdt, const struct dt_node *dn)
 {
-	save_err(fdt_begin_node(fdt, name));
+	save_err(fdt_begin_node(fdt, dn->name));
 
 	/*
 	 * We add both the new style "phandle" and the legacy
-	 * "linux,phandle" properties
+	 * "linux,phandle" properties. Also, another property
+	 * added to indicate its parent node.
 	 */
-	dt_property_cell("linux,phandle", phandle);
-	dt_property_cell("phandle", phandle);
+	if (dn->parent)
+		dt_property_cell(fdt, "ibm,parent-phandle",
+				 dn->parent->phandle);
+	dt_property_cell(fdt, "linux,phandle", dn->phandle);
+	dt_property_cell(fdt, "phandle", dn->phandle);
 }
 
-static void dt_property(const char *name, const void *val, size_t size)
+static void dt_property(void *fdt, const char *name, const void *val, size_t size)
 {
 	save_err(fdt_property(fdt, name, val, size));
 }
 
-static void dt_end_node(void)
+static void dt_end_node(void *fdt)
 {
 	save_err(fdt_end_node(fdt));
 }
 
-static void dump_fdt(void)
-{
 #ifdef DEBUG_FDT
+static void dump_fdt(void *fdt)
+{
 	int i, off, depth, err;
 
 	printf("Device tree %u@%p\n", fdt_totalsize(fdt), fdt);
@@ -110,35 +113,48 @@ static void dump_fdt(void)
 		}
 		printf("name: %s [%u]\n", name, off);
 	}
-#endif
 }
+#else
+#define dump_fdt(x...)
+#endif
 
-static void flatten_dt_node(const struct dt_node *root)
+static void dt_flatten_properties(void *fdt, const struct dt_node *dn)
 {
-	const struct dt_node *i;
-	const struct dt_property *p;
+	struct dt_property *p;
 
-#ifdef DEBUG_FDT
-	printf("FDT: node: %s\n", root->name);
-#endif
-
-	list_for_each(&root->properties, p, list) {
+	list_for_each(&dn->properties, p, list) {
 		if (strstarts(p->name, DT_PRIVATE))
 			continue;
 #ifdef DEBUG_FDT
 		printf("FDT:   prop: %s size: %ld\n", p->name, p->len);
 #endif
-		dt_property(p->name, p->prop, p->len);
+		dt_property(fdt, p->name, p->prop, p->len);
 	}
+}
 
-	list_for_each(&root->children, i, list) {
-		dt_begin_node(i->name, i->phandle);
-		flatten_dt_node(i);
-		dt_end_node();
+static void dt_flatten_node(void *fdt,
+			    const struct dt_node *dn,
+			    uint64_t counter)
+{
+	const struct dt_node *child;
+
+#ifdef DEBUG_FDT
+	printf("FDT: node: %s\n", dn->name);
+#endif
+
+	if (dn->counter >= counter) {
+		dt_begin_node(fdt, dn);
+		dt_flatten_properties(fdt, dn);
 	}
+
+	list_for_each(&dn->children, child, list)
+		dt_flatten_node(fdt, child, counter);
+
+	if (dn->counter >= counter)
+		dt_end_node(fdt);
 }
 
-static void create_dtb_reservemap(const struct dt_node *root)
+static void create_dtb_reservemap(void *fdt, const struct dt_node *root)
 {
 	uint64_t base, size;
 	const uint64_t *ranges;
@@ -160,42 +176,63 @@ static void create_dtb_reservemap(const struct dt_node *root)
 	save_err(fdt_finish_reservemap(fdt));
 }
 
-void *create_dtb(const struct dt_node *root)
+static int __create_dtb(const struct dt_node *root,
+			void *fdt, size_t len,
+			uint64_t counter, bool has_reservemap)
 {
-	size_t len = DEVICE_TREE_MAX_SIZE;
-	uint32_t old_last_phandle = last_phandle;
+	/* Clear cached error */
+	fdt_error = 0;
 
-	do {
-		if (fdt)
-			free(fdt);
-		last_phandle = old_last_phandle;
-		fdt_error = 0;
-		fdt = malloc(len);
-		if (!fdt) {
-			prerror("dtb: could not malloc %lu\n", (long)len);
-			return NULL;
-		}
+	/* FDT header */
+	fdt_create(fdt, len);
 
-		fdt_create(fdt, len);
+	/* Reserved memory chunks */
+	if (has_reservemap)
+		create_dtb_reservemap(fdt, root);
 
-		create_dtb_reservemap(root);
+	/* Unflatten our live tree */
+	dt_flatten_node(fdt, root, counter);
 
-		/* Open root node */
-		dt_begin_node(root->name, root->phandle);
-
-		/* Unflatten our live tree */
-		flatten_dt_node(root);
-
-		/* Close root node */
-		dt_end_node();
-
-		save_err(fdt_finish(fdt));
+	save_err(fdt_finish(fdt));
+	if (fdt_error) {
+		free(fdt);
+		return fdt_error;
+	}
 
-		if (!fdt_error)
-			break;
+	return 0;
+}
 
-		len *= 2;
-	} while (fdt_error == -FDT_ERR_NOSPACE);
+void *create_dtb(const struct dt_node *root, void *buf,
+		 size_t size, uint64_t counter)
+{
+	size_t len;
+	uint32_t old_last_phandle = last_phandle;
+	void *fdt = NULL;
+	int ret;
+
+	/* Get the initial length */
+	if (buf && size) {
+		fdt = buf;
+		len = size;
+		ret = __create_dtb(root, fdt, len, counter, false);
+	} else {
+		len = DEVICE_TREE_MAX_SIZE;
+
+		do {
+			fdt = malloc(len);
+			if (!fdt) {
+				prerror("dtb: cannot alloc %lu\n", (long)len);
+				return NULL;
+			}
+
+			last_phandle = old_last_phandle;
+			ret = __create_dtb(root, fdt, len, counter, true);
+			if (!ret)
+				break;
+
+			len *= 2;
+		} while (fdt_error == -FDT_ERR_NOSPACE);
+	}
 
 	dump_fdt();
 
@@ -203,5 +240,22 @@ void *create_dtb(const struct dt_node *root)
 		prerror("dtb: error %s\n", fdt_strerror(fdt_error));
 		return NULL;
 	}
+
 	return fdt;
 }
+
+static int64_t opal_get_overlay_dt(uint64_t *counter, void *buf, uint64_t len)
+{
+	/* Getting the DT global counter */
+	if (!buf && !len) {
+		*counter = dt_update_counter(NULL);
+		return OPAL_SUCCESS;
+	}
+
+	/* Create the FDT blob */
+	if (!create_dtb(dt_root, buf, len, *counter))
+		return OPAL_NO_MEM;
+
+	return OPAL_SUCCESS;
+}
+opal_call(OPAL_GET_OVERLAY_DT, opal_get_overlay_dt, 3);
diff --git a/core/init.c b/core/init.c
index 88a4037..3140231 100644
--- a/core/init.c
+++ b/core/init.c
@@ -432,7 +432,7 @@ void __noreturn load_and_boot_kernel(bool is_reboot)
 	op_display(OP_LOG, OP_MOD_INIT, 0x000B);
 
 	/* Create the device tree blob to boot OS. */
-	fdt = create_dtb(dt_root);
+	fdt = create_dtb(dt_root, NULL, 0, 0ul);
 	if (!fdt) {
 		op_display(OP_FATAL, OP_MOD_INIT, 2);
 		abort();
diff --git a/include/device_tree.h b/include/device_tree.h
index d04f20a..a32821c 100644
--- a/include/device_tree.h
+++ b/include/device_tree.h
@@ -18,11 +18,6 @@
 #define __DEVICE_TREE_H
 #include <stdint.h>
 
-/* Note: Device tree creation has no locks. It's assumed to be done
- * by a single processor in a non racy way
- */
-void *create_dtb(const struct dt_node *root);
-
 /* Helpers to cache errors in fdt; use this instead of fdt_* */
 uint32_t dt_begin_node(const char *name); /* returns phandle */
 void dt_property_string(const char *name, const char *value);
diff --git a/include/opal-api.h b/include/opal-api.h
index 29421c1..d711489 100644
--- a/include/opal-api.h
+++ b/include/opal-api.h
@@ -161,6 +161,7 @@
 #define OPAL_PRD_MSG				113
 #define OPAL_LEDS_GET_INDICATOR			114
 #define OPAL_LEDS_SET_INDICATOR			115
+#define OPAL_GET_OVERLAY_DT			116
 #define OPAL_LAST				115
 
 /* Device tree flags */
diff --git a/include/skiboot.h b/include/skiboot.h
index 9751a31..b226c7d 100644
--- a/include/skiboot.h
+++ b/include/skiboot.h
@@ -252,7 +252,8 @@ extern void prd_occ_reset(uint32_t proc);
 extern void prd_init(void);
 
 /* Flatten device-tree */
-extern void *create_dtb(const struct dt_node *root);
+void *create_dtb(const struct dt_node *root, void *buf,
+		 size_t size, uint64_t counter);
 
 /* SLW reinit function for switching core settings */
 extern int64_t slw_reinit(uint64_t flags);
-- 
2.1.0



More information about the Skiboot mailing list