1 /* Copyright 2022 The ChromiumOS Authors
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 *
5 * Build up the list of updater resources from an archive.
6 */
7
8 #include <assert.h>
9 #if defined(__OpenBSD__)
10 #include <sys/types.h>
11 #endif
12
13 #include "updater.h"
14 #include "util_misc.h"
15
16 /*
17 * The updater reads image files from a package. The package is usually an
18 * archive (see updater_archive.c) with image files and configuration files, and
19 * the meta data is maintained by a "manifest" that described below.
20 *
21 * A package for a single board (i.e., not Unified Build) will have all the
22 * image files in the top folder:
23 * - host: 'image.bin'
24 * - ec: 'ec.bin'
25 *
26 * A package for Unified Build is more complicated.
27 *
28 * You need to look at the signer_config.csv file to find the columns of
29 * model_name, image files (firmware_image, ec_image) and then search for
30 * patch files (root key, vblock files, GSC verification data, ...) in the
31 * keyset/ folder:
32 *
33 * - rootkey.$MODEL_NAME
34 * - vblock_A.$MODEL_NAME
35 * - vblock_B.$MODEL_NAME
36 * - gscvd.$MODEL_NAME
37 *
38 * In the runtime, the updater should query for firmware manifest key (
39 * `crosid -f FIRMWARE_MANIFEST_KEY`) and use that to match the 'model_name'
40 * in the manifest database.
41 *
42 * If the model_name in `signer_config.csv` contains '-' then it is a custom
43 * label device. Today the FIRMWARE_MANIFEST_KEY from crosid won't handle custom
44 * label information and we have to add the custom label tag in the matching
45 * process.
46 *
47 * To do that, find the custom label tag from the VPD.
48 * - Newer devices: model_name = FIRMWARE_MANIFEST_KEY-$custom_label_tag
49 * - Old devices: model_name = FIRMWARE_MANIFEST_KEY-$whitelabel_tag
50 *
51 * For legacy devices manufactured before Unified Build, they have the VPD
52 * 'customization_id' in a special format: LOEM[-VARIANT].
53 * For example: "A-B" => LOEM="A".
54 * - Legacy devices: model_name = FIRMWARE_MANIFEST_KEY-$LOEM
55 */
56
57 static const char * const DEFAULT_MODEL_NAME = "default",
58 * const VPD_CUSTOM_LABEL_TAG = "custom_label_tag",
59 * const VPD_CUSTOM_LABEL_TAG_LEGACY = "whitelabel_tag",
60 * const VPD_CUSTOMIZATION_ID = "customization_id",
61 * const PATH_KEYSET_FOLDER = "keyset/",
62 * const PATH_SIGNER_CONFIG = "signer_config.csv";
63
64 /* Utility function to convert a string. */
str_convert(char * s,int (* convert)(int c))65 static void str_convert(char *s, int (*convert)(int c))
66 {
67 int c;
68
69 for (; *s; s++) {
70 c = *s;
71 if (!isascii(c))
72 continue;
73 *s = convert(c);
74 }
75 }
76
77 /* Returns the VPD value by given key name, or NULL on error (or no value). */
vpd_get_value(const char * fpath,const char * key)78 static char *vpd_get_value(const char *fpath, const char *key)
79 {
80 char *command, *result;
81
82 assert(fpath);
83 ASPRINTF(&command, "vpd -g %s -f %s 2>/dev/null", key, fpath);
84 result = host_shell(command);
85 free(command);
86
87 if (result && !*result) {
88 free(result);
89 result = NULL;
90 }
91 return result;
92 }
93
94 /*
95 * Changes the rootkey in firmware GBB to given new key.
96 * Returns 0 on success, otherwise failure.
97 */
change_gbb_rootkey(struct firmware_image * image,const char * section_name,const uint8_t * rootkey,uint32_t rootkey_len)98 static int change_gbb_rootkey(struct firmware_image *image,
99 const char *section_name,
100 const uint8_t *rootkey, uint32_t rootkey_len)
101 {
102 const struct vb2_gbb_header *gbb = find_gbb(image);
103 uint8_t *gbb_rootkey;
104 if (!gbb) {
105 ERROR("Cannot find GBB in image %s.\n", image->file_name);
106 return -1;
107 }
108 if (gbb->rootkey_size < rootkey_len) {
109 ERROR("New root key (%u bytes) larger than GBB (%u bytes).\n",
110 rootkey_len, gbb->rootkey_size);
111 return -1;
112 }
113
114 gbb_rootkey = (uint8_t *)gbb + gbb->rootkey_offset;
115 /* See cmd_gbb_utility: root key must be first cleared with zero. */
116 memset(gbb_rootkey, 0, gbb->rootkey_size);
117 memcpy(gbb_rootkey, rootkey, rootkey_len);
118 return 0;
119 }
120
121 /*
122 * Changes the firmware section (for example vblock or GSCVD) to new data.
123 * Returns 0 on success, otherwise failure.
124 */
change_section(struct firmware_image * image,const char * section_name,const uint8_t * data,uint32_t data_len)125 static int change_section(struct firmware_image *image,
126 const char *section_name,
127 const uint8_t *data, uint32_t data_len)
128 {
129 struct firmware_section section;
130
131 find_firmware_section(§ion, image, section_name);
132 if (!section.data) {
133 ERROR("Need section %s in image %s.\n", section_name,
134 image->file_name);
135 return -1;
136 }
137 if (section.size < data_len) {
138 ERROR("'%s' is too small (%zu bytes) for patching %u bytes.\n",
139 section_name, section.size, data_len);
140 return -1;
141 }
142 /* First erase (0xff) the section in case the new data is smaller. */
143 memset(section.data, 0xff, section.size);
144 memcpy(section.data, data, data_len);
145 return 0;
146 }
147
148 /*
149 * Applies a key file to firmware image.
150 * Returns 0 on success, otherwise failure.
151 */
apply_key_file(struct firmware_image * image,const char * path,struct u_archive * archive,const char * section_name,int (* apply)(struct firmware_image * image,const char * section,const uint8_t * data,uint32_t len))152 static int apply_key_file(
153 struct firmware_image *image, const char *path,
154 struct u_archive *archive, const char *section_name,
155 int (*apply)(struct firmware_image *image, const char *section,
156 const uint8_t *data, uint32_t len))
157 {
158 int r = 0;
159 uint8_t *data = NULL;
160 uint32_t len;
161
162 r = archive_read_file(archive, path, &data, &len, NULL);
163 if (r == 0) {
164 VB2_DEBUG("Loaded file: %s\n", path);
165 r = apply(image, section_name, data, len);
166 if (r)
167 ERROR("Failed applying %s to %s\n", path, section_name);
168 } else {
169 ERROR("Failed reading: %s\n", path);
170 }
171 free(data);
172 return r;
173 }
174
175 /*
176 * Modifies a firmware image from patch information specified in model config.
177 * Returns 0 on success, otherwise number of failures.
178 */
patch_image_by_model(struct firmware_image * image,const struct model_config * model,struct u_archive * archive)179 int patch_image_by_model(
180 struct firmware_image *image, const struct model_config *model,
181 struct u_archive *archive)
182 {
183 int err = 0;
184 if (model->patches.rootkey)
185 err += !!apply_key_file(
186 image, model->patches.rootkey, archive,
187 FMAP_RO_GBB, change_gbb_rootkey);
188 if (model->patches.vblock_a)
189 err += !!apply_key_file(
190 image, model->patches.vblock_a, archive,
191 FMAP_RW_VBLOCK_A, change_section);
192 if (model->patches.vblock_b)
193 err += !!apply_key_file(
194 image, model->patches.vblock_b, archive,
195 FMAP_RW_VBLOCK_B, change_section);
196 if (model->patches.gscvd)
197 err += !!apply_key_file(
198 image, model->patches.gscvd, archive,
199 FMAP_RO_GSCVD, change_section);
200 return err;
201 }
202
203 /*
204 * Finds available patch files by given model.
205 * Updates `model` argument with path of patch files.
206 */
find_patches_for_model(struct model_config * model,struct u_archive * archive)207 static void find_patches_for_model(struct model_config *model,
208 struct u_archive *archive)
209 {
210 char *path;
211 int i;
212
213 const char * const names[] = {
214 "rootkey",
215 "vblock_A",
216 "vblock_B",
217 "gscvd",
218 };
219
220 char **targets[] = {
221 &model->patches.rootkey,
222 &model->patches.vblock_a,
223 &model->patches.vblock_b,
224 &model->patches.gscvd,
225 };
226
227 assert(ARRAY_SIZE(names) == ARRAY_SIZE(targets));
228 for (i = 0; i < ARRAY_SIZE(names); i++) {
229 ASPRINTF(&path, "%s%s.%s", PATH_KEYSET_FOLDER, names[i], model->name);
230 if (archive_has_entry(archive, path))
231 *targets[i] = path;
232 else
233 free(path);
234 }
235 }
236
237 /*
238 * Adds and copies one new model config to the existing list of given manifest.
239 * Returns a pointer to the newly allocated config, or NULL on failure.
240 */
manifest_add_model(struct manifest * manifest,const struct model_config * cfg)241 static struct model_config *manifest_add_model(
242 struct manifest *manifest,
243 const struct model_config *cfg)
244 {
245 struct model_config *model;
246 manifest->num++;
247 manifest->models = (struct model_config *)realloc(
248 manifest->models, manifest->num * sizeof(*model));
249 if (!manifest->models) {
250 ERROR("Internal error: failed to allocate buffer.\n");
251 return NULL;
252 }
253 model = &manifest->models[manifest->num - 1];
254 memcpy(model, cfg, sizeof(*model));
255 return model;
256 }
257
258 /*
259 * A callback function for manifest to scan files in raw /firmware archive.
260 * Returns 0 to keep scanning, or non-zero to stop.
261 */
manifest_scan_raw_entries(const char * name,void * arg)262 static int manifest_scan_raw_entries(const char *name, void *arg)
263 {
264 struct manifest *manifest = (struct manifest *)arg;
265 struct u_archive *archive = manifest->archive;
266 struct model_config model = {0};
267 char *ec_name = NULL;
268 int chars_read = 0;
269
270 /*
271 * /build/$BOARD/firmware (or CPFE firmware archives) layout:
272 * - image-${MODEL}{,.serial,.dev...}.bin
273 * - ${MODEL}/ec.bin
274 */
275
276 if (sscanf(name, "image-%m[^.].bin%n", &model.name, &chars_read) != 1)
277 return 0;
278
279 /* Ignore the names with extra modifiers like image-$MODEL.serial.bin */
280 if (!chars_read || name[chars_read]) {
281 free(model.name);
282 return 0;
283 }
284
285 VB2_DEBUG("Found model <%s>: %s\n", model.name, name);
286 model.image = strdup(name);
287
288 ASPRINTF(&ec_name, "%s/ec.bin", model.name);
289 if (archive_has_entry(archive, ec_name))
290 model.ec_image = strdup(ec_name);
291 free(ec_name);
292
293 return !manifest_add_model(manifest, &model);
294 }
295
296 /* Returns the matched model config from the manifest, or NULL if not found. */
manifest_get_model_config(const struct manifest * manifest,const char * name)297 static struct model_config *manifest_get_model_config(
298 const struct manifest *manifest, const char *name)
299 {
300 int i = 0;
301
302 for (i = 0; i < manifest->num; i++) {
303 if (!strcmp(name, manifest->models[i].name))
304 return &manifest->models[i];
305 }
306 return NULL;
307 }
308
309 /* Releases (and zeros) the data inside a patch config. */
clear_patch_config(struct patch_config * patch)310 static void clear_patch_config(struct patch_config *patch)
311 {
312 free(patch->rootkey);
313 free(patch->vblock_a);
314 free(patch->vblock_b);
315 free(patch->gscvd);
316 memset(patch, 0, sizeof(*patch));
317 }
318
319 /*
320 * Creates the manifest from the 'signer_config.csv' file.
321 * Returns 0 on success (loaded), otherwise failure.
322 */
manifest_from_signer_config(struct manifest * manifest)323 static int manifest_from_signer_config(struct manifest *manifest)
324 {
325 struct u_archive *archive = manifest->archive;
326 uint32_t size;
327 uint8_t *data;
328 char *s, *tok_ptr = NULL;
329
330 VB2_DEBUG("Try to build the manifest from %s\n", PATH_SIGNER_CONFIG);
331
332 if (!archive_has_entry(archive, PATH_SIGNER_CONFIG))
333 return -1;
334
335 /*
336 * CSV format: model_name,firmware_image,key_id,ec_image
337 *
338 * Note the key_id is for signer and won't be used by the updater,
339 * and ec_image may be optional (for example sarien).
340 */
341
342 if (archive_read_file(archive, PATH_SIGNER_CONFIG, &data, &size,NULL)) {
343 ERROR("Failed reading: %s\n", PATH_SIGNER_CONFIG);
344 return -1;
345 }
346
347 /* Skip headers. */
348 s = strtok_r((char *)data, "\n", &tok_ptr);
349 if (!s || !strchr(s, ',')) {
350 ERROR("Invalid %s: missing header.\n", PATH_SIGNER_CONFIG);
351 free(data);
352 return -1;
353 }
354
355 for (s = strtok_r(NULL, "\n", &tok_ptr); s != NULL;
356 s = strtok_r(NULL, "\n", &tok_ptr)) {
357
358 struct model_config model = {0};
359
360 /*
361 * Both keyid (%3) and ec_image (%4) are optional so we want to
362 * read at least 2 fields.
363 */
364 if (sscanf(s, "%m[^,],%m[^,],%*[^,],%m[^,]",
365 &model.name, &model.image, &model.ec_image) < 2) {
366 ERROR("Invalid entry(%s): %s\n", PATH_SIGNER_CONFIG, s);
367 free(model.name);
368 free(model.image);
369 free(model.ec_image);
370 continue;
371 }
372
373 if (strchr(model.name, '-')) {
374 /* format: BaseModelName-CustomLabelTag */
375 struct model_config *base_model;
376 char *tok_dash;
377 char *base_name = strdup(model.name);
378
379 VB2_DEBUG("Found custom-label: %s\n", model.name);
380 base_name = strtok_r(base_name, "-", &tok_dash);
381 assert(base_name);
382
383 /*
384 * Currently we assume the base model (e.g., base_name)
385 * is always listed before CL models in the CSV file -
386 * this is based on how the signerbot and the
387 * chromeos-config works today (validated on octopus).
388 */
389 base_model = manifest_get_model_config(manifest, base_name);
390
391 if (!base_model) {
392 ERROR("Invalid base model for custom label: %s\n", base_name);
393 } else if (!base_model->has_custom_label) {
394 base_model->has_custom_label = true;
395 }
396
397 free(base_name);
398 }
399
400 /* Find patch files. */
401 find_patches_for_model(&model, archive);
402
403 if (!manifest_add_model(manifest, &model))
404 break;
405 }
406 free(data);
407 return 0;
408 }
409
410 /*
411 * Creates the manifest from a simple (legacy) folder with only 1 set of
412 * firmware images.
413 * Returns 0 on success (loaded), otherwise failure.
414 */
manifest_from_simple_folder(struct manifest * manifest)415 static int manifest_from_simple_folder(struct manifest *manifest)
416 {
417 const char * const host_image_name = "image.bin",
418 * const old_host_image_name = "bios.bin",
419 * const ec_name = "ec.bin";
420 struct u_archive *archive = manifest->archive;
421 const char *image_name = NULL;
422 struct firmware_image image = {0};
423 struct model_config model = {0};
424
425 VB2_DEBUG("Try to build the manifest from a simple folder\n");
426
427 /* Try to load from current folder. */
428 if (archive_has_entry(archive, old_host_image_name))
429 image_name = old_host_image_name;
430 else if (archive_has_entry(archive, host_image_name))
431 image_name = host_image_name;
432 else
433 return 1;
434
435 model.image = strdup(image_name);
436 if (archive_has_entry(archive, ec_name))
437 model.ec_image = strdup(ec_name);
438 /* Extract model name from FWID: $Vendor_$Platform.$Version */
439 if (!load_firmware_image(&image, image_name, archive)) {
440 char *token = NULL;
441 if (strtok(image.ro_version, "_"))
442 token = strtok(NULL, ".");
443 if (token && *token) {
444 str_convert(token, tolower);
445 model.name = strdup(token);
446 }
447 free_firmware_image(&image);
448 }
449 if (!model.name)
450 model.name = strdup(DEFAULT_MODEL_NAME);
451 manifest_add_model(manifest, &model);
452 manifest->default_model = manifest->num - 1;
453
454 return 0;
455 }
456
457 /*
458 * Finds the existing model_config from manifest that best matches current
459 * system (as defined by model_name).
460 * Returns a model_config from manifest, or NULL if not found.
461 */
manifest_find_model(struct updater_config * cfg,const struct manifest * manifest,const char * model_name)462 const struct model_config *manifest_find_model(struct updater_config *cfg,
463 const struct manifest *manifest,
464 const char *model_name)
465 {
466 char *manifest_key = NULL;
467 const struct model_config *model = NULL;
468 int i;
469 int matched_index;
470
471 /*
472 * For manifest with single model defined, we should just return because
473 * there are other mechanisms like platform name check to double confirm
474 * if the firmware is valid.
475 */
476 if (manifest->num == 1)
477 return &manifest->models[0];
478
479 if (!model_name) {
480 matched_index = dut_get_manifest_key(&manifest_key, cfg);
481 if (matched_index < 0) {
482 ERROR("Failed to get device identity. "
483 "Run \"crosid -v\" for explanation.\n");
484 return NULL;
485 }
486
487 INFO("Identified the device using libcrosid, "
488 "matched chromeos-config index: %d, "
489 "manifest key (model): %s\n",
490 matched_index, manifest_key);
491 model_name = manifest_key;
492 }
493
494 model = manifest_get_model_config(manifest, model_name);
495
496 if (!model) {
497 ERROR("Unsupported model: '%s'.\n", model_name);
498
499 fprintf(stderr,
500 "The firmware manifest key '%s' is not present in this "
501 "updater archive. The known keys to this updater "
502 "archive are:\n", model_name);
503
504 for (i = 0; i < manifest->num; i++)
505 fprintf(stderr, " %s", manifest->models[i].name);
506 fprintf(stderr, "\n\n");
507 fprintf(stderr,
508 "Perhaps you are trying to use an updater archive for "
509 "the wrong board, or designed for an older OS version "
510 "before this model was supported.\n");
511 fprintf(stderr,
512 "Hint: Read the FIRMWARE_MANIFEST_KEY from the output "
513 "of the crosid command.\n");
514 }
515
516
517 free(manifest_key);
518 return model;
519 }
520
521 const struct model_config *
manifest_detect_model_from_frid(struct updater_config * cfg,struct manifest * manifest)522 manifest_detect_model_from_frid(struct updater_config *cfg,
523 struct manifest *manifest)
524 {
525 const struct model_config *result = NULL;
526 struct firmware_image current_ro_frid = {0};
527 current_ro_frid.programmer = cfg->image_current.programmer;
528 int error = flashrom_read_region(¤t_ro_frid, FMAP_RO_FRID,
529 cfg->verbosity + 1);
530 const char *from_dot;
531 int len;
532
533 if (error)
534 return NULL;
535
536 current_ro_frid.data[current_ro_frid.size - 1] = '\0';
537 from_dot = strchr((const char *)current_ro_frid.data, '.');
538 if (!from_dot) {
539 VB2_DEBUG("Missing dot (%s)\n",
540 (const char *)current_ro_frid.data);
541 goto cleanup;
542 }
543 len = from_dot - (const char *)current_ro_frid.data + 1;
544
545 for (int i = 0; i < manifest->num && !result; ++i) {
546 struct model_config *m = &manifest->models[i];
547 struct firmware_image image = {0};
548
549 if (load_firmware_image(&image, m->image, manifest->archive))
550 return NULL;
551
552 VB2_DEBUG("Comparing '%*.*s' with '%*.*s'\n", len, len,
553 (const char *)current_ro_frid.data, len, len,
554 image.ro_version);
555 if (strncasecmp((const char *)current_ro_frid.data,
556 image.ro_version, len) == 0) {
557 result = m;
558 }
559 free_firmware_image(&image);
560 }
561 if (result) {
562 INFO("Detected model: '%s'\n", result->name);
563 } else {
564 ERROR("Unsupported FRID: '%*.*s'.\n", len - 1, len - 1,
565 (const char *)current_ro_frid.data);
566 }
567 cleanup:
568 free_firmware_image(¤t_ro_frid);
569
570 return result;
571 }
572
573 /*
574 * Determines the custom label tag.
575 * Returns the tag string, or NULL if not found.
576 * Caller must free the returned string.
577 */
get_custom_label_tag(const char * image_file)578 static char *get_custom_label_tag(const char *image_file)
579 {
580 /* TODO(hungte) Switch to look at /sys/firmware/vpd/ro/$KEY. */
581 char *tag;
582
583 tag = vpd_get_value(image_file, VPD_CUSTOM_LABEL_TAG);
584 if (tag)
585 return tag;
586
587 tag = vpd_get_value(image_file, VPD_CUSTOM_LABEL_TAG_LEGACY);
588 if (tag)
589 return tag;
590
591 tag = vpd_get_value(image_file, VPD_CUSTOMIZATION_ID);
592 /* VPD_CUSTOMIZATION_ID is complicated and can't be returned directly. */
593 if (!tag)
594 return NULL;
595
596 /* For VPD_CUSTOMIZATION_ID=LOEM[-VARIANT], we need only capitalized LOEM. */
597 INFO("Using deprecated custom label tag: %s=%s\n", VPD_CUSTOMIZATION_ID, tag);
598 char *dash = strchr(tag, '-');
599 if (dash)
600 *dash = '\0';
601 str_convert(tag, toupper);
602 VB2_DEBUG("Applied tag from %s: %s\n", tag, VPD_CUSTOMIZATION_ID);
603 return tag;
604 }
605
manifest_find_custom_label_model(struct updater_config * cfg,const struct manifest * manifest,const struct model_config * base_model)606 const struct model_config *manifest_find_custom_label_model(
607 struct updater_config *cfg,
608 const struct manifest *manifest,
609 const struct model_config *base_model)
610 {
611 const struct model_config *model;
612
613 /*
614 * Some custom label devices shipped with wrong key and must change
615 * their model names to match the right data.
616 */
617 if (get_config_quirk(QUIRK_OVERRIDE_CUSTOM_LABEL, cfg)) {
618 model = quirk_override_custom_label(cfg, manifest, base_model);
619 if (model)
620 return model;
621 }
622
623 assert(cfg->image_current.data);
624 const char *tmp_image = get_firmware_image_temp_file(
625 &cfg->image_current, &cfg->tempfiles);
626 if (!tmp_image) {
627 ERROR("Failed to save the system firmware to a file.\n");
628 return NULL;
629 }
630
631 char *tag = get_custom_label_tag(tmp_image);
632 if (!tag) {
633 WARN("No custom label tag (VPD '%s'). "
634 "Use default keys from the base model '%s'.\n",
635 VPD_CUSTOM_LABEL_TAG, base_model->name);
636 return base_model;
637 }
638
639 VB2_DEBUG("Found custom label tag: %s (base=%s)\n", tag, base_model->name);
640 char *name;
641 ASPRINTF(&name, "%s-%s", base_model->name, tag);
642 free(tag);
643
644 INFO("Find custom label model info using '%s'...\n", name);
645 model = manifest_find_model(cfg, manifest, name);
646
647 if (model) {
648 INFO("Applied custom label model: %s\n", name);
649 } else {
650 ERROR("Invalid custom label model: %s\n", name);
651 }
652 free(name);
653 return model;
654 }
655
manifest_from_build_artifacts(struct manifest * manifest)656 static int manifest_from_build_artifacts(struct manifest *manifest) {
657 VB2_DEBUG("Try to build the manifest from a */firmware folder\n");
658 return archive_walk(manifest->archive, manifest, manifest_scan_raw_entries);
659 }
660
661 /*
662 * Creates a new manifest object by scanning files in archive.
663 * Returns the manifest on success, otherwise NULL for failure.
664 */
new_manifest_from_archive(struct u_archive * archive)665 struct manifest *new_manifest_from_archive(struct u_archive *archive)
666 {
667 int i;
668 struct manifest manifest = {0}, *new_manifest;
669 int (*manifest_builders[])(struct manifest *) = {
670 manifest_from_signer_config,
671 manifest_from_build_artifacts,
672 manifest_from_simple_folder,
673 };
674
675 manifest.archive = archive;
676 manifest.default_model = -1;
677
678 for (i = 0; !manifest.num && i < ARRAY_SIZE(manifest_builders); i++) {
679 /*
680 * For archives manually updated (for testing), it is possible a
681 * builder can successfully scan the archive but no valid models
682 * were found, so here we don't need to check the return value.
683 * Only stop when manifest.num is non-zero.
684 */
685 (void) manifest_builders[i](&manifest);
686 }
687
688 VB2_DEBUG("%d model(s) loaded.\n", manifest.num);
689 if (!manifest.num) {
690 ERROR("No valid configurations found from the archive.\n");
691 return NULL;
692 }
693
694 new_manifest = (struct manifest *)malloc(sizeof(manifest));
695 if (!new_manifest) {
696 ERROR("Internal error: memory allocation error.\n");
697 return NULL;
698 }
699 memcpy(new_manifest, &manifest, sizeof(manifest));
700 return new_manifest;
701 }
702
703 /* Releases all resources allocated by given manifest object. */
delete_manifest(struct manifest * manifest)704 void delete_manifest(struct manifest *manifest)
705 {
706 int i;
707 assert(manifest);
708 for (i = 0; i < manifest->num; i++) {
709 struct model_config *model = &manifest->models[i];
710 free(model->name);
711 free(model->image);
712 free(model->ec_image);
713 clear_patch_config(&model->patches);
714 }
715 free(manifest->models);
716 free(manifest);
717 }
718
get_gbb_key_hash(const struct vb2_gbb_header * gbb,int32_t offset,int32_t size)719 static const char *get_gbb_key_hash(const struct vb2_gbb_header *gbb,
720 int32_t offset, int32_t size)
721 {
722 struct vb2_packed_key *key;
723
724 if (!gbb)
725 return "<No GBB>";
726 key = (struct vb2_packed_key *)((uint8_t *)gbb + offset);
727 if (vb2_packed_key_looks_ok(key, size))
728 return "<Invalid key>";
729 return packed_key_sha1_string(key);
730 }
731
732 /* Prints the information of given image file in JSON format. */
print_json_image(const char * name,const char * fpath,struct model_config * m,struct u_archive * archive,int indent,int is_host,bool is_first)733 static void print_json_image(
734 const char *name, const char *fpath, struct model_config *m,
735 struct u_archive *archive, int indent, int is_host,
736 bool is_first)
737 {
738 struct firmware_image image = {0};
739 const struct vb2_gbb_header *gbb = NULL;
740 if (!fpath)
741 return;
742 if (load_firmware_image(&image, fpath, archive))
743 return;
744 if (!is_first)
745 printf(",\n");
746 printf("%*s\"%s\": {", indent, "", name);
747 indent += 2;
748 printf("\n%*s\"versions\": {", indent, "");
749 indent += 2;
750 printf("\n%*s\"ro\": \"%s\"", indent, "", image.ro_version);
751 printf(",\n%*s\"rw\": \"%s\"", indent, "", image.rw_version_a);
752 if (is_host && image.ecrw_version_a[0] != '\0')
753 printf(",\n%*s\"ecrw\": \"%s\"", indent, "",
754 image.ecrw_version_a);
755 indent -= 2;
756 printf("\n%*s},", indent, "");
757 if (is_host) {
758 if (patch_image_by_model(&image, m, archive))
759 ERROR("Failed to patch images by model: %s\n", m->name);
760 else
761 gbb = find_gbb(&image);
762 }
763 if (gbb != NULL) {
764 printf("\n%*s\"keys\": { \"root\": \"%s\", ",
765 indent, "",
766 get_gbb_key_hash(gbb, gbb->rootkey_offset,
767 gbb->rootkey_size));
768 printf("\"recovery\": \"%s\" },",
769 get_gbb_key_hash(gbb, gbb->recovery_key_offset,
770 gbb->recovery_key_size));
771 }
772 printf("\n%*s\"image\": \"%s\"", indent, "", fpath);
773 indent -= 2;
774 printf("\n%*s}", indent, "");
775 check_firmware_versions(&image);
776 free_firmware_image(&image);
777 }
778
779 /* Prints the information of objects in manifest (models and images) in JSON. */
print_json_manifest(const struct manifest * manifest)780 void print_json_manifest(const struct manifest *manifest)
781 {
782 int i, j, indent;
783 struct u_archive *ar = manifest->archive;
784
785 printf("{\n");
786 for (i = 0, indent = 2; i < manifest->num; i++) {
787 struct model_config *m = &manifest->models[i];
788 struct {
789 const char *name;
790 const char *fpath;
791 bool is_host;
792 } images[] = {
793 {"host", m->image, true},
794 {"ec", m->ec_image},
795 };
796 bool is_first = true;
797 printf("%s%*s\"%s\": {\n", i ? ",\n" : "", indent, "", m->name);
798 indent += 2;
799 for (j = 0; j < ARRAY_SIZE(images); j++) {
800 if (!images[j].fpath)
801 continue;
802 print_json_image(images[j].name, images[j].fpath, m, ar,
803 indent, images[j].is_host, is_first);
804 is_first = false;
805 }
806 if (m->patches.rootkey) {
807 struct patch_config *p = &m->patches;
808 printf(",\n%*s\"patches\": { \"rootkey\": \"%s\", "
809 "\"vblock_a\": \"%s\", \"vblock_b\": \"%s\"",
810 indent, "", p->rootkey, p->vblock_a,
811 p->vblock_b);
812 if (p->gscvd)
813 printf(", \"gscvd\": \"%s\"", p->gscvd);
814 printf(" }");
815 }
816 printf("\n }");
817 indent -= 2;
818 assert(indent == 2);
819 }
820 printf("\n}\n");
821 }
822