[v4,1/2] of: create of_root if no dtb provided
Commit Message
When enabling CONFIG_OF on a platform where of_root is not populated by
firmware, we end up without a root node. In order to apply overlays and
create subnodes of the root node, we need one. Create this root node
by unflattening an empty builtin dtb.
If firmware provides a flattened device tree (FDT) then the FDT is
unflattened via setup_arch(). Otherwise setup_of(), which is called
immediately after setup_arch(), will create the default root node
if it does not exist.
Signed-off-by: Frank Rowand <frowand.list@gmail.com>
---
Please wait for test results from Clement before accepting.
Changes since version 3:
- refresh for 6.3-rc1
- unflatten_device_tree() - calculate of_fdt_crc32 if setting
initial_boot_params to __dtb_empty_root_begin so CRC check
in of_fdt_raw_init() will not fail
Changes since version 2:
- change of __dtb_empty_root_* from "void *" to "uint8_t []"
Changes since version 1:
- refresh for 6.2-rc1
- update Signed-off-by
- fix typo in of_fdt.h: s/of_setup/setup_of
- unflatten_device_tree(): validate size in header field dtb_empty_root
that will be used to copy dtb_empty_root
- add Kconfig option to manually select CONFIG_OF_EARLY_FLATTREE
drivers/of/Kconfig | 7 ++++++-
drivers/of/Makefile | 2 +-
drivers/of/empty_root.dts | 6 ++++++
drivers/of/fdt.c | 29 ++++++++++++++++++++++++++++-
include/linux/of_fdt.h | 2 ++
init/main.c | 2 ++
6 files changed, 45 insertions(+), 3 deletions(-)
create mode 100644 drivers/of/empty_root.dts
Comments
On Fri, Mar 17, 2023 at 12:34:14AM -0500, Frank Rowand wrote:
> When enabling CONFIG_OF on a platform where of_root is not populated by
> firmware, we end up without a root node. In order to apply overlays and
> create subnodes of the root node, we need one. Create this root node
> by unflattening an empty builtin dtb.
>
> If firmware provides a flattened device tree (FDT) then the FDT is
> unflattened via setup_arch(). Otherwise setup_of(), which is called
> immediately after setup_arch(), will create the default root node
> if it does not exist.
I thought of another way to handle this. Every arch except IIRC sparc,
s390, and ia64 calls unflatten(_and_copy)?_device_tree already. At
least any arch anyone is going to care about for this stuff does. It's
just conditional in some cases. So why not make the existing calls
unconditional?
Either way, I think that of_have_populated_dt() calls will need to be
checked whether this change.
Rob
@@ -54,9 +54,14 @@ config OF_FLATTREE
select CRC32
config OF_EARLY_FLATTREE
- bool
+ bool "Functions for accessing Flat Devicetree (FDT) early in boot"
select DMA_DECLARE_COHERENT if HAS_DMA
select OF_FLATTREE
+ help
+ Normally selected by platforms that process an FDT that has been
+ passed to the kernel by the bootloader. If the bootloader does not
+ pass an FDT to the kernel and you need an empty devicetree that
+ contains only a root node to exist, then say Y here.
config OF_PROMTREE
bool
@@ -2,7 +2,7 @@
obj-y = base.o device.o platform.o property.o
obj-$(CONFIG_OF_KOBJ) += kobj.o
obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
-obj-$(CONFIG_OF_FLATTREE) += fdt.o
+obj-$(CONFIG_OF_FLATTREE) += fdt.o empty_root.dtb.o
obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
obj-$(CONFIG_OF_PROMTREE) += pdt.o
obj-$(CONFIG_OF_ADDRESS) += address.o
new file mode 100644
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/dts-v1/;
+
+/ {
+
+};
@@ -32,6 +32,13 @@
#include "of_private.h"
+/*
+ * __dtb_empty_root_begin[] and __dtb_empty_root_end[] magically created by
+ * cmd_dt_S_dtb in scripts/Makefile.lib
+ */
+extern uint8_t __dtb_empty_root_begin[];
+extern uint8_t __dtb_empty_root_end[];
+
/*
* of_fdt_limit_memory - limit the number of regions in the /memory node
* @limit: maximum entries
@@ -1326,8 +1333,21 @@ bool __init early_init_dt_scan(void *params)
*/
void __init unflatten_device_tree(void)
{
- __unflatten_device_tree(initial_boot_params, NULL, &of_root,
+ if (!initial_boot_params) {
+ initial_boot_params = (void *) __dtb_empty_root_begin;
+ /* fdt_totalsize() will be used for copy size */
+ if (fdt_totalsize(initial_boot_params) >
+ __dtb_empty_root_end - __dtb_empty_root_begin) {
+ pr_err("invalid size in dtb_empty_root\n");
+ return;
+ }
+ of_fdt_crc32 = crc32_be(~0, initial_boot_params,
+ fdt_totalsize(initial_boot_params));
+ unflatten_and_copy_device_tree();
+ } else {
+ __unflatten_device_tree(initial_boot_params, NULL, &of_root,
early_init_dt_alloc_memory_arch, false);
+ }
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
of_alias_scan(early_init_dt_alloc_memory_arch);
@@ -1367,6 +1387,13 @@ void __init unflatten_and_copy_device_tree(void)
unflatten_device_tree();
}
+void __init setup_of(void)
+{
+ /* if architecture did not unflatten devicetree, do it now */
+ if (!of_root)
+ unflatten_device_tree();
+}
+
#ifdef CONFIG_SYSFS
static ssize_t of_fdt_raw_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
@@ -81,6 +81,7 @@ extern const void *of_flat_dt_match_machine(const void *default_match,
/* Other Prototypes */
extern void unflatten_device_tree(void);
extern void unflatten_and_copy_device_tree(void);
+extern void setup_of(void);
extern void early_init_devtree(void *);
extern void early_get_first_memblock_info(void *, phys_addr_t *);
#else /* CONFIG_OF_EARLY_FLATTREE */
@@ -91,6 +92,7 @@ static inline void early_init_fdt_reserve_self(void) {}
static inline const char *of_flat_dt_get_machine_name(void) { return NULL; }
static inline void unflatten_device_tree(void) {}
static inline void unflatten_and_copy_device_tree(void) {}
+static inline void setup_of(void) {}
#endif /* CONFIG_OF_EARLY_FLATTREE */
#endif /* __ASSEMBLY__ */
@@ -101,6 +101,7 @@
#include <linux/init_syscalls.h>
#include <linux/stackdepot.h>
#include <linux/randomize_kstack.h>
+#include <linux/of_fdt.h>
#include <net/net_namespace.h>
#include <asm/io.h>
@@ -961,6 +962,7 @@ asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
pr_notice("%s", linux_banner);
early_security_init();
setup_arch(&command_line);
+ setup_of();
setup_boot_config();
setup_command_line(command_line);
setup_nr_cpu_ids();