blob: fbc2067bc12d4a730fbc115944e5759a9ab5183f [file] [log] [blame]
Masami Hiramatsuca09ad72023-05-31 00:29:24 -05001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2023, Linaro Limited
4 */
5
6#include <errno.h>
7#include <getopt.h>
8#include <limits.h>
9#include <stdio.h>
10#include <stdint.h>
11#include <stdlib.h>
12#include <string.h>
Masami Hiramatsuca09ad72023-05-31 00:29:24 -050013#include <unistd.h>
Sughosh Ganu081c0892024-03-22 16:27:28 +053014#include <generated/autoconf.h>
Sughosh Ganu910bdb22024-03-22 16:27:29 +053015#include <sys/types.h>
16#include <sys/stat.h>
Sughosh Ganu081c0892024-03-22 16:27:28 +053017#include <u-boot/crc.h>
Masami Hiramatsuca09ad72023-05-31 00:29:24 -050018#include <uuid/uuid.h>
19
Masami Hiramatsuca09ad72023-05-31 00:29:24 -050020typedef uint8_t u8;
21typedef int16_t s16;
22typedef uint16_t u16;
23typedef uint32_t u32;
24typedef uint64_t u64;
25
Sughosh Ganu081c0892024-03-22 16:27:28 +053026#undef CONFIG_FWU_NUM_BANKS
27#undef CONFIG_FWU_NUM_IMAGES_PER_BANK
28
29/* This will dynamically allocate the fwu_mdata */
30#define CONFIG_FWU_NUM_BANKS 0
31#define CONFIG_FWU_NUM_IMAGES_PER_BANK 0
32
33/* version 2 supports maximum of 4 banks */
34#define MAX_BANKS_V2 4
35
36#define BANK_INVALID (u8)0xFF
37#define BANK_ACCEPTED (u8)0xFC
38
Masami Hiramatsuca09ad72023-05-31 00:29:24 -050039#include <fwu_mdata.h>
40
Sughosh Ganu910bdb22024-03-22 16:27:29 +053041static const char *opts_short = "b:i:a:p:v:V:gh";
Masami Hiramatsuca09ad72023-05-31 00:29:24 -050042
43static struct option options[] = {
44 {"banks", required_argument, NULL, 'b'},
45 {"images", required_argument, NULL, 'i'},
46 {"guid", required_argument, NULL, 'g'},
47 {"active-bank", required_argument, NULL, 'a'},
48 {"previous-bank", required_argument, NULL, 'p'},
Sughosh Ganu081c0892024-03-22 16:27:28 +053049 {"version", required_argument, NULL, 'v'},
Sughosh Ganu910bdb22024-03-22 16:27:29 +053050 {"vendor-file", required_argument, NULL, 'V'},
Masami Hiramatsuca09ad72023-05-31 00:29:24 -050051 {"help", no_argument, NULL, 'h'},
52 {NULL, 0, NULL, 0},
53};
54
55static void print_usage(void)
56{
57 fprintf(stderr, "Usage: mkfwumdata [options] <UUIDs list> <output file>\n");
58 fprintf(stderr, "Options:\n"
59 "\t-i, --images <num> Number of images (mandatory)\n"
60 "\t-b, --banks <num> Number of banks (mandatory)\n"
Sughosh Ganu081c0892024-03-22 16:27:28 +053061 "\t-v, --version Metadata version (mandatory)\n"
Masami Hiramatsuca09ad72023-05-31 00:29:24 -050062 "\t-a, --active-bank <num> Active bank (default=0)\n"
63 "\t-p, --previous-bank <num> Previous active bank (default=active_bank - 1)\n"
64 "\t-g, --guid Use GUID instead of UUID\n"
Sughosh Ganu910bdb22024-03-22 16:27:29 +053065 "\t-V, --vendor-file Vendor data file to append to the metadata\n"
Masami Hiramatsuca09ad72023-05-31 00:29:24 -050066 "\t-h, --help print a help message\n"
67 );
68 fprintf(stderr, " UUIDs list syntax:\n"
69 "\t <location uuid>,<image type uuid>,<images uuid list>\n"
70 "\t images uuid list syntax:\n"
71 "\t img_uuid_00,img_uuid_01...img_uuid_0b,\n"
72 "\t img_uuid_10,img_uuid_11...img_uuid_1b,\n"
73 "\t ...,\n"
74 "\t img_uuid_i0,img_uuid_i1...img_uuid_ib,\n"
75 "\t where 'b' and 'i' are number of banks and number\n"
76 "\t of images in a bank respectively.\n"
77 );
78}
79
80struct fwu_mdata_object {
81 size_t images;
82 size_t banks;
83 size_t size;
Sughosh Ganu081c0892024-03-22 16:27:28 +053084 u8 version;
Sughosh Ganu910bdb22024-03-22 16:27:29 +053085 size_t vsize;
86 void *vbuf;
Masami Hiramatsuca09ad72023-05-31 00:29:24 -050087 struct fwu_mdata *mdata;
88};
89
90static int previous_bank, active_bank;
91static bool __use_guid;
92
Sughosh Ganu081c0892024-03-22 16:27:28 +053093static bool supported_mdata_version(unsigned long version)
94{
95 switch (version) {
96 case 1:
97 case 2:
98 return true;
99 default:
100 return false;
101 }
102}
103
104static struct fwu_mdata_object *fwu_alloc_mdata(size_t images, size_t banks,
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530105 u8 version, size_t vendor_size)
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500106{
107 struct fwu_mdata_object *mobj;
108
109 mobj = calloc(1, sizeof(*mobj));
110 if (!mobj)
111 return NULL;
112
Sughosh Ganu081c0892024-03-22 16:27:28 +0530113 if (version == 1) {
114 mobj->size = sizeof(struct fwu_mdata) +
115 (sizeof(struct fwu_image_entry) +
116 sizeof(struct fwu_image_bank_info) * banks) * images;
117 } else {
118 mobj->size = sizeof(struct fwu_mdata) +
119 sizeof(struct fwu_fw_store_desc) +
120 (sizeof(struct fwu_image_entry) +
121 sizeof(struct fwu_image_bank_info) * banks) * images;
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530122
123 mobj->size += vendor_size;
124 mobj->vsize = vendor_size;
Sughosh Ganu081c0892024-03-22 16:27:28 +0530125 }
126
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500127 mobj->images = images;
128 mobj->banks = banks;
Sughosh Ganu081c0892024-03-22 16:27:28 +0530129 mobj->version = version;
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500130
131 mobj->mdata = calloc(1, mobj->size);
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530132 if (!mobj->mdata)
133 goto alloc_err;
134
135 if (vendor_size) {
136 mobj->vbuf = calloc(1, mobj->vsize);
137 if (!mobj->vbuf)
138 goto alloc_err;
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500139 }
140
141 return mobj;
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530142
143alloc_err:
144 free(mobj->mdata);
145 free(mobj);
146 return NULL;
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500147}
148
149static struct fwu_image_entry *
150fwu_get_image(struct fwu_mdata_object *mobj, size_t idx)
151{
152 size_t offset;
153
Sughosh Ganu081c0892024-03-22 16:27:28 +0530154 if (mobj->version == 1) {
155 offset = sizeof(struct fwu_mdata) +
156 (sizeof(struct fwu_image_entry) +
157 sizeof(struct fwu_image_bank_info) * mobj->banks) *
158 idx;
159 } else {
160 offset = sizeof(struct fwu_mdata) +
161 sizeof(struct fwu_fw_store_desc) +
162 (sizeof(struct fwu_image_entry) +
163 sizeof(struct fwu_image_bank_info) * mobj->banks) *
164 idx;
165 }
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500166
167 return (struct fwu_image_entry *)((char *)mobj->mdata + offset);
168}
169
170static struct fwu_image_bank_info *
171fwu_get_bank(struct fwu_mdata_object *mobj, size_t img_idx, size_t bnk_idx)
172{
173 size_t offset;
174
Sughosh Ganu081c0892024-03-22 16:27:28 +0530175 if (mobj->version == 1) {
176 offset = sizeof(struct fwu_mdata) +
177 (sizeof(struct fwu_image_entry) +
178 sizeof(struct fwu_image_bank_info) * mobj->banks) *
179 img_idx + sizeof(struct fwu_image_entry) +
180 sizeof(struct fwu_image_bank_info) * bnk_idx;
181 } else {
182 offset = sizeof(struct fwu_mdata) +
183 sizeof(struct fwu_fw_store_desc) +
184 (sizeof(struct fwu_image_entry) +
185 sizeof(struct fwu_image_bank_info) * mobj->banks) *
186 img_idx + sizeof(struct fwu_image_entry) +
187 sizeof(struct fwu_image_bank_info) * bnk_idx;
188 }
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500189
190 return (struct fwu_image_bank_info *)((char *)mobj->mdata + offset);
191}
192
193/**
194 * convert_uuid_to_guid() - convert UUID to GUID
195 * @buf: UUID binary
196 *
197 * UUID and GUID have the same data structure, but their binary
198 * formats are different due to the endianness. See lib/uuid.c.
199 * Since uuid_parse() can handle only UUID, this function must
200 * be called to get correct data for GUID when parsing a string.
201 *
202 * The correct data will be returned in @buf.
203 */
204static void convert_uuid_to_guid(unsigned char *buf)
205{
206 unsigned char c;
207
208 c = buf[0];
209 buf[0] = buf[3];
210 buf[3] = c;
211 c = buf[1];
212 buf[1] = buf[2];
213 buf[2] = c;
214
215 c = buf[4];
216 buf[4] = buf[5];
217 buf[5] = c;
218
219 c = buf[6];
220 buf[6] = buf[7];
221 buf[7] = c;
222}
223
224static int uuid_guid_parse(char *uuidstr, unsigned char *uuid)
225{
226 int ret;
227
228 ret = uuid_parse(uuidstr, uuid);
229 if (ret < 0)
230 return ret;
231
232 if (__use_guid)
233 convert_uuid_to_guid(uuid);
234
235 return ret;
236}
237
238static int
239fwu_parse_fill_image_uuid(struct fwu_mdata_object *mobj,
240 size_t idx, char *uuids)
241{
242 struct fwu_image_entry *image = fwu_get_image(mobj, idx);
243 struct fwu_image_bank_info *bank;
244 char *p = uuids, *uuid;
245 int i;
246
247 if (!image)
248 return -ENOENT;
249
250 /* Image location UUID */
251 uuid = strsep(&p, ",");
252 if (!uuid)
253 return -EINVAL;
254
255 if (strcmp(uuid, "0") &&
Sughosh Ganu081c0892024-03-22 16:27:28 +0530256 uuid_guid_parse(uuid, (unsigned char *)&image->location_guid) < 0)
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500257 return -EINVAL;
258
259 /* Image type UUID */
260 uuid = strsep(&p, ",");
261 if (!uuid)
262 return -EINVAL;
263
Sughosh Ganu081c0892024-03-22 16:27:28 +0530264 if (uuid_guid_parse(uuid, (unsigned char *)&image->image_type_guid) < 0)
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500265 return -EINVAL;
266
267 /* Fill bank image-UUID */
268 for (i = 0; i < mobj->banks; i++) {
269 bank = fwu_get_bank(mobj, idx, i);
270 if (!bank)
271 return -ENOENT;
272 bank->accepted = 1;
273 uuid = strsep(&p, ",");
274 if (!uuid)
275 return -EINVAL;
276
277 if (strcmp(uuid, "0") &&
Sughosh Ganu081c0892024-03-22 16:27:28 +0530278 uuid_guid_parse(uuid, (unsigned char *)&bank->image_guid) < 0)
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500279 return -EINVAL;
280 }
281 return 0;
282}
Sughosh Ganu081c0892024-03-22 16:27:28 +0530283
284#if defined(CONFIG_FWU_MDATA_V1)
285static void fwu_fill_version_specific_mdata(struct fwu_mdata_object *mobj)
286{
287}
288#else
289static void fwu_fill_version_specific_mdata(struct fwu_mdata_object *mobj)
290{
291 int i;
292 struct fwu_fw_store_desc *fw_desc;
293 struct fwu_mdata *mdata = mobj->mdata;
294
295 mdata->metadata_size = mobj->size;
296 mdata->desc_offset = sizeof(struct fwu_mdata);
297
298 for (i = 0; i < MAX_BANKS_V2; i++)
299 mdata->bank_state[i] = i < mobj->banks ?
300 BANK_ACCEPTED : BANK_INVALID;
301
302 fw_desc = (struct fwu_fw_store_desc *)((u8 *)mdata + sizeof(*mdata));
303 fw_desc->num_banks = mobj->banks;
304 fw_desc->num_images = mobj->images;
305 fw_desc->img_entry_size = sizeof(struct fwu_image_entry) +
306 (sizeof(struct fwu_image_bank_info) * mobj->banks);
307 fw_desc->bank_info_entry_size =
308 sizeof(struct fwu_image_bank_info);
309}
310#endif /* CONFIG_FWU_MDATA_V1 */
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500311
312/* Caller must ensure that @uuids[] has @mobj->images entries. */
313static int fwu_parse_fill_uuids(struct fwu_mdata_object *mobj, char *uuids[])
314{
315 struct fwu_mdata *mdata = mobj->mdata;
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530316 char *vdata;
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500317 int i, ret;
318
Sughosh Ganu081c0892024-03-22 16:27:28 +0530319 mdata->version = mobj->version;
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500320 mdata->active_index = active_bank;
321 mdata->previous_active_index = previous_bank;
322
Sughosh Ganu081c0892024-03-22 16:27:28 +0530323 fwu_fill_version_specific_mdata(mobj);
324
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500325 for (i = 0; i < mobj->images; i++) {
326 ret = fwu_parse_fill_image_uuid(mobj, i, uuids[i]);
327 if (ret < 0)
328 return ret;
329 }
330
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530331 if (mobj->vsize) {
332 vdata = (char *)mobj->mdata + (mobj->size - mobj->vsize);
333 memcpy(vdata, mobj->vbuf, mobj->vsize);
334 }
335
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500336 mdata->crc32 = crc32(0, (const unsigned char *)&mdata->version,
337 mobj->size - sizeof(uint32_t));
338
339 return 0;
340}
341
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530342static int fwu_read_vendor_data(struct fwu_mdata_object *mobj,
343 const char *vendor_file)
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500344{
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530345 int ret = 0;
346 FILE *vfile = NULL;
347
348 vfile = fopen(vendor_file, "r");
349 if (!vfile) {
350 ret = -1;
351 goto out;
352 }
353
354 if (fread(mobj->vbuf, 1, mobj->vsize, vfile) != mobj->vsize)
355 ret = -1;
356
357out:
358 fclose(vfile);
359 return ret;
360}
361
362static int fwu_make_mdata(size_t images, size_t banks, u8 version,
363 const char *vendor_file, char *uuids[],
364 char *output)
365{
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500366 int ret;
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530367 FILE *file;
368 struct stat sbuf;
369 size_t vendor_size = 0;
370 struct fwu_mdata_object *mobj;
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500371
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530372 if (vendor_file) {
373 ret = stat(vendor_file, &sbuf);
374 if (ret)
375 return -errno;
376
377 vendor_size = sbuf.st_size;
378 }
379
380 mobj = fwu_alloc_mdata(images, banks, version, vendor_size);
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500381 if (!mobj)
382 return -ENOMEM;
383
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530384 if (vendor_file) {
385 ret = fwu_read_vendor_data(mobj, vendor_file);
386 if (ret)
387 goto done_make;
388 }
389
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500390 ret = fwu_parse_fill_uuids(mobj, uuids);
391 if (ret < 0)
392 goto done_make;
393
394 file = fopen(output, "w");
395 if (!file) {
396 ret = -errno;
397 goto done_make;
398 }
399
Sughosh Ganu9c267782024-03-22 16:27:15 +0530400 ret = fwrite(mobj->mdata, 1, mobj->size, file);
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500401 if (ret != mobj->size)
402 ret = -errno;
403 else
404 ret = 0;
405
406 fclose(file);
407
408done_make:
409 free(mobj->mdata);
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530410 free(mobj->vbuf);
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500411 free(mobj);
412
413 return ret;
414}
415
416int main(int argc, char *argv[])
417{
Sughosh Ganu081c0892024-03-22 16:27:28 +0530418 unsigned long banks = 0, images = 0, version = 0;
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500419 int c, ret;
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530420 const char *vendor_file;
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500421
422 /* Explicitly initialize defaults */
423 active_bank = 0;
424 __use_guid = false;
425 previous_bank = INT_MAX;
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530426 vendor_file = NULL;
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500427
428 do {
429 c = getopt_long(argc, argv, opts_short, options, NULL);
430 switch (c) {
431 case 'h':
432 print_usage();
433 return 0;
434 case 'b':
435 banks = strtoul(optarg, NULL, 0);
436 break;
437 case 'i':
438 images = strtoul(optarg, NULL, 0);
439 break;
440 case 'g':
441 __use_guid = true;
442 break;
443 case 'p':
444 previous_bank = strtoul(optarg, NULL, 0);
445 break;
446 case 'a':
447 active_bank = strtoul(optarg, NULL, 0);
448 break;
Sughosh Ganu081c0892024-03-22 16:27:28 +0530449 case 'v':
450 version = strtoul(optarg, NULL, 0);
451 break;
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530452 case 'V':
453 vendor_file = optarg;
454 break;
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500455 }
456 } while (c != -1);
457
458 if (!banks || !images) {
459 fprintf(stderr, "Error: The number of banks and images must not be 0.\n");
460 return -EINVAL;
461 }
462
Sughosh Ganu081c0892024-03-22 16:27:28 +0530463 if (!version || !supported_mdata_version(version)) {
464 fprintf(stderr, "Error: Version value can only be either 1 or 2, not %ld.\n",
465 version);
466 return -EINVAL;
467 }
468
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530469 if (version == 1 && vendor_file) {
470 fprintf(stderr, "Error: Vendor Data can only be appended in version 2 of FWU Metadata.\n");
471 return -EINVAL;
472 }
473
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500474 /* This command takes UUIDs * images and output file. */
475 if (optind + images + 1 != argc) {
476 fprintf(stderr, "Error: UUID list or output file is not specified or too much.\n");
477 print_usage();
478 return -ERANGE;
479 }
480
481 if (previous_bank == INT_MAX) {
482 /* set to the earlier bank in round-robin scheme */
483 previous_bank = active_bank > 0 ? active_bank - 1 : banks - 1;
484 }
485
Sughosh Ganu910bdb22024-03-22 16:27:29 +0530486 ret = fwu_make_mdata(images, banks, (u8)version, vendor_file,
487 argv + optind, argv[argc - 1]);
Masami Hiramatsuca09ad72023-05-31 00:29:24 -0500488 if (ret < 0)
489 fprintf(stderr, "Error: Failed to parse and write image: %s\n",
490 strerror(-ret));
491
492 return ret;
493}