uboot-d1: add bootloader for upcoming d1 target
[openwrt/staging/mans0n.git] / package / boot / uboot-d1 / patches / 0033-tools-mkimage-Add-Allwinner-TOC1-support.patch
1 From ff0e952a3a380ba191375d5f68609cdbe026d535 Mon Sep 17 00:00:00 2001
2 From: Samuel Holland <samuel@sholland.org>
3 Date: Sat, 7 Aug 2021 19:55:20 -0500
4 Subject: [PATCH 33/90] tools: mkimage: Add Allwinner TOC1 support
5
6 TOC1 is an container format used by Allwinner's boot0 that can hold
7 multiple images. It supports encryption and signatures, but that
8 functionality is not implemented, only the basic "non-secure" subset.
9
10 A config file is used to provide the list of data files to include. Its
11 path is passed as the argument to "-d". It contains sections of the
12 following form:
13
14 [name]
15 file = /path/to/file
16 addr = 0x12345678
17
18 Specific well-known names, such as "dtb", "opensbi", and "u-boot", are
19 used by the bootloader to distinguish the items inside the image.
20
21 Cover-letter:
22 tools: mkimage: Add Allwinner TOC1 support
23
24 The SPL port for the Allwinner D1 RISC-V SoC will probably take a while
25 longer than porting U-Boot proper, as none of the relevant drivers are
26 set up for DM in SPL. In the meantime, we are using[1][2] a fork[3] of
27 Allwinner's boot0 loader, which they also call "spl" in their BSP. boot0
28 uses this TOC1 image format.
29
30 The vendor tools for generating TOC1 images require a binary config file
31 generated by their FEX compiler. Instead of trying to support that, I
32 made up a simple human-readable config file format. I didn't see any
33 existing platform-agnostic parser for multi-image containers in mkimage.
34
35 I am sending this as RFC because it is only of temporary/limited use.
36 It only works with one specific fork of boot0 which was modified to
37 "behave" (the the original vendor version monkey-patches a custom header
38 inside the U-Boot image during boot). So it will be obsolete once U-Boot
39 SPL is ported. And it is Yet Another Image Format. On the other hand, it
40 does work, and it is currently being used.
41
42 [1]: https://linux-sunxi.org/Allwinner_Nezha#U-Boot
43 [2]: https://fedoraproject.org/wiki/Architectures/RISC-V/Allwinner
44 [3]: https://github.com/smaeul/sun20i_d1_spl
45 END
46 Series-prefix: RFC
47 Series-to: sunxi
48 Signed-off-by: Samuel Holland <samuel@sholland.org>
49 ---
50 boot/image.c | 1 +
51 include/image.h | 1 +
52 include/sunxi_image.h | 26 ++++
53 tools/Makefile | 1 +
54 tools/sunxi_toc1.c | 318 ++++++++++++++++++++++++++++++++++++++++++
55 5 files changed, 347 insertions(+)
56 create mode 100644 tools/sunxi_toc1.c
57
58 --- a/boot/image.c
59 +++ b/boot/image.c
60 @@ -180,6 +180,7 @@ static const table_entry_t uimage_type[]
61 { IH_TYPE_COPRO, "copro", "Coprocessor Image"},
62 { IH_TYPE_SUNXI_EGON, "sunxi_egon", "Allwinner eGON Boot Image" },
63 { IH_TYPE_SUNXI_TOC0, "sunxi_toc0", "Allwinner TOC0 Boot Image" },
64 + { IH_TYPE_SUNXI_TOC1, "sunxi_toc1", "Allwinner TOC1 Boot Image" },
65 { -1, "", "", },
66 };
67
68 --- a/include/image.h
69 +++ b/include/image.h
70 @@ -229,6 +229,7 @@ enum image_type_t {
71 IH_TYPE_COPRO, /* Coprocessor Image for remoteproc*/
72 IH_TYPE_SUNXI_EGON, /* Allwinner eGON Boot Image */
73 IH_TYPE_SUNXI_TOC0, /* Allwinner TOC0 Boot Image */
74 + IH_TYPE_SUNXI_TOC1, /* Allwinner TOC1 Boot Image */
75
76 IH_TYPE_COUNT, /* Number of image types */
77 };
78 --- a/include/sunxi_image.h
79 +++ b/include/sunxi_image.h
80 @@ -116,4 +116,30 @@ struct __packed toc0_item_info {
81 #define TOC0_ITEM_INFO_NAME_KEY 0x00010303
82 #define TOC0_ITEM_INFO_END "IIE;"
83
84 +struct __packed toc1_main_info {
85 + uint8_t name[16];
86 + __le32 magic;
87 + __le32 checksum;
88 + __le32 serial;
89 + __le32 status;
90 + __le32 num_items;
91 + __le32 length;
92 + __le32 major_version;
93 + __le32 minor_version;
94 + __le32 reserved[3];
95 + uint8_t end[4];
96 +};
97 +
98 +struct __packed toc1_item_info {
99 + uint8_t name[64];
100 + __le32 offset;
101 + __le32 length;
102 + __le32 encryption;
103 + __le32 type;
104 + __le32 load_addr;
105 + __le32 index;
106 + __le32 reserved[69];
107 + uint8_t end[4];
108 +};
109 +
110 #endif
111 --- a/tools/Makefile
112 +++ b/tools/Makefile
113 @@ -132,6 +132,7 @@ dumpimage-mkimage-objs := aisimage.o \
114 $(ROCKCHIP_OBS) \
115 socfpgaimage.o \
116 sunxi_egon.o \
117 + sunxi_toc1.o \
118 lib/crc16-ccitt.o \
119 lib/hash-checksum.o \
120 lib/sha1.o \
121 --- /dev/null
122 +++ b/tools/sunxi_toc1.c
123 @@ -0,0 +1,318 @@
124 +// SPDX-License-Identifier: GPL-2.0+
125 +/*
126 + * (C) Copyright 2018 Arm Ltd.
127 + * (C) Copyright 2020-2021 Samuel Holland <samuel@sholland.org>
128 + */
129 +
130 +#include <assert.h>
131 +#include <stdint.h>
132 +#include <stdio.h>
133 +#include <stdlib.h>
134 +#include <string.h>
135 +
136 +#include <image.h>
137 +#include <sunxi_image.h>
138 +
139 +#include "imagetool.h"
140 +#include "mkimage.h"
141 +
142 +#define SECTOR_SIZE 512
143 +
144 +struct item_desc {
145 + const char *name;
146 + const char *file;
147 + unsigned long addr;
148 + long length;
149 +};
150 +
151 +static uint32_t toc1_header_length(uint32_t num_items)
152 +{
153 + return ALIGN(sizeof(struct toc1_main_info) +
154 + sizeof(struct toc1_item_info) * num_items, SECTOR_SIZE);
155 +}
156 +
157 +static int toc1_parse_cfg(const char *file, struct item_desc **desc,
158 + uint32_t *main_length, uint32_t *num_items)
159 +{
160 + struct item_desc *descs = NULL;
161 + int ret = EXIT_FAILURE;
162 + FILE *cfg, *fp = NULL;
163 + uint32_t ndescs = 0;
164 + char *line = NULL;
165 + size_t len = 0;
166 +
167 + *desc = NULL;
168 + *main_length = 0;
169 + *num_items = 0;
170 +
171 + cfg = fopen(file, "r");
172 + if (!cfg)
173 + return ret;
174 +
175 + while (getline(&line, &len, cfg) > 0) {
176 + char *end, *s;
177 +
178 + if (line[0] == '[') {
179 + s = line + 1;
180 + end = strchr(s, ']');
181 + if (!end || end[1] != '\n')
182 + goto err;
183 + end[0] = '\0';
184 +
185 + ndescs++;
186 + descs = reallocarray(descs, ndescs, sizeof(*descs));
187 + if (!descs)
188 + goto err;
189 +
190 + descs[ndescs - 1].name = strdup(s);
191 + } else if (line[0] != '#' && line[0] != '\n') {
192 + s = strchr(line, '=');
193 + if (!s)
194 + goto err;
195 + while ((++s)[0] == ' ')
196 + ;
197 + end = strchr(s, '\n');
198 + if (!end)
199 + goto err;
200 + end[0] = '\0';
201 +
202 + if (!strncmp(line, "file", strlen("file"))) {
203 + fp = fopen(s, "rb");
204 + if (!fp)
205 + goto err;
206 + if (fseek(fp, 0, SEEK_END) < 0)
207 + goto err;
208 + descs[ndescs - 1].file = strdup(s);
209 + descs[ndescs - 1].length = ftell(fp);
210 + *main_length += ALIGN(descs[ndescs - 1].length,
211 + SECTOR_SIZE);
212 + fclose(fp);
213 + fp = NULL;
214 + } else if (!strncmp(line, "addr", strlen("addr"))) {
215 + descs[ndescs - 1].addr = strtoul(s, NULL, 0);
216 + } else {
217 + goto err;
218 + }
219 + }
220 + }
221 +
222 + *desc = descs;
223 + *main_length += toc1_header_length(ndescs);
224 + *num_items = ndescs;
225 +
226 + ret = EXIT_SUCCESS;
227 +
228 +err:
229 + if (fp)
230 + fclose(fp);
231 + if (ret)
232 + free(descs);
233 + free(line);
234 + fclose(cfg);
235 +
236 + return ret;
237 +}
238 +
239 +static int toc1_create(uint8_t *buf, uint32_t len,
240 + const struct item_desc *desc, uint32_t num_items)
241 +{
242 + struct toc1_main_info *main = (void *)buf;
243 + struct toc1_item_info *item = (void *)(main + 1);
244 + uint32_t item_offset, item_length;
245 + uint32_t *buf32 = (void *)buf;
246 + int ret = EXIT_FAILURE;
247 + uint32_t checksum = 0;
248 + FILE *fp = NULL;
249 + int i;
250 +
251 + /* Create the main TOC1 header. */
252 + main->magic = cpu_to_le32(TOC0_MAIN_INFO_MAGIC);
253 + main->checksum = cpu_to_le32(BROM_STAMP_VALUE);
254 + main->num_items = cpu_to_le32(num_items);
255 + memcpy(main->end, TOC0_MAIN_INFO_END, sizeof(main->end));
256 +
257 + item_offset = 0;
258 + item_length = toc1_header_length(num_items);
259 +
260 + for (i = 0; i < num_items; ++i, ++item, ++desc) {
261 + item_offset = item_offset + item_length;
262 + item_length = desc->length;
263 +
264 + /* Create the item header. */
265 + memcpy(item->name, desc->name,
266 + strnlen(desc->name, sizeof(item->name)));
267 + item->offset = cpu_to_le32(item_offset);
268 + item->length = cpu_to_le32(item_length);
269 + item->load_addr = cpu_to_le32(desc->addr);
270 + memcpy(item->end, TOC0_ITEM_INFO_END, sizeof(item->end));
271 +
272 + /* Read in the data. */
273 + fp = fopen(desc->file, "rb");
274 + if (!fp)
275 + goto err;
276 + if (!fread(buf + item_offset, item_length, 1, fp))
277 + goto err;
278 + fclose(fp);
279 + fp = NULL;
280 +
281 + /* Pad the sectors with 0xff to be flash-friendly. */
282 + item_offset = item_offset + item_length;
283 + item_length = ALIGN(item_offset, SECTOR_SIZE) - item_offset;
284 + memset(buf + item_offset, 0xff, item_length);
285 + }
286 +
287 + /* Fill in the total padded file length. */
288 + item_offset = item_offset + item_length;
289 + main->length = cpu_to_le32(item_offset);
290 +
291 + /* Verify enough space was provided when creating the image. */
292 + assert(len >= item_offset);
293 +
294 + /* Calculate the checksum. Yes, it's that simple. */
295 + for (i = 0; i < item_offset / 4; ++i)
296 + checksum += le32_to_cpu(buf32[i]);
297 + main->checksum = cpu_to_le32(checksum);
298 +
299 + ret = EXIT_SUCCESS;
300 +
301 +err:
302 + if (fp)
303 + fclose(fp);
304 +
305 + return ret;
306 +}
307 +
308 +static int toc1_verify(const uint8_t *buf, uint32_t len)
309 +{
310 + const struct toc1_main_info *main = (void *)buf;
311 + const struct toc1_item_info *item = (void *)(main + 1);
312 + uint32_t checksum = BROM_STAMP_VALUE;
313 + uint32_t main_length, num_items;
314 + uint32_t *buf32 = (void *)buf;
315 + int ret = EXIT_FAILURE;
316 + int i;
317 +
318 + num_items = le32_to_cpu(main->num_items);
319 + main_length = le32_to_cpu(main->length);
320 +
321 + if (len < main_length || main_length < toc1_header_length(num_items))
322 + goto err;
323 +
324 + /* Verify the main header. */
325 + if (le32_to_cpu(main->magic) != TOC0_MAIN_INFO_MAGIC)
326 + goto err;
327 + /* Verify the checksum without modifying the buffer. */
328 + for (i = 0; i < main_length / 4; ++i)
329 + checksum += le32_to_cpu(buf32[i]);
330 + if (checksum != 2 * le32_to_cpu(main->checksum))
331 + goto err;
332 + /* The length must be at least 512 byte aligned. */
333 + if (main_length % SECTOR_SIZE)
334 + goto err;
335 + if (memcmp(main->end, TOC0_MAIN_INFO_END, sizeof(main->end)))
336 + goto err;
337 +
338 + /* Verify each item header. */
339 + for (i = 0; i < num_items; ++i, ++item)
340 + if (memcmp(item->end, TOC0_ITEM_INFO_END, sizeof(item->end)))
341 + goto err;
342 +
343 + ret = EXIT_SUCCESS;
344 +
345 +err:
346 + return ret;
347 +}
348 +
349 +static int toc1_check_params(struct image_tool_params *params)
350 +{
351 + if (!params->dflag)
352 + return -EINVAL;
353 +
354 + return 0;
355 +}
356 +
357 +static int toc1_verify_header(unsigned char *buf, int image_size,
358 + struct image_tool_params *params)
359 +{
360 + return toc1_verify(buf, image_size);
361 +}
362 +
363 +static void toc1_print_header(const void *buf)
364 +{
365 + const struct toc1_main_info *main = buf;
366 + const struct toc1_item_info *item = (void *)(main + 1);
367 + uint32_t head_length, main_length, num_items;
368 + uint32_t item_offset, item_length, item_addr;
369 + int i;
370 +
371 + num_items = le32_to_cpu(main->num_items);
372 + head_length = sizeof(*main) + num_items * sizeof(*item);
373 + main_length = le32_to_cpu(main->length);
374 +
375 + printf("Allwinner TOC1 Image\n"
376 + "Size: %d bytes\n"
377 + "Contents: %d items\n"
378 + " 00000000:%08x Headers\n",
379 + main_length, num_items, head_length);
380 +
381 + for (i = 0; i < num_items; ++i, ++item) {
382 + item_offset = le32_to_cpu(item->offset);
383 + item_length = le32_to_cpu(item->length);
384 + item_addr = le32_to_cpu(item->load_addr);
385 +
386 + printf(" %08x:%08x => %08x %s\n",
387 + item_offset, item_length, item_addr, item->name);
388 + }
389 +}
390 +
391 +static void toc1_set_header(void *buf, struct stat *sbuf, int ifd,
392 + struct image_tool_params *params)
393 +{
394 + /* Image is already written below. */
395 +}
396 +
397 +static int toc1_check_image_type(uint8_t type)
398 +{
399 + return type == IH_TYPE_SUNXI_TOC1 ? 0 : 1;
400 +}
401 +
402 +static int toc1_vrec_header(struct image_tool_params *params,
403 + struct image_type_params *tparams)
404 +{
405 + uint32_t main_length, num_items;
406 + struct item_desc *desc;
407 + int ret;
408 +
409 + /* This "header" contains the entire image. */
410 + params->skipcpy = 1;
411 +
412 + ret = toc1_parse_cfg(params->datafile, &desc, &main_length, &num_items);
413 + if (ret)
414 + exit(ret);
415 +
416 + tparams->header_size = main_length;
417 + tparams->hdr = calloc(tparams->header_size, 1);
418 + if (!tparams->hdr)
419 + exit(ret);
420 +
421 + ret = toc1_create(tparams->hdr, tparams->header_size, desc, num_items);
422 + if (ret)
423 + exit(ret);
424 +
425 + return 0;
426 +}
427 +
428 +U_BOOT_IMAGE_TYPE(
429 + sunxi_toc1,
430 + "Allwinner TOC1 Boot Image support",
431 + 0,
432 + NULL,
433 + toc1_check_params,
434 + toc1_verify_header,
435 + toc1_print_header,
436 + toc1_set_header,
437 + NULL,
438 + toc1_check_image_type,
439 + NULL,
440 + toc1_vrec_header
441 +);