]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
f1dcee59 SG |
2 | /* |
3 | * Copyright (C) 2016 Google, Inc | |
4 | * Written by Simon Glass <[email protected]> | |
f1dcee59 SG |
5 | */ |
6 | ||
7 | #include <common.h> | |
8 | #include <errno.h> | |
3313ae66 | 9 | #include <fpga.h> |
0c670fc1 | 10 | #include <gzip.h> |
f1dcee59 | 11 | #include <image.h> |
f7ae49fc | 12 | #include <log.h> |
ea376ebc | 13 | #include <malloc.h> |
f1dcee59 | 14 | #include <spl.h> |
3a8ee3df | 15 | #include <sysinfo.h> |
90526e9f | 16 | #include <asm/cache.h> |
401d1c4f | 17 | #include <asm/global_data.h> |
ea376ebc | 18 | #include <linux/libfdt.h> |
f1dcee59 | 19 | |
b83edfbd LA |
20 | DECLARE_GLOBAL_DATA_PTR; |
21 | ||
ea376ebc JJH |
22 | #ifndef CONFIG_SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ |
23 | #define CONFIG_SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ (64 * 1024) | |
24 | #endif | |
25 | ||
7264f292 YS |
26 | #ifndef CONFIG_SYS_BOOTM_LEN |
27 | #define CONFIG_SYS_BOOTM_LEN (64 << 20) | |
28 | #endif | |
29 | ||
e246bfcf YL |
30 | __weak void board_spl_fit_post_load(ulong load_addr, size_t length) |
31 | { | |
32 | } | |
33 | ||
34 | __weak ulong board_spl_fit_size_align(ulong size) | |
35 | { | |
36 | return size; | |
37 | } | |
38 | ||
152781d4 JJH |
39 | static int find_node_from_desc(const void *fit, int node, const char *str) |
40 | { | |
41 | int child; | |
42 | ||
43 | if (node < 0) | |
44 | return -EINVAL; | |
45 | ||
46 | /* iterate the FIT nodes and find a matching description */ | |
47 | for (child = fdt_first_subnode(fit, node); child >= 0; | |
48 | child = fdt_next_subnode(fit, child)) { | |
49 | int len; | |
50 | const char *desc = fdt_getprop(fit, child, "description", &len); | |
51 | ||
52 | if (!desc) | |
53 | continue; | |
54 | ||
55 | if (!strcmp(desc, str)) | |
56 | return child; | |
57 | } | |
58 | ||
59 | return -ENOENT; | |
60 | } | |
61 | ||
736806fb | 62 | /** |
a616c783 | 63 | * spl_fit_get_image_name(): By using the matching configuration subnode, |
736806fb AP |
64 | * retrieve the name of an image, specified by a property name and an index |
65 | * into that. | |
66 | * @fit: Pointer to the FDT blob. | |
67 | * @images: Offset of the /images subnode. | |
68 | * @type: Name of the property within the configuration subnode. | |
69 | * @index: Index into the list of strings in this property. | |
a616c783 | 70 | * @outname: Name of the image |
736806fb | 71 | * |
a616c783 | 72 | * Return: 0 on success, or a negative error number |
736806fb | 73 | */ |
a616c783 PT |
74 | static int spl_fit_get_image_name(const void *fit, int images, |
75 | const char *type, int index, | |
c1648d05 | 76 | const char **outname) |
4b9340ab | 77 | { |
3a8ee3df | 78 | struct udevice *sysinfo; |
4b9340ab | 79 | const char *name, *str; |
a616c783 PT |
80 | __maybe_unused int node; |
81 | int conf_node; | |
4b9340ab | 82 | int len, i; |
152781d4 | 83 | bool found = true; |
4b9340ab | 84 | |
3863f840 | 85 | conf_node = fit_find_config_node(fit); |
4b9340ab AP |
86 | if (conf_node < 0) { |
87 | #ifdef CONFIG_SPL_LIBCOMMON_SUPPORT | |
88 | printf("No matching DT out of these options:\n"); | |
89 | for (node = fdt_first_subnode(fit, conf_node); | |
90 | node >= 0; | |
91 | node = fdt_next_subnode(fit, node)) { | |
92 | name = fdt_getprop(fit, node, "description", &len); | |
93 | printf(" %s\n", name); | |
f1dcee59 | 94 | } |
4b9340ab AP |
95 | #endif |
96 | return conf_node; | |
97 | } | |
f1dcee59 | 98 | |
4b9340ab AP |
99 | name = fdt_getprop(fit, conf_node, type, &len); |
100 | if (!name) { | |
101 | debug("cannot find property '%s': %d\n", type, len); | |
102 | return -EINVAL; | |
103 | } | |
f1dcee59 | 104 | |
4b9340ab AP |
105 | str = name; |
106 | for (i = 0; i < index; i++) { | |
107 | str = strchr(str, '\0') + 1; | |
108 | if (!str || (str - name >= len)) { | |
152781d4 JJH |
109 | found = false; |
110 | break; | |
4b9340ab | 111 | } |
f1dcee59 SG |
112 | } |
113 | ||
3a8ee3df | 114 | if (!found && CONFIG_IS_ENABLED(SYSINFO) && !sysinfo_get(&sysinfo)) { |
152781d4 JJH |
115 | int rc; |
116 | /* | |
3a8ee3df SG |
117 | * no string in the property for this index. Check if the |
118 | * sysinfo-level code can supply one. | |
152781d4 | 119 | */ |
3a8ee3df SG |
120 | rc = sysinfo_get_fit_loadable(sysinfo, index - i - 1, type, |
121 | &str); | |
152781d4 JJH |
122 | if (rc && rc != -ENOENT) |
123 | return rc; | |
124 | ||
125 | if (!rc) { | |
126 | /* | |
3a8ee3df | 127 | * The sysinfo provided a name for a loadable. |
152781d4 JJH |
128 | * Try to match it against the description properties |
129 | * first. If no matching node is found, use it as a | |
130 | * node name. | |
131 | */ | |
132 | int node; | |
133 | int images = fdt_path_offset(fit, FIT_IMAGES_PATH); | |
134 | ||
135 | node = find_node_from_desc(fit, images, str); | |
136 | if (node > 0) | |
137 | str = fdt_get_name(fit, node, NULL); | |
138 | ||
139 | found = true; | |
140 | } | |
141 | } | |
142 | ||
143 | if (!found) { | |
144 | debug("no string for index %d\n", index); | |
145 | return -E2BIG; | |
146 | } | |
147 | ||
148 | *outname = str; | |
a616c783 PT |
149 | return 0; |
150 | } | |
151 | ||
152 | /** | |
153 | * spl_fit_get_image_node(): By using the matching configuration subnode, | |
154 | * retrieve the name of an image, specified by a property name and an index | |
155 | * into that. | |
156 | * @fit: Pointer to the FDT blob. | |
157 | * @images: Offset of the /images subnode. | |
158 | * @type: Name of the property within the configuration subnode. | |
159 | * @index: Index into the list of strings in this property. | |
160 | * | |
161 | * Return: the node offset of the respective image node or a negative | |
162 | * error number. | |
163 | */ | |
164 | static int spl_fit_get_image_node(const void *fit, int images, | |
165 | const char *type, int index) | |
166 | { | |
c1648d05 | 167 | const char *str; |
a616c783 PT |
168 | int err; |
169 | int node; | |
170 | ||
171 | err = spl_fit_get_image_name(fit, images, type, index, &str); | |
172 | if (err) | |
173 | return err; | |
174 | ||
4b9340ab | 175 | debug("%s: '%s'\n", type, str); |
a616c783 | 176 | |
4b9340ab AP |
177 | node = fdt_subnode_offset(fit, images, str); |
178 | if (node < 0) { | |
19141d69 | 179 | pr_err("cannot find image node '%s': %d\n", str, node); |
4b9340ab | 180 | return -EINVAL; |
f1dcee59 | 181 | } |
f1dcee59 | 182 | |
736806fb | 183 | return node; |
f1dcee59 SG |
184 | } |
185 | ||
eafd5410 LV |
186 | static int get_aligned_image_offset(struct spl_load_info *info, int offset) |
187 | { | |
188 | /* | |
189 | * If it is a FS read, get the first address before offset which is | |
190 | * aligned to ARCH_DMA_MINALIGN. If it is raw read return the | |
191 | * block number to which offset belongs. | |
192 | */ | |
193 | if (info->filename) | |
194 | return offset & ~(ARCH_DMA_MINALIGN - 1); | |
195 | ||
196 | return offset / info->bl_len; | |
197 | } | |
198 | ||
199 | static int get_aligned_image_overhead(struct spl_load_info *info, int offset) | |
200 | { | |
201 | /* | |
202 | * If it is a FS read, get the difference between the offset and | |
203 | * the first address before offset which is aligned to | |
204 | * ARCH_DMA_MINALIGN. If it is raw read return the offset within the | |
205 | * block. | |
206 | */ | |
207 | if (info->filename) | |
208 | return offset & (ARCH_DMA_MINALIGN - 1); | |
209 | ||
210 | return offset % info->bl_len; | |
211 | } | |
212 | ||
213 | static int get_aligned_image_size(struct spl_load_info *info, int data_size, | |
214 | int offset) | |
215 | { | |
3cc1f380 LV |
216 | data_size = data_size + get_aligned_image_overhead(info, offset); |
217 | ||
eafd5410 | 218 | if (info->filename) |
3cc1f380 | 219 | return data_size; |
eafd5410 LV |
220 | |
221 | return (data_size + info->bl_len - 1) / info->bl_len; | |
222 | } | |
223 | ||
8baa3818 AP |
224 | /** |
225 | * spl_load_fit_image(): load the image described in a certain FIT node | |
226 | * @info: points to information about the device to load data from | |
227 | * @sector: the start sector of the FIT image on the device | |
228 | * @fit: points to the flattened device tree blob describing the FIT | |
a616c783 | 229 | * image |
8baa3818 AP |
230 | * @base_offset: the beginning of the data area containing the actual |
231 | * image data, relative to the beginning of the FIT | |
232 | * @node: offset of the DT node describing the image to load (relative | |
a616c783 | 233 | * to @fit) |
8baa3818 | 234 | * @image_info: will be filled with information about the loaded image |
a616c783 PT |
235 | * If the FIT node does not contain a "load" (address) property, |
236 | * the image gets loaded to the address pointed to by the | |
237 | * load_addr member in this struct. | |
8baa3818 AP |
238 | * |
239 | * Return: 0 on success or a negative error number. | |
240 | */ | |
241 | static int spl_load_fit_image(struct spl_load_info *info, ulong sector, | |
242 | void *fit, ulong base_offset, int node, | |
243 | struct spl_image_info *image_info) | |
244 | { | |
3313ae66 | 245 | int offset; |
8baa3818 | 246 | size_t length; |
5fd13d97 | 247 | int len; |
933f67aa | 248 | ulong size; |
8baa3818 AP |
249 | ulong load_addr, load_ptr; |
250 | void *src; | |
251 | ulong overhead; | |
252 | int nr_sectors; | |
253 | int align_len = ARCH_DMA_MINALIGN - 1; | |
7264f292 | 254 | uint8_t image_comp = -1, type = -1; |
5fd13d97 | 255 | const void *data; |
a1be94b6 | 256 | bool external_data = false; |
7264f292 | 257 | |
29bd8ada | 258 | if (IS_ENABLED(CONFIG_SPL_FPGA) || |
56419ea5 MV |
259 | (IS_ENABLED(CONFIG_SPL_OS_BOOT) && IS_ENABLED(CONFIG_SPL_GZIP))) { |
260 | if (fit_image_get_type(fit, node, &type)) | |
261 | puts("Cannot get image type.\n"); | |
262 | else | |
263 | debug("%s ", genimg_get_type_name(type)); | |
264 | } | |
265 | ||
602ce1d1 KS |
266 | if (IS_ENABLED(CONFIG_SPL_GZIP)) { |
267 | fit_image_get_comp(fit, node, &image_comp); | |
268 | debug("%s ", genimg_get_comp_name(image_comp)); | |
7264f292 | 269 | } |
8baa3818 | 270 | |
5fd13d97 | 271 | if (fit_image_get_load(fit, node, &load_addr)) |
8baa3818 | 272 | load_addr = image_info->load_addr; |
8baa3818 | 273 | |
a1be94b6 PF |
274 | if (!fit_image_get_data_position(fit, node, &offset)) { |
275 | external_data = true; | |
276 | } else if (!fit_image_get_data_offset(fit, node, &offset)) { | |
5fd13d97 | 277 | offset += base_offset; |
a1be94b6 PF |
278 | external_data = true; |
279 | } | |
280 | ||
281 | if (external_data) { | |
282 | /* External data */ | |
5fd13d97 YS |
283 | if (fit_image_get_data_size(fit, node, &len)) |
284 | return -ENOENT; | |
285 | ||
286 | load_ptr = (load_addr + align_len) & ~align_len; | |
287 | length = len; | |
288 | ||
289 | overhead = get_aligned_image_overhead(info, offset); | |
290 | nr_sectors = get_aligned_image_size(info, length, offset); | |
291 | ||
3313ae66 MS |
292 | if (info->read(info, |
293 | sector + get_aligned_image_offset(info, offset), | |
5fd13d97 YS |
294 | nr_sectors, (void *)load_ptr) != nr_sectors) |
295 | return -EIO; | |
296 | ||
297 | debug("External data: dst=%lx, offset=%x, size=%lx\n", | |
298 | load_ptr, offset, (unsigned long)length); | |
299 | src = (void *)load_ptr + overhead; | |
300 | } else { | |
301 | /* Embedded data */ | |
302 | if (fit_image_get_data(fit, node, &data, &length)) { | |
303 | puts("Cannot get image data/size\n"); | |
304 | return -ENOENT; | |
305 | } | |
306 | debug("Embedded data: dst=%lx, size=%lx\n", load_addr, | |
307 | (unsigned long)length); | |
308 | src = (void *)data; | |
309 | } | |
8baa3818 | 310 | |
d154ca60 BW |
311 | #ifdef CONFIG_SPL_FIT_SIGNATURE |
312 | printf("## Checking hash(es) for Image %s ... ", | |
313 | fit_get_name(fit, node, NULL)); | |
314 | if (!fit_image_verify_with_data(fit, node, | |
315 | src, length)) | |
316 | return -EPERM; | |
317 | puts("OK\n"); | |
318 | #endif | |
319 | ||
8baa3818 AP |
320 | #ifdef CONFIG_SPL_FIT_IMAGE_POST_PROCESS |
321 | board_fit_image_post_process(&src, &length); | |
322 | #endif | |
323 | ||
975e7893 | 324 | if (IS_ENABLED(CONFIG_SPL_GZIP) && image_comp == IH_COMP_GZIP) { |
933f67aa | 325 | size = length; |
7264f292 | 326 | if (gunzip((void *)load_addr, CONFIG_SYS_BOOTM_LEN, |
933f67aa | 327 | src, &size)) { |
7264f292 YS |
328 | puts("Uncompressing error\n"); |
329 | return -EIO; | |
330 | } | |
933f67aa | 331 | length = size; |
7264f292 YS |
332 | } else { |
333 | memcpy((void *)load_addr, src, length); | |
334 | } | |
8baa3818 AP |
335 | |
336 | if (image_info) { | |
caa7fc2c MS |
337 | ulong entry_point; |
338 | ||
8baa3818 AP |
339 | image_info->load_addr = load_addr; |
340 | image_info->size = length; | |
caa7fc2c MS |
341 | |
342 | if (!fit_image_get_entry(fit, node, &entry_point)) | |
343 | image_info->entry_point = entry_point; | |
344 | else | |
345 | image_info->entry_point = FDT_ERROR; | |
8baa3818 AP |
346 | } |
347 | ||
348 | return 0; | |
349 | } | |
350 | ||
d879616e PT |
351 | static int spl_fit_append_fdt(struct spl_image_info *spl_image, |
352 | struct spl_load_info *info, ulong sector, | |
353 | void *fit, int images, ulong base_offset) | |
354 | { | |
355 | struct spl_image_info image_info; | |
9d13b872 | 356 | int node, ret = 0, index = 0; |
b83edfbd LA |
357 | |
358 | /* | |
359 | * Use the address following the image as target address for the | |
5675ed7c | 360 | * device tree. |
b83edfbd | 361 | */ |
5675ed7c | 362 | image_info.load_addr = spl_image->load_addr + spl_image->size; |
d879616e PT |
363 | |
364 | /* Figure out which device tree the board wants to use */ | |
9d13b872 | 365 | node = spl_fit_get_image_node(fit, images, FIT_FDT_PROP, index++); |
d879616e PT |
366 | if (node < 0) { |
367 | debug("%s: cannot find FDT node\n", __func__); | |
d879616e | 368 | |
b83edfbd LA |
369 | /* |
370 | * U-Boot did not find a device tree inside the FIT image. Use | |
371 | * the U-Boot device tree instead. | |
372 | */ | |
373 | if (gd->fdt_blob) | |
374 | memcpy((void *)image_info.load_addr, gd->fdt_blob, | |
375 | fdt_totalsize(gd->fdt_blob)); | |
376 | else | |
377 | return node; | |
378 | } else { | |
379 | ret = spl_load_fit_image(info, sector, fit, base_offset, node, | |
380 | &image_info); | |
381 | if (ret < 0) | |
382 | return ret; | |
383 | } | |
a616c783 PT |
384 | |
385 | /* Make the load-address of the FDT available for the SPL framework */ | |
386 | spl_image->fdt_addr = (void *)image_info.load_addr; | |
337bbb62 | 387 | #if !CONFIG_IS_ENABLED(FIT_IMAGE_TINY) |
9d13b872 | 388 | if (CONFIG_IS_ENABLED(LOAD_FIT_APPLY_OVERLAY)) { |
ea376ebc JJH |
389 | void *tmpbuffer = NULL; |
390 | ||
9d13b872 MS |
391 | for (; ; index++) { |
392 | node = spl_fit_get_image_node(fit, images, FIT_FDT_PROP, | |
393 | index); | |
24bf44cf | 394 | if (node == -E2BIG) { |
9d13b872 | 395 | debug("%s: No additional FDT node\n", __func__); |
ea376ebc | 396 | break; |
24bf44cf JJH |
397 | } else if (node < 0) { |
398 | debug("%s: unable to find FDT node %d\n", | |
399 | __func__, index); | |
400 | continue; | |
9d13b872 MS |
401 | } |
402 | ||
ea376ebc JJH |
403 | if (!tmpbuffer) { |
404 | /* | |
405 | * allocate memory to store the DT overlay | |
406 | * before it is applied. It may not be used | |
407 | * depending on how the overlay is stored, so | |
408 | * don't fail yet if the allocation failed. | |
409 | */ | |
410 | tmpbuffer = malloc(CONFIG_SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ); | |
411 | if (!tmpbuffer) | |
412 | debug("%s: unable to allocate space for overlays\n", | |
413 | __func__); | |
414 | } | |
415 | image_info.load_addr = (ulong)tmpbuffer; | |
9d13b872 MS |
416 | ret = spl_load_fit_image(info, sector, fit, base_offset, |
417 | node, &image_info); | |
418 | if (ret < 0) | |
ea376ebc | 419 | break; |
9d13b872 | 420 | |
99329be2 JJH |
421 | /* Make room in FDT for changes from the overlay */ |
422 | ret = fdt_increase_size(spl_image->fdt_addr, | |
423 | image_info.size); | |
424 | if (ret < 0) | |
ea376ebc | 425 | break; |
99329be2 | 426 | |
9d13b872 MS |
427 | ret = fdt_overlay_apply_verbose(spl_image->fdt_addr, |
428 | (void *)image_info.load_addr); | |
19141d69 JJH |
429 | if (ret) { |
430 | pr_err("failed to apply DT overlay %s\n", | |
431 | fit_get_name(fit, node, NULL)); | |
ea376ebc | 432 | break; |
19141d69 | 433 | } |
9d13b872 MS |
434 | |
435 | debug("%s: DT overlay %s applied\n", __func__, | |
436 | fit_get_name(fit, node, NULL)); | |
437 | } | |
077e72c6 | 438 | free(tmpbuffer); |
ea376ebc JJH |
439 | if (ret) |
440 | return ret; | |
9d13b872 | 441 | } |
99329be2 JJH |
442 | /* Try to make space, so we can inject details on the loadables */ |
443 | ret = fdt_shrink_to_minimum(spl_image->fdt_addr, 8192); | |
444 | if (ret < 0) | |
445 | return ret; | |
446 | #endif | |
447 | ||
a616c783 PT |
448 | return ret; |
449 | } | |
450 | ||
451 | static int spl_fit_record_loadable(const void *fit, int images, int index, | |
452 | void *blob, struct spl_image_info *image) | |
453 | { | |
337bbb62 PT |
454 | int ret = 0; |
455 | #if !CONFIG_IS_ENABLED(FIT_IMAGE_TINY) | |
c1648d05 | 456 | const char *name; |
337bbb62 | 457 | int node; |
a616c783 PT |
458 | |
459 | ret = spl_fit_get_image_name(fit, images, "loadables", | |
460 | index, &name); | |
461 | if (ret < 0) | |
462 | return ret; | |
463 | ||
464 | node = spl_fit_get_image_node(fit, images, "loadables", index); | |
465 | ||
466 | ret = fdt_record_loadable(blob, index, name, image->load_addr, | |
467 | image->size, image->entry_point, | |
468 | fdt_getprop(fit, node, "type", NULL), | |
469 | fdt_getprop(fit, node, "os", NULL)); | |
337bbb62 | 470 | #endif |
d879616e PT |
471 | return ret; |
472 | } | |
473 | ||
337bbb62 PT |
474 | static int spl_fit_image_get_os(const void *fit, int noffset, uint8_t *os) |
475 | { | |
7296a336 | 476 | #if CONFIG_IS_ENABLED(FIT_IMAGE_TINY) && !defined(CONFIG_SPL_OS_BOOT) |
cf70553e SH |
477 | const char *name = fdt_getprop(fit, noffset, FIT_OS_PROP, NULL); |
478 | ||
479 | if (!name) | |
480 | return -ENOENT; | |
481 | ||
482 | /* | |
483 | * We don't care what the type of the image actually is, | |
484 | * only whether or not it is U-Boot. This saves some | |
485 | * space by omitting the large table of OS types. | |
486 | */ | |
487 | if (!strcmp(name, "u-boot")) | |
488 | *os = IH_OS_U_BOOT; | |
489 | else | |
490 | *os = IH_OS_INVALID; | |
491 | ||
492 | return 0; | |
337bbb62 PT |
493 | #else |
494 | return fit_image_get_os(fit, noffset, os); | |
495 | #endif | |
496 | } | |
497 | ||
03f1f78a AG |
498 | /* |
499 | * The purpose of the FIT load buffer is to provide a memory location that is | |
500 | * independent of the load address of any FIT component. | |
501 | */ | |
502 | static void *spl_get_fit_load_buffer(size_t size) | |
503 | { | |
504 | void *buf; | |
505 | ||
506 | buf = malloc(size); | |
507 | if (!buf) { | |
508 | pr_err("Could not get FIT buffer of %lu bytes\n", (ulong)size); | |
509 | pr_err("\tcheck CONFIG_SYS_SPL_MALLOC_SIZE\n"); | |
510 | buf = spl_get_load_buffer(0, size); | |
511 | } | |
512 | return buf; | |
513 | } | |
514 | ||
e1eb6ada AD |
515 | /* |
516 | * Weak default function to allow customizing SPL fit loading for load-only | |
517 | * use cases by allowing to skip the parsing/processing of the FIT contents | |
518 | * (so that this can be done separately in a more customized fashion) | |
519 | */ | |
520 | __weak bool spl_load_simple_fit_skip_processing(void) | |
521 | { | |
522 | return false; | |
523 | } | |
524 | ||
f4d7d859 SG |
525 | int spl_load_simple_fit(struct spl_image_info *spl_image, |
526 | struct spl_load_info *info, ulong sector, void *fit) | |
f1dcee59 SG |
527 | { |
528 | int sectors; | |
03f1f78a | 529 | ulong size, hsize; |
f1dcee59 | 530 | unsigned long count; |
8baa3818 | 531 | struct spl_image_info image_info; |
c8bc3c0c YS |
532 | int node = -1; |
533 | int images, ret; | |
03f1f78a | 534 | int base_offset; |
411cf32d | 535 | int index = 0; |
6b8b98d5 | 536 | int firmware_node; |
f1dcee59 SG |
537 | |
538 | /* | |
c8bc3c0c YS |
539 | * For FIT with external data, figure out where the external images |
540 | * start. This is the base for the data-offset properties in each | |
541 | * image. | |
f1dcee59 SG |
542 | */ |
543 | size = fdt_totalsize(fit); | |
544 | size = (size + 3) & ~3; | |
e246bfcf | 545 | size = board_spl_fit_size_align(size); |
f1dcee59 SG |
546 | base_offset = (size + 3) & ~3; |
547 | ||
548 | /* | |
549 | * So far we only have one block of data from the FIT. Read the entire | |
03f1f78a | 550 | * thing, including that first block. |
c8bc3c0c YS |
551 | * |
552 | * For FIT with data embedded, data is loaded as part of FIT image. | |
553 | * For FIT with external data, data is not loaded in this step. | |
f1dcee59 | 554 | */ |
eafd5410 | 555 | sectors = get_aligned_image_size(info, size, 0); |
03f1f78a AG |
556 | hsize = sectors * info->bl_len; |
557 | fit = spl_get_fit_load_buffer(hsize); | |
f1dcee59 | 558 | count = info->read(info, sector, sectors, fit); |
e246bfcf YL |
559 | debug("fit read sector %lx, sectors=%d, dst=%p, count=%lu, size=0x%lx\n", |
560 | sector, sectors, fit, count, size); | |
561 | ||
f1dcee59 SG |
562 | if (count == 0) |
563 | return -EIO; | |
564 | ||
e1eb6ada AD |
565 | /* skip further processing if requested to enable load-only use cases */ |
566 | if (spl_load_simple_fit_skip_processing()) | |
567 | return 0; | |
568 | ||
7d5b1bf6 PR |
569 | if (IS_ENABLED(CONFIG_SPL_FIT_SIGNATURE)) { |
570 | int conf_offset = fit_find_config_node(fit); | |
571 | ||
572 | printf("## Checking hash(es) for config %s ... ", | |
573 | fit_get_name(fit, conf_offset, NULL)); | |
574 | if (fit_config_verify(fit, conf_offset)) | |
575 | return -EPERM; | |
576 | puts("OK\n"); | |
577 | } | |
578 | ||
736806fb | 579 | /* find the node holding the images information */ |
f1dcee59 SG |
580 | images = fdt_path_offset(fit, FIT_IMAGES_PATH); |
581 | if (images < 0) { | |
582 | debug("%s: Cannot find /images node: %d\n", __func__, images); | |
583 | return -1; | |
584 | } | |
736806fb | 585 | |
29bd8ada | 586 | #ifdef CONFIG_SPL_FPGA |
26a64223 MV |
587 | node = spl_fit_get_image_node(fit, images, "fpga", 0); |
588 | if (node >= 0) { | |
589 | /* Load the image and set up the spl_image structure */ | |
590 | ret = spl_load_fit_image(info, sector, fit, base_offset, node, | |
591 | spl_image); | |
592 | if (ret) { | |
593 | printf("%s: Cannot load the FPGA: %i\n", __func__, ret); | |
594 | return ret; | |
595 | } | |
3313ae66 MS |
596 | |
597 | debug("FPGA bitstream at: %x, size: %x\n", | |
598 | (u32)spl_image->load_addr, spl_image->size); | |
599 | ||
600 | ret = fpga_load(0, (const void *)spl_image->load_addr, | |
601 | spl_image->size, BIT_FULL); | |
602 | if (ret) { | |
603 | printf("%s: Cannot load the image to the FPGA\n", | |
604 | __func__); | |
605 | return ret; | |
606 | } | |
607 | ||
3907eef1 | 608 | puts("FPGA image loaded from FIT\n"); |
26a64223 MV |
609 | node = -1; |
610 | } | |
611 | #endif | |
612 | ||
d879616e PT |
613 | /* |
614 | * Find the U-Boot image using the following search order: | |
615 | * - start at 'firmware' (e.g. an ARM Trusted Firmware) | |
616 | * - fall back 'kernel' (e.g. a Falcon-mode OS boot | |
617 | * - fall back to using the first 'loadables' entry | |
618 | */ | |
619 | if (node < 0) | |
1f8e4bf5 MS |
620 | node = spl_fit_get_image_node(fit, images, FIT_FIRMWARE_PROP, |
621 | 0); | |
c8bc3c0c | 622 | #ifdef CONFIG_SPL_OS_BOOT |
c8bc3c0c | 623 | if (node < 0) |
d879616e | 624 | node = spl_fit_get_image_node(fit, images, FIT_KERNEL_PROP, 0); |
c8bc3c0c | 625 | #endif |
f1dcee59 | 626 | if (node < 0) { |
736806fb AP |
627 | debug("could not find firmware image, trying loadables...\n"); |
628 | node = spl_fit_get_image_node(fit, images, "loadables", 0); | |
411cf32d AP |
629 | /* |
630 | * If we pick the U-Boot image from "loadables", start at | |
631 | * the second image when later loading additional images. | |
632 | */ | |
633 | index = 1; | |
736806fb AP |
634 | } |
635 | if (node < 0) { | |
636 | debug("%s: Cannot find u-boot image node: %d\n", | |
637 | __func__, node); | |
f1dcee59 SG |
638 | return -1; |
639 | } | |
640 | ||
8baa3818 AP |
641 | /* Load the image and set up the spl_image structure */ |
642 | ret = spl_load_fit_image(info, sector, fit, base_offset, node, | |
643 | spl_image); | |
644 | if (ret) | |
645 | return ret; | |
da74d1f3 | 646 | |
d879616e PT |
647 | /* |
648 | * For backward compatibility, we treat the first node that is | |
649 | * as a U-Boot image, if no OS-type has been declared. | |
650 | */ | |
337bbb62 | 651 | if (!spl_fit_image_get_os(fit, node, &spl_image->os)) |
c8bc3c0c | 652 | debug("Image OS is %s\n", genimg_get_os_name(spl_image->os)); |
d879616e PT |
653 | #if !defined(CONFIG_SPL_OS_BOOT) |
654 | else | |
655 | spl_image->os = IH_OS_U_BOOT; | |
c8bc3c0c | 656 | #endif |
f1dcee59 | 657 | |
d879616e PT |
658 | /* |
659 | * Booting a next-stage U-Boot may require us to append the FDT. | |
660 | * We allow this to fail, as the U-Boot image might embed its FDT. | |
661 | */ | |
585b468a DB |
662 | if (spl_image->os == IH_OS_U_BOOT) { |
663 | ret = spl_fit_append_fdt(spl_image, info, sector, fit, | |
664 | images, base_offset); | |
665 | if (!IS_ENABLED(CONFIG_OF_EMBED) && ret < 0) | |
666 | return ret; | |
667 | } | |
411cf32d | 668 | |
6b8b98d5 | 669 | firmware_node = node; |
411cf32d AP |
670 | /* Now check if there are more images for us to load */ |
671 | for (; ; index++) { | |
d879616e PT |
672 | uint8_t os_type = IH_OS_INVALID; |
673 | ||
411cf32d AP |
674 | node = spl_fit_get_image_node(fit, images, "loadables", index); |
675 | if (node < 0) | |
676 | break; | |
677 | ||
6b8b98d5 JJH |
678 | /* |
679 | * if the firmware is also a loadable, skip it because | |
680 | * it already has been loaded. This is typically the case with | |
681 | * u-boot.img generated by mkimage. | |
682 | */ | |
683 | if (firmware_node == node) | |
684 | continue; | |
685 | ||
411cf32d AP |
686 | ret = spl_load_fit_image(info, sector, fit, base_offset, node, |
687 | &image_info); | |
c61b2bf3 PR |
688 | if (ret < 0) { |
689 | printf("%s: can't load image loadables index %d (ret = %d)\n", | |
690 | __func__, index, ret); | |
691 | return ret; | |
692 | } | |
411cf32d | 693 | |
337bbb62 | 694 | if (!spl_fit_image_get_os(fit, node, &os_type)) |
d879616e PT |
695 | debug("Loadable is %s\n", genimg_get_os_name(os_type)); |
696 | ||
a616c783 PT |
697 | if (os_type == IH_OS_U_BOOT) { |
698 | spl_fit_append_fdt(&image_info, info, sector, | |
d879616e | 699 | fit, images, base_offset); |
a616c783 PT |
700 | spl_image->fdt_addr = image_info.fdt_addr; |
701 | } | |
d879616e | 702 | |
411cf32d AP |
703 | /* |
704 | * If the "firmware" image did not provide an entry point, | |
705 | * use the first valid entry point from the loadables. | |
706 | */ | |
707 | if (spl_image->entry_point == FDT_ERROR && | |
708 | image_info.entry_point != FDT_ERROR) | |
709 | spl_image->entry_point = image_info.entry_point; | |
a616c783 PT |
710 | |
711 | /* Record our loadables into the FDT */ | |
712 | if (spl_image->fdt_addr) | |
713 | spl_fit_record_loadable(fit, images, index, | |
714 | spl_image->fdt_addr, | |
715 | &image_info); | |
411cf32d AP |
716 | } |
717 | ||
718 | /* | |
719 | * If a platform does not provide CONFIG_SYS_UBOOT_START, U-Boot's | |
720 | * Makefile will set it to 0 and it will end up as the entry point | |
721 | * here. What it actually means is: use the load address. | |
722 | */ | |
723 | if (spl_image->entry_point == FDT_ERROR || spl_image->entry_point == 0) | |
724 | spl_image->entry_point = spl_image->load_addr; | |
725 | ||
e246bfcf YL |
726 | spl_image->flags |= SPL_FIT_FOUND; |
727 | ||
d714a75f | 728 | #ifdef CONFIG_IMX_HAB |
e246bfcf YL |
729 | board_spl_fit_post_load((ulong)fit, size); |
730 | #endif | |
731 | ||
411cf32d | 732 | return 0; |
f1dcee59 | 733 | } |