]> git.ipfire.org Git - thirdparty/u-boot.git/blobdiff - lib/fdtdec.c
Revert "Merge patch series "arm: dts: am62-beagleplay: Fix Beagleplay Ethernet""
[thirdparty/u-boot.git] / lib / fdtdec.c
index ad090ea51eb5d46d433112bb8e258b43e95bd36a..b2c59ab3818b3235e36b61b054c76a803423de63 100644 (file)
@@ -1,18 +1,25 @@
 // 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>
@@ -23,6 +30,8 @@
 #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>
@@ -62,7 +71,6 @@ static const char * const compat_names[COMPAT_COUNT] = {
        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"),
@@ -76,6 +84,20 @@ static const char * const compat_names[COMPAT_COUNT] = {
        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 */
@@ -238,14 +260,7 @@ int fdtdec_get_pci_bar32(const struct udevice *dev, struct fdt_pci_addr *addr,
 
        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;
 }
@@ -581,6 +596,34 @@ int fdtdec_get_chosen_node(const void *blob, const char *name)
        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)
 {
        /*
@@ -589,34 +632,7 @@ 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;
 }
 
@@ -643,7 +659,7 @@ int fdtdec_lookup_phandle(const void *blob, int node, const char *prop_name)
  * @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,
@@ -1054,7 +1070,7 @@ ofnode get_next_memory_node(ofnode mem)
 {
        do {
                mem = ofnode_by_prop_value(mem, "device_type", "memory", 7);
-       } while (!ofnode_is_available(mem));
+       } while (!ofnode_is_enabled(mem));
 
        return mem;
 }
@@ -1146,11 +1162,10 @@ int fdtdec_setup_mem_size_base_lowest(void)
        return 0;
 }
 
-#if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
-# if CONFIG_IS_ENABLED(MULTI_DTB_FIT_GZIP) ||\
-       CONFIG_IS_ENABLED(MULTI_DTB_FIT_LZO)
 static int uncompress_blob(const void *src, ulong sz_src, void **dstp)
 {
+#if CONFIG_IS_ENABLED(MULTI_DTB_FIT_GZIP) ||\
+       CONFIG_IS_ENABLED(MULTI_DTB_FIT_LZO)
        size_t sz_out = CONFIG_VAL(MULTI_DTB_FIT_UNCOMPRESS_SZ);
        bool gzip = 0, lzo = 0;
        ulong sz_in = sz_src;
@@ -1175,11 +1190,11 @@ static int uncompress_blob(const void *src, ulong sz_src, void **dstp)
                        return -ENOMEM;
                }
        } else  {
-#  if CONFIG_IS_ENABLED(MULTI_DTB_FIT_USER_DEFINED_AREA)
+# if CONFIG_IS_ENABLED(MULTI_DTB_FIT_USER_DEFINED_AREA)
                dst = (void *)CONFIG_VAL(MULTI_DTB_FIT_USER_DEF_ADDR);
-#  else
+# else
                return -ENOTSUPP;
-#  endif
+# endif
        }
 
        if (CONFIG_IS_ENABLED(GZIP) && gzip)
@@ -1197,38 +1212,61 @@ static int uncompress_blob(const void *src, ulong sz_src, void **dstp)
                return -EBADMSG;
        }
        *dstp = dst;
-       return 0;
-}
-# else
-static int uncompress_blob(const void *src, ulong sz_src, void **dstp)
-{
+#else
+       *dstp = (void *)src;
        *dstp = (void *)src;
+#endif
        return 0;
 }
-# endif
-#endif
 
-#if defined(CONFIG_OF_BOARD) || defined(CONFIG_OF_SEPARATE)
-/*
- * 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(void)
+static void *fdt_find_separate(void)
 {
        void *fdt_blob = NULL;
+
+       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;
 }
-#endif
 
 int fdtdec_set_ethernet_mac_address(void *fdt, const u8 *mac, size_t size)
 {
@@ -1586,73 +1624,109 @@ int fdtdec_set_carveout(void *blob, const char *node, const char *prop_name,
        return 0;
 }
 
+/* TODO(sjg@chromium.org): This function should not be weak */
 __weak int fdtdec_board_setup(const void *fdt_blob)
 {
        return 0;
 }
 
-int fdtdec_setup(void)
+/**
+ * setup_multi_dtb_fit() - locate the correct dtb from a FIT
+ *
+ * This supports the CONFIG_MULTI_DTB_FIT feature, looking for the dtb in a
+ * supplied FIT
+ *
+ * It accepts the current value of gd->fdt_blob, which points to the FIT, then
+ * updates that gd->fdt_blob, to point to the chosen dtb so that U-Boot uses the
+ * correct one
+ */
+static void setup_multi_dtb_fit(void)
 {
-       int ret;
-#if CONFIG_IS_ENABLED(OF_CONTROL)
-# if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
-       void *fdt_blob;
-# endif
-# ifdef CONFIG_OF_EMBED
-       /* Get a pointer to the FDT */
-#  ifdef CONFIG_SPL_BUILD
-       gd->fdt_blob = __dtb_dt_spl_begin;
-#  else
-       gd->fdt_blob = __dtb_dt_begin;
-#  endif
-# elif defined(CONFIG_OF_BOARD) || defined(CONFIG_OF_SEPARATE)
-       /* Allow the board to override the fdt address. */
-       gd->fdt_blob = board_fdt_blob_setup();
-# elif defined(CONFIG_OF_HOSTFILE)
-       if (sandbox_read_fdt_from_file()) {
-               puts("Failed to read control FDT\n");
-               return -1;
-       }
-# elif defined(CONFIG_OF_PRIOR_STAGE)
-       gd->fdt_blob = (void *)(uintptr_t)prior_stage_fdt_address;
-# endif
-# ifndef 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);
-# endif
+       void *blob;
 
-# if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
        /*
         * Try and uncompress the blob.
         * Unfortunately there is no way to know how big the input blob really
         * is. So let us set the maximum input size arbitrarily high. 16MB
         * ought to be more than enough for packed DTBs.
         */
-       if (uncompress_blob(gd->fdt_blob, 0x1000000, &fdt_blob) == 0)
-               gd->fdt_blob = fdt_blob;
+       if (uncompress_blob(gd->fdt_blob, 0x1000000, &blob) == 0)
+               gd->fdt_blob = blob;
 
        /*
         * Check if blob is a FIT images containings DTBs.
         * If so, pick the most relevant
         */
-       fdt_blob = locate_dtb_in_fit(gd->fdt_blob);
-       if (fdt_blob) {
-               gd->multi_dtb_fit = gd->fdt_blob;
-               gd->fdt_blob = fdt_blob;
+       blob = locate_dtb_in_fit(gd->fdt_blob);
+       if (blob) {
+               gd_set_multi_dtb_fit(gd->fdt_blob);
+               gd->fdt_blob = blob;
+               gd->fdt_src = FDTSRC_FIT;
        }
+}
 
-# endif
-#endif
+int fdtdec_setup(void)
+{
+       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. */
+       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)) {
+               ulong addr;
 
-       ret = fdtdec_prepare_fdt();
+               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(gd->fdt_blob);
        if (!ret)
                ret = fdtdec_board_setup(gd->fdt_blob);
+       oftree_reset();
+
        return ret;
 }
 
-#if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
 int fdtdec_resetup(int *rescan)
 {
        void *fdt_blob;
@@ -1663,8 +1737,8 @@ int fdtdec_resetup(int *rescan)
         * FIT image stillpresent there. Save the time and space
         * required to uncompress it again.
         */
-       if (gd->multi_dtb_fit) {
-               fdt_blob = locate_dtb_in_fit(gd->multi_dtb_fit);
+       if (gd_multi_dtb_fit()) {
+               fdt_blob = locate_dtb_in_fit(gd_multi_dtb_fit());
 
                if (fdt_blob == gd->fdt_blob) {
                        /*
@@ -1677,7 +1751,7 @@ int fdtdec_resetup(int *rescan)
 
                *rescan = 1;
                gd->fdt_blob = fdt_blob;
-               return fdtdec_prepare_fdt();
+               return fdtdec_prepare_fdt(fdt_blob);
        }
 
        /*
@@ -1688,7 +1762,6 @@ int fdtdec_resetup(int *rescan)
        *rescan = 0;
        return 0;
 }
-#endif
 
 int fdtdec_decode_ram_size(const void *blob, const char *area, int board_id,
                           phys_addr_t *basep, phys_size_t *sizep,