blob: 70d8bb331e6dacfc3a415102760cc2ec55fa8744 [file] [log] [blame]
Joel Kitching9adf2aa2019-08-20 17:43:50 +08001/* Copyright 2018 The Chromium OS Authors. All rights reserved.
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +08002 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 *
5 * Accessing updater resources from an archive.
6 */
7
8#include <assert.h>
Hung-Te Lin1f2e4772018-10-14 21:39:26 +08009#include <ctype.h>
Mike Frysingerc48a5932019-11-13 00:07:56 -050010#include <errno.h>
Idwer Volleringb384db32021-05-10 21:15:45 +020011#if defined(__OpenBSD__)
12#include <sys/types.h>
13#endif
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +080014#include <fts.h>
15#include <string.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <sys/stat.h>
Mike Frysingerc48a5932019-11-13 00:07:56 -050019#include <sys/time.h>
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +080020#include <unistd.h>
21
22#ifdef HAVE_LIBZIP
Julius Werner927a9522020-08-19 18:14:43 -070023#ifndef __clang__
24/* If libzip headers were built for Clang but later get included with GCC you
25 need this. This check should really be in libzip but apparently they think
26 it's fine to ship compiler-specific system headers or something... */
27#define _Nullable
28#define _Nonnull
29#endif
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +080030#include <zip.h>
31#endif
32
33#include "host_misc.h"
34#include "updater.h"
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +080035#include "util_misc.h"
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +080036#include "vb2_common.h"
37
Hung-Te Lin3e6397d2018-10-06 03:15:19 +080038/*
39 * A firmware update package (archive) is a file packed by either shar(1) or
40 * zip(1). See https://chromium.googlesource.com/chromiumos/platform/firmware/
41 * for more information.
42 *
43 * A package for single board (i.e., not Unified Build) will have all the image
44 * files in top folder:
Hung-Te Lin48d08d72018-11-09 09:41:03 +080045 * - host: 'image.bin' (or 'bios.bin' as legacy name before CL:1318712)
Hung-Te Lin3e6397d2018-10-06 03:15:19 +080046 * - ec: 'ec.bin'
47 * - pd: 'pd.bin'
48 * If white label is supported, a 'keyset/' folder will be available, with key
49 * files in it:
50 * - rootkey.$WLTAG
51 * - vblock_A.$WLTAG
52 * - vblock_B.$WLTAG
53 * The $WLTAG should come from VPD value 'whitelabel_tag', or the
54 * 'customization_id'. Note 'customization_id' is in format LOEM[-VARIANT] and
55 * we can only take LOEM as $WLTAG, for example A-B => $WLTAG=A.
56 *
57 * A package for Unified Build is more complicated. There will be a models/
58 * folder, and each model (by $(mosys platform model) ) should appear as a sub
59 * folder, with a 'setvars.sh' file inside. The 'setvars.sh' is a shell script
60 * describing what files should be used and the signature ID ($SIGID) to use.
61 *
62 * Similar to write label in non-Unified-Build, the keys and vblock files will
63 * be in 'keyset/' folder:
64 * - rootkey.$SIGID
65 * - vblock_A.$SIGID
66 * - vblock_B.$SIGID
67 * If $SIGID starts with 'sig-id-in-*' then we have to replace it by VPD value
68 * 'whitelabel_tag' as '$MODEL-$WLTAG'.
69 */
70
71static const char * const SETVARS_IMAGE_MAIN = "IMAGE_MAIN",
72 * const SETVARS_IMAGE_EC = "IMAGE_EC",
73 * const SETVARS_IMAGE_PD = "IMAGE_PD",
74 * const SETVARS_SIGNATURE_ID = "SIGNATURE_ID",
75 * const SIG_ID_IN_VPD_PREFIX = "sig-id-in",
76 * const DIR_KEYSET = "keyset",
77 * const DIR_MODELS = "models",
78 * const DEFAULT_MODEL_NAME = "default",
Hung-Te Lin4e066902018-10-15 15:33:47 +080079 * const VPD_WHITELABEL_TAG = "whitelabel_tag",
80 * const VPD_CUSTOMIZATION_ID = "customization_id",
Hung-Te Lin92fe37c2018-10-15 14:57:34 +080081 * const ENV_VAR_MODEL_DIR = "${MODEL_DIR}",
Hung-Te Lin3e6397d2018-10-06 03:15:19 +080082 * const PATH_STARTSWITH_KEYSET = "keyset/",
83 * const PATH_ENDSWITH_SERVARS = "/setvars.sh";
84
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +080085struct archive {
86 void *handle;
87
88 void * (*open)(const char *name);
89 int (*close)(void *handle);
90
91 int (*walk)(void *handle, void *arg,
92 int (*callback)(const char *path, void *arg));
93 int (*has_entry)(void *handle, const char *name);
94 int (*read_file)(void *handle, const char *fname,
Mike Frysingerc48a5932019-11-13 00:07:56 -050095 uint8_t **data, uint32_t *size, int64_t *mtime);
Hung-Te Line1cc2b82018-10-17 12:03:24 +080096 int (*write_file)(void *handle, const char *fname,
Mike Frysingerc48a5932019-11-13 00:07:56 -050097 uint8_t *data, uint32_t size, int64_t mtime);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +080098};
99
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800100/*
101 * -- Begin of archive implementations --
102 */
103
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800104/* Callback for archive_open on a general file system. */
105static void *archive_fallback_open(const char *name)
106{
107 assert(name && *name);
108 return strdup(name);
109}
110
111/* Callback for archive_close on a general file system. */
112static int archive_fallback_close(void *handle)
113{
114 free(handle);
115 return 0;
116}
117
118/* Callback for archive_walk on a general file system. */
119static int archive_fallback_walk(
120 void *handle, void *arg,
121 int (*callback)(const char *path, void *arg))
122{
123 FTS *fts_handle;
124 FTSENT *ent;
125 char *fts_argv[2] = {};
126 char default_path[] = ".";
127 char *root = default_path;
128 size_t root_len;
129
130 if (handle)
131 root = (char *)handle;
132 root_len = strlen(root);
133 fts_argv[0] = root;
134
135 fts_handle = fts_open(fts_argv, FTS_NOCHDIR, NULL);
136 if (!fts_handle)
137 return -1;
138
139 while ((ent = fts_read(fts_handle)) != NULL) {
140 char *path = ent->fts_path + root_len;
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800141 if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL)
142 continue;
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800143 while (*path == '/')
144 path++;
145 if (!*path)
146 continue;
147 if (callback(path, arg))
148 break;
149 }
150 return 0;
151}
152
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800153/* Callback for fallback drivers to get full path easily. */
154static const char *archive_fallback_get_path(void *handle, const char *fname,
155 char **temp_path)
156{
157 if (handle && *fname != '/') {
158 ASPRINTF(temp_path, "%s/%s", (char *)handle, fname);
159 return *temp_path;
160 }
161 return fname;
162}
163
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800164/* Callback for archive_has_entry on a general file system. */
165static int archive_fallback_has_entry(void *handle, const char *fname)
166{
167 int r;
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800168 char *temp_path = NULL;
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800169 const char *path = archive_fallback_get_path(handle, fname, &temp_path);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800170
Julius Werner88a47ff2019-05-08 13:33:20 -0700171 VB2_DEBUG("Checking %s\n", path);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800172 r = access(path, R_OK);
173 free(temp_path);
174 return r == 0;
175}
176
177/* Callback for archive_read_file on a general file system. */
178static int archive_fallback_read_file(void *handle, const char *fname,
Mike Frysingerc48a5932019-11-13 00:07:56 -0500179 uint8_t **data, uint32_t *size, int64_t *mtime)
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800180{
181 int r;
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800182 char *temp_path = NULL;
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800183 const char *path = archive_fallback_get_path(handle, fname, &temp_path);
Mike Frysingerc48a5932019-11-13 00:07:56 -0500184 struct stat st;
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800185
Julius Werner88a47ff2019-05-08 13:33:20 -0700186 VB2_DEBUG("Reading %s\n", path);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800187 *data = NULL;
188 *size = 0;
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800189 r = vb2_read_file(path, data, size) != VB2_SUCCESS;
Mike Frysingerc48a5932019-11-13 00:07:56 -0500190 if (mtime) {
191 if (stat(path, &st) == 0)
192 *mtime = st.st_mtime;
193 else
194 WARN("Unable to stat %s: %s\n", path, strerror(errno));
195 }
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800196 free(temp_path);
197 return r;
198}
199
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800200/* Callback for archive_write_file on a general file system. */
201static int archive_fallback_write_file(void *handle, const char *fname,
Mike Frysingerc48a5932019-11-13 00:07:56 -0500202 uint8_t *data, uint32_t size, int64_t mtime)
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800203{
204 int r;
205 char *temp_path = NULL;
206 const char *path = archive_fallback_get_path(handle, fname, &temp_path);
207
Julius Werner88a47ff2019-05-08 13:33:20 -0700208 VB2_DEBUG("Writing %s\n", path);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800209 if (strchr(path, '/')) {
210 char *dirname = strdup(path);
211 *strrchr(dirname, '/') = '\0';
212 /* TODO(hungte): call mkdir(2) instead of shell invocation. */
213 if (access(dirname, W_OK) != 0) {
214 char *command;
215 ASPRINTF(&command, "mkdir -p %s", dirname);
216 free(host_shell(command));
217 free(command);
218 }
219 free(dirname);
220 }
221 r = vb2_write_file(path, data, size) != VB2_SUCCESS;
Mike Frysingerc48a5932019-11-13 00:07:56 -0500222 if (mtime) {
223 struct timeval times[2] = {
224 {.tv_sec = mtime, .tv_usec = 0},
225 {.tv_sec = mtime, .tv_usec = 0},
226 };
227 if (utimes(path, times) != 0)
228 WARN("Unable to set times on %s: %s\n", path, strerror(errno));
229 }
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800230 free(temp_path);
231 return r;
232}
233
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800234#ifdef HAVE_LIBZIP
235
236/* Callback for archive_open on a ZIP file. */
237static void *archive_zip_open(const char *name)
238{
239 return zip_open(name, 0, NULL);
240}
241
242/* Callback for archive_close on a ZIP file. */
243static int archive_zip_close(void *handle)
244{
245 struct zip *zip = (struct zip *)handle;
246
247 if (zip)
248 return zip_close(zip);
249 return 0;
250}
251
252/* Callback for archive_has_entry on a ZIP file. */
253static int archive_zip_has_entry(void *handle, const char *fname)
254{
255 struct zip *zip = (struct zip *)handle;
256 assert(zip);
257 return zip_name_locate(zip, fname, 0) != -1;
258}
259
260/* Callback for archive_walk on a ZIP file. */
261static int archive_zip_walk(
262 void *handle, void *arg,
263 int (*callback)(const char *name, void *arg))
264{
265 zip_int64_t num, i;
266 struct zip *zip = (struct zip *)handle;
267 assert(zip);
268
269 num = zip_get_num_entries(zip, 0);
270 if (num < 0)
271 return 1;
272 for (i = 0; i < num; i++) {
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800273 const char *name = zip_get_name(zip, i, 0);
274 if (*name && name[strlen(name) - 1] == '/')
275 continue;
276 if (callback(name, arg))
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800277 break;
278 }
279 return 0;
280}
281
282/* Callback for archive_zip_read_file on a ZIP file. */
283static int archive_zip_read_file(void *handle, const char *fname,
Mike Frysingerc48a5932019-11-13 00:07:56 -0500284 uint8_t **data, uint32_t *size, int64_t *mtime)
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800285{
286 struct zip *zip = (struct zip *)handle;
287 struct zip_file *fp;
288 struct zip_stat stat;
289
290 assert(zip);
291 *data = NULL;
292 *size = 0;
293 zip_stat_init(&stat);
294 if (zip_stat(zip, fname, 0, &stat)) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700295 ERROR("Fail to stat entry in ZIP: %s\n", fname);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800296 return 1;
297 }
298 fp = zip_fopen(zip, fname, 0);
299 if (!fp) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700300 ERROR("Failed to open entry in ZIP: %s\n", fname);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800301 return 1;
302 }
303 *data = (uint8_t *)malloc(stat.size);
304 if (*data) {
305 if (zip_fread(fp, *data, stat.size) == stat.size) {
Mike Frysingerc48a5932019-11-13 00:07:56 -0500306 if (mtime)
307 *mtime = stat.mtime;
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800308 *size = stat.size;
309 } else {
Julius Werner88a47ff2019-05-08 13:33:20 -0700310 ERROR("Failed to read entry in zip: %s\n", fname);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800311 free(*data);
312 *data = NULL;
313 }
314 }
315 zip_fclose(fp);
316 return *data == NULL;
317}
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800318
319/* Callback for archive_zip_write_file on a ZIP file. */
320static int archive_zip_write_file(void *handle, const char *fname,
Mike Frysingerc48a5932019-11-13 00:07:56 -0500321 uint8_t *data, uint32_t size, int64_t mtime)
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800322{
323 struct zip *zip = (struct zip *)handle;
324 struct zip_source *src;
325
Julius Werner88a47ff2019-05-08 13:33:20 -0700326 VB2_DEBUG("Writing %s\n", fname);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800327 assert(zip);
328 src = zip_source_buffer(zip, data, size, 0);
329 if (!src) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700330 ERROR("Internal error: cannot allocate buffer: %s\n", fname);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800331 return 1;
332 }
333
334 if (zip_file_add(zip, fname, src, ZIP_FL_OVERWRITE) < 0) {
335 zip_source_free(src);
Julius Werner88a47ff2019-05-08 13:33:20 -0700336 ERROR("Internal error: failed to add: %s\n", fname);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800337 return 1;
338 }
339 /* zip_source_free is not needed if zip_file_add success. */
340#if LIBZIP_VERSION_MAJOR >= 1
Mike Frysingerc48a5932019-11-13 00:07:56 -0500341 zip_file_set_mtime(zip, zip_name_locate(zip, fname, 0), mtime, 0);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800342#endif
343 return 0;
344}
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800345#endif
346
347/*
348 * Opens an archive from given path.
349 * The type of archive will be determined automatically.
350 * Returns a pointer to reference to archive (must be released by archive_close
351 * when not used), otherwise NULL on error.
352 */
353struct archive *archive_open(const char *path)
354{
355 struct stat path_stat;
356 struct archive *ar;
357
358 if (stat(path, &path_stat) != 0) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700359 ERROR("Cannot identify type of path: %s\n", path);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800360 return NULL;
361 }
362
363 ar = (struct archive *)malloc(sizeof(*ar));
364 if (!ar) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700365 ERROR("Internal error: allocation failure.\n");
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800366 return NULL;
367 }
368
369 if (S_ISDIR(path_stat.st_mode)) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700370 VB2_DEBUG("Found directory, use fallback (fs) driver: %s\n",
371 path);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800372 /* Regular file system. */
373 ar->open = archive_fallback_open;
374 ar->close = archive_fallback_close;
375 ar->walk = archive_fallback_walk;
376 ar->has_entry = archive_fallback_has_entry;
377 ar->read_file = archive_fallback_read_file;
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800378 ar->write_file = archive_fallback_write_file;
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800379 } else {
380#ifdef HAVE_LIBZIP
Julius Werner88a47ff2019-05-08 13:33:20 -0700381 VB2_DEBUG("Found file, use ZIP driver: %s\n", path);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800382 ar->open = archive_zip_open;
383 ar->close = archive_zip_close;
384 ar->walk = archive_zip_walk;
385 ar->has_entry = archive_zip_has_entry;
386 ar->read_file = archive_zip_read_file;
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800387 ar->write_file = archive_zip_write_file;
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800388#else
Julius Werner88a47ff2019-05-08 13:33:20 -0700389 ERROR("Found file, but no drivers were enabled: %s\n", path);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800390 free(ar);
391 return NULL;
392#endif
393 }
394 ar->handle = ar->open(path);
395 if (!ar->handle) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700396 ERROR("Failed to open archive: %s\n", path);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800397 free(ar);
398 return NULL;
399 }
400 return ar;
401}
402
403/*
404 * Closes an archive reference.
405 * Returns 0 on success, otherwise non-zero as failure.
406 */
407int archive_close(struct archive *ar)
408{
409 int r = ar->close(ar->handle);
410 free(ar);
411 return r;
412}
413
414/*
415 * Checks if an entry (either file or directory) exists in archive.
416 * If entry name (fname) is an absolute path (/file), always check
417 * with real file system.
418 * Returns 1 if exists, otherwise 0
419 */
420int archive_has_entry(struct archive *ar, const char *name)
421{
422 if (!ar || *name == '/')
423 return archive_fallback_has_entry(NULL, name);
424 return ar->has_entry(ar->handle, name);
425}
426
427/*
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800428 * Traverses all files within archive (directories are ignored).
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800429 * For every entry, the path (relative the archive root) will be passed to
430 * callback function, until the callback returns non-zero.
431 * The arg argument will also be passed to callback.
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800432 * Returns 0 on success otherwise non-zero as failure.
433 */
Julius Werner52fa8c12019-05-07 12:59:47 -0700434static int archive_walk(struct archive *ar, void *arg,
435 int (*callback)(const char *path, void *arg))
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800436{
437 if (!ar)
438 return archive_fallback_walk(NULL, arg, callback);
439 return ar->walk(ar->handle, arg, callback);
440}
441
442/*
443 * Reads a file from archive.
444 * If entry name (fname) is an absolute path (/file), always read
445 * from real file system.
446 * Returns 0 on success (data and size reflects the file content),
447 * otherwise non-zero as failure.
448 */
449int archive_read_file(struct archive *ar, const char *fname,
Mike Frysingerc48a5932019-11-13 00:07:56 -0500450 uint8_t **data, uint32_t *size, int64_t *mtime)
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800451{
452 if (!ar || *fname == '/')
Mike Frysingerc48a5932019-11-13 00:07:56 -0500453 return archive_fallback_read_file(NULL, fname, data, size, mtime);
454 return ar->read_file(ar->handle, fname, data, size, mtime);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800455}
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800456
457/*
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800458 * Writes a file into archive.
459 * If entry name (fname) is an absolute path (/file), always write into real
460 * file system.
461 * Returns 0 on success, otherwise non-zero as failure.
462 */
463int archive_write_file(struct archive *ar, const char *fname,
Mike Frysingerc48a5932019-11-13 00:07:56 -0500464 uint8_t *data, uint32_t size, int64_t mtime)
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800465{
466 if (!ar || *fname == '/')
Mike Frysingerc48a5932019-11-13 00:07:56 -0500467 return archive_fallback_write_file(NULL, fname, data, size, mtime);
468 return ar->write_file(ar->handle, fname, data, size, mtime);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800469}
470
471struct _copy_arg {
472 struct archive *from, *to;
473};
474
475/* Callback for archive_copy. */
476static int archive_copy_callback(const char *path, void *_arg)
477{
478 const struct _copy_arg *arg = (const struct _copy_arg*)_arg;
479 uint32_t size;
480 uint8_t *data;
Mike Frysingerc48a5932019-11-13 00:07:56 -0500481 int64_t mtime;
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800482 int r;
483
Julius Werner88a47ff2019-05-08 13:33:20 -0700484 INFO("Copying: %s\n", path);
Mike Frysingerc48a5932019-11-13 00:07:56 -0500485 if (archive_read_file(arg->from, path, &data, &size, &mtime)) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700486 ERROR("Failed reading: %s\n", path);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800487 return 1;
488 }
Mike Frysingerc48a5932019-11-13 00:07:56 -0500489 r = archive_write_file(arg->to, path, data, size, mtime);
Julius Werner88a47ff2019-05-08 13:33:20 -0700490 VB2_DEBUG("result=%d\n", r);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800491 free(data);
492 return r;
493}
494
495/*
496 * Copies all entries from one archive to another.
497 * Returns 0 on success, otherwise non-zero as failure.
498 */
499int archive_copy(struct archive *from, struct archive *to)
500{
501 struct _copy_arg arg = { .from = from, .to = to };
502 return archive_walk(from, &arg, archive_copy_callback);
503}
504
505/*
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800506 * -- End of archive implementations --
507 */
508
Hung-Te Lin4e066902018-10-15 15:33:47 +0800509/* Utility function to convert a string. */
510static void str_convert(char *s, int (*convert)(int c))
Hung-Te Lin1f2e4772018-10-14 21:39:26 +0800511{
512 int c;
513
514 for (; *s; s++) {
515 c = *s;
Hung-Te Lin4e066902018-10-15 15:33:47 +0800516 if (!isascii(c))
Hung-Te Lin1f2e4772018-10-14 21:39:26 +0800517 continue;
Hung-Te Lin4e066902018-10-15 15:33:47 +0800518 *s = convert(c);
Hung-Te Lin1f2e4772018-10-14 21:39:26 +0800519 }
520}
521
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800522/* Returns 1 if name ends by given pattern, otherwise 0. */
523static int str_endswith(const char *name, const char *pattern)
524{
525 size_t name_len = strlen(name), pattern_len = strlen(pattern);
526 if (name_len < pattern_len)
527 return 0;
528 return strcmp(name + name_len - pattern_len, pattern) == 0;
529}
530
531/* Returns 1 if name starts by given pattern, otherwise 0. */
532static int str_startswith(const char *name, const char *pattern)
533{
534 return strncmp(name, pattern, strlen(pattern)) == 0;
535}
536
Hung-Te Lin4e066902018-10-15 15:33:47 +0800537/* Returns the VPD value by given key name, or NULL on error (or no value). */
538static char *vpd_get_value(const char *fpath, const char *key)
539{
540 char *command, *result;
541
542 assert(fpath);
543 ASPRINTF(&command, "vpd -g %s -f %s 2>/dev/null", key, fpath);
544 result = host_shell(command);
545 free(command);
546
547 if (result && !*result) {
548 free(result);
549 result = NULL;
550 }
551 return result;
552}
553
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800554/*
555 * Reads and parses a setvars type file from archive, then stores into config.
556 * Returns 0 on success (at least one entry found), otherwise failure.
557 */
558static int model_config_parse_setvars_file(
559 struct model_config *cfg, struct archive *archive,
560 const char *fpath)
561{
562 uint8_t *data;
563 uint32_t len;
564
565 char *ptr_line, *ptr_token;
566 char *line, *k, *v;
567 int valid = 0;
568
Mike Frysingerc48a5932019-11-13 00:07:56 -0500569 if (archive_read_file(archive, fpath, &data, &len, NULL) != 0) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700570 ERROR("Failed reading: %s\n", fpath);
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800571 return -1;
572 }
573
574 /* Valid content should end with \n, or \"; ensure ASCIIZ for parsing */
575 if (len)
576 data[len - 1] = '\0';
577
578 for (line = strtok_r((char *)data, "\n\r", &ptr_line); line;
579 line = strtok_r(NULL, "\n\r", &ptr_line)) {
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800580 char *expand_path = NULL;
581 int found_valid = 1;
582
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800583 /* Format: KEY="value" */
584 k = strtok_r(line, "=", &ptr_token);
585 if (!k)
586 continue;
587 v = strtok_r(NULL, "\"", &ptr_token);
588 if (!v)
589 continue;
590
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800591 /* Some legacy updaters may be still using ${MODEL_DIR}. */
592 if (str_startswith(v, ENV_VAR_MODEL_DIR)) {
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800593 ASPRINTF(&expand_path, "%s/%s%s", DIR_MODELS, cfg->name,
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800594 v + strlen(ENV_VAR_MODEL_DIR));
595 }
596
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800597 if (strcmp(k, SETVARS_IMAGE_MAIN) == 0)
598 cfg->image = strdup(v);
599 else if (strcmp(k, SETVARS_IMAGE_EC) == 0)
600 cfg->ec_image = strdup(v);
601 else if (strcmp(k, SETVARS_IMAGE_PD) == 0)
602 cfg->pd_image = strdup(v);
Hung-Te Lin4e066902018-10-15 15:33:47 +0800603 else if (strcmp(k, SETVARS_SIGNATURE_ID) == 0) {
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800604 cfg->signature_id = strdup(v);
Hung-Te Lin4e066902018-10-15 15:33:47 +0800605 if (str_startswith(v, SIG_ID_IN_VPD_PREFIX))
606 cfg->is_white_label = 1;
607 } else
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800608 found_valid = 0;
609 free(expand_path);
610 valid += found_valid;
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800611 }
612 free(data);
613 return valid == 0;
614}
615
616/*
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800617 * Changes the rootkey in firmware GBB to given new key.
618 * Returns 0 on success, otherwise failure.
619 */
620static int change_gbb_rootkey(struct firmware_image *image,
621 const char *section_name,
622 const uint8_t *rootkey, uint32_t rootkey_len)
623{
624 const struct vb2_gbb_header *gbb = find_gbb(image);
625 uint8_t *gbb_rootkey;
626 if (!gbb) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700627 ERROR("Cannot find GBB in image %s.\n", image->file_name);
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800628 return -1;
629 }
630 if (gbb->rootkey_size < rootkey_len) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700631 ERROR("New root key (%u bytes) larger than GBB (%u bytes).\n",
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800632 rootkey_len, gbb->rootkey_size);
633 return -1;
634 }
635
636 gbb_rootkey = (uint8_t *)gbb + gbb->rootkey_offset;
637 /* See cmd_gbb_utility: root key must be first cleared with zero. */
638 memset(gbb_rootkey, 0, gbb->rootkey_size);
639 memcpy(gbb_rootkey, rootkey, rootkey_len);
640 return 0;
641}
642
643/*
644 * Changes the VBlock in firmware section to new data.
645 * Returns 0 on success, otherwise failure.
646 */
647static int change_vblock(struct firmware_image *image, const char *section_name,
648 const uint8_t *vblock, uint32_t vblock_len)
649{
650 struct firmware_section section;
651
652 find_firmware_section(&section, image, section_name);
653 if (!section.data) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700654 ERROR("Need section %s in image %s.\n", section_name,
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800655 image->file_name);
656 return -1;
657 }
658 if (section.size < vblock_len) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700659 ERROR("Section %s too small (%zu bytes) for vblock (%u bytes).\n",
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800660 section_name, section.size, vblock_len);
661 return -1;
662 }
663 memcpy(section.data, vblock, vblock_len);
664 return 0;
665}
666
667/*
668 * Applies a key file to firmware image.
669 * Returns 0 on success, otherwise failure.
670 */
671static int apply_key_file(
672 struct firmware_image *image, const char *path,
673 struct archive *archive, const char *section_name,
674 int (*apply)(struct firmware_image *image, const char *section,
675 const uint8_t *data, uint32_t len))
676{
677 int r = 0;
678 uint8_t *data = NULL;
679 uint32_t len;
680
Mike Frysingerc48a5932019-11-13 00:07:56 -0500681 r = archive_read_file(archive, path, &data, &len, NULL);
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800682 if (r == 0) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700683 VB2_DEBUG("Loaded file: %s\n", path);
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800684 r = apply(image, section_name, data, len);
685 if (r)
Julius Werner88a47ff2019-05-08 13:33:20 -0700686 ERROR("Failed applying %s to %s\n", path, section_name);
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800687 } else {
Julius Werner88a47ff2019-05-08 13:33:20 -0700688 ERROR("Failed reading: %s\n", path);
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800689 }
690 free(data);
691 return r;
692}
693
694/*
695 * Modifies a firmware image from patch information specified in model config.
696 * Returns 0 on success, otherwise number of failures.
697 */
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800698int patch_image_by_model(
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800699 struct firmware_image *image, const struct model_config *model,
700 struct archive *archive)
701{
702 int err = 0;
703 if (model->patches.rootkey)
704 err += !!apply_key_file(
705 image, model->patches.rootkey, archive,
706 FMAP_RO_GBB, change_gbb_rootkey);
707 if (model->patches.vblock_a)
708 err += !!apply_key_file(
709 image, model->patches.vblock_a, archive,
710 FMAP_RW_VBLOCK_A, change_vblock);
711 if (model->patches.vblock_b)
712 err += !!apply_key_file(
713 image, model->patches.vblock_b, archive,
714 FMAP_RW_VBLOCK_B, change_vblock);
715 return err;
716}
717
718/*
719 * Finds available patch files by given model.
720 * Updates `model` argument with path of patch files.
721 */
722static void find_patches_for_model(struct model_config *model,
723 struct archive *archive,
724 const char *signature_id)
725{
726 char *path;
727 int i;
728
729 const char *names[] = {
730 "rootkey",
731 "vblock_A",
732 "vblock_B",
733 };
734
735 char **targets[] = {
736 &model->patches.rootkey,
737 &model->patches.vblock_a,
738 &model->patches.vblock_b,
739 };
740
741 assert(ARRAY_SIZE(names) == ARRAY_SIZE(targets));
742 for (i = 0; i < ARRAY_SIZE(names); i++) {
743 ASPRINTF(&path, "%s/%s.%s", DIR_KEYSET, names[i], signature_id);
744 if (archive_has_entry(archive, path))
745 *targets[i] = path;
746 else
747 free(path);
748 }
749}
750
751/*
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800752 * Adds and copies one new model config to the existing list of given manifest.
753 * Returns a pointer to the newly allocated config, or NULL on failure.
754 */
755static struct model_config *manifest_add_model(
756 struct manifest *manifest,
757 const struct model_config *cfg)
758{
759 struct model_config *model;
760 manifest->num++;
761 manifest->models = (struct model_config *)realloc(
762 manifest->models, manifest->num * sizeof(*model));
763 if (!manifest->models) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700764 ERROR("Internal error: failed to allocate buffer.\n");
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800765 return NULL;
766 }
767 model = &manifest->models[manifest->num - 1];
768 memcpy(model, cfg, sizeof(*model));
769 return model;
770}
771
772/*
773 * A callback function for manifest to scan files in archive.
774 * Returns 0 to keep scanning, or non-zero to stop.
775 */
776static int manifest_scan_entries(const char *name, void *arg)
777{
778 struct manifest *manifest = (struct manifest *)arg;
779 struct archive *archive = manifest->archive;
780 struct model_config model = {0};
781 char *slash;
782
783 if (str_startswith(name, PATH_STARTSWITH_KEYSET))
784 manifest->has_keyset = 1;
785 if (!str_endswith(name, PATH_ENDSWITH_SERVARS))
786 return 0;
787
788 /* name: models/$MODEL/setvars.sh */
789 model.name = strdup(strchr(name, '/') + 1);
790 slash = strchr(model.name, '/');
791 if (slash)
792 *slash = '\0';
793
Julius Werner88a47ff2019-05-08 13:33:20 -0700794 VB2_DEBUG("Found model <%s> setvars: %s\n", model.name, name);
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800795 if (model_config_parse_setvars_file(&model, archive, name)) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700796 ERROR("Invalid setvars file: %s\n", name);
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800797 return 0;
798 }
799
800 /* In legacy setvars.sh, the ec_image and pd_image may not exist. */
801 if (model.ec_image && !archive_has_entry(archive, model.ec_image)) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700802 VB2_DEBUG("Ignore non-exist EC image: %s\n", model.ec_image);
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800803 free(model.ec_image);
804 model.ec_image = NULL;
805 }
806 if (model.pd_image && !archive_has_entry(archive, model.pd_image)) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700807 VB2_DEBUG("Ignore non-exist PD image: %s\n", model.pd_image);
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800808 free(model.pd_image);
809 model.pd_image = NULL;
810 }
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800811
812 /* Find patch files. */
813 if (model.signature_id)
814 find_patches_for_model(&model, archive, model.signature_id);
815
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800816 return !manifest_add_model(manifest, &model);
817}
818
819/*
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800820 * Finds the existing model_config from manifest that best matches current
821 * system (as defined by model_name).
822 * Returns a model_config from manifest, or NULL if not found.
823 */
824const struct model_config *manifest_find_model(const struct manifest *manifest,
825 const char *model_name)
826{
827 char *sys_model_name = NULL;
828 const struct model_config *model = NULL;
829 int i;
830
Hung-Te Lin3f02fc12019-03-11 12:14:22 +0800831 /*
832 * For manifest with single model defined, we should just return because
833 * there are other mechanisms like platform name check to double confirm
834 * if the firmware is valid.
835 */
836 if (manifest->num == 1)
837 return &manifest->models[0];
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800838
Hung-Te Lin3f02fc12019-03-11 12:14:22 +0800839 if (!model_name) {
Hung-Te Linb01c83f2018-10-26 12:42:03 +0800840 sys_model_name = host_shell("mosys platform model");
Julius Werner88a47ff2019-05-08 13:33:20 -0700841 VB2_DEBUG("System model name: '%s'\n", sys_model_name);
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800842 model_name = sys_model_name;
843 }
844
845 for (i = 0; !model && i < manifest->num; i++) {
846 if (strcmp(model_name, manifest->models[i].name) == 0)
847 model = &manifest->models[i];
848 }
Hung-Te Linba37ad22018-11-27 10:16:36 +0800849 if (!model) {
850 if (!*model_name)
Julius Werner88a47ff2019-05-08 13:33:20 -0700851 ERROR("Cannot get model name.\n");
Hung-Te Linba37ad22018-11-27 10:16:36 +0800852 else
Julius Werner88a47ff2019-05-08 13:33:20 -0700853 ERROR("Unsupported model: '%s'.\n", model_name);
Hung-Te Linba37ad22018-11-27 10:16:36 +0800854
855 fprintf(stderr,
856 "You are probably running an image for wrong board, or "
857 "a device in early stage that 'mosys' command is not "
858 "ready, or image from old (or factory) branches that "
859 "Unified Build config is not updated yet for 'mosys'.\n"
860 "Please check command 'mosys platform model', "
861 "which should output one of the supported models below:"
862 "\n");
863
864 for (i = 0; i < manifest->num; i++)
865 fprintf(stderr, " %s", manifest->models[i].name);
866 fprintf(stderr, "\n");
867 }
868
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800869
870 free(sys_model_name);
871 return model;
872}
873
874/*
Hung-Te Lin9c064132019-03-05 08:24:47 +0800875 * Determines the signature ID to use for white label.
876 * Returns the signature ID for looking up rootkey and vblock files.
877 * Caller must free the returned string.
878 */
879static char *resolve_signature_id(struct model_config *model, const char *image)
880{
881 int is_unibuild = model->signature_id ? 1 : 0;
882 char *wl_tag = vpd_get_value(image, VPD_WHITELABEL_TAG);
883 char *sig_id = NULL;
884
885 /* Unified build: $model.$wl_tag, or $model (b/126800200). */
886 if (is_unibuild) {
887 if (!wl_tag) {
888 WARN("No VPD '%s' set for white label - use model name "
Julius Werner88a47ff2019-05-08 13:33:20 -0700889 "'%s' as default.\n", VPD_WHITELABEL_TAG,
Hung-Te Lin9c064132019-03-05 08:24:47 +0800890 model->name);
891 return strdup(model->name);
892 }
893
894 ASPRINTF(&sig_id, "%s-%s", model->name, wl_tag);
895 free(wl_tag);
896 return sig_id;
897 }
898
899 /* Non-Unibuild: Upper($wl_tag), or Upper(${cid%%-*}). */
900 if (!wl_tag) {
901 char *cid = vpd_get_value(image, VPD_CUSTOMIZATION_ID);
902 if (cid) {
903 /* customization_id in format LOEM[-VARIANT]. */
904 char *dash = strchr(cid, '-');
905 if (dash)
906 *dash = '\0';
907 wl_tag = cid;
908 }
909 }
910 if (wl_tag)
911 str_convert(wl_tag, toupper);
912 return wl_tag;
913}
914
915/*
Hung-Te Lin4e066902018-10-15 15:33:47 +0800916 * Applies white label information to an existing model configuration.
917 * Collects signature ID information from either parameter signature_id or
918 * image file (via VPD) and updates model.patches for key files.
919 * Returns 0 on success, otherwise failure.
920 */
921int model_apply_white_label(
922 struct model_config *model,
923 struct archive *archive,
924 const char *signature_id,
925 const char *image)
926{
927 char *sig_id = NULL;
928 int r = 0;
929
930 if (!signature_id) {
Hung-Te Lin9c064132019-03-05 08:24:47 +0800931 sig_id = resolve_signature_id(model, image);
Hung-Te Lin4e066902018-10-15 15:33:47 +0800932 signature_id = sig_id;
933 }
934
Hung-Te Lin9c064132019-03-05 08:24:47 +0800935 if (signature_id) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700936 VB2_DEBUG("Find white label patches by signature ID: '%s'.\n",
Hung-Te Lin9c064132019-03-05 08:24:47 +0800937 signature_id);
938 find_patches_for_model(model, archive, signature_id);
939 } else {
940 signature_id = "";
Julius Werner88a47ff2019-05-08 13:33:20 -0700941 WARN("No VPD '%s' set for white label - use default keys.\n",
Hung-Te Lin9c064132019-03-05 08:24:47 +0800942 VPD_WHITELABEL_TAG);
943 }
Hung-Te Lin4e066902018-10-15 15:33:47 +0800944 if (!model->patches.rootkey) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700945 ERROR("No keys found for signature_id: '%s'\n", signature_id);
Hung-Te Lin4e066902018-10-15 15:33:47 +0800946 r = 1;
947 } else {
Julius Werner88a47ff2019-05-08 13:33:20 -0700948 INFO("Applied for white label: %s\n", signature_id);
Hung-Te Lin4e066902018-10-15 15:33:47 +0800949 }
950 free(sig_id);
951 return r;
952}
953
954/*
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800955 * Creates a new manifest object by scanning files in archive.
956 * Returns the manifest on success, otherwise NULL for failure.
957 */
958struct manifest *new_manifest_from_archive(struct archive *archive)
959{
960 struct manifest manifest = {0}, *new_manifest;
961 struct model_config model = {0};
Hung-Te Lin48d08d72018-11-09 09:41:03 +0800962 const char * const host_image_name = "image.bin",
963 * const old_host_image_name = "bios.bin",
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800964 * const ec_name = "ec.bin",
965 * const pd_name = "pd.bin";
966
967 manifest.archive = archive;
968 manifest.default_model = -1;
969 archive_walk(archive, &manifest, manifest_scan_entries);
970 if (manifest.num == 0) {
Hung-Te Lin48d08d72018-11-09 09:41:03 +0800971 const char *image_name = NULL;
Hung-Te Lin1f2e4772018-10-14 21:39:26 +0800972 struct firmware_image image = {0};
Hung-Te Lin48d08d72018-11-09 09:41:03 +0800973
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800974 /* Try to load from current folder. */
Hung-Te Lin48d08d72018-11-09 09:41:03 +0800975 if (archive_has_entry(archive, old_host_image_name))
976 image_name = old_host_image_name;
977 else if (archive_has_entry(archive, host_image_name))
978 image_name = host_image_name;
979 else
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800980 return 0;
Hung-Te Lin48d08d72018-11-09 09:41:03 +0800981
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800982 model.image = strdup(image_name);
983 if (archive_has_entry(archive, ec_name))
984 model.ec_image = strdup(ec_name);
985 if (archive_has_entry(archive, pd_name))
986 model.pd_image = strdup(pd_name);
Hung-Te Lin1f2e4772018-10-14 21:39:26 +0800987 /* Extract model name from FWID: $Vendor_$Platform.$Version */
988 if (!load_firmware_image(&image, image_name, archive)) {
989 char *token = NULL;
990 if (strtok(image.ro_version, "_"))
991 token = strtok(NULL, ".");
992 if (token && *token) {
Hung-Te Lin4e066902018-10-15 15:33:47 +0800993 str_convert(token, tolower);
Hung-Te Lin1f2e4772018-10-14 21:39:26 +0800994 model.name = strdup(token);
995 }
996 free_firmware_image(&image);
997 }
998 if (!model.name)
999 model.name = strdup(DEFAULT_MODEL_NAME);
Hung-Te Lin4e066902018-10-15 15:33:47 +08001000 if (manifest.has_keyset)
1001 model.is_white_label = 1;
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001002 manifest_add_model(&manifest, &model);
1003 manifest.default_model = manifest.num - 1;
1004 }
Julius Werner88a47ff2019-05-08 13:33:20 -07001005 VB2_DEBUG("%d model(s) loaded.\n", manifest.num);
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001006 if (!manifest.num) {
Julius Werner88a47ff2019-05-08 13:33:20 -07001007 ERROR("No valid configurations found from archive.\n");
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001008 return NULL;
1009 }
1010
1011 new_manifest = (struct manifest *)malloc(sizeof(manifest));
1012 if (!new_manifest) {
Julius Werner88a47ff2019-05-08 13:33:20 -07001013 ERROR("Internal error: memory allocation error.\n");
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001014 return NULL;
1015 }
1016 memcpy(new_manifest, &manifest, sizeof(manifest));
1017 return new_manifest;
1018}
1019
1020/* Releases all resources allocated by given manifest object. */
1021void delete_manifest(struct manifest *manifest)
1022{
1023 int i;
1024 assert(manifest);
1025 for (i = 0; i < manifest->num; i++) {
1026 struct model_config *model = &manifest->models[i];
1027 free(model->name);
1028 free(model->signature_id);
1029 free(model->image);
1030 free(model->ec_image);
1031 free(model->pd_image);
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001032 free(model->patches.rootkey);
1033 free(model->patches.vblock_a);
1034 free(model->patches.vblock_b);
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001035 }
1036 free(manifest->models);
1037 free(manifest);
1038}
1039
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001040static const char *get_gbb_key_hash(const struct vb2_gbb_header *gbb,
1041 int32_t offset, int32_t size)
1042{
1043 struct vb2_packed_key *key;
1044
1045 if (!gbb)
1046 return "<No GBB>";
1047 key = (struct vb2_packed_key *)((uint8_t *)gbb + offset);
Joel Kitching582453d2019-10-07 15:28:43 +08001048 if (vb2_packed_key_looks_ok(key, size))
1049 return "<Invalid key>";
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001050 return packed_key_sha1_string(key);
1051}
1052
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001053/* Prints the information of given image file in JSON format. */
1054static void print_json_image(
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001055 const char *name, const char *fpath, struct model_config *m,
1056 struct archive *archive, int indent, int is_host)
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001057{
1058 struct firmware_image image = {0};
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001059 const struct vb2_gbb_header *gbb = NULL;
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001060 if (!fpath)
1061 return;
Jacob Garber26c3f192019-08-08 16:49:14 -06001062 if (load_firmware_image(&image, fpath, archive))
1063 return;
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001064 if (is_host)
1065 gbb = find_gbb(&image);
1066 else
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001067 printf(",\n");
1068 printf("%*s\"%s\": { \"versions\": { \"ro\": \"%s\", \"rw\": \"%s\" },",
1069 indent, "", name, image.ro_version, image.rw_version_a);
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001070 indent += 2;
1071 if (is_host && patch_image_by_model(&image, m, archive) != 0) {
Julius Werner88a47ff2019-05-08 13:33:20 -07001072 ERROR("Failed to patch images by model: %s\n", m->name);
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001073 } else if (gbb) {
1074 printf("\n%*s\"keys\": { \"root\": \"%s\", ",
1075 indent, "",
1076 get_gbb_key_hash(gbb, gbb->rootkey_offset,
1077 gbb->rootkey_size));
1078 printf("\"recovery\": \"%s\" },",
1079 get_gbb_key_hash(gbb, gbb->recovery_key_offset,
1080 gbb->recovery_key_size));
1081 }
1082 printf("\n%*s\"image\": \"%s\" }", indent, "", fpath);
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001083 free_firmware_image(&image);
1084}
1085
1086/* Prints the information of objects in manifest (models and images) in JSON. */
1087void print_json_manifest(const struct manifest *manifest)
1088{
1089 int i, indent;
1090 struct archive *ar = manifest->archive;
1091
1092 printf("{\n");
1093 for (i = 0, indent = 2; i < manifest->num; i++) {
1094 struct model_config *m = &manifest->models[i];
1095 printf("%s%*s\"%s\": {\n", i ? ",\n" : "", indent, "", m->name);
1096 indent += 2;
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001097 print_json_image("host", m->image, m, ar, indent, 1);
1098 print_json_image("ec", m->ec_image, m, ar, indent, 0);
1099 print_json_image("pd", m->pd_image, m, ar, indent, 0);
1100 if (m->patches.rootkey) {
1101 struct patch_config *p = &m->patches;
1102 printf(",\n%*s\"patches\": { \"rootkey\": \"%s\", "
1103 "\"vblock_a\": \"%s\", \"vblock_b\": \"%s\" }",
1104 indent, "", p->rootkey, p->vblock_a,
1105 p->vblock_b);
1106 }
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001107 if (m->signature_id)
1108 printf(",\n%*s\"signature_id\": \"%s\"", indent, "",
1109 m->signature_id);
1110 printf("\n }");
1111 indent -= 2;
1112 assert(indent == 2);
1113 }
1114 printf("\n}\n");
1115}