Commit f1dcee59a2afc4cf39699eef7631edbff8693933
Committed by
Tom Rini
1 parent
4b307f2387
Exists in
v2017.01-smarct4x
and in
29 other branches
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 <sjg@chromium.org>
Showing 5 changed files with 225 additions and 1 deletions Side-by-side Diff
Kconfig
... | ... | @@ -273,6 +273,17 @@ |
273 | 273 | help |
274 | 274 | TODO: Move CONFIG_SYS_TEXT_BASE for all the architecture |
275 | 275 | |
276 | +config SPL_LOAD_FIT | |
277 | + bool "Enable SPL loading U-Boot as a FIT" | |
278 | + depends on FIT | |
279 | + help | |
280 | + Normally with the SPL framework a legacy image is generated as part | |
281 | + of the build. This contains U-Boot along with information as to | |
282 | + where it should be loaded. This option instead enables generation | |
283 | + of a FIT (Flat Image Tree) which provides more flexibility. In | |
284 | + particular it can handle selecting from multiple device tree | |
285 | + and passing the correct one to U-Boot. | |
286 | + | |
276 | 287 | config SYS_CLK_FREQ |
277 | 288 | depends on ARC || ARCH_SUNXI |
278 | 289 | int "CPU clock frequency" |
common/spl/Makefile
... | ... | @@ -10,6 +10,7 @@ |
10 | 10 | |
11 | 11 | ifdef CONFIG_SPL_BUILD |
12 | 12 | obj-$(CONFIG_SPL_FRAMEWORK) += spl.o |
13 | +obj-$(CONFIG_SPL_LOAD_FIT) += spl_fit.o | |
13 | 14 | obj-$(CONFIG_SPL_NOR_SUPPORT) += spl_nor.o |
14 | 15 | obj-$(CONFIG_SPL_YMODEM_SUPPORT) += spl_ymodem.o |
15 | 16 | obj-$(CONFIG_SPL_NAND_SUPPORT) += spl_nand.o |
common/spl/spl_fit.c
1 | +/* | |
2 | + * Copyright (C) 2016 Google, Inc | |
3 | + * Written by Simon Glass <sjg@chromium.org> | |
4 | + * | |
5 | + * SPDX-License-Identifier: GPL-2.0+ | |
6 | + */ | |
7 | + | |
8 | +#include <common.h> | |
9 | +#include <errno.h> | |
10 | +#include <image.h> | |
11 | +#include <libfdt.h> | |
12 | +#include <spl.h> | |
13 | + | |
14 | +static ulong fdt_getprop_u32(const void *fdt, int node, const char *prop) | |
15 | +{ | |
16 | + const u32 *cell; | |
17 | + int len; | |
18 | + | |
19 | + cell = fdt_getprop(fdt, node, prop, &len); | |
20 | + if (len != sizeof(*cell)) | |
21 | + return -1U; | |
22 | + return fdt32_to_cpu(*cell); | |
23 | +} | |
24 | + | |
25 | +static int spl_fit_select_fdt(const void *fdt, int images, int *fdt_offsetp) | |
26 | +{ | |
27 | + const char *name, *fdt_name; | |
28 | + int conf, node, fdt_node; | |
29 | + int len; | |
30 | + | |
31 | + *fdt_offsetp = 0; | |
32 | + conf = fdt_path_offset(fdt, FIT_CONFS_PATH); | |
33 | + if (conf < 0) { | |
34 | + debug("%s: Cannot find /configurations node: %d\n", __func__, | |
35 | + conf); | |
36 | + return -EINVAL; | |
37 | + } | |
38 | + for (node = fdt_first_subnode(fdt, conf); | |
39 | + node >= 0; | |
40 | + node = fdt_next_subnode(fdt, node)) { | |
41 | + name = fdt_getprop(fdt, node, "description", &len); | |
42 | + if (!name) | |
43 | + return -EINVAL; | |
44 | + if (board_fit_config_name_match(name)) | |
45 | + continue; | |
46 | + | |
47 | + debug("Selecting config '%s'", name); | |
48 | + fdt_name = fdt_getprop(fdt, node, FIT_FDT_PROP, &len); | |
49 | + if (!fdt_name) { | |
50 | + debug("%s: Cannot find fdt name property: %d\n", | |
51 | + __func__, len); | |
52 | + return -EINVAL; | |
53 | + } | |
54 | + | |
55 | + debug(", fdt '%s'\n", fdt_name); | |
56 | + fdt_node = fdt_subnode_offset(fdt, images, fdt_name); | |
57 | + if (fdt_node < 0) { | |
58 | + debug("%s: Cannot find fdt node '%s': %d\n", | |
59 | + __func__, fdt_name, fdt_node); | |
60 | + return -EINVAL; | |
61 | + } | |
62 | + | |
63 | + *fdt_offsetp = fdt_getprop_u32(fdt, fdt_node, "data-offset"); | |
64 | + len = fdt_getprop_u32(fdt, fdt_node, "data-size"); | |
65 | +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT | |
66 | + printf("FIT: Selected '%s'\n", name); | |
67 | +#endif | |
68 | + | |
69 | + return len; | |
70 | + } | |
71 | + | |
72 | +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT | |
73 | + printf("No matching DT out of these options:\n"); | |
74 | + for (node = fdt_first_subnode(fdt, conf); | |
75 | + node >= 0; | |
76 | + node = fdt_next_subnode(fdt, node)) { | |
77 | + name = fdt_getprop(fdt, node, "name", &len); | |
78 | + printf(" %s\n", name); | |
79 | + } | |
80 | +#endif | |
81 | + | |
82 | + return -ENOENT; | |
83 | +} | |
84 | + | |
85 | +int spl_load_simple_fit(struct spl_load_info *info, ulong sector, void *fit) | |
86 | +{ | |
87 | + int sectors; | |
88 | + ulong size, load; | |
89 | + unsigned long count; | |
90 | + int node, images; | |
91 | + void *load_ptr; | |
92 | + int fdt_offset, fdt_len; | |
93 | + int data_offset, data_size; | |
94 | + int base_offset; | |
95 | + int src_sector; | |
96 | + void *dst; | |
97 | + | |
98 | + /* | |
99 | + * Figure out where the external images start. This is the base for the | |
100 | + * data-offset properties in each image. | |
101 | + */ | |
102 | + size = fdt_totalsize(fit); | |
103 | + size = (size + 3) & ~3; | |
104 | + base_offset = (size + 3) & ~3; | |
105 | + | |
106 | + /* | |
107 | + * So far we only have one block of data from the FIT. Read the entire | |
108 | + * thing, including that first block, placing it so it finishes before | |
109 | + * where we will load the image. | |
110 | + * | |
111 | + * Note that we will load the image such that its first byte will be | |
112 | + * at the load address. Since that byte may be part-way through a | |
113 | + * block, we may load the image up to one block before the load | |
114 | + * address. So take account of that here by subtracting an addition | |
115 | + * block length from the FIT start position. | |
116 | + * | |
117 | + * In fact the FIT has its own load address, but we assume it cannot | |
118 | + * be before CONFIG_SYS_TEXT_BASE. | |
119 | + */ | |
120 | + fit = (void *)(CONFIG_SYS_TEXT_BASE - size - info->bl_len); | |
121 | + sectors = (size + info->bl_len - 1) / info->bl_len; | |
122 | + count = info->read(info, sector, sectors, fit); | |
123 | + debug("fit read sector %lx, sectors=%d, dst=%p, count=%lu\n", | |
124 | + sector, sectors, fit, count); | |
125 | + if (count == 0) | |
126 | + return -EIO; | |
127 | + | |
128 | + /* find the firmware image to load */ | |
129 | + images = fdt_path_offset(fit, FIT_IMAGES_PATH); | |
130 | + if (images < 0) { | |
131 | + debug("%s: Cannot find /images node: %d\n", __func__, images); | |
132 | + return -1; | |
133 | + } | |
134 | + node = fdt_first_subnode(fit, images); | |
135 | + if (node < 0) { | |
136 | + debug("%s: Cannot find first image node: %d\n", __func__, node); | |
137 | + return -1; | |
138 | + } | |
139 | + | |
140 | + /* Get its information and set up the spl_image structure */ | |
141 | + data_offset = fdt_getprop_u32(fit, node, "data-offset"); | |
142 | + data_size = fdt_getprop_u32(fit, node, "data-size"); | |
143 | + load = fdt_getprop_u32(fit, node, "load"); | |
144 | + debug("data_offset=%x, data_size=%x\n", data_offset, data_size); | |
145 | + spl_image.load_addr = load; | |
146 | + spl_image.entry_point = load; | |
147 | + spl_image.os = IH_OS_U_BOOT; | |
148 | + | |
149 | + /* | |
150 | + * Work out where to place the image. We read it so that the first | |
151 | + * byte will be at 'load'. This may mean we need to load it starting | |
152 | + * before then, since we can only read whole blocks. | |
153 | + */ | |
154 | + sectors = (data_size + info->bl_len - 1) / info->bl_len; | |
155 | + data_offset += base_offset; | |
156 | + load_ptr = (void *)load; | |
157 | + debug("U-Boot size %x, data %p\n", data_size, load_ptr); | |
158 | + dst = load_ptr - (data_offset % info->bl_len); | |
159 | + | |
160 | + /* Read the image */ | |
161 | + src_sector = sector + data_offset / info->bl_len; | |
162 | + debug("image: data_offset=%x, dst=%p, src_sector=%x, sectors=%x\n", | |
163 | + data_offset, dst, src_sector, sectors); | |
164 | + count = info->read(info, src_sector, sectors, dst); | |
165 | + if (count != sectors) | |
166 | + return -EIO; | |
167 | + | |
168 | + /* Figure out which device tree the board wants to use */ | |
169 | + fdt_len = spl_fit_select_fdt(fit, images, &fdt_offset); | |
170 | + if (fdt_len < 0) | |
171 | + return fdt_len; | |
172 | + | |
173 | + /* | |
174 | + * Read the device tree and place it after the image. There may be | |
175 | + * some extra data before it since we can only read entire blocks. | |
176 | + */ | |
177 | + dst = load_ptr + data_size; | |
178 | + fdt_offset += base_offset; | |
179 | + count = info->read(info, sector + fdt_offset / info->bl_len, sectors, | |
180 | + dst); | |
181 | + debug("fit read %x sectors to %x, dst %p, data_offset %x\n", | |
182 | + sectors, spl_image.load_addr, dst, fdt_offset); | |
183 | + if (count != sectors) | |
184 | + return -EIO; | |
185 | + | |
186 | + /* | |
187 | + * Copy the device tree so that it starts immediately after the image. | |
188 | + * After this we will have the U-Boot image and its device tree ready | |
189 | + * for us to start. | |
190 | + */ | |
191 | + memcpy(dst, dst + fdt_offset % info->bl_len, fdt_len); | |
192 | + | |
193 | + return 0; | |
194 | +} |
include/image.h
... | ... | @@ -780,7 +780,6 @@ |
780 | 780 | /*******************************************************************/ |
781 | 781 | /* New uImage format specific code (prefixed with fit_) */ |
782 | 782 | /*******************************************************************/ |
783 | -#if IMAGE_ENABLE_FIT | |
784 | 783 | |
785 | 784 | #define FIT_IMAGES_PATH "/images" |
786 | 785 | #define FIT_CONFS_PATH "/configurations" |
... | ... | @@ -813,6 +812,7 @@ |
813 | 812 | |
814 | 813 | #define FIT_MAX_HASH_LEN HASH_MAX_DIGEST_SIZE |
815 | 814 | |
815 | +#if IMAGE_ENABLE_FIT | |
816 | 816 | /* cmdline argument format parsing */ |
817 | 817 | int fit_parse_conf(const char *spec, ulong addr_curr, |
818 | 818 | ulong *addr, const char **conf_name); |
include/spl.h
... | ... | @@ -29,6 +29,24 @@ |
29 | 29 | u32 flags; |
30 | 30 | }; |
31 | 31 | |
32 | +/* | |
33 | + * Information required to load data from a device | |
34 | + * | |
35 | + * @dev: Pointer to the device, e.g. struct mmc * | |
36 | + * @priv: Private data for the device | |
37 | + * @bl_len: Block length for reading in bytes | |
38 | + * @read: Function to call to read from the device | |
39 | + */ | |
40 | +struct spl_load_info { | |
41 | + void *dev; | |
42 | + void *priv; | |
43 | + int bl_len; | |
44 | + ulong (*read)(struct spl_load_info *load, ulong sector, ulong count, | |
45 | + void *buf); | |
46 | +}; | |
47 | + | |
48 | +int spl_load_simple_fit(struct spl_load_info *info, ulong sector, void *fdt); | |
49 | + | |
32 | 50 | #define SPL_COPY_PAYLOAD_ONLY 1 |
33 | 51 | |
34 | 52 | extern struct spl_image_info spl_image; |