From f1dcee59a2afc4cf39699eef7631edbff8693933 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 22 Feb 2016 22:55:56 -0700 Subject: [PATCH] spl: Add an option to load a FIT containing U-Boot This provides a way to load a FIT containing U-Boot and a selection of device tree files. The board can select the correct device tree by probing the hardware. Then U-Boot is started with the selected device tree. Signed-off-by: Simon Glass --- Kconfig | 11 +++ common/spl/Makefile | 1 + common/spl/spl_fit.c | 194 +++++++++++++++++++++++++++++++++++++++++++ include/image.h | 2 +- include/spl.h | 18 ++++ 5 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 common/spl/spl_fit.c diff --git a/Kconfig b/Kconfig index a9a4938401..e7002edcb9 100644 --- a/Kconfig +++ b/Kconfig @@ -273,6 +273,17 @@ config SYS_TEXT_BASE help TODO: Move CONFIG_SYS_TEXT_BASE for all the architecture +config SPL_LOAD_FIT + bool "Enable SPL loading U-Boot as a FIT" + depends on FIT + help + Normally with the SPL framework a legacy image is generated as part + of the build. This contains U-Boot along with information as to + where it should be loaded. This option instead enables generation + of a FIT (Flat Image Tree) which provides more flexibility. In + particular it can handle selecting from multiple device tree + and passing the correct one to U-Boot. + config SYS_CLK_FREQ depends on ARC || ARCH_SUNXI int "CPU clock frequency" diff --git a/common/spl/Makefile b/common/spl/Makefile index 10a4589969..2e0f695e46 100644 --- a/common/spl/Makefile +++ b/common/spl/Makefile @@ -10,6 +10,7 @@ ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_FRAMEWORK) += spl.o +obj-$(CONFIG_SPL_LOAD_FIT) += spl_fit.o obj-$(CONFIG_SPL_NOR_SUPPORT) += spl_nor.o obj-$(CONFIG_SPL_YMODEM_SUPPORT) += spl_ymodem.o obj-$(CONFIG_SPL_NAND_SUPPORT) += spl_nand.o diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c new file mode 100644 index 0000000000..1a5c0275a7 --- /dev/null +++ b/common/spl/spl_fit.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2016 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +static ulong fdt_getprop_u32(const void *fdt, int node, const char *prop) +{ + const u32 *cell; + int len; + + cell = fdt_getprop(fdt, node, prop, &len); + if (len != sizeof(*cell)) + return -1U; + return fdt32_to_cpu(*cell); +} + +static int spl_fit_select_fdt(const void *fdt, int images, int *fdt_offsetp) +{ + const char *name, *fdt_name; + int conf, node, fdt_node; + int len; + + *fdt_offsetp = 0; + conf = fdt_path_offset(fdt, FIT_CONFS_PATH); + if (conf < 0) { + debug("%s: Cannot find /configurations node: %d\n", __func__, + conf); + return -EINVAL; + } + for (node = fdt_first_subnode(fdt, conf); + node >= 0; + node = fdt_next_subnode(fdt, node)) { + name = fdt_getprop(fdt, node, "description", &len); + if (!name) + return -EINVAL; + if (board_fit_config_name_match(name)) + continue; + + debug("Selecting config '%s'", name); + fdt_name = fdt_getprop(fdt, node, FIT_FDT_PROP, &len); + if (!fdt_name) { + debug("%s: Cannot find fdt name property: %d\n", + __func__, len); + return -EINVAL; + } + + debug(", fdt '%s'\n", fdt_name); + fdt_node = fdt_subnode_offset(fdt, images, fdt_name); + if (fdt_node < 0) { + debug("%s: Cannot find fdt node '%s': %d\n", + __func__, fdt_name, fdt_node); + return -EINVAL; + } + + *fdt_offsetp = fdt_getprop_u32(fdt, fdt_node, "data-offset"); + len = fdt_getprop_u32(fdt, fdt_node, "data-size"); +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("FIT: Selected '%s'\n", name); +#endif + + return len; + } + +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("No matching DT out of these options:\n"); + for (node = fdt_first_subnode(fdt, conf); + node >= 0; + node = fdt_next_subnode(fdt, node)) { + name = fdt_getprop(fdt, node, "name", &len); + printf(" %s\n", name); + } +#endif + + return -ENOENT; +} + +int spl_load_simple_fit(struct spl_load_info *info, ulong sector, void *fit) +{ + int sectors; + ulong size, load; + unsigned long count; + int node, images; + void *load_ptr; + int fdt_offset, fdt_len; + int data_offset, data_size; + int base_offset; + int src_sector; + void *dst; + + /* + * Figure out where the external images start. This is the base for the + * data-offset properties in each image. + */ + size = fdt_totalsize(fit); + size = (size + 3) & ~3; + base_offset = (size + 3) & ~3; + + /* + * So far we only have one block of data from the FIT. Read the entire + * thing, including that first block, placing it so it finishes before + * where we will load the image. + * + * Note that we will load the image such that its first byte will be + * at the load address. Since that byte may be part-way through a + * block, we may load the image up to one block before the load + * address. So take account of that here by subtracting an addition + * block length from the FIT start position. + * + * In fact the FIT has its own load address, but we assume it cannot + * be before CONFIG_SYS_TEXT_BASE. + */ + fit = (void *)(CONFIG_SYS_TEXT_BASE - size - info->bl_len); + sectors = (size + info->bl_len - 1) / info->bl_len; + count = info->read(info, sector, sectors, fit); + debug("fit read sector %lx, sectors=%d, dst=%p, count=%lu\n", + sector, sectors, fit, count); + if (count == 0) + return -EIO; + + /* find the firmware image to load */ + images = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images < 0) { + debug("%s: Cannot find /images node: %d\n", __func__, images); + return -1; + } + node = fdt_first_subnode(fit, images); + if (node < 0) { + debug("%s: Cannot find first image node: %d\n", __func__, node); + return -1; + } + + /* Get its information and set up the spl_image structure */ + data_offset = fdt_getprop_u32(fit, node, "data-offset"); + data_size = fdt_getprop_u32(fit, node, "data-size"); + load = fdt_getprop_u32(fit, node, "load"); + debug("data_offset=%x, data_size=%x\n", data_offset, data_size); + spl_image.load_addr = load; + spl_image.entry_point = load; + spl_image.os = IH_OS_U_BOOT; + + /* + * Work out where to place the image. We read it so that the first + * byte will be at 'load'. This may mean we need to load it starting + * before then, since we can only read whole blocks. + */ + sectors = (data_size + info->bl_len - 1) / info->bl_len; + data_offset += base_offset; + load_ptr = (void *)load; + debug("U-Boot size %x, data %p\n", data_size, load_ptr); + dst = load_ptr - (data_offset % info->bl_len); + + /* Read the image */ + src_sector = sector + data_offset / info->bl_len; + debug("image: data_offset=%x, dst=%p, src_sector=%x, sectors=%x\n", + data_offset, dst, src_sector, sectors); + count = info->read(info, src_sector, sectors, dst); + if (count != sectors) + return -EIO; + + /* Figure out which device tree the board wants to use */ + fdt_len = spl_fit_select_fdt(fit, images, &fdt_offset); + if (fdt_len < 0) + return fdt_len; + + /* + * Read the device tree and place it after the image. There may be + * some extra data before it since we can only read entire blocks. + */ + dst = load_ptr + data_size; + fdt_offset += base_offset; + count = info->read(info, sector + fdt_offset / info->bl_len, sectors, + dst); + debug("fit read %x sectors to %x, dst %p, data_offset %x\n", + sectors, spl_image.load_addr, dst, fdt_offset); + if (count != sectors) + return -EIO; + + /* + * Copy the device tree so that it starts immediately after the image. + * After this we will have the U-Boot image and its device tree ready + * for us to start. + */ + memcpy(dst, dst + fdt_offset % info->bl_len, fdt_len); + + return 0; +} diff --git a/include/image.h b/include/image.h index 0605280c20..f9ee5649c5 100644 --- a/include/image.h +++ b/include/image.h @@ -780,7 +780,6 @@ int bootz_setup(ulong image, ulong *start, ulong *end); /*******************************************************************/ /* New uImage format specific code (prefixed with fit_) */ /*******************************************************************/ -#if IMAGE_ENABLE_FIT #define FIT_IMAGES_PATH "/images" #define FIT_CONFS_PATH "/configurations" @@ -813,6 +812,7 @@ int bootz_setup(ulong image, ulong *start, ulong *end); #define FIT_MAX_HASH_LEN HASH_MAX_DIGEST_SIZE +#if IMAGE_ENABLE_FIT /* cmdline argument format parsing */ int fit_parse_conf(const char *spec, ulong addr_curr, ulong *addr, const char **conf_name); diff --git a/include/spl.h b/include/spl.h index 92cdc049d4..16f2f6a9ee 100644 --- a/include/spl.h +++ b/include/spl.h @@ -29,6 +29,24 @@ struct spl_image_info { u32 flags; }; +/* + * Information required to load data from a device + * + * @dev: Pointer to the device, e.g. struct mmc * + * @priv: Private data for the device + * @bl_len: Block length for reading in bytes + * @read: Function to call to read from the device + */ +struct spl_load_info { + void *dev; + void *priv; + int bl_len; + ulong (*read)(struct spl_load_info *load, ulong sector, ulong count, + void *buf); +}; + +int spl_load_simple_fit(struct spl_load_info *info, ulong sector, void *fdt); + #define SPL_COPY_PAYLOAD_ONLY 1 extern struct spl_image_info spl_image; -- 2.25.1