// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2011 The Chromium OS Authors.
+ *
+ * NOTE: Please do not add new devicetree-reading functionality into this file.
+ * Add it to the ofnode API instead, since that is compatible with livetree.
*/
#ifndef USE_HOSTCC
-#include <common.h>
+
+#define LOG_CATEGORY LOGC_DT
+
+#include <bloblist.h>
#include <boot_fit.h>
+#include <display_options.h>
#include <dm.h>
#include <hang.h>
#include <init.h>
#include <log.h>
#include <malloc.h>
#include <net.h>
-#include <dm/of_extra.h>
+#include <spl.h>
#include <env.h>
#include <errno.h>
#include <fdtdec.h>
#include <serial.h>
#include <asm/global_data.h>
#include <asm/sections.h>
+#include <dm/ofnode.h>
+#include <dm/of_extra.h>
#include <linux/ctype.h>
#include <linux/lzo.h>
#include <linux/ioport.h>
COMPAT(INTEL_BAYTRAIL_FSP, "intel,baytrail-fsp"),
COMPAT(INTEL_BAYTRAIL_FSP_MDP, "intel,baytrail-fsp-mdp"),
COMPAT(INTEL_IVYBRIDGE_FSP, "intel,ivybridge-fsp"),
- COMPAT(COMPAT_SUNXI_NAND, "allwinner,sun4i-a10-nand"),
COMPAT(ALTERA_SOCFPGA_CLK, "altr,clk-mgr"),
COMPAT(ALTERA_SOCFPGA_PINCTRL_SINGLE, "pinctrl-single"),
COMPAT(ALTERA_SOCFPGA_H2F_BRG, "altr,socfpga-hps2fpga-bridge"),
COMPAT(ALTERA_SOCFPGA_CLK_INIT, "altr,socfpga-a10-clk-init")
};
+static const char *const fdt_src_name[] = {
+ [FDTSRC_SEPARATE] = "separate",
+ [FDTSRC_FIT] = "fit",
+ [FDTSRC_BOARD] = "board",
+ [FDTSRC_EMBED] = "embed",
+ [FDTSRC_ENV] = "env",
+ [FDTSRC_BLOBLIST] = "bloblist",
+};
+
+const char *fdtdec_get_srcname(void)
+{
+ return fdt_src_name[gd->fdt_src];
+}
+
const char *fdtdec_get_compatible(enum fdt_compat_id id)
{
/* We allow reading of the 'unknown' ID for testing purposes */
barnum = (barnum - PCI_BASE_ADDRESS_0) / 4;
- /*
- * There is a strange toolchain bug with nds32 which complains about
- * an undefined reference here, even if fdtdec_get_pci_bar32() is never
- * called. An #ifdef seems to be the only fix!
- */
-#if !IS_ENABLED(CONFIG_NDS32)
*bar = dm_pci_read_bar32(dev, barnum);
-#endif
return 0;
}
return fdt_path_offset(blob, prop);
}
+/**
+ * fdtdec_prepare_fdt() - Check we have a valid fdt available to control U-Boot
+ *
+ * @blob: Blob to check
+ *
+ * If not, a message is printed to the console if the console is ready.
+ *
+ * Return: 0 if all ok, -ENOENT if not
+ */
+static int fdtdec_prepare_fdt(const void *blob)
+{
+ if (!blob || ((uintptr_t)blob & 3) || fdt_check_header(blob)) {
+ if (spl_phase() <= PHASE_SPL) {
+ puts("Missing DTB\n");
+ } else {
+ printf("No valid device tree binary found at %p\n",
+ blob);
+ if (_DEBUG && blob) {
+ printf("fdt_blob=%p\n", blob);
+ print_buffer((ulong)blob, blob, 4, 32, 0);
+ }
+ }
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
int fdtdec_check_fdt(void)
{
/*
* FDT (prior to console ready) will need to make their own
* arrangements and do their own checks.
*/
- assert(!fdtdec_prepare_fdt());
- return 0;
-}
-
-/*
- * This function is a little odd in that it accesses global data. At some
- * point if the architecture board.c files merge this will make more sense.
- * Even now, it is common code.
- */
-int fdtdec_prepare_fdt(void)
-{
- if (!gd->fdt_blob || ((uintptr_t)gd->fdt_blob & 3) ||
- fdt_check_header(gd->fdt_blob)) {
-#ifdef CONFIG_SPL_BUILD
- puts("Missing DTB\n");
-#else
- printf("No valid device tree binary found at %p\n",
- gd->fdt_blob);
-# ifdef DEBUG
- if (gd->fdt_blob) {
- printf("fdt_blob=%p\n", gd->fdt_blob);
- print_buffer((ulong)gd->fdt_blob, gd->fdt_blob, 4,
- 32, 0);
- }
-# endif
-#endif
- return -1;
- }
+ assert(!fdtdec_prepare_fdt(gd->fdt_blob));
return 0;
}
* @param min_len minimum property length in bytes
* @param err 0 if ok, or -FDT_ERR_NOTFOUND if the property is not
found, or -FDT_ERR_BADLAYOUT if not enough data
- * @return pointer to cell, which is only valid if err == 0
+ * Return: pointer to cell, which is only valid if err == 0
*/
static const void *get_prop_check_min_len(const void *blob, int node,
const char *prop_name, int min_len,
{
do {
mem = ofnode_by_prop_value(mem, "device_type", "memory", 7);
- } while (!ofnode_is_available(mem));
+ } while (!ofnode_is_enabled(mem));
return mem;
}
return 0;
}
-/*
- * For CONFIG_OF_SEPARATE, the board may optionally implement this to
- * provide and/or fixup the fdt.
+/**
+ * fdt_find_separate() - Find a devicetree at the end of the image
+ *
+ * Return: pointer to FDT blob
*/
-__weak void *board_fdt_blob_setup(int *err)
+static void *fdt_find_separate(void)
{
void *fdt_blob = NULL;
- *err = 0;
+ if (IS_ENABLED(CONFIG_SANDBOX))
+ return NULL;
+
#ifdef CONFIG_SPL_BUILD
/* FDT is at end of BSS unless it is in a different memory region */
if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS))
- fdt_blob = (ulong *)&_image_binary_end;
+ fdt_blob = (ulong *)_image_binary_end;
else
- fdt_blob = (ulong *)&__bss_end;
+ fdt_blob = (ulong *)__bss_end;
#else
/* FDT is at end of image */
- fdt_blob = (ulong *)&_end;
+ fdt_blob = (ulong *)_end;
+
+ if (_DEBUG && !fdtdec_prepare_fdt(fdt_blob)) {
+ int stack_ptr;
+ const void *top = fdt_blob + fdt_totalsize(fdt_blob);
+
+ /*
+ * Perform a sanity check on the memory layout. If this fails,
+ * it indicates that the device tree is positioned above the
+ * global data pointer or the stack pointer. This should not
+ * happen.
+ *
+ * If this fails, check that SYS_INIT_SP_ADDR has enough space
+ * below it for SYS_MALLOC_F_LEN and global_data, as well as the
+ * stack, without overwriting the device tree or U-Boot itself.
+ * Since the device tree is sitting at _end (the start of the
+ * BSS region), we need the top of the device tree to be below
+ * any memory allocated by board_init_f_alloc_reserve().
+ */
+ if (top > (void *)gd || top > (void *)&stack_ptr) {
+ printf("FDT %p gd %p\n", fdt_blob, gd);
+ panic("FDT overlap");
+ }
+ }
#endif
return fdt_blob;
return 0;
}
+/* TODO(sjg@chromium.org): This function should not be weak */
__weak int fdtdec_board_setup(const void *fdt_blob)
{
return 0;
if (blob) {
gd_set_multi_dtb_fit(gd->fdt_blob);
gd->fdt_blob = blob;
+ gd->fdt_src = FDTSRC_FIT;
}
}
int fdtdec_setup(void)
{
- int ret;
-#ifdef CONFIG_OF_EMBED
- /* Get a pointer to the FDT */
- gd->fdt_blob = dtb_dt_embedded();
-#elif defined(CONFIG_OF_BOARD) || defined(CONFIG_OF_SEPARATE)
+ int ret = -ENOENT;
+
+ /* If allowing a bloblist, check that first */
+ if (CONFIG_IS_ENABLED(BLOBLIST)) {
+ ret = bloblist_maybe_init();
+ if (!ret) {
+ gd->fdt_blob = bloblist_find(BLOBLISTT_CONTROL_FDT, 0);
+ if (gd->fdt_blob) {
+ gd->fdt_src = FDTSRC_BLOBLIST;
+ log_debug("Devicetree is in bloblist at %p\n",
+ gd->fdt_blob);
+ } else {
+ log_debug("No FDT found in bloblist\n");
+ ret = -ENOENT;
+ }
+ }
+ }
+
+ /* Otherwise, the devicetree is typically appended to U-Boot */
+ if (ret) {
+ if (IS_ENABLED(CONFIG_OF_SEPARATE)) {
+ gd->fdt_blob = fdt_find_separate();
+ gd->fdt_src = FDTSRC_SEPARATE;
+ } else { /* embed dtb in ELF file for testing / development */
+ gd->fdt_blob = dtb_dt_embedded();
+ gd->fdt_src = FDTSRC_EMBED;
+ }
+ }
+
/* Allow the board to override the fdt address. */
- gd->fdt_blob = board_fdt_blob_setup(&ret);
- if (ret)
- return ret;
-#endif
+ if (IS_ENABLED(CONFIG_OF_BOARD)) {
+ gd->fdt_blob = board_fdt_blob_setup(&ret);
+ if (!ret)
+ gd->fdt_src = FDTSRC_BOARD;
+ else if (ret != -EEXIST)
+ return ret;
+ }
+
+ /* Allow the early environment to override the fdt address */
if (!IS_ENABLED(CONFIG_SPL_BUILD)) {
- /* Allow the early environment to override the fdt address */
- gd->fdt_blob = map_sysmem(env_get_ulong("fdtcontroladdr", 16,
- (unsigned long)map_to_sysmem(gd->fdt_blob)), 0);
+ ulong addr;
+
+ addr = env_get_hex("fdtcontroladdr", 0);
+ if (addr) {
+ gd->fdt_blob = map_sysmem(addr, 0);
+ gd->fdt_src = FDTSRC_ENV;
+ }
}
if (CONFIG_IS_ENABLED(MULTI_DTB_FIT))
setup_multi_dtb_fit();
- ret = fdtdec_prepare_fdt();
+ ret = fdtdec_prepare_fdt(gd->fdt_blob);
if (!ret)
ret = fdtdec_board_setup(gd->fdt_blob);
+ oftree_reset();
+
return ret;
}
*rescan = 1;
gd->fdt_blob = fdt_blob;
- return fdtdec_prepare_fdt();
+ return fdtdec_prepare_fdt(fdt_blob);
}
/*