blob: 1d916db6b33463a33c434a124f3445b8373010dc [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
Jack Rosenthal2407b672022-01-05 17:40:29 -070033#ifdef HAVE_CROSID
34#include <crosid.h>
35#endif
36
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +080037#include "host_misc.h"
38#include "updater.h"
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +080039#include "util_misc.h"
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +080040
Hung-Te Lin3e6397d2018-10-06 03:15:19 +080041/*
42 * A firmware update package (archive) is a file packed by either shar(1) or
43 * zip(1). See https://chromium.googlesource.com/chromiumos/platform/firmware/
44 * for more information.
45 *
46 * A package for single board (i.e., not Unified Build) will have all the image
47 * files in top folder:
Hung-Te Lin48d08d72018-11-09 09:41:03 +080048 * - host: 'image.bin' (or 'bios.bin' as legacy name before CL:1318712)
Hung-Te Lin3e6397d2018-10-06 03:15:19 +080049 * - ec: 'ec.bin'
50 * - pd: 'pd.bin'
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +080051 * If custom label is supported, a 'keyset/' folder will be available, with key
Hung-Te Lin3e6397d2018-10-06 03:15:19 +080052 * files in it:
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +080053 * - rootkey.$CLTAG
54 * - vblock_A.$CLTAG
55 * - vblock_B.$CLTAG
Hung-Te Lin8f302632022-03-18 14:38:19 +080056 * The $CLTAG should come from VPD value 'custom_label_tag'. For legacy devices,
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +080057 * the VPD name may be 'whitelabel_tag', or 'customization_id'.
58 * The 'customization_id' has a different format: LOEM[-VARIANT] and we can only
59 * take LOEM as $CLTAG, for example A-B => $CLTAG=A.
Hung-Te Lin3e6397d2018-10-06 03:15:19 +080060 *
61 * A package for Unified Build is more complicated. There will be a models/
62 * folder, and each model (by $(mosys platform model) ) should appear as a sub
63 * folder, with a 'setvars.sh' file inside. The 'setvars.sh' is a shell script
64 * describing what files should be used and the signature ID ($SIGID) to use.
65 *
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +080066 * Similar to custom label in non-Unified-Build, the keys and vblock files will
Hung-Te Lin3e6397d2018-10-06 03:15:19 +080067 * be in 'keyset/' folder:
68 * - rootkey.$SIGID
69 * - vblock_A.$SIGID
70 * - vblock_B.$SIGID
71 * If $SIGID starts with 'sig-id-in-*' then we have to replace it by VPD value
Hung-Te Lin8f302632022-03-18 14:38:19 +080072 * 'custom_label_tag' as '$MODEL-$CLTAG'.
Hung-Te Lin3e6397d2018-10-06 03:15:19 +080073 */
74
75static const char * const SETVARS_IMAGE_MAIN = "IMAGE_MAIN",
76 * const SETVARS_IMAGE_EC = "IMAGE_EC",
77 * const SETVARS_IMAGE_PD = "IMAGE_PD",
78 * const SETVARS_SIGNATURE_ID = "SIGNATURE_ID",
79 * const SIG_ID_IN_VPD_PREFIX = "sig-id-in",
80 * const DIR_KEYSET = "keyset",
81 * const DIR_MODELS = "models",
82 * const DEFAULT_MODEL_NAME = "default",
Hung-Te Lin8f302632022-03-18 14:38:19 +080083 * const VPD_CUSTOM_LABEL_TAG = "custom_label_tag",
84 * const VPD_CUSTOM_LABEL_TAG_LEGACY = "whitelabel_tag",
Hung-Te Lin4e066902018-10-15 15:33:47 +080085 * const VPD_CUSTOMIZATION_ID = "customization_id",
Hung-Te Lin92fe37c2018-10-15 14:57:34 +080086 * const ENV_VAR_MODEL_DIR = "${MODEL_DIR}",
Hung-Te Lin3e6397d2018-10-06 03:15:19 +080087 * const PATH_STARTSWITH_KEYSET = "keyset/",
88 * const PATH_ENDSWITH_SERVARS = "/setvars.sh";
89
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +080090struct archive {
91 void *handle;
92
93 void * (*open)(const char *name);
94 int (*close)(void *handle);
95
96 int (*walk)(void *handle, void *arg,
97 int (*callback)(const char *path, void *arg));
98 int (*has_entry)(void *handle, const char *name);
99 int (*read_file)(void *handle, const char *fname,
Mike Frysingerc48a5932019-11-13 00:07:56 -0500100 uint8_t **data, uint32_t *size, int64_t *mtime);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800101 int (*write_file)(void *handle, const char *fname,
Mike Frysingerc48a5932019-11-13 00:07:56 -0500102 uint8_t *data, uint32_t size, int64_t mtime);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800103};
104
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800105/*
106 * -- Begin of archive implementations --
107 */
108
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800109/* Callback for archive_open on a general file system. */
110static void *archive_fallback_open(const char *name)
111{
112 assert(name && *name);
113 return strdup(name);
114}
115
116/* Callback for archive_close on a general file system. */
117static int archive_fallback_close(void *handle)
118{
119 free(handle);
120 return 0;
121}
122
123/* Callback for archive_walk on a general file system. */
124static int archive_fallback_walk(
125 void *handle, void *arg,
126 int (*callback)(const char *path, void *arg))
127{
128 FTS *fts_handle;
129 FTSENT *ent;
130 char *fts_argv[2] = {};
131 char default_path[] = ".";
132 char *root = default_path;
133 size_t root_len;
134
135 if (handle)
136 root = (char *)handle;
137 root_len = strlen(root);
138 fts_argv[0] = root;
139
140 fts_handle = fts_open(fts_argv, FTS_NOCHDIR, NULL);
141 if (!fts_handle)
142 return -1;
143
144 while ((ent = fts_read(fts_handle)) != NULL) {
145 char *path = ent->fts_path + root_len;
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800146 if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL)
147 continue;
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800148 while (*path == '/')
149 path++;
150 if (!*path)
151 continue;
152 if (callback(path, arg))
153 break;
154 }
155 return 0;
156}
157
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800158/* Callback for fallback drivers to get full path easily. */
159static const char *archive_fallback_get_path(void *handle, const char *fname,
160 char **temp_path)
161{
162 if (handle && *fname != '/') {
163 ASPRINTF(temp_path, "%s/%s", (char *)handle, fname);
164 return *temp_path;
165 }
166 return fname;
167}
168
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800169/* Callback for archive_has_entry on a general file system. */
170static int archive_fallback_has_entry(void *handle, const char *fname)
171{
172 int r;
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800173 char *temp_path = NULL;
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800174 const char *path = archive_fallback_get_path(handle, fname, &temp_path);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800175
Julius Werner88a47ff2019-05-08 13:33:20 -0700176 VB2_DEBUG("Checking %s\n", path);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800177 r = access(path, R_OK);
178 free(temp_path);
179 return r == 0;
180}
181
182/* Callback for archive_read_file on a general file system. */
183static int archive_fallback_read_file(void *handle, const char *fname,
Mike Frysingerc48a5932019-11-13 00:07:56 -0500184 uint8_t **data, uint32_t *size, int64_t *mtime)
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800185{
186 int r;
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800187 char *temp_path = NULL;
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800188 const char *path = archive_fallback_get_path(handle, fname, &temp_path);
Mike Frysingerc48a5932019-11-13 00:07:56 -0500189 struct stat st;
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800190
Julius Werner88a47ff2019-05-08 13:33:20 -0700191 VB2_DEBUG("Reading %s\n", path);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800192 *data = NULL;
193 *size = 0;
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800194 r = vb2_read_file(path, data, size) != VB2_SUCCESS;
Mike Frysingerc48a5932019-11-13 00:07:56 -0500195 if (mtime) {
196 if (stat(path, &st) == 0)
197 *mtime = st.st_mtime;
198 else
199 WARN("Unable to stat %s: %s\n", path, strerror(errno));
200 }
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800201 free(temp_path);
202 return r;
203}
204
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800205/* Callback for archive_write_file on a general file system. */
206static int archive_fallback_write_file(void *handle, const char *fname,
Mike Frysingerc48a5932019-11-13 00:07:56 -0500207 uint8_t *data, uint32_t size, int64_t mtime)
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800208{
209 int r;
210 char *temp_path = NULL;
211 const char *path = archive_fallback_get_path(handle, fname, &temp_path);
212
Julius Werner88a47ff2019-05-08 13:33:20 -0700213 VB2_DEBUG("Writing %s\n", path);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800214 if (strchr(path, '/')) {
215 char *dirname = strdup(path);
216 *strrchr(dirname, '/') = '\0';
217 /* TODO(hungte): call mkdir(2) instead of shell invocation. */
218 if (access(dirname, W_OK) != 0) {
219 char *command;
220 ASPRINTF(&command, "mkdir -p %s", dirname);
221 free(host_shell(command));
222 free(command);
223 }
224 free(dirname);
225 }
226 r = vb2_write_file(path, data, size) != VB2_SUCCESS;
Mike Frysingerc48a5932019-11-13 00:07:56 -0500227 if (mtime) {
228 struct timeval times[2] = {
229 {.tv_sec = mtime, .tv_usec = 0},
230 {.tv_sec = mtime, .tv_usec = 0},
231 };
232 if (utimes(path, times) != 0)
233 WARN("Unable to set times on %s: %s\n", path, strerror(errno));
234 }
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800235 free(temp_path);
236 return r;
237}
238
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800239#ifdef HAVE_LIBZIP
240
241/* Callback for archive_open on a ZIP file. */
242static void *archive_zip_open(const char *name)
243{
244 return zip_open(name, 0, NULL);
245}
246
247/* Callback for archive_close on a ZIP file. */
248static int archive_zip_close(void *handle)
249{
250 struct zip *zip = (struct zip *)handle;
251
252 if (zip)
253 return zip_close(zip);
254 return 0;
255}
256
257/* Callback for archive_has_entry on a ZIP file. */
258static int archive_zip_has_entry(void *handle, const char *fname)
259{
260 struct zip *zip = (struct zip *)handle;
261 assert(zip);
262 return zip_name_locate(zip, fname, 0) != -1;
263}
264
265/* Callback for archive_walk on a ZIP file. */
266static int archive_zip_walk(
267 void *handle, void *arg,
268 int (*callback)(const char *name, void *arg))
269{
270 zip_int64_t num, i;
271 struct zip *zip = (struct zip *)handle;
272 assert(zip);
273
274 num = zip_get_num_entries(zip, 0);
275 if (num < 0)
276 return 1;
277 for (i = 0; i < num; i++) {
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800278 const char *name = zip_get_name(zip, i, 0);
279 if (*name && name[strlen(name) - 1] == '/')
280 continue;
281 if (callback(name, arg))
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800282 break;
283 }
284 return 0;
285}
286
287/* Callback for archive_zip_read_file on a ZIP file. */
288static int archive_zip_read_file(void *handle, const char *fname,
Mike Frysingerc48a5932019-11-13 00:07:56 -0500289 uint8_t **data, uint32_t *size, int64_t *mtime)
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800290{
291 struct zip *zip = (struct zip *)handle;
292 struct zip_file *fp;
293 struct zip_stat stat;
294
295 assert(zip);
296 *data = NULL;
297 *size = 0;
298 zip_stat_init(&stat);
299 if (zip_stat(zip, fname, 0, &stat)) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700300 ERROR("Fail to stat entry in ZIP: %s\n", fname);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800301 return 1;
302 }
303 fp = zip_fopen(zip, fname, 0);
304 if (!fp) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700305 ERROR("Failed to open entry in ZIP: %s\n", fname);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800306 return 1;
307 }
308 *data = (uint8_t *)malloc(stat.size);
309 if (*data) {
310 if (zip_fread(fp, *data, stat.size) == stat.size) {
Mike Frysingerc48a5932019-11-13 00:07:56 -0500311 if (mtime)
312 *mtime = stat.mtime;
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800313 *size = stat.size;
314 } else {
Julius Werner88a47ff2019-05-08 13:33:20 -0700315 ERROR("Failed to read entry in zip: %s\n", fname);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800316 free(*data);
317 *data = NULL;
318 }
319 }
320 zip_fclose(fp);
321 return *data == NULL;
322}
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800323
324/* Callback for archive_zip_write_file on a ZIP file. */
325static int archive_zip_write_file(void *handle, const char *fname,
Mike Frysingerc48a5932019-11-13 00:07:56 -0500326 uint8_t *data, uint32_t size, int64_t mtime)
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800327{
328 struct zip *zip = (struct zip *)handle;
329 struct zip_source *src;
330
Julius Werner88a47ff2019-05-08 13:33:20 -0700331 VB2_DEBUG("Writing %s\n", fname);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800332 assert(zip);
333 src = zip_source_buffer(zip, data, size, 0);
334 if (!src) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700335 ERROR("Internal error: cannot allocate buffer: %s\n", fname);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800336 return 1;
337 }
338
339 if (zip_file_add(zip, fname, src, ZIP_FL_OVERWRITE) < 0) {
340 zip_source_free(src);
Julius Werner88a47ff2019-05-08 13:33:20 -0700341 ERROR("Internal error: failed to add: %s\n", fname);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800342 return 1;
343 }
344 /* zip_source_free is not needed if zip_file_add success. */
345#if LIBZIP_VERSION_MAJOR >= 1
Mike Frysingerc48a5932019-11-13 00:07:56 -0500346 zip_file_set_mtime(zip, zip_name_locate(zip, fname, 0), mtime, 0);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800347#endif
348 return 0;
349}
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800350#endif
351
352/*
353 * Opens an archive from given path.
354 * The type of archive will be determined automatically.
355 * Returns a pointer to reference to archive (must be released by archive_close
356 * when not used), otherwise NULL on error.
357 */
358struct archive *archive_open(const char *path)
359{
360 struct stat path_stat;
361 struct archive *ar;
362
363 if (stat(path, &path_stat) != 0) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700364 ERROR("Cannot identify type of path: %s\n", path);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800365 return NULL;
366 }
367
368 ar = (struct archive *)malloc(sizeof(*ar));
369 if (!ar) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700370 ERROR("Internal error: allocation failure.\n");
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800371 return NULL;
372 }
373
374 if (S_ISDIR(path_stat.st_mode)) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700375 VB2_DEBUG("Found directory, use fallback (fs) driver: %s\n",
376 path);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800377 /* Regular file system. */
378 ar->open = archive_fallback_open;
379 ar->close = archive_fallback_close;
380 ar->walk = archive_fallback_walk;
381 ar->has_entry = archive_fallback_has_entry;
382 ar->read_file = archive_fallback_read_file;
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800383 ar->write_file = archive_fallback_write_file;
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800384 } else {
385#ifdef HAVE_LIBZIP
Julius Werner88a47ff2019-05-08 13:33:20 -0700386 VB2_DEBUG("Found file, use ZIP driver: %s\n", path);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800387 ar->open = archive_zip_open;
388 ar->close = archive_zip_close;
389 ar->walk = archive_zip_walk;
390 ar->has_entry = archive_zip_has_entry;
391 ar->read_file = archive_zip_read_file;
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800392 ar->write_file = archive_zip_write_file;
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800393#else
Julius Werner88a47ff2019-05-08 13:33:20 -0700394 ERROR("Found file, but no drivers were enabled: %s\n", path);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800395 free(ar);
396 return NULL;
397#endif
398 }
399 ar->handle = ar->open(path);
400 if (!ar->handle) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700401 ERROR("Failed to open archive: %s\n", path);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800402 free(ar);
403 return NULL;
404 }
405 return ar;
406}
407
408/*
409 * Closes an archive reference.
410 * Returns 0 on success, otherwise non-zero as failure.
411 */
412int archive_close(struct archive *ar)
413{
414 int r = ar->close(ar->handle);
415 free(ar);
416 return r;
417}
418
419/*
420 * Checks if an entry (either file or directory) exists in archive.
421 * If entry name (fname) is an absolute path (/file), always check
422 * with real file system.
423 * Returns 1 if exists, otherwise 0
424 */
425int archive_has_entry(struct archive *ar, const char *name)
426{
427 if (!ar || *name == '/')
428 return archive_fallback_has_entry(NULL, name);
429 return ar->has_entry(ar->handle, name);
430}
431
432/*
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800433 * Traverses all files within archive (directories are ignored).
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800434 * For every entry, the path (relative the archive root) will be passed to
435 * callback function, until the callback returns non-zero.
436 * The arg argument will also be passed to callback.
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800437 * Returns 0 on success otherwise non-zero as failure.
438 */
Julius Werner52fa8c12019-05-07 12:59:47 -0700439static int archive_walk(struct archive *ar, void *arg,
440 int (*callback)(const char *path, void *arg))
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800441{
442 if (!ar)
443 return archive_fallback_walk(NULL, arg, callback);
444 return ar->walk(ar->handle, arg, callback);
445}
446
447/*
448 * Reads a file from archive.
449 * If entry name (fname) is an absolute path (/file), always read
450 * from real file system.
451 * Returns 0 on success (data and size reflects the file content),
452 * otherwise non-zero as failure.
453 */
454int archive_read_file(struct archive *ar, const char *fname,
Mike Frysingerc48a5932019-11-13 00:07:56 -0500455 uint8_t **data, uint32_t *size, int64_t *mtime)
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800456{
457 if (!ar || *fname == '/')
Mike Frysingerc48a5932019-11-13 00:07:56 -0500458 return archive_fallback_read_file(NULL, fname, data, size, mtime);
459 return ar->read_file(ar->handle, fname, data, size, mtime);
Hung-Te Lin7db7a6d2018-10-01 17:03:42 +0800460}
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800461
462/*
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800463 * Writes a file into archive.
464 * If entry name (fname) is an absolute path (/file), always write into real
465 * file system.
466 * Returns 0 on success, otherwise non-zero as failure.
467 */
468int archive_write_file(struct archive *ar, const char *fname,
Mike Frysingerc48a5932019-11-13 00:07:56 -0500469 uint8_t *data, uint32_t size, int64_t mtime)
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800470{
471 if (!ar || *fname == '/')
Mike Frysingerc48a5932019-11-13 00:07:56 -0500472 return archive_fallback_write_file(NULL, fname, data, size, mtime);
473 return ar->write_file(ar->handle, fname, data, size, mtime);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800474}
475
476struct _copy_arg {
477 struct archive *from, *to;
478};
479
480/* Callback for archive_copy. */
481static int archive_copy_callback(const char *path, void *_arg)
482{
483 const struct _copy_arg *arg = (const struct _copy_arg*)_arg;
484 uint32_t size;
485 uint8_t *data;
Mike Frysingerc48a5932019-11-13 00:07:56 -0500486 int64_t mtime;
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800487 int r;
488
Julius Werner88a47ff2019-05-08 13:33:20 -0700489 INFO("Copying: %s\n", path);
Mike Frysingerc48a5932019-11-13 00:07:56 -0500490 if (archive_read_file(arg->from, path, &data, &size, &mtime)) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700491 ERROR("Failed reading: %s\n", path);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800492 return 1;
493 }
Mike Frysingerc48a5932019-11-13 00:07:56 -0500494 r = archive_write_file(arg->to, path, data, size, mtime);
Julius Werner88a47ff2019-05-08 13:33:20 -0700495 VB2_DEBUG("result=%d\n", r);
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800496 free(data);
497 return r;
498}
499
500/*
501 * Copies all entries from one archive to another.
502 * Returns 0 on success, otherwise non-zero as failure.
503 */
504int archive_copy(struct archive *from, struct archive *to)
505{
506 struct _copy_arg arg = { .from = from, .to = to };
507 return archive_walk(from, &arg, archive_copy_callback);
508}
509
510/*
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800511 * -- End of archive implementations --
512 */
513
Hung-Te Lin4e066902018-10-15 15:33:47 +0800514/* Utility function to convert a string. */
515static void str_convert(char *s, int (*convert)(int c))
Hung-Te Lin1f2e4772018-10-14 21:39:26 +0800516{
517 int c;
518
519 for (; *s; s++) {
520 c = *s;
Hung-Te Lin4e066902018-10-15 15:33:47 +0800521 if (!isascii(c))
Hung-Te Lin1f2e4772018-10-14 21:39:26 +0800522 continue;
Hung-Te Lin4e066902018-10-15 15:33:47 +0800523 *s = convert(c);
Hung-Te Lin1f2e4772018-10-14 21:39:26 +0800524 }
525}
526
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800527/* Returns 1 if name ends by given pattern, otherwise 0. */
528static int str_endswith(const char *name, const char *pattern)
529{
530 size_t name_len = strlen(name), pattern_len = strlen(pattern);
531 if (name_len < pattern_len)
532 return 0;
533 return strcmp(name + name_len - pattern_len, pattern) == 0;
534}
535
536/* Returns 1 if name starts by given pattern, otherwise 0. */
537static int str_startswith(const char *name, const char *pattern)
538{
539 return strncmp(name, pattern, strlen(pattern)) == 0;
540}
541
Hung-Te Lin4e066902018-10-15 15:33:47 +0800542/* Returns the VPD value by given key name, or NULL on error (or no value). */
543static char *vpd_get_value(const char *fpath, const char *key)
544{
545 char *command, *result;
546
547 assert(fpath);
548 ASPRINTF(&command, "vpd -g %s -f %s 2>/dev/null", key, fpath);
549 result = host_shell(command);
550 free(command);
551
552 if (result && !*result) {
553 free(result);
554 result = NULL;
555 }
556 return result;
557}
558
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800559/*
560 * Reads and parses a setvars type file from archive, then stores into config.
561 * Returns 0 on success (at least one entry found), otherwise failure.
562 */
563static int model_config_parse_setvars_file(
564 struct model_config *cfg, struct archive *archive,
565 const char *fpath)
566{
567 uint8_t *data;
568 uint32_t len;
569
570 char *ptr_line, *ptr_token;
571 char *line, *k, *v;
572 int valid = 0;
573
Mike Frysingerc48a5932019-11-13 00:07:56 -0500574 if (archive_read_file(archive, fpath, &data, &len, NULL) != 0) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700575 ERROR("Failed reading: %s\n", fpath);
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800576 return -1;
577 }
578
579 /* Valid content should end with \n, or \"; ensure ASCIIZ for parsing */
580 if (len)
581 data[len - 1] = '\0';
582
583 for (line = strtok_r((char *)data, "\n\r", &ptr_line); line;
584 line = strtok_r(NULL, "\n\r", &ptr_line)) {
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800585 char *expand_path = NULL;
586 int found_valid = 1;
587
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800588 /* Format: KEY="value" */
589 k = strtok_r(line, "=", &ptr_token);
590 if (!k)
591 continue;
592 v = strtok_r(NULL, "\"", &ptr_token);
593 if (!v)
594 continue;
595
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800596 /* Some legacy updaters may be still using ${MODEL_DIR}. */
597 if (str_startswith(v, ENV_VAR_MODEL_DIR)) {
Hung-Te Line1cc2b82018-10-17 12:03:24 +0800598 ASPRINTF(&expand_path, "%s/%s%s", DIR_MODELS, cfg->name,
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800599 v + strlen(ENV_VAR_MODEL_DIR));
600 }
601
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800602 if (strcmp(k, SETVARS_IMAGE_MAIN) == 0)
603 cfg->image = strdup(v);
604 else if (strcmp(k, SETVARS_IMAGE_EC) == 0)
605 cfg->ec_image = strdup(v);
606 else if (strcmp(k, SETVARS_IMAGE_PD) == 0)
607 cfg->pd_image = strdup(v);
Hung-Te Lin4e066902018-10-15 15:33:47 +0800608 else if (strcmp(k, SETVARS_SIGNATURE_ID) == 0) {
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800609 cfg->signature_id = strdup(v);
Hung-Te Lin4e066902018-10-15 15:33:47 +0800610 if (str_startswith(v, SIG_ID_IN_VPD_PREFIX))
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +0800611 cfg->is_custom_label = 1;
Hung-Te Lin4e066902018-10-15 15:33:47 +0800612 } else
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800613 found_valid = 0;
614 free(expand_path);
615 valid += found_valid;
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800616 }
617 free(data);
618 return valid == 0;
619}
620
621/*
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800622 * Changes the rootkey in firmware GBB to given new key.
623 * Returns 0 on success, otherwise failure.
624 */
625static int change_gbb_rootkey(struct firmware_image *image,
626 const char *section_name,
627 const uint8_t *rootkey, uint32_t rootkey_len)
628{
629 const struct vb2_gbb_header *gbb = find_gbb(image);
630 uint8_t *gbb_rootkey;
631 if (!gbb) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700632 ERROR("Cannot find GBB in image %s.\n", image->file_name);
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800633 return -1;
634 }
635 if (gbb->rootkey_size < rootkey_len) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700636 ERROR("New root key (%u bytes) larger than GBB (%u bytes).\n",
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800637 rootkey_len, gbb->rootkey_size);
638 return -1;
639 }
640
641 gbb_rootkey = (uint8_t *)gbb + gbb->rootkey_offset;
642 /* See cmd_gbb_utility: root key must be first cleared with zero. */
643 memset(gbb_rootkey, 0, gbb->rootkey_size);
644 memcpy(gbb_rootkey, rootkey, rootkey_len);
645 return 0;
646}
647
648/*
649 * Changes the VBlock in firmware section to new data.
650 * Returns 0 on success, otherwise failure.
651 */
652static int change_vblock(struct firmware_image *image, const char *section_name,
653 const uint8_t *vblock, uint32_t vblock_len)
654{
655 struct firmware_section section;
656
657 find_firmware_section(&section, image, section_name);
658 if (!section.data) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700659 ERROR("Need section %s in image %s.\n", section_name,
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800660 image->file_name);
661 return -1;
662 }
663 if (section.size < vblock_len) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700664 ERROR("Section %s too small (%zu bytes) for vblock (%u bytes).\n",
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800665 section_name, section.size, vblock_len);
666 return -1;
667 }
668 memcpy(section.data, vblock, vblock_len);
669 return 0;
670}
671
672/*
673 * Applies a key file to firmware image.
674 * Returns 0 on success, otherwise failure.
675 */
676static int apply_key_file(
677 struct firmware_image *image, const char *path,
678 struct archive *archive, const char *section_name,
679 int (*apply)(struct firmware_image *image, const char *section,
680 const uint8_t *data, uint32_t len))
681{
682 int r = 0;
683 uint8_t *data = NULL;
684 uint32_t len;
685
Mike Frysingerc48a5932019-11-13 00:07:56 -0500686 r = archive_read_file(archive, path, &data, &len, NULL);
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800687 if (r == 0) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700688 VB2_DEBUG("Loaded file: %s\n", path);
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800689 r = apply(image, section_name, data, len);
690 if (r)
Julius Werner88a47ff2019-05-08 13:33:20 -0700691 ERROR("Failed applying %s to %s\n", path, section_name);
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800692 } else {
Julius Werner88a47ff2019-05-08 13:33:20 -0700693 ERROR("Failed reading: %s\n", path);
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800694 }
695 free(data);
696 return r;
697}
698
699/*
700 * Modifies a firmware image from patch information specified in model config.
701 * Returns 0 on success, otherwise number of failures.
702 */
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800703int patch_image_by_model(
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800704 struct firmware_image *image, const struct model_config *model,
705 struct archive *archive)
706{
707 int err = 0;
708 if (model->patches.rootkey)
709 err += !!apply_key_file(
710 image, model->patches.rootkey, archive,
711 FMAP_RO_GBB, change_gbb_rootkey);
712 if (model->patches.vblock_a)
713 err += !!apply_key_file(
714 image, model->patches.vblock_a, archive,
715 FMAP_RW_VBLOCK_A, change_vblock);
716 if (model->patches.vblock_b)
717 err += !!apply_key_file(
718 image, model->patches.vblock_b, archive,
719 FMAP_RW_VBLOCK_B, change_vblock);
720 return err;
721}
722
723/*
724 * Finds available patch files by given model.
725 * Updates `model` argument with path of patch files.
726 */
727static void find_patches_for_model(struct model_config *model,
728 struct archive *archive,
729 const char *signature_id)
730{
731 char *path;
732 int i;
733
734 const char *names[] = {
735 "rootkey",
736 "vblock_A",
737 "vblock_B",
738 };
739
740 char **targets[] = {
741 &model->patches.rootkey,
742 &model->patches.vblock_a,
743 &model->patches.vblock_b,
744 };
745
746 assert(ARRAY_SIZE(names) == ARRAY_SIZE(targets));
747 for (i = 0; i < ARRAY_SIZE(names); i++) {
748 ASPRINTF(&path, "%s/%s.%s", DIR_KEYSET, names[i], signature_id);
749 if (archive_has_entry(archive, path))
750 *targets[i] = path;
751 else
752 free(path);
753 }
754}
755
756/*
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800757 * Adds and copies one new model config to the existing list of given manifest.
758 * Returns a pointer to the newly allocated config, or NULL on failure.
759 */
760static struct model_config *manifest_add_model(
761 struct manifest *manifest,
762 const struct model_config *cfg)
763{
764 struct model_config *model;
765 manifest->num++;
766 manifest->models = (struct model_config *)realloc(
767 manifest->models, manifest->num * sizeof(*model));
768 if (!manifest->models) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700769 ERROR("Internal error: failed to allocate buffer.\n");
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800770 return NULL;
771 }
772 model = &manifest->models[manifest->num - 1];
773 memcpy(model, cfg, sizeof(*model));
774 return model;
775}
776
777/*
778 * A callback function for manifest to scan files in archive.
779 * Returns 0 to keep scanning, or non-zero to stop.
780 */
781static int manifest_scan_entries(const char *name, void *arg)
782{
783 struct manifest *manifest = (struct manifest *)arg;
784 struct archive *archive = manifest->archive;
785 struct model_config model = {0};
786 char *slash;
787
788 if (str_startswith(name, PATH_STARTSWITH_KEYSET))
789 manifest->has_keyset = 1;
790 if (!str_endswith(name, PATH_ENDSWITH_SERVARS))
791 return 0;
792
793 /* name: models/$MODEL/setvars.sh */
794 model.name = strdup(strchr(name, '/') + 1);
795 slash = strchr(model.name, '/');
796 if (slash)
797 *slash = '\0';
798
Julius Werner88a47ff2019-05-08 13:33:20 -0700799 VB2_DEBUG("Found model <%s> setvars: %s\n", model.name, name);
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800800 if (model_config_parse_setvars_file(&model, archive, name)) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700801 ERROR("Invalid setvars file: %s\n", name);
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800802 return 0;
803 }
804
805 /* In legacy setvars.sh, the ec_image and pd_image may not exist. */
806 if (model.ec_image && !archive_has_entry(archive, model.ec_image)) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700807 VB2_DEBUG("Ignore non-exist EC image: %s\n", model.ec_image);
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800808 free(model.ec_image);
809 model.ec_image = NULL;
810 }
811 if (model.pd_image && !archive_has_entry(archive, model.pd_image)) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700812 VB2_DEBUG("Ignore non-exist PD image: %s\n", model.pd_image);
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800813 free(model.pd_image);
814 model.pd_image = NULL;
815 }
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +0800816
817 /* Find patch files. */
818 if (model.signature_id)
819 find_patches_for_model(&model, archive, model.signature_id);
820
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800821 return !manifest_add_model(manifest, &model);
822}
823
Jack Rosenthal2407b672022-01-05 17:40:29 -0700824/**
825 * get_manifest_key() - Wrapper to get the firmware manifest key from crosid
826 *
827 * @manifest_key_out - Output parameter of the firmware manifest key.
828 *
829 * Returns:
830 * - <0 if libcrosid is unavailable or there was an error reading
831 * device data
832 * - >=0 (the matched device index) success
833 */
834static int get_manifest_key(char **manifest_key_out)
835{
836#ifdef HAVE_CROSID
837 return crosid_get_firmware_manifest_key(manifest_key_out);
838#else
839 ERROR("This version of futility was compiled without libcrosid "
840 "(perhaps compiled outside of the Chrome OS build system?) and "
841 "the update command is not fully supported. Either compile "
842 "from the Chrome OS build, or pass --model to manually specify "
843 "the machine model.\n");
844 return -1;
845#endif
846}
847
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800848/*
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800849 * Finds the existing model_config from manifest that best matches current
850 * system (as defined by model_name).
851 * Returns a model_config from manifest, or NULL if not found.
852 */
853const struct model_config *manifest_find_model(const struct manifest *manifest,
854 const char *model_name)
855{
Jack Rosenthal2407b672022-01-05 17:40:29 -0700856 char *manifest_key = NULL;
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800857 const struct model_config *model = NULL;
858 int i;
Jack Rosenthal2407b672022-01-05 17:40:29 -0700859 int matched_index;
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800860
Hung-Te Lin3f02fc12019-03-11 12:14:22 +0800861 /*
862 * For manifest with single model defined, we should just return because
863 * there are other mechanisms like platform name check to double confirm
864 * if the firmware is valid.
865 */
866 if (manifest->num == 1)
867 return &manifest->models[0];
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800868
Hung-Te Lin3f02fc12019-03-11 12:14:22 +0800869 if (!model_name) {
Jack Rosenthal2407b672022-01-05 17:40:29 -0700870 matched_index = get_manifest_key(&manifest_key);
871 if (matched_index < 0) {
872 ERROR("Failed to get device identity. "
873 "Run \"crosid -v\" for explanation.\n");
874 return NULL;
875 }
876
Hung-Te Linf1144f42022-02-14 19:02:24 +0800877 INFO("Identified the device using libcrosid, "
878 "matched chromeos-config index: %d, "
879 "manifest key (model): %s\n",
880 matched_index, manifest_key);
Jack Rosenthal2407b672022-01-05 17:40:29 -0700881 model_name = manifest_key;
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800882 }
883
884 for (i = 0; !model && i < manifest->num; i++) {
885 if (strcmp(model_name, manifest->models[i].name) == 0)
886 model = &manifest->models[i];
887 }
Hung-Te Linba37ad22018-11-27 10:16:36 +0800888 if (!model) {
Jack Rosenthal2407b672022-01-05 17:40:29 -0700889 ERROR("Unsupported model: '%s'.\n", model_name);
Hung-Te Linba37ad22018-11-27 10:16:36 +0800890
891 fprintf(stderr,
Jack Rosenthal2407b672022-01-05 17:40:29 -0700892 "The firmware manifest key '%s' is not present in this "
893 "updater archive. The known keys to this updater "
894 "archive are:\n", model_name);
Hung-Te Linba37ad22018-11-27 10:16:36 +0800895
896 for (i = 0; i < manifest->num; i++)
897 fprintf(stderr, " %s", manifest->models[i].name);
Jack Rosenthal2407b672022-01-05 17:40:29 -0700898 fprintf(stderr, "\n\n");
899 fprintf(stderr,
900 "Perhaps you are trying to use an updater archive for "
901 "the wrong board, or designed for an older OS version "
902 "before this model was supported.\n");
903 fprintf(stderr,
904 "Hint: Read the FIRMWARE_MANIFEST_KEY from the output "
905 "of the crosid command.\n");
Hung-Te Linba37ad22018-11-27 10:16:36 +0800906 }
907
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800908
Jack Rosenthal2407b672022-01-05 17:40:29 -0700909 free(manifest_key);
Hung-Te Lin92fe37c2018-10-15 14:57:34 +0800910 return model;
911}
912
913/*
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +0800914 * Determines the signature ID to use for custom label.
Hung-Te Lin9c064132019-03-05 08:24:47 +0800915 * Returns the signature ID for looking up rootkey and vblock files.
916 * Caller must free the returned string.
917 */
918static char *resolve_signature_id(struct model_config *model, const char *image)
919{
920 int is_unibuild = model->signature_id ? 1 : 0;
Hung-Te Lin8f302632022-03-18 14:38:19 +0800921 char *tag = vpd_get_value(image, VPD_CUSTOM_LABEL_TAG);
Hung-Te Lin9c064132019-03-05 08:24:47 +0800922 char *sig_id = NULL;
923
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +0800924 if (tag == NULL)
Hung-Te Lin8f302632022-03-18 14:38:19 +0800925 tag = vpd_get_value(image, VPD_CUSTOM_LABEL_TAG_LEGACY);
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +0800926
927 /* Unified build: $model.$tag, or $model (b/126800200). */
Hung-Te Lin9c064132019-03-05 08:24:47 +0800928 if (is_unibuild) {
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +0800929 if (!tag) {
930 WARN("No VPD '%s' set for custom label. "
931 "Use model name '%s' as default.\n",
Hung-Te Lin8f302632022-03-18 14:38:19 +0800932 VPD_CUSTOM_LABEL_TAG, model->name);
Hung-Te Lin9c064132019-03-05 08:24:47 +0800933 return strdup(model->name);
934 }
935
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +0800936 ASPRINTF(&sig_id, "%s-%s", model->name, tag);
937 free(tag);
Hung-Te Lin9c064132019-03-05 08:24:47 +0800938 return sig_id;
939 }
940
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +0800941 /* Non-Unibuild: Upper($tag), or Upper(${cid%%-*}). */
942 if (!tag) {
Hung-Te Lin9c064132019-03-05 08:24:47 +0800943 char *cid = vpd_get_value(image, VPD_CUSTOMIZATION_ID);
944 if (cid) {
945 /* customization_id in format LOEM[-VARIANT]. */
946 char *dash = strchr(cid, '-');
947 if (dash)
948 *dash = '\0';
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +0800949 tag = cid;
Hung-Te Lin9c064132019-03-05 08:24:47 +0800950 }
951 }
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +0800952 if (tag)
953 str_convert(tag, toupper);
954 return tag;
Hung-Te Lin9c064132019-03-05 08:24:47 +0800955}
956
957/*
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +0800958 * Applies custom label information to an existing model configuration.
Hung-Te Lin4e066902018-10-15 15:33:47 +0800959 * Collects signature ID information from either parameter signature_id or
960 * image file (via VPD) and updates model.patches for key files.
961 * Returns 0 on success, otherwise failure.
962 */
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +0800963int model_apply_custom_label(
Hung-Te Lin4e066902018-10-15 15:33:47 +0800964 struct model_config *model,
965 struct archive *archive,
966 const char *signature_id,
967 const char *image)
968{
969 char *sig_id = NULL;
970 int r = 0;
971
972 if (!signature_id) {
Hung-Te Lin9c064132019-03-05 08:24:47 +0800973 sig_id = resolve_signature_id(model, image);
Hung-Te Lin4e066902018-10-15 15:33:47 +0800974 signature_id = sig_id;
975 }
976
Hung-Te Lin9c064132019-03-05 08:24:47 +0800977 if (signature_id) {
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +0800978 VB2_DEBUG("Find custom label patches by signature ID: '%s'.\n",
Hung-Te Lin9c064132019-03-05 08:24:47 +0800979 signature_id);
980 find_patches_for_model(model, archive, signature_id);
981 } else {
982 signature_id = "";
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +0800983 WARN("No VPD '%s' set for custom label - use default keys.\n",
Hung-Te Lin8f302632022-03-18 14:38:19 +0800984 VPD_CUSTOM_LABEL_TAG);
Hung-Te Lin9c064132019-03-05 08:24:47 +0800985 }
Hung-Te Lin4e066902018-10-15 15:33:47 +0800986 if (!model->patches.rootkey) {
Julius Werner88a47ff2019-05-08 13:33:20 -0700987 ERROR("No keys found for signature_id: '%s'\n", signature_id);
Hung-Te Lin4e066902018-10-15 15:33:47 +0800988 r = 1;
989 } else {
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +0800990 INFO("Applied for custom label: %s\n", signature_id);
Hung-Te Lin4e066902018-10-15 15:33:47 +0800991 }
992 free(sig_id);
993 return r;
994}
995
996/*
Hung-Te Lin3e6397d2018-10-06 03:15:19 +0800997 * Creates a new manifest object by scanning files in archive.
998 * Returns the manifest on success, otherwise NULL for failure.
999 */
1000struct manifest *new_manifest_from_archive(struct archive *archive)
1001{
1002 struct manifest manifest = {0}, *new_manifest;
1003 struct model_config model = {0};
Hung-Te Lin48d08d72018-11-09 09:41:03 +08001004 const char * const host_image_name = "image.bin",
1005 * const old_host_image_name = "bios.bin",
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001006 * const ec_name = "ec.bin",
1007 * const pd_name = "pd.bin";
1008
1009 manifest.archive = archive;
1010 manifest.default_model = -1;
1011 archive_walk(archive, &manifest, manifest_scan_entries);
1012 if (manifest.num == 0) {
Hung-Te Lin48d08d72018-11-09 09:41:03 +08001013 const char *image_name = NULL;
Hung-Te Lin1f2e4772018-10-14 21:39:26 +08001014 struct firmware_image image = {0};
Hung-Te Lin48d08d72018-11-09 09:41:03 +08001015
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001016 /* Try to load from current folder. */
Hung-Te Lin48d08d72018-11-09 09:41:03 +08001017 if (archive_has_entry(archive, old_host_image_name))
1018 image_name = old_host_image_name;
1019 else if (archive_has_entry(archive, host_image_name))
1020 image_name = host_image_name;
1021 else
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001022 return 0;
Hung-Te Lin48d08d72018-11-09 09:41:03 +08001023
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001024 model.image = strdup(image_name);
1025 if (archive_has_entry(archive, ec_name))
1026 model.ec_image = strdup(ec_name);
1027 if (archive_has_entry(archive, pd_name))
1028 model.pd_image = strdup(pd_name);
Hung-Te Lin1f2e4772018-10-14 21:39:26 +08001029 /* Extract model name from FWID: $Vendor_$Platform.$Version */
1030 if (!load_firmware_image(&image, image_name, archive)) {
1031 char *token = NULL;
1032 if (strtok(image.ro_version, "_"))
1033 token = strtok(NULL, ".");
1034 if (token && *token) {
Hung-Te Lin4e066902018-10-15 15:33:47 +08001035 str_convert(token, tolower);
Hung-Te Lin1f2e4772018-10-14 21:39:26 +08001036 model.name = strdup(token);
1037 }
1038 free_firmware_image(&image);
1039 }
1040 if (!model.name)
1041 model.name = strdup(DEFAULT_MODEL_NAME);
Hung-Te Lin4e066902018-10-15 15:33:47 +08001042 if (manifest.has_keyset)
Hung-Te Lin41cc9ec2022-03-07 09:39:37 +08001043 model.is_custom_label = 1;
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001044 manifest_add_model(&manifest, &model);
1045 manifest.default_model = manifest.num - 1;
1046 }
Julius Werner88a47ff2019-05-08 13:33:20 -07001047 VB2_DEBUG("%d model(s) loaded.\n", manifest.num);
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001048 if (!manifest.num) {
Julius Werner88a47ff2019-05-08 13:33:20 -07001049 ERROR("No valid configurations found from archive.\n");
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001050 return NULL;
1051 }
1052
1053 new_manifest = (struct manifest *)malloc(sizeof(manifest));
1054 if (!new_manifest) {
Julius Werner88a47ff2019-05-08 13:33:20 -07001055 ERROR("Internal error: memory allocation error.\n");
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001056 return NULL;
1057 }
1058 memcpy(new_manifest, &manifest, sizeof(manifest));
1059 return new_manifest;
1060}
1061
1062/* Releases all resources allocated by given manifest object. */
1063void delete_manifest(struct manifest *manifest)
1064{
1065 int i;
1066 assert(manifest);
1067 for (i = 0; i < manifest->num; i++) {
1068 struct model_config *model = &manifest->models[i];
1069 free(model->name);
1070 free(model->signature_id);
1071 free(model->image);
1072 free(model->ec_image);
1073 free(model->pd_image);
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001074 free(model->patches.rootkey);
1075 free(model->patches.vblock_a);
1076 free(model->patches.vblock_b);
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001077 }
1078 free(manifest->models);
1079 free(manifest);
1080}
1081
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001082static const char *get_gbb_key_hash(const struct vb2_gbb_header *gbb,
1083 int32_t offset, int32_t size)
1084{
1085 struct vb2_packed_key *key;
1086
1087 if (!gbb)
1088 return "<No GBB>";
1089 key = (struct vb2_packed_key *)((uint8_t *)gbb + offset);
Joel Kitching582453d2019-10-07 15:28:43 +08001090 if (vb2_packed_key_looks_ok(key, size))
1091 return "<Invalid key>";
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001092 return packed_key_sha1_string(key);
1093}
1094
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001095/* Prints the information of given image file in JSON format. */
1096static void print_json_image(
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001097 const char *name, const char *fpath, struct model_config *m,
1098 struct archive *archive, int indent, int is_host)
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001099{
1100 struct firmware_image image = {0};
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001101 const struct vb2_gbb_header *gbb = NULL;
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001102 if (!fpath)
1103 return;
Jacob Garber26c3f192019-08-08 16:49:14 -06001104 if (load_firmware_image(&image, fpath, archive))
1105 return;
Isaac Lee0bffea92021-06-01 15:47:34 +08001106 if (!is_host)
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001107 printf(",\n");
1108 printf("%*s\"%s\": { \"versions\": { \"ro\": \"%s\", \"rw\": \"%s\" },",
1109 indent, "", name, image.ro_version, image.rw_version_a);
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001110 indent += 2;
Isaac Lee0bffea92021-06-01 15:47:34 +08001111 if (!is_host) {
1112 /* No extra information to be printed */
1113 } else if (patch_image_by_model(&image, m, archive) != 0) {
Julius Werner88a47ff2019-05-08 13:33:20 -07001114 ERROR("Failed to patch images by model: %s\n", m->name);
Isaac Lee0bffea92021-06-01 15:47:34 +08001115 } else if (NULL != (gbb = find_gbb(&image))) {
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001116 printf("\n%*s\"keys\": { \"root\": \"%s\", ",
1117 indent, "",
1118 get_gbb_key_hash(gbb, gbb->rootkey_offset,
1119 gbb->rootkey_size));
1120 printf("\"recovery\": \"%s\" },",
1121 get_gbb_key_hash(gbb, gbb->recovery_key_offset,
1122 gbb->recovery_key_size));
1123 }
1124 printf("\n%*s\"image\": \"%s\" }", indent, "", fpath);
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001125 free_firmware_image(&image);
1126}
1127
1128/* Prints the information of objects in manifest (models and images) in JSON. */
1129void print_json_manifest(const struct manifest *manifest)
1130{
1131 int i, indent;
1132 struct archive *ar = manifest->archive;
1133
1134 printf("{\n");
1135 for (i = 0, indent = 2; i < manifest->num; i++) {
1136 struct model_config *m = &manifest->models[i];
1137 printf("%s%*s\"%s\": {\n", i ? ",\n" : "", indent, "", m->name);
1138 indent += 2;
Hung-Te Linc5cdf6f2018-10-09 12:00:10 +08001139 print_json_image("host", m->image, m, ar, indent, 1);
1140 print_json_image("ec", m->ec_image, m, ar, indent, 0);
1141 print_json_image("pd", m->pd_image, m, ar, indent, 0);
1142 if (m->patches.rootkey) {
1143 struct patch_config *p = &m->patches;
1144 printf(",\n%*s\"patches\": { \"rootkey\": \"%s\", "
1145 "\"vblock_a\": \"%s\", \"vblock_b\": \"%s\" }",
1146 indent, "", p->rootkey, p->vblock_a,
1147 p->vblock_b);
1148 }
Hung-Te Lin3e6397d2018-10-06 03:15:19 +08001149 if (m->signature_id)
1150 printf(",\n%*s\"signature_id\": \"%s\"", indent, "",
1151 m->signature_id);
1152 printf("\n }");
1153 indent -= 2;
1154 assert(indent == 2);
1155 }
1156 printf("\n}\n");
1157}