183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2f1dcee59SSimon Glass /*
3f1dcee59SSimon Glass * Copyright (C) 2016 Google, Inc
4f1dcee59SSimon Glass * Written by Simon Glass <sjg@chromium.org>
5f1dcee59SSimon Glass */
6f1dcee59SSimon Glass
7f1dcee59SSimon Glass #include <common.h>
8f1dcee59SSimon Glass #include <errno.h>
93313ae66SMichal Simek #include <fpga.h>
10f1dcee59SSimon Glass #include <image.h>
11b08c8c48SMasahiro Yamada #include <linux/libfdt.h>
12f1dcee59SSimon Glass #include <spl.h>
13f1dcee59SSimon Glass
147264f292SYork Sun #ifndef CONFIG_SYS_BOOTM_LEN
157264f292SYork Sun #define CONFIG_SYS_BOOTM_LEN (64 << 20)
167264f292SYork Sun #endif
177264f292SYork Sun
board_spl_fit_post_load(ulong load_addr,size_t length)18e246bfcfSYe Li __weak void board_spl_fit_post_load(ulong load_addr, size_t length)
19e246bfcfSYe Li {
20e246bfcfSYe Li }
21e246bfcfSYe Li
board_spl_fit_size_align(ulong size)22e246bfcfSYe Li __weak ulong board_spl_fit_size_align(ulong size)
23e246bfcfSYe Li {
24e246bfcfSYe Li return size;
25e246bfcfSYe Li }
26e246bfcfSYe Li
27736806fbSAndre Przywara /**
28a616c783SPhilipp Tomsich * spl_fit_get_image_name(): By using the matching configuration subnode,
29736806fbSAndre Przywara * retrieve the name of an image, specified by a property name and an index
30736806fbSAndre Przywara * into that.
31736806fbSAndre Przywara * @fit: Pointer to the FDT blob.
32736806fbSAndre Przywara * @images: Offset of the /images subnode.
33736806fbSAndre Przywara * @type: Name of the property within the configuration subnode.
34736806fbSAndre Przywara * @index: Index into the list of strings in this property.
35a616c783SPhilipp Tomsich * @outname: Name of the image
36736806fbSAndre Przywara *
37a616c783SPhilipp Tomsich * Return: 0 on success, or a negative error number
38736806fbSAndre Przywara */
spl_fit_get_image_name(const void * fit,int images,const char * type,int index,char ** outname)39a616c783SPhilipp Tomsich static int spl_fit_get_image_name(const void *fit, int images,
40a616c783SPhilipp Tomsich const char *type, int index,
41a616c783SPhilipp Tomsich char **outname)
424b9340abSAndre Przywara {
434b9340abSAndre Przywara const char *name, *str;
44a616c783SPhilipp Tomsich __maybe_unused int node;
45a616c783SPhilipp Tomsich int conf_node;
464b9340abSAndre Przywara int len, i;
47f1dcee59SSimon Glass
483863f840SCooper Jr., Franklin conf_node = fit_find_config_node(fit);
494b9340abSAndre Przywara if (conf_node < 0) {
50f1dcee59SSimon Glass #ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
51f1dcee59SSimon Glass printf("No matching DT out of these options:\n");
524b9340abSAndre Przywara for (node = fdt_first_subnode(fit, conf_node);
53f1dcee59SSimon Glass node >= 0;
544b9340abSAndre Przywara node = fdt_next_subnode(fit, node)) {
554b9340abSAndre Przywara name = fdt_getprop(fit, node, "description", &len);
56f1dcee59SSimon Glass printf(" %s\n", name);
57f1dcee59SSimon Glass }
58f1dcee59SSimon Glass #endif
594b9340abSAndre Przywara return conf_node;
604b9340abSAndre Przywara }
61f1dcee59SSimon Glass
624b9340abSAndre Przywara name = fdt_getprop(fit, conf_node, type, &len);
634b9340abSAndre Przywara if (!name) {
644b9340abSAndre Przywara debug("cannot find property '%s': %d\n", type, len);
654b9340abSAndre Przywara return -EINVAL;
664b9340abSAndre Przywara }
674b9340abSAndre Przywara
684b9340abSAndre Przywara str = name;
694b9340abSAndre Przywara for (i = 0; i < index; i++) {
704b9340abSAndre Przywara str = strchr(str, '\0') + 1;
714b9340abSAndre Przywara if (!str || (str - name >= len)) {
724b9340abSAndre Przywara debug("no string for index %d\n", index);
734b9340abSAndre Przywara return -E2BIG;
744b9340abSAndre Przywara }
754b9340abSAndre Przywara }
764b9340abSAndre Przywara
77a616c783SPhilipp Tomsich *outname = (char *)str;
78a616c783SPhilipp Tomsich return 0;
79a616c783SPhilipp Tomsich }
80a616c783SPhilipp Tomsich
81a616c783SPhilipp Tomsich /**
82a616c783SPhilipp Tomsich * spl_fit_get_image_node(): By using the matching configuration subnode,
83a616c783SPhilipp Tomsich * retrieve the name of an image, specified by a property name and an index
84a616c783SPhilipp Tomsich * into that.
85a616c783SPhilipp Tomsich * @fit: Pointer to the FDT blob.
86a616c783SPhilipp Tomsich * @images: Offset of the /images subnode.
87a616c783SPhilipp Tomsich * @type: Name of the property within the configuration subnode.
88a616c783SPhilipp Tomsich * @index: Index into the list of strings in this property.
89a616c783SPhilipp Tomsich *
90a616c783SPhilipp Tomsich * Return: the node offset of the respective image node or a negative
91a616c783SPhilipp Tomsich * error number.
92a616c783SPhilipp Tomsich */
spl_fit_get_image_node(const void * fit,int images,const char * type,int index)93a616c783SPhilipp Tomsich static int spl_fit_get_image_node(const void *fit, int images,
94a616c783SPhilipp Tomsich const char *type, int index)
95a616c783SPhilipp Tomsich {
96a616c783SPhilipp Tomsich char *str;
97a616c783SPhilipp Tomsich int err;
98a616c783SPhilipp Tomsich int node;
99a616c783SPhilipp Tomsich
100a616c783SPhilipp Tomsich err = spl_fit_get_image_name(fit, images, type, index, &str);
101a616c783SPhilipp Tomsich if (err)
102a616c783SPhilipp Tomsich return err;
103a616c783SPhilipp Tomsich
1044b9340abSAndre Przywara debug("%s: '%s'\n", type, str);
105a616c783SPhilipp Tomsich
1064b9340abSAndre Przywara node = fdt_subnode_offset(fit, images, str);
1074b9340abSAndre Przywara if (node < 0) {
1084b9340abSAndre Przywara debug("cannot find image node '%s': %d\n", str, node);
1094b9340abSAndre Przywara return -EINVAL;
1104b9340abSAndre Przywara }
1114b9340abSAndre Przywara
112736806fbSAndre Przywara return node;
113f1dcee59SSimon Glass }
114f1dcee59SSimon Glass
get_aligned_image_offset(struct spl_load_info * info,int offset)115eafd5410SLokesh Vutla static int get_aligned_image_offset(struct spl_load_info *info, int offset)
116eafd5410SLokesh Vutla {
117eafd5410SLokesh Vutla /*
118eafd5410SLokesh Vutla * If it is a FS read, get the first address before offset which is
119eafd5410SLokesh Vutla * aligned to ARCH_DMA_MINALIGN. If it is raw read return the
120eafd5410SLokesh Vutla * block number to which offset belongs.
121eafd5410SLokesh Vutla */
122eafd5410SLokesh Vutla if (info->filename)
123eafd5410SLokesh Vutla return offset & ~(ARCH_DMA_MINALIGN - 1);
124eafd5410SLokesh Vutla
125eafd5410SLokesh Vutla return offset / info->bl_len;
126eafd5410SLokesh Vutla }
127eafd5410SLokesh Vutla
get_aligned_image_overhead(struct spl_load_info * info,int offset)128eafd5410SLokesh Vutla static int get_aligned_image_overhead(struct spl_load_info *info, int offset)
129eafd5410SLokesh Vutla {
130eafd5410SLokesh Vutla /*
131eafd5410SLokesh Vutla * If it is a FS read, get the difference between the offset and
132eafd5410SLokesh Vutla * the first address before offset which is aligned to
133eafd5410SLokesh Vutla * ARCH_DMA_MINALIGN. If it is raw read return the offset within the
134eafd5410SLokesh Vutla * block.
135eafd5410SLokesh Vutla */
136eafd5410SLokesh Vutla if (info->filename)
137eafd5410SLokesh Vutla return offset & (ARCH_DMA_MINALIGN - 1);
138eafd5410SLokesh Vutla
139eafd5410SLokesh Vutla return offset % info->bl_len;
140eafd5410SLokesh Vutla }
141eafd5410SLokesh Vutla
get_aligned_image_size(struct spl_load_info * info,int data_size,int offset)142eafd5410SLokesh Vutla static int get_aligned_image_size(struct spl_load_info *info, int data_size,
143eafd5410SLokesh Vutla int offset)
144eafd5410SLokesh Vutla {
1453cc1f380SLokesh Vutla data_size = data_size + get_aligned_image_overhead(info, offset);
1463cc1f380SLokesh Vutla
147eafd5410SLokesh Vutla if (info->filename)
1483cc1f380SLokesh Vutla return data_size;
149eafd5410SLokesh Vutla
150eafd5410SLokesh Vutla return (data_size + info->bl_len - 1) / info->bl_len;
151eafd5410SLokesh Vutla }
152eafd5410SLokesh Vutla
1538baa3818SAndre Przywara /**
1548baa3818SAndre Przywara * spl_load_fit_image(): load the image described in a certain FIT node
1558baa3818SAndre Przywara * @info: points to information about the device to load data from
1568baa3818SAndre Przywara * @sector: the start sector of the FIT image on the device
1578baa3818SAndre Przywara * @fit: points to the flattened device tree blob describing the FIT
1588baa3818SAndre Przywara * image
1598baa3818SAndre Przywara * @base_offset: the beginning of the data area containing the actual
1608baa3818SAndre Przywara * image data, relative to the beginning of the FIT
1618baa3818SAndre Przywara * @node: offset of the DT node describing the image to load (relative
1628baa3818SAndre Przywara * to @fit)
1638baa3818SAndre Przywara * @image_info: will be filled with information about the loaded image
1648baa3818SAndre Przywara * If the FIT node does not contain a "load" (address) property,
1658baa3818SAndre Przywara * the image gets loaded to the address pointed to by the
1668baa3818SAndre Przywara * load_addr member in this struct.
1678baa3818SAndre Przywara *
1688baa3818SAndre Przywara * Return: 0 on success or a negative error number.
1698baa3818SAndre Przywara */
spl_load_fit_image(struct spl_load_info * info,ulong sector,void * fit,ulong base_offset,int node,struct spl_image_info * image_info)1708baa3818SAndre Przywara static int spl_load_fit_image(struct spl_load_info *info, ulong sector,
1718baa3818SAndre Przywara void *fit, ulong base_offset, int node,
1728baa3818SAndre Przywara struct spl_image_info *image_info)
1738baa3818SAndre Przywara {
1743313ae66SMichal Simek int offset;
1758baa3818SAndre Przywara size_t length;
1765fd13d97SYork Sun int len;
177933f67aaSYork Sun ulong size;
1788baa3818SAndre Przywara ulong load_addr, load_ptr;
1798baa3818SAndre Przywara void *src;
1808baa3818SAndre Przywara ulong overhead;
1818baa3818SAndre Przywara int nr_sectors;
1828baa3818SAndre Przywara int align_len = ARCH_DMA_MINALIGN - 1;
1837264f292SYork Sun uint8_t image_comp = -1, type = -1;
1845fd13d97SYork Sun const void *data;
185a1be94b6SPeng Fan bool external_data = false;
1867264f292SYork Sun
18756419ea5SMarek Vasut if (IS_ENABLED(CONFIG_SPL_FPGA_SUPPORT) ||
18856419ea5SMarek Vasut (IS_ENABLED(CONFIG_SPL_OS_BOOT) && IS_ENABLED(CONFIG_SPL_GZIP))) {
18956419ea5SMarek Vasut if (fit_image_get_type(fit, node, &type))
19056419ea5SMarek Vasut puts("Cannot get image type.\n");
19156419ea5SMarek Vasut else
19256419ea5SMarek Vasut debug("%s ", genimg_get_type_name(type));
19356419ea5SMarek Vasut }
19456419ea5SMarek Vasut
1957264f292SYork Sun if (IS_ENABLED(CONFIG_SPL_OS_BOOT) && IS_ENABLED(CONFIG_SPL_GZIP)) {
1967264f292SYork Sun if (fit_image_get_comp(fit, node, &image_comp))
1977264f292SYork Sun puts("Cannot get image compression format.\n");
1987264f292SYork Sun else
1997264f292SYork Sun debug("%s ", genimg_get_comp_name(image_comp));
2007264f292SYork Sun }
2018baa3818SAndre Przywara
2025fd13d97SYork Sun if (fit_image_get_load(fit, node, &load_addr))
2038baa3818SAndre Przywara load_addr = image_info->load_addr;
2045fd13d97SYork Sun
205a1be94b6SPeng Fan if (!fit_image_get_data_position(fit, node, &offset)) {
206a1be94b6SPeng Fan external_data = true;
207a1be94b6SPeng Fan } else if (!fit_image_get_data_offset(fit, node, &offset)) {
2085fd13d97SYork Sun offset += base_offset;
209a1be94b6SPeng Fan external_data = true;
210a1be94b6SPeng Fan }
211a1be94b6SPeng Fan
212a1be94b6SPeng Fan if (external_data) {
213a1be94b6SPeng Fan /* External data */
2145fd13d97SYork Sun if (fit_image_get_data_size(fit, node, &len))
2155fd13d97SYork Sun return -ENOENT;
2165fd13d97SYork Sun
2178baa3818SAndre Przywara load_ptr = (load_addr + align_len) & ~align_len;
2185fd13d97SYork Sun length = len;
2198baa3818SAndre Przywara
2208baa3818SAndre Przywara overhead = get_aligned_image_overhead(info, offset);
2218baa3818SAndre Przywara nr_sectors = get_aligned_image_size(info, length, offset);
2228baa3818SAndre Przywara
2233313ae66SMichal Simek if (info->read(info,
2243313ae66SMichal Simek sector + get_aligned_image_offset(info, offset),
2258baa3818SAndre Przywara nr_sectors, (void *)load_ptr) != nr_sectors)
2268baa3818SAndre Przywara return -EIO;
2278baa3818SAndre Przywara
2285fd13d97SYork Sun debug("External data: dst=%lx, offset=%x, size=%lx\n",
2295fd13d97SYork Sun load_ptr, offset, (unsigned long)length);
2308baa3818SAndre Przywara src = (void *)load_ptr + overhead;
2315fd13d97SYork Sun } else {
2325fd13d97SYork Sun /* Embedded data */
2335fd13d97SYork Sun if (fit_image_get_data(fit, node, &data, &length)) {
2345fd13d97SYork Sun puts("Cannot get image data/size\n");
2355fd13d97SYork Sun return -ENOENT;
2365fd13d97SYork Sun }
2375fd13d97SYork Sun debug("Embedded data: dst=%lx, size=%lx\n", load_addr,
2385fd13d97SYork Sun (unsigned long)length);
2395fd13d97SYork Sun src = (void *)data;
2405fd13d97SYork Sun }
2415fd13d97SYork Sun
242d154ca60SBen Whitten #ifdef CONFIG_SPL_FIT_SIGNATURE
243d154ca60SBen Whitten printf("## Checking hash(es) for Image %s ... ",
244d154ca60SBen Whitten fit_get_name(fit, node, NULL));
245d154ca60SBen Whitten if (!fit_image_verify_with_data(fit, node,
246d154ca60SBen Whitten src, length))
247d154ca60SBen Whitten return -EPERM;
248d154ca60SBen Whitten puts("OK\n");
249d154ca60SBen Whitten #endif
250d154ca60SBen Whitten
2518baa3818SAndre Przywara #ifdef CONFIG_SPL_FIT_IMAGE_POST_PROCESS
252*b7d9107fSChia-Wei Wang board_fit_image_post_process(fit, node, &src, &length);
2538baa3818SAndre Przywara #endif
2548baa3818SAndre Przywara
255975e7893SMichal Simek if (IS_ENABLED(CONFIG_SPL_GZIP) && image_comp == IH_COMP_GZIP) {
256933f67aaSYork Sun size = length;
2577264f292SYork Sun if (gunzip((void *)load_addr, CONFIG_SYS_BOOTM_LEN,
258933f67aaSYork Sun src, &size)) {
2597264f292SYork Sun puts("Uncompressing error\n");
2607264f292SYork Sun return -EIO;
2617264f292SYork Sun }
262933f67aaSYork Sun length = size;
2637264f292SYork Sun } else {
2648baa3818SAndre Przywara memcpy((void *)load_addr, src, length);
2657264f292SYork Sun }
2668baa3818SAndre Przywara
2678baa3818SAndre Przywara if (image_info) {
2688baa3818SAndre Przywara image_info->load_addr = load_addr;
2698baa3818SAndre Przywara image_info->size = length;
2708baa3818SAndre Przywara image_info->entry_point = fdt_getprop_u32(fit, node, "entry");
2718baa3818SAndre Przywara }
2728baa3818SAndre Przywara
2738baa3818SAndre Przywara return 0;
2748baa3818SAndre Przywara }
2758baa3818SAndre Przywara
spl_fit_append_fdt(struct spl_image_info * spl_image,struct spl_load_info * info,ulong sector,void * fit,int images,ulong base_offset)276d879616eSPhilipp Tomsich static int spl_fit_append_fdt(struct spl_image_info *spl_image,
277d879616eSPhilipp Tomsich struct spl_load_info *info, ulong sector,
278d879616eSPhilipp Tomsich void *fit, int images, ulong base_offset)
279d879616eSPhilipp Tomsich {
280d879616eSPhilipp Tomsich struct spl_image_info image_info;
281d879616eSPhilipp Tomsich int node, ret;
282d879616eSPhilipp Tomsich
283d879616eSPhilipp Tomsich /* Figure out which device tree the board wants to use */
284d879616eSPhilipp Tomsich node = spl_fit_get_image_node(fit, images, FIT_FDT_PROP, 0);
285d879616eSPhilipp Tomsich if (node < 0) {
286d879616eSPhilipp Tomsich debug("%s: cannot find FDT node\n", __func__);
287d879616eSPhilipp Tomsich return node;
288d879616eSPhilipp Tomsich }
289d879616eSPhilipp Tomsich
290d879616eSPhilipp Tomsich /*
291d879616eSPhilipp Tomsich * Read the device tree and place it after the image.
292d879616eSPhilipp Tomsich * Align the destination address to ARCH_DMA_MINALIGN.
293d879616eSPhilipp Tomsich */
294d879616eSPhilipp Tomsich image_info.load_addr = spl_image->load_addr + spl_image->size;
295d879616eSPhilipp Tomsich ret = spl_load_fit_image(info, sector, fit, base_offset, node,
296d879616eSPhilipp Tomsich &image_info);
297a616c783SPhilipp Tomsich
298a616c783SPhilipp Tomsich if (ret < 0)
299a616c783SPhilipp Tomsich return ret;
300a616c783SPhilipp Tomsich
301a616c783SPhilipp Tomsich /* Make the load-address of the FDT available for the SPL framework */
302a616c783SPhilipp Tomsich spl_image->fdt_addr = (void *)image_info.load_addr;
303337bbb62SPhilipp Tomsich #if !CONFIG_IS_ENABLED(FIT_IMAGE_TINY)
304a616c783SPhilipp Tomsich /* Try to make space, so we can inject details on the loadables */
305a616c783SPhilipp Tomsich ret = fdt_shrink_to_minimum(spl_image->fdt_addr, 8192);
306337bbb62SPhilipp Tomsich #endif
307a616c783SPhilipp Tomsich
308a616c783SPhilipp Tomsich return ret;
309a616c783SPhilipp Tomsich }
310a616c783SPhilipp Tomsich
spl_fit_record_loadable(const void * fit,int images,int index,void * blob,struct spl_image_info * image)311a616c783SPhilipp Tomsich static int spl_fit_record_loadable(const void *fit, int images, int index,
312a616c783SPhilipp Tomsich void *blob, struct spl_image_info *image)
313a616c783SPhilipp Tomsich {
314337bbb62SPhilipp Tomsich int ret = 0;
315337bbb62SPhilipp Tomsich #if !CONFIG_IS_ENABLED(FIT_IMAGE_TINY)
316a616c783SPhilipp Tomsich char *name;
317337bbb62SPhilipp Tomsich int node;
318a616c783SPhilipp Tomsich
319a616c783SPhilipp Tomsich ret = spl_fit_get_image_name(fit, images, "loadables",
320a616c783SPhilipp Tomsich index, &name);
321a616c783SPhilipp Tomsich if (ret < 0)
322a616c783SPhilipp Tomsich return ret;
323a616c783SPhilipp Tomsich
324a616c783SPhilipp Tomsich node = spl_fit_get_image_node(fit, images, "loadables", index);
325a616c783SPhilipp Tomsich
326a616c783SPhilipp Tomsich ret = fdt_record_loadable(blob, index, name, image->load_addr,
327a616c783SPhilipp Tomsich image->size, image->entry_point,
328a616c783SPhilipp Tomsich fdt_getprop(fit, node, "type", NULL),
329a616c783SPhilipp Tomsich fdt_getprop(fit, node, "os", NULL));
330337bbb62SPhilipp Tomsich #endif
331d879616eSPhilipp Tomsich return ret;
332d879616eSPhilipp Tomsich }
333d879616eSPhilipp Tomsich
spl_fit_image_get_os(const void * fit,int noffset,uint8_t * os)334337bbb62SPhilipp Tomsich static int spl_fit_image_get_os(const void *fit, int noffset, uint8_t *os)
335337bbb62SPhilipp Tomsich {
336337bbb62SPhilipp Tomsich #if CONFIG_IS_ENABLED(FIT_IMAGE_TINY)
337337bbb62SPhilipp Tomsich return -ENOTSUPP;
338337bbb62SPhilipp Tomsich #else
339337bbb62SPhilipp Tomsich return fit_image_get_os(fit, noffset, os);
340337bbb62SPhilipp Tomsich #endif
341337bbb62SPhilipp Tomsich }
342337bbb62SPhilipp Tomsich
34365f2e689SAndreas Dannenberg /*
34465f2e689SAndreas Dannenberg * Weak default function to allow customizing SPL fit loading for load-only
34565f2e689SAndreas Dannenberg * use cases by allowing to skip the parsing/processing of the FIT contents
34665f2e689SAndreas Dannenberg * (so that this can be done separately in a more customized fashion)
34765f2e689SAndreas Dannenberg */
spl_load_simple_fit_skip_processing(void)34865f2e689SAndreas Dannenberg __weak bool spl_load_simple_fit_skip_processing(void)
34965f2e689SAndreas Dannenberg {
35065f2e689SAndreas Dannenberg return false;
35165f2e689SAndreas Dannenberg }
35265f2e689SAndreas Dannenberg
spl_load_simple_fit(struct spl_image_info * spl_image,struct spl_load_info * info,ulong sector,void * fit)3536b2936f2SChia-Wei, Wang int spl_load_simple_fit(struct spl_image_info *spl_image,
3546b2936f2SChia-Wei, Wang struct spl_load_info *info, ulong sector, void *fit)
355f1dcee59SSimon Glass {
3566b2936f2SChia-Wei, Wang int sectors;
3576b2936f2SChia-Wei, Wang ulong size;
3586b2936f2SChia-Wei, Wang unsigned long count;
3597e52eee8SChia-Wei, Wang struct spl_image_info image_info;
3606b2936f2SChia-Wei, Wang int node = -1;
3616b2936f2SChia-Wei, Wang int images, ret;
3626b2936f2SChia-Wei, Wang int base_offset, hsize, align_len = ARCH_DMA_MINALIGN - 1;
3636b2936f2SChia-Wei, Wang int index = 0;
36447cd0118SJean-Jacques Hiblot int firmware_node;
365f1dcee59SSimon Glass
366f1dcee59SSimon Glass /*
367c8bc3c0cSYork Sun * For FIT with external data, figure out where the external images
368c8bc3c0cSYork Sun * start. This is the base for the data-offset properties in each
369c8bc3c0cSYork Sun * image.
370f1dcee59SSimon Glass */
3716b2936f2SChia-Wei, Wang size = fdt_totalsize(fit);
3726b2936f2SChia-Wei, Wang size = (size + 3) & ~3;
3736b2936f2SChia-Wei, Wang size = board_spl_fit_size_align(size);
3746b2936f2SChia-Wei, Wang base_offset = (size + 3) & ~3;
3756b2936f2SChia-Wei, Wang
3766b2936f2SChia-Wei, Wang /*
3776b2936f2SChia-Wei, Wang * So far we only have one block of data from the FIT. Read the entire
3786b2936f2SChia-Wei, Wang * thing, including that first block, placing it so it finishes before
3796b2936f2SChia-Wei, Wang * where we will load the image.
3806b2936f2SChia-Wei, Wang *
3816b2936f2SChia-Wei, Wang * Note that we will load the image such that its first byte will be
3826b2936f2SChia-Wei, Wang * at the load address. Since that byte may be part-way through a
3836b2936f2SChia-Wei, Wang * block, we may load the image up to one block before the load
3846b2936f2SChia-Wei, Wang * address. So take account of that here by subtracting an addition
3856b2936f2SChia-Wei, Wang * block length from the FIT start position.
3866b2936f2SChia-Wei, Wang *
3876b2936f2SChia-Wei, Wang * In fact the FIT has its own load address, but we assume it cannot
3886b2936f2SChia-Wei, Wang * be before CONFIG_SYS_TEXT_BASE.
3896b2936f2SChia-Wei, Wang *
3906b2936f2SChia-Wei, Wang * For FIT with data embedded, data is loaded as part of FIT image.
3916b2936f2SChia-Wei, Wang * For FIT with external data, data is not loaded in this step.
3926b2936f2SChia-Wei, Wang */
3936b2936f2SChia-Wei, Wang hsize = (size + info->bl_len + align_len) & ~align_len;
3946b2936f2SChia-Wei, Wang fit = spl_get_load_buffer(-hsize, hsize);
3956b2936f2SChia-Wei, Wang sectors = get_aligned_image_size(info, size, 0);
3966b2936f2SChia-Wei, Wang count = info->read(info, sector, sectors, fit);
3976b2936f2SChia-Wei, Wang debug("fit read sector %lx, sectors=%d, dst=%p, count=%lu, size=0x%lx\n",
3986b2936f2SChia-Wei, Wang sector, sectors, fit, count, size);
3996b2936f2SChia-Wei, Wang
4006b2936f2SChia-Wei, Wang if (count == 0)
4016b2936f2SChia-Wei, Wang return -EIO;
402f1dcee59SSimon Glass
40365f2e689SAndreas Dannenberg /* skip further processing if requested to enable load-only use cases */
40465f2e689SAndreas Dannenberg if (spl_load_simple_fit_skip_processing())
40565f2e689SAndreas Dannenberg return 0;
40665f2e689SAndreas Dannenberg
407736806fbSAndre Przywara /* find the node holding the images information */
408f1dcee59SSimon Glass images = fdt_path_offset(fit, FIT_IMAGES_PATH);
409f1dcee59SSimon Glass if (images < 0) {
410f1dcee59SSimon Glass debug("%s: Cannot find /images node: %d\n", __func__, images);
411f1dcee59SSimon Glass return -1;
412f1dcee59SSimon Glass }
413736806fbSAndre Przywara
41426a64223SMarek Vasut #ifdef CONFIG_SPL_FPGA_SUPPORT
41526a64223SMarek Vasut node = spl_fit_get_image_node(fit, images, "fpga", 0);
41626a64223SMarek Vasut if (node >= 0) {
41726a64223SMarek Vasut /* Load the image and set up the spl_image structure */
41826a64223SMarek Vasut ret = spl_load_fit_image(info, sector, fit, base_offset, node,
41926a64223SMarek Vasut spl_image);
42026a64223SMarek Vasut if (ret) {
42126a64223SMarek Vasut printf("%s: Cannot load the FPGA: %i\n", __func__, ret);
42226a64223SMarek Vasut return ret;
42326a64223SMarek Vasut }
4243313ae66SMichal Simek
4253313ae66SMichal Simek debug("FPGA bitstream at: %x, size: %x\n",
4263313ae66SMichal Simek (u32)spl_image->load_addr, spl_image->size);
4273313ae66SMichal Simek
4283313ae66SMichal Simek ret = fpga_load(0, (const void *)spl_image->load_addr,
4293313ae66SMichal Simek spl_image->size, BIT_FULL);
4303313ae66SMichal Simek if (ret) {
4313313ae66SMichal Simek printf("%s: Cannot load the image to the FPGA\n",
4323313ae66SMichal Simek __func__);
4333313ae66SMichal Simek return ret;
4343313ae66SMichal Simek }
4353313ae66SMichal Simek
4363907eef1SLuis Araneda puts("FPGA image loaded from FIT\n");
43726a64223SMarek Vasut node = -1;
43826a64223SMarek Vasut }
43926a64223SMarek Vasut #endif
44026a64223SMarek Vasut
441d879616eSPhilipp Tomsich /*
442d879616eSPhilipp Tomsich * Find the U-Boot image using the following search order:
443d879616eSPhilipp Tomsich * - start at 'firmware' (e.g. an ARM Trusted Firmware)
444d879616eSPhilipp Tomsich * - fall back 'kernel' (e.g. a Falcon-mode OS boot
445d879616eSPhilipp Tomsich * - fall back to using the first 'loadables' entry
446d879616eSPhilipp Tomsich */
447c8bc3c0cSYork Sun if (node < 0)
4481f8e4bf5SMichal Simek node = spl_fit_get_image_node(fit, images, FIT_FIRMWARE_PROP,
4491f8e4bf5SMichal Simek 0);
450d879616eSPhilipp Tomsich #ifdef CONFIG_SPL_OS_BOOT
451d879616eSPhilipp Tomsich if (node < 0)
452d879616eSPhilipp Tomsich node = spl_fit_get_image_node(fit, images, FIT_KERNEL_PROP, 0);
453d879616eSPhilipp Tomsich #endif
454f1dcee59SSimon Glass if (node < 0) {
455736806fbSAndre Przywara debug("could not find firmware image, trying loadables...\n");
456736806fbSAndre Przywara node = spl_fit_get_image_node(fit, images, "loadables", 0);
457411cf32dSAndre Przywara /*
458411cf32dSAndre Przywara * If we pick the U-Boot image from "loadables", start at
459411cf32dSAndre Przywara * the second image when later loading additional images.
460411cf32dSAndre Przywara */
461411cf32dSAndre Przywara index = 1;
462736806fbSAndre Przywara }
463736806fbSAndre Przywara if (node < 0) {
464736806fbSAndre Przywara debug("%s: Cannot find u-boot image node: %d\n",
465736806fbSAndre Przywara __func__, node);
466f1dcee59SSimon Glass return -1;
467f1dcee59SSimon Glass }
468f1dcee59SSimon Glass
4698baa3818SAndre Przywara /* Load the image and set up the spl_image structure */
4708baa3818SAndre Przywara ret = spl_load_fit_image(info, sector, fit, base_offset, node,
4718baa3818SAndre Przywara spl_image);
4728baa3818SAndre Przywara if (ret)
4738baa3818SAndre Przywara return ret;
4748baa3818SAndre Przywara
475d879616eSPhilipp Tomsich /*
476d879616eSPhilipp Tomsich * For backward compatibility, we treat the first node that is
477d879616eSPhilipp Tomsich * as a U-Boot image, if no OS-type has been declared.
478d879616eSPhilipp Tomsich */
479337bbb62SPhilipp Tomsich if (!spl_fit_image_get_os(fit, node, &spl_image->os))
480c8bc3c0cSYork Sun debug("Image OS is %s\n", genimg_get_os_name(spl_image->os));
481d879616eSPhilipp Tomsich #if !defined(CONFIG_SPL_OS_BOOT)
482d879616eSPhilipp Tomsich else
483f4d7d859SSimon Glass spl_image->os = IH_OS_U_BOOT;
484c8bc3c0cSYork Sun #endif
485f1dcee59SSimon Glass
486f1dcee59SSimon Glass /*
487d879616eSPhilipp Tomsich * Booting a next-stage U-Boot may require us to append the FDT.
488d879616eSPhilipp Tomsich * We allow this to fail, as the U-Boot image might embed its FDT.
489f1dcee59SSimon Glass */
490d879616eSPhilipp Tomsich if (spl_image->os == IH_OS_U_BOOT)
491d879616eSPhilipp Tomsich spl_fit_append_fdt(spl_image, info, sector, fit,
492d879616eSPhilipp Tomsich images, base_offset);
493411cf32dSAndre Przywara
49447cd0118SJean-Jacques Hiblot firmware_node = node;
495411cf32dSAndre Przywara /* Now check if there are more images for us to load */
496411cf32dSAndre Przywara for (; ; index++) {
497d879616eSPhilipp Tomsich uint8_t os_type = IH_OS_INVALID;
498d879616eSPhilipp Tomsich
499411cf32dSAndre Przywara node = spl_fit_get_image_node(fit, images, "loadables", index);
500411cf32dSAndre Przywara if (node < 0)
501411cf32dSAndre Przywara break;
502411cf32dSAndre Przywara
50347cd0118SJean-Jacques Hiblot /*
50447cd0118SJean-Jacques Hiblot * if the firmware is also a loadable, skip it because
50547cd0118SJean-Jacques Hiblot * it already has been loaded. This is typically the case with
50647cd0118SJean-Jacques Hiblot * u-boot.img generated by mkimage.
50747cd0118SJean-Jacques Hiblot */
50847cd0118SJean-Jacques Hiblot if (firmware_node == node)
50947cd0118SJean-Jacques Hiblot continue;
51047cd0118SJean-Jacques Hiblot
511411cf32dSAndre Przywara ret = spl_load_fit_image(info, sector, fit, base_offset, node,
512411cf32dSAndre Przywara &image_info);
513411cf32dSAndre Przywara if (ret < 0)
514411cf32dSAndre Przywara continue;
515411cf32dSAndre Przywara
516337bbb62SPhilipp Tomsich if (!spl_fit_image_get_os(fit, node, &os_type))
517d879616eSPhilipp Tomsich debug("Loadable is %s\n", genimg_get_os_name(os_type));
518cf8dcc5dSAbel Vesa #if CONFIG_IS_ENABLED(FIT_IMAGE_TINY)
519cf8dcc5dSAbel Vesa else
520cf8dcc5dSAbel Vesa os_type = IH_OS_U_BOOT;
521cf8dcc5dSAbel Vesa #endif
522d879616eSPhilipp Tomsich
523a616c783SPhilipp Tomsich if (os_type == IH_OS_U_BOOT) {
524a616c783SPhilipp Tomsich spl_fit_append_fdt(&image_info, info, sector,
525d879616eSPhilipp Tomsich fit, images, base_offset);
526a616c783SPhilipp Tomsich spl_image->fdt_addr = image_info.fdt_addr;
527a616c783SPhilipp Tomsich }
528d879616eSPhilipp Tomsich
529411cf32dSAndre Przywara /*
530411cf32dSAndre Przywara * If the "firmware" image did not provide an entry point,
531411cf32dSAndre Przywara * use the first valid entry point from the loadables.
532411cf32dSAndre Przywara */
533411cf32dSAndre Przywara if (spl_image->entry_point == FDT_ERROR &&
534411cf32dSAndre Przywara image_info.entry_point != FDT_ERROR)
535411cf32dSAndre Przywara spl_image->entry_point = image_info.entry_point;
536a616c783SPhilipp Tomsich
537a616c783SPhilipp Tomsich /* Record our loadables into the FDT */
538a616c783SPhilipp Tomsich if (spl_image->fdt_addr)
539a616c783SPhilipp Tomsich spl_fit_record_loadable(fit, images, index,
540a616c783SPhilipp Tomsich spl_image->fdt_addr,
541a616c783SPhilipp Tomsich &image_info);
542411cf32dSAndre Przywara }
543411cf32dSAndre Przywara
544411cf32dSAndre Przywara /*
545411cf32dSAndre Przywara * If a platform does not provide CONFIG_SYS_UBOOT_START, U-Boot's
546411cf32dSAndre Przywara * Makefile will set it to 0 and it will end up as the entry point
547411cf32dSAndre Przywara * here. What it actually means is: use the load address.
548411cf32dSAndre Przywara */
549411cf32dSAndre Przywara if (spl_image->entry_point == FDT_ERROR || spl_image->entry_point == 0)
550411cf32dSAndre Przywara spl_image->entry_point = spl_image->load_addr;
551411cf32dSAndre Przywara
552e246bfcfSYe Li spl_image->flags |= SPL_FIT_FOUND;
553e246bfcfSYe Li
5546b2936f2SChia-Wei, Wang #ifdef CONFIG_SECURE_BOOT
555e246bfcfSYe Li board_spl_fit_post_load((ulong)fit, size);
5566b2936f2SChia-Wei, Wang #endif
557e246bfcfSYe Li
558411cf32dSAndre Przywara return 0;
559f1dcee59SSimon Glass }
560