blob: d2ce66071bc0fa85bd35a9e86a25e76f7d4c8b43 [file] [log] [blame]
Simon Glassee112092021-03-07 17:35:15 -07001// SPDX-License-Identifier: GPL-2.0+
2/*
Sean Andersondcd91b12023-10-14 16:47:57 -04003 * Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
Simon Glassee112092021-03-07 17:35:15 -07004 */
5
6#include <common.h>
Sean Andersonf65dcc02023-10-14 16:47:59 -04007#include <image.h>
8#include <imx_container.h>
Simon Glassee112092021-03-07 17:35:15 -07009#include <mapmem.h>
Sean Andersonf65dcc02023-10-14 16:47:59 -040010#include <memalign.h>
11#include <rand.h>
12#include <spl.h>
13#include <test/spl.h>
14#include <test/ut.h>
15#include <u-boot/crc.h>
Simon Glassee112092021-03-07 17:35:15 -070016
17int board_fit_config_name_match(const char *name)
18{
19 return 0;
20}
21
Simon Glassbb7d3bb2022-09-06 20:26:52 -060022struct legacy_img_hdr *spl_get_load_buffer(ssize_t offset, size_t size)
Simon Glassee112092021-03-07 17:35:15 -070023{
24 return map_sysmem(0x100000, 0);
25}
Sean Andersonf65dcc02023-10-14 16:47:59 -040026
27/* Try to reuse the load buffer to conserve memory */
28void *board_spl_fit_buffer_addr(ulong fit_size, int sectors, int bl_len)
29{
30 static void *buf;
31 static size_t size;
32
33 if (size < sectors * bl_len) {
34 free(buf);
35 size = sectors * bl_len;
36 buf = malloc_cache_aligned(size);
37 }
38 return buf;
39}
40
41/* Local flags for spl_image; start from the "top" to avoid conflicts */
42#define SPL_IMX_CONTAINER 0x80000000
43
44void generate_data(char *data, size_t size, const char *test_name)
45{
46 int i;
47 unsigned int seed = 1;
48
49 while (*test_name) {
50 seed += *test_name++;
51 rand_r(&seed);
52 }
53
54 for (i = 0; i < size; i++)
55 data[i] = (i & 0xf) << 4 | (rand_r(&seed) & 0xf);
56}
57
58static size_t create_legacy(void *dst, struct spl_image_info *spl_image,
59 size_t *data_offset)
60{
61 struct legacy_img_hdr *hdr = dst;
62 void *data = dst + sizeof(*hdr);
63
64 if (data_offset)
65 *data_offset = data - dst;
66
67 if (!dst)
68 goto out;
69
70 image_set_magic(hdr, IH_MAGIC);
71 image_set_time(hdr, 0);
72 image_set_size(hdr, spl_image->size);
73 image_set_load(hdr, spl_image->load_addr);
74 image_set_ep(hdr, spl_image->entry_point);
75 image_set_dcrc(hdr, crc32(0, data, spl_image->size));
76 image_set_os(hdr, spl_image->os);
77 image_set_arch(hdr, IH_ARCH_DEFAULT);
78 image_set_type(hdr, IH_TYPE_FIRMWARE);
79 image_set_comp(hdr, IH_COMP_NONE);
80 image_set_name(hdr, spl_image->name);
81 image_set_hcrc(hdr, crc32(0, (void *)hdr, sizeof(*hdr)));
82
83out:
84 return sizeof(*hdr) + spl_image->size;
85}
86
87static size_t create_imx8(void *dst, struct spl_image_info *spl_image,
88 size_t *data_offset)
89{
90 struct container_hdr *hdr = dst;
91 struct boot_img_t *img = dst + sizeof(*hdr);
92 size_t length = sizeof(*hdr) + sizeof(*img);
93 /* Align to MMC block size for now */
94 void *data = dst + 512;
95
96 if (data_offset)
97 *data_offset = data - dst;
98
99 if (!dst)
100 goto out;
101
102 hdr->version = CONTAINER_HDR_VERSION;
103 hdr->length_lsb = length & 0xff;
104 hdr->length_msb = length >> 8;
105 hdr->tag = CONTAINER_HDR_TAG;
106 hdr->num_images = 1;
107
108 /* spl_load_imx_container doesn't handle endianness; whoops! */
109 img->offset = data - dst;
110 img->size = spl_image->size;
111 img->dst = spl_image->load_addr;
112 img->entry = spl_image->entry_point;
113
114out:
115 return data - dst + spl_image->size;
116}
117
118#define ADDRESS_CELLS (sizeof(uintptr_t) / sizeof(u32))
119
120static inline int fdt_property_addr(void *fdt, const char *name, uintptr_t val)
121{
122 if (sizeof(uintptr_t) == sizeof(u32))
123 return fdt_property_u32(fdt, name, val);
124 return fdt_property_u64(fdt, name, val);
125}
126
127static size_t start_fit(void *dst, size_t fit_size, size_t data_size,
128 bool external)
129{
130 void *data;
131
132 if (fdt_create(dst, fit_size))
133 return 0;
134 if (fdt_finish_reservemap(dst))
135 return 0;
136 if (fdt_begin_node(dst, ""))
137 return 0;
138 if (fdt_property_u32(dst, FIT_TIMESTAMP_PROP, 0))
139 return 0;
140 if (fdt_property_u32(dst, "#address-cells", ADDRESS_CELLS))
141 return 0;
142 if (fdt_property_string(dst, FIT_DESC_PROP, ""))
143 return 0;
144
145 if (fdt_begin_node(dst, "images"))
146 return 0;
147 if (fdt_begin_node(dst, "u-boot"))
148 return 0;
149
150 if (external) {
151 if (fdt_property_u32(dst, FIT_DATA_OFFSET_PROP, 0))
152 return 0;
153 return fit_size;
154 }
155
156 if (fdt_property_placeholder(dst, FIT_DATA_PROP, data_size, &data))
157 return 0;
158 return data - dst;
159}
160
161static size_t create_fit(void *dst, struct spl_image_info *spl_image,
162 size_t *data_offset, bool external)
163{
164 size_t prop_size = 596, total_size = prop_size + spl_image->size;
165 size_t off, size;
166
167 if (external) {
168 size = prop_size;
169 off = size;
170 } else {
171 char tmp[256];
172
173 size = total_size;
174 off = start_fit(tmp, sizeof(tmp), 0, false);
175 if (!off)
176 return 0;
177 }
178
179 if (data_offset)
180 *data_offset = off;
181
182 if (!dst)
183 goto out;
184
185 if (start_fit(dst, size, spl_image->size, external) != off)
186 return 0;
187
188 if (fdt_property_string(dst, FIT_DESC_PROP, spl_image->name))
189 return 0;
190 if (fdt_property_string(dst, FIT_TYPE_PROP, "firmware"))
191 return 0;
192 if (fdt_property_string(dst, FIT_COMP_PROP, "none"))
193 return 0;
194 if (fdt_property_u32(dst, FIT_DATA_SIZE_PROP, spl_image->size))
195 return 0;
196 if (fdt_property_string(dst, FIT_OS_PROP,
197 genimg_get_os_short_name(spl_image->os)))
198 return 0;
199 if (fdt_property_string(dst, FIT_ARCH_PROP,
200 genimg_get_arch_short_name(IH_ARCH_DEFAULT)))
201 return 0;
202 if (fdt_property_addr(dst, FIT_ENTRY_PROP, spl_image->entry_point))
203 return 0;
204 if (fdt_property_addr(dst, FIT_LOAD_PROP, spl_image->load_addr))
205 return 0;
206 if (fdt_end_node(dst)) /* u-boot */
207 return 0;
208 if (fdt_end_node(dst)) /* images */
209 return 0;
210
211 if (fdt_begin_node(dst, "configurations"))
212 return 0;
213 if (fdt_property_string(dst, FIT_DEFAULT_PROP, "config-1"))
214 return 0;
215 if (fdt_begin_node(dst, "config-1"))
216 return 0;
217 if (fdt_property_string(dst, FIT_DESC_PROP, spl_image->name))
218 return 0;
219 if (fdt_property_string(dst, FIT_FIRMWARE_PROP, "u-boot"))
220 return 0;
221 if (fdt_end_node(dst)) /* configurations */
222 return 0;
223 if (fdt_end_node(dst)) /* config-1 */
224 return 0;
225
226 if (fdt_end_node(dst)) /* root */
227 return 0;
228 if (fdt_finish(dst))
229 return 0;
230
231 if (external) {
232 if (fdt_totalsize(dst) > size)
233 return 0;
234 fdt_set_totalsize(dst, size);
235 }
236
237out:
238 return total_size;
239}
240
241size_t create_image(void *dst, enum spl_test_image type,
242 struct spl_image_info *info, size_t *data_offset)
243{
244 bool external = false;
245
246 info->os = IH_OS_U_BOOT;
247 info->load_addr = CONFIG_TEXT_BASE;
248 info->entry_point = CONFIG_TEXT_BASE + 0x100;
249 info->flags = 0;
250
251 switch (type) {
252 case LEGACY:
253 return create_legacy(dst, info, data_offset);
254 case IMX8:
255 info->flags = SPL_IMX_CONTAINER;
256 return create_imx8(dst, info, data_offset);
257 case FIT_EXTERNAL:
258 /*
259 * spl_fit_append_fdt will clobber external images with U-Boot's
260 * FDT if the image doesn't have one. Just set the OS to
261 * something which doesn't take a devicetree.
262 */
263 if (!IS_ENABLED(CONFIG_LOAD_FIT_FULL))
264 info->os = IH_OS_TEE;
265 external = true;
266 case FIT_INTERNAL:
267 info->flags = SPL_FIT_FOUND;
268 return create_fit(dst, info, data_offset, external);
269 }
270
271 return 0;
272}
273
274int check_image_info(struct unit_test_state *uts, struct spl_image_info *info1,
275 struct spl_image_info *info2)
276{
277 if (info2->name) {
278 if (info1->flags & SPL_FIT_FOUND)
279 ut_asserteq_str(genimg_get_os_name(info1->os),
280 info2->name);
281 else
282 ut_asserteq_str(info1->name, info2->name);
283 }
284
285 if (info1->flags & SPL_IMX_CONTAINER)
286 ut_asserteq(IH_OS_INVALID, info2->os);
287 else
288 ut_asserteq(info1->os, info2->os);
289
290 ut_asserteq(info1->entry_point, info2->entry_point);
291 if (info1->flags & (SPL_FIT_FOUND | SPL_IMX_CONTAINER) ||
292 info2->flags & SPL_COPY_PAYLOAD_ONLY) {
293 ut_asserteq(info1->load_addr, info2->load_addr);
294 if (info1->flags & SPL_IMX_CONTAINER)
295 ut_asserteq(0, info2->size);
296 else
297 ut_asserteq(info1->size, info2->size);
298 } else {
299 ut_asserteq(info1->load_addr - sizeof(struct legacy_img_hdr),
300 info2->load_addr);
301 ut_asserteq(info1->size + sizeof(struct legacy_img_hdr),
302 info2->size);
303 }
304
305 return 0;
306}
307
308static ulong spl_test_read(struct spl_load_info *load, ulong sector,
309 ulong count, void *buf)
310{
311 memcpy(buf, load->priv + sector, count);
312 return count;
313}
314
315static int spl_test_image(struct unit_test_state *uts, const char *test_name,
316 enum spl_test_image type)
317{
318 size_t img_size, img_data, data_size = SPL_TEST_DATA_SIZE;
319 struct spl_image_info info_write = {
320 .name = test_name,
321 .size = data_size,
322 }, info_read = { };
323 char *data;
324 void *img;
325
326 img_size = create_image(NULL, type, &info_write, &img_data);
327 ut_assert(img_size);
328 img = calloc(img_size, 1);
329 ut_assertnonnull(img);
330
331 data = img + img_data;
332 generate_data(data, data_size, test_name);
333 ut_asserteq(img_size, create_image(img, type, &info_write, NULL));
334
335 if (type == LEGACY) {
336 ut_assertok(spl_parse_image_header(&info_read, NULL, img));
337 if (check_image_info(uts, &info_write, &info_read))
338 return CMD_RET_FAILURE;
339 } else {
340 struct spl_load_info load = {
341 .bl_len = 1,
342 .priv = img,
343 .read = spl_test_read,
344 };
345
346 if (type == IMX8)
347 ut_assertok(spl_load_imx_container(&info_read, &load,
348 0));
349 else if (IS_ENABLED(CONFIG_SPL_FIT_FULL))
350 ut_assertok(spl_parse_image_header(&info_read, NULL,
351 img));
352 else
353 ut_assertok(spl_load_simple_fit(&info_read, &load, 0,
354 img));
355 if (check_image_info(uts, &info_write, &info_read))
356 return CMD_RET_FAILURE;
357 ut_asserteq_mem(data, phys_to_virt(info_write.load_addr),
358 data_size);
359 }
360
361 free(img);
362 return 0;
363}
364SPL_IMG_TEST(spl_test_image, LEGACY, 0);
365SPL_IMG_TEST(spl_test_image, IMX8, 0);
366SPL_IMG_TEST(spl_test_image, FIT_INTERNAL, 0);
367SPL_IMG_TEST(spl_test_image, FIT_EXTERNAL, 0);
Sean Anderson576295d2023-10-14 16:48:02 -0400368
369int do_spl_test_load(struct unit_test_state *uts, const char *test_name,
370 enum spl_test_image type, struct spl_image_loader *loader,
371 int (*write_image)(struct unit_test_state *, void *, size_t))
372{
373 size_t img_size, img_data, plain_size = SPL_TEST_DATA_SIZE;
374 struct spl_image_info info_write = {
375 .name = test_name,
376 .size = plain_size,
377 }, info_read = { };
378 struct spl_boot_device bootdev = {
379 .boot_device = loader->boot_device,
380 };
381 char *plain;
382 void *img;
383
384 img_size = create_image(NULL, type, &info_write, &img_data);
385 ut_assert(img_size);
386 img = calloc(img_size, 1);
387 ut_assertnonnull(img);
388
389 plain = img + img_data;
390 generate_data(plain, plain_size, test_name);
391 ut_asserteq(img_size, create_image(img, type, &info_write, NULL));
392
393 if (write_image(uts, img, img_size))
394 return CMD_RET_FAILURE;
395
396 ut_assertok(loader->load_image(&info_read, &bootdev));
397 if (check_image_info(uts, &info_write, &info_read))
398 return CMD_RET_FAILURE;
399 ut_asserteq_mem(plain, phys_to_virt(info_write.load_addr), plain_size);
400
401 free(img);
402 return 0;
403}