Joel Kitching | 9adf2aa | 2019-08-20 17:43:50 +0800 | [diff] [blame] | 1 | /* Copyright 2018 The Chromium OS Authors. All rights reserved. |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 2 | * 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 Lin | 1f2e477 | 2018-10-14 21:39:26 +0800 | [diff] [blame] | 9 | #include <ctype.h> |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 10 | #include <errno.h> |
Idwer Vollering | b384db3 | 2021-05-10 21:15:45 +0200 | [diff] [blame] | 11 | #if defined(__OpenBSD__) |
| 12 | #include <sys/types.h> |
| 13 | #endif |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 14 | #include <fts.h> |
| 15 | #include <string.h> |
| 16 | #include <stdio.h> |
| 17 | #include <stdlib.h> |
| 18 | #include <sys/stat.h> |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 19 | #include <sys/time.h> |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 20 | #include <unistd.h> |
| 21 | |
| 22 | #ifdef HAVE_LIBZIP |
Julius Werner | 927a952 | 2020-08-19 18:14:43 -0700 | [diff] [blame] | 23 | #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 Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 30 | #include <zip.h> |
| 31 | #endif |
| 32 | |
Jack Rosenthal | 2407b67 | 2022-01-05 17:40:29 -0700 | [diff] [blame] | 33 | #ifdef HAVE_CROSID |
| 34 | #include <crosid.h> |
| 35 | #endif |
| 36 | |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 37 | #include "host_misc.h" |
| 38 | #include "updater.h" |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 39 | #include "util_misc.h" |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 40 | |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 41 | /* |
| 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 Lin | 48d08d7 | 2018-11-09 09:41:03 +0800 | [diff] [blame] | 48 | * - host: 'image.bin' (or 'bios.bin' as legacy name before CL:1318712) |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 49 | * - ec: 'ec.bin' |
| 50 | * - pd: 'pd.bin' |
Hung-Te Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 51 | * If custom label is supported, a 'keyset/' folder will be available, with key |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 52 | * files in it: |
Hung-Te Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 53 | * - rootkey.$CLTAG |
| 54 | * - vblock_A.$CLTAG |
| 55 | * - vblock_B.$CLTAG |
Hung-Te Lin | 8f30263 | 2022-03-18 14:38:19 +0800 | [diff] [blame] | 56 | * The $CLTAG should come from VPD value 'custom_label_tag'. For legacy devices, |
Hung-Te Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 57 | * 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 Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 60 | * |
| 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 Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 66 | * Similar to custom label in non-Unified-Build, the keys and vblock files will |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 67 | * 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 Lin | 8f30263 | 2022-03-18 14:38:19 +0800 | [diff] [blame] | 72 | * 'custom_label_tag' as '$MODEL-$CLTAG'. |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 73 | */ |
| 74 | |
| 75 | static 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 Lin | 8f30263 | 2022-03-18 14:38:19 +0800 | [diff] [blame] | 83 | * const VPD_CUSTOM_LABEL_TAG = "custom_label_tag", |
| 84 | * const VPD_CUSTOM_LABEL_TAG_LEGACY = "whitelabel_tag", |
Hung-Te Lin | 4e06690 | 2018-10-15 15:33:47 +0800 | [diff] [blame] | 85 | * const VPD_CUSTOMIZATION_ID = "customization_id", |
Hung-Te Lin | 92fe37c | 2018-10-15 14:57:34 +0800 | [diff] [blame] | 86 | * const ENV_VAR_MODEL_DIR = "${MODEL_DIR}", |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 87 | * const PATH_STARTSWITH_KEYSET = "keyset/", |
| 88 | * const PATH_ENDSWITH_SERVARS = "/setvars.sh"; |
| 89 | |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 90 | struct 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 Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 100 | uint8_t **data, uint32_t *size, int64_t *mtime); |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 101 | int (*write_file)(void *handle, const char *fname, |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 102 | uint8_t *data, uint32_t size, int64_t mtime); |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 103 | }; |
| 104 | |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 105 | /* |
| 106 | * -- Begin of archive implementations -- |
| 107 | */ |
| 108 | |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 109 | /* Callback for archive_open on a general file system. */ |
| 110 | static 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. */ |
| 117 | static 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. */ |
| 124 | static 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 Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 146 | if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL) |
| 147 | continue; |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 148 | while (*path == '/') |
| 149 | path++; |
| 150 | if (!*path) |
| 151 | continue; |
| 152 | if (callback(path, arg)) |
| 153 | break; |
| 154 | } |
| 155 | return 0; |
| 156 | } |
| 157 | |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 158 | /* Callback for fallback drivers to get full path easily. */ |
| 159 | static 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 Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 169 | /* Callback for archive_has_entry on a general file system. */ |
| 170 | static int archive_fallback_has_entry(void *handle, const char *fname) |
| 171 | { |
| 172 | int r; |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 173 | char *temp_path = NULL; |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 174 | const char *path = archive_fallback_get_path(handle, fname, &temp_path); |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 175 | |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 176 | VB2_DEBUG("Checking %s\n", path); |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 177 | 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. */ |
| 183 | static int archive_fallback_read_file(void *handle, const char *fname, |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 184 | uint8_t **data, uint32_t *size, int64_t *mtime) |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 185 | { |
| 186 | int r; |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 187 | char *temp_path = NULL; |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 188 | const char *path = archive_fallback_get_path(handle, fname, &temp_path); |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 189 | struct stat st; |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 190 | |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 191 | VB2_DEBUG("Reading %s\n", path); |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 192 | *data = NULL; |
| 193 | *size = 0; |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 194 | r = vb2_read_file(path, data, size) != VB2_SUCCESS; |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 195 | 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 Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 201 | free(temp_path); |
| 202 | return r; |
| 203 | } |
| 204 | |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 205 | /* Callback for archive_write_file on a general file system. */ |
| 206 | static int archive_fallback_write_file(void *handle, const char *fname, |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 207 | uint8_t *data, uint32_t size, int64_t mtime) |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 208 | { |
| 209 | int r; |
| 210 | char *temp_path = NULL; |
| 211 | const char *path = archive_fallback_get_path(handle, fname, &temp_path); |
| 212 | |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 213 | VB2_DEBUG("Writing %s\n", path); |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 214 | 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 Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 227 | 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 Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 235 | free(temp_path); |
| 236 | return r; |
| 237 | } |
| 238 | |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 239 | #ifdef HAVE_LIBZIP |
| 240 | |
| 241 | /* Callback for archive_open on a ZIP file. */ |
| 242 | static 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. */ |
| 248 | static 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. */ |
| 258 | static 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. */ |
| 266 | static 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 Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 278 | 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 Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 282 | break; |
| 283 | } |
| 284 | return 0; |
| 285 | } |
| 286 | |
| 287 | /* Callback for archive_zip_read_file on a ZIP file. */ |
| 288 | static int archive_zip_read_file(void *handle, const char *fname, |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 289 | uint8_t **data, uint32_t *size, int64_t *mtime) |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 290 | { |
| 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 Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 300 | ERROR("Fail to stat entry in ZIP: %s\n", fname); |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 301 | return 1; |
| 302 | } |
| 303 | fp = zip_fopen(zip, fname, 0); |
| 304 | if (!fp) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 305 | ERROR("Failed to open entry in ZIP: %s\n", fname); |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 306 | return 1; |
| 307 | } |
| 308 | *data = (uint8_t *)malloc(stat.size); |
| 309 | if (*data) { |
| 310 | if (zip_fread(fp, *data, stat.size) == stat.size) { |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 311 | if (mtime) |
| 312 | *mtime = stat.mtime; |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 313 | *size = stat.size; |
| 314 | } else { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 315 | ERROR("Failed to read entry in zip: %s\n", fname); |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 316 | free(*data); |
| 317 | *data = NULL; |
| 318 | } |
| 319 | } |
| 320 | zip_fclose(fp); |
| 321 | return *data == NULL; |
| 322 | } |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 323 | |
| 324 | /* Callback for archive_zip_write_file on a ZIP file. */ |
| 325 | static int archive_zip_write_file(void *handle, const char *fname, |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 326 | uint8_t *data, uint32_t size, int64_t mtime) |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 327 | { |
| 328 | struct zip *zip = (struct zip *)handle; |
| 329 | struct zip_source *src; |
| 330 | |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 331 | VB2_DEBUG("Writing %s\n", fname); |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 332 | assert(zip); |
| 333 | src = zip_source_buffer(zip, data, size, 0); |
| 334 | if (!src) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 335 | ERROR("Internal error: cannot allocate buffer: %s\n", fname); |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 336 | return 1; |
| 337 | } |
| 338 | |
| 339 | if (zip_file_add(zip, fname, src, ZIP_FL_OVERWRITE) < 0) { |
| 340 | zip_source_free(src); |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 341 | ERROR("Internal error: failed to add: %s\n", fname); |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 342 | return 1; |
| 343 | } |
| 344 | /* zip_source_free is not needed if zip_file_add success. */ |
| 345 | #if LIBZIP_VERSION_MAJOR >= 1 |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 346 | zip_file_set_mtime(zip, zip_name_locate(zip, fname, 0), mtime, 0); |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 347 | #endif |
| 348 | return 0; |
| 349 | } |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 350 | #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 | */ |
| 358 | struct 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 Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 364 | ERROR("Cannot identify type of path: %s\n", path); |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 365 | return NULL; |
| 366 | } |
| 367 | |
| 368 | ar = (struct archive *)malloc(sizeof(*ar)); |
| 369 | if (!ar) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 370 | ERROR("Internal error: allocation failure.\n"); |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 371 | return NULL; |
| 372 | } |
| 373 | |
| 374 | if (S_ISDIR(path_stat.st_mode)) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 375 | VB2_DEBUG("Found directory, use fallback (fs) driver: %s\n", |
| 376 | path); |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 377 | /* 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 Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 383 | ar->write_file = archive_fallback_write_file; |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 384 | } else { |
| 385 | #ifdef HAVE_LIBZIP |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 386 | VB2_DEBUG("Found file, use ZIP driver: %s\n", path); |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 387 | 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 Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 392 | ar->write_file = archive_zip_write_file; |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 393 | #else |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 394 | ERROR("Found file, but no drivers were enabled: %s\n", path); |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 395 | free(ar); |
| 396 | return NULL; |
| 397 | #endif |
| 398 | } |
| 399 | ar->handle = ar->open(path); |
| 400 | if (!ar->handle) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 401 | ERROR("Failed to open archive: %s\n", path); |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 402 | 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 | */ |
| 412 | int 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 | */ |
| 425 | int 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 Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 433 | * Traverses all files within archive (directories are ignored). |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 434 | * 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 Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 437 | * Returns 0 on success otherwise non-zero as failure. |
| 438 | */ |
Julius Werner | 52fa8c1 | 2019-05-07 12:59:47 -0700 | [diff] [blame] | 439 | static int archive_walk(struct archive *ar, void *arg, |
| 440 | int (*callback)(const char *path, void *arg)) |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 441 | { |
| 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 | */ |
| 454 | int archive_read_file(struct archive *ar, const char *fname, |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 455 | uint8_t **data, uint32_t *size, int64_t *mtime) |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 456 | { |
| 457 | if (!ar || *fname == '/') |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 458 | return archive_fallback_read_file(NULL, fname, data, size, mtime); |
| 459 | return ar->read_file(ar->handle, fname, data, size, mtime); |
Hung-Te Lin | 7db7a6d | 2018-10-01 17:03:42 +0800 | [diff] [blame] | 460 | } |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 461 | |
| 462 | /* |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 463 | * 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 | */ |
| 468 | int archive_write_file(struct archive *ar, const char *fname, |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 469 | uint8_t *data, uint32_t size, int64_t mtime) |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 470 | { |
| 471 | if (!ar || *fname == '/') |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 472 | return archive_fallback_write_file(NULL, fname, data, size, mtime); |
| 473 | return ar->write_file(ar->handle, fname, data, size, mtime); |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 474 | } |
| 475 | |
| 476 | struct _copy_arg { |
| 477 | struct archive *from, *to; |
| 478 | }; |
| 479 | |
| 480 | /* Callback for archive_copy. */ |
| 481 | static 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 Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 486 | int64_t mtime; |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 487 | int r; |
| 488 | |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 489 | INFO("Copying: %s\n", path); |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 490 | if (archive_read_file(arg->from, path, &data, &size, &mtime)) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 491 | ERROR("Failed reading: %s\n", path); |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 492 | return 1; |
| 493 | } |
Mike Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 494 | r = archive_write_file(arg->to, path, data, size, mtime); |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 495 | VB2_DEBUG("result=%d\n", r); |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 496 | 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 | */ |
| 504 | int 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 Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 511 | * -- End of archive implementations -- |
| 512 | */ |
| 513 | |
Hung-Te Lin | 4e06690 | 2018-10-15 15:33:47 +0800 | [diff] [blame] | 514 | /* Utility function to convert a string. */ |
| 515 | static void str_convert(char *s, int (*convert)(int c)) |
Hung-Te Lin | 1f2e477 | 2018-10-14 21:39:26 +0800 | [diff] [blame] | 516 | { |
| 517 | int c; |
| 518 | |
| 519 | for (; *s; s++) { |
| 520 | c = *s; |
Hung-Te Lin | 4e06690 | 2018-10-15 15:33:47 +0800 | [diff] [blame] | 521 | if (!isascii(c)) |
Hung-Te Lin | 1f2e477 | 2018-10-14 21:39:26 +0800 | [diff] [blame] | 522 | continue; |
Hung-Te Lin | 4e06690 | 2018-10-15 15:33:47 +0800 | [diff] [blame] | 523 | *s = convert(c); |
Hung-Te Lin | 1f2e477 | 2018-10-14 21:39:26 +0800 | [diff] [blame] | 524 | } |
| 525 | } |
| 526 | |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 527 | /* Returns 1 if name ends by given pattern, otherwise 0. */ |
| 528 | static 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. */ |
| 537 | static int str_startswith(const char *name, const char *pattern) |
| 538 | { |
| 539 | return strncmp(name, pattern, strlen(pattern)) == 0; |
| 540 | } |
| 541 | |
Hung-Te Lin | 4e06690 | 2018-10-15 15:33:47 +0800 | [diff] [blame] | 542 | /* Returns the VPD value by given key name, or NULL on error (or no value). */ |
| 543 | static 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 Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 559 | /* |
| 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 | */ |
| 563 | static 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 Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 574 | if (archive_read_file(archive, fpath, &data, &len, NULL) != 0) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 575 | ERROR("Failed reading: %s\n", fpath); |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 576 | 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 Lin | 92fe37c | 2018-10-15 14:57:34 +0800 | [diff] [blame] | 585 | char *expand_path = NULL; |
| 586 | int found_valid = 1; |
| 587 | |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 588 | /* 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 Lin | 92fe37c | 2018-10-15 14:57:34 +0800 | [diff] [blame] | 596 | /* Some legacy updaters may be still using ${MODEL_DIR}. */ |
| 597 | if (str_startswith(v, ENV_VAR_MODEL_DIR)) { |
Hung-Te Lin | e1cc2b8 | 2018-10-17 12:03:24 +0800 | [diff] [blame] | 598 | ASPRINTF(&expand_path, "%s/%s%s", DIR_MODELS, cfg->name, |
Hung-Te Lin | 92fe37c | 2018-10-15 14:57:34 +0800 | [diff] [blame] | 599 | v + strlen(ENV_VAR_MODEL_DIR)); |
| 600 | } |
| 601 | |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 602 | 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 Lin | 4e06690 | 2018-10-15 15:33:47 +0800 | [diff] [blame] | 608 | else if (strcmp(k, SETVARS_SIGNATURE_ID) == 0) { |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 609 | cfg->signature_id = strdup(v); |
Hung-Te Lin | 4e06690 | 2018-10-15 15:33:47 +0800 | [diff] [blame] | 610 | if (str_startswith(v, SIG_ID_IN_VPD_PREFIX)) |
Hung-Te Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 611 | cfg->is_custom_label = 1; |
Hung-Te Lin | 4e06690 | 2018-10-15 15:33:47 +0800 | [diff] [blame] | 612 | } else |
Hung-Te Lin | 92fe37c | 2018-10-15 14:57:34 +0800 | [diff] [blame] | 613 | found_valid = 0; |
| 614 | free(expand_path); |
| 615 | valid += found_valid; |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 616 | } |
| 617 | free(data); |
| 618 | return valid == 0; |
| 619 | } |
| 620 | |
| 621 | /* |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 622 | * Changes the rootkey in firmware GBB to given new key. |
| 623 | * Returns 0 on success, otherwise failure. |
| 624 | */ |
| 625 | static 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 Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 632 | ERROR("Cannot find GBB in image %s.\n", image->file_name); |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 633 | return -1; |
| 634 | } |
| 635 | if (gbb->rootkey_size < rootkey_len) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 636 | ERROR("New root key (%u bytes) larger than GBB (%u bytes).\n", |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 637 | 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 | */ |
| 652 | static 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(§ion, image, section_name); |
| 658 | if (!section.data) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 659 | ERROR("Need section %s in image %s.\n", section_name, |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 660 | image->file_name); |
| 661 | return -1; |
| 662 | } |
| 663 | if (section.size < vblock_len) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 664 | ERROR("Section %s too small (%zu bytes) for vblock (%u bytes).\n", |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 665 | 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 | */ |
| 676 | static 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 Frysinger | c48a593 | 2019-11-13 00:07:56 -0500 | [diff] [blame] | 686 | r = archive_read_file(archive, path, &data, &len, NULL); |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 687 | if (r == 0) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 688 | VB2_DEBUG("Loaded file: %s\n", path); |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 689 | r = apply(image, section_name, data, len); |
| 690 | if (r) |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 691 | ERROR("Failed applying %s to %s\n", path, section_name); |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 692 | } else { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 693 | ERROR("Failed reading: %s\n", path); |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 694 | } |
| 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 Lin | 92fe37c | 2018-10-15 14:57:34 +0800 | [diff] [blame] | 703 | int patch_image_by_model( |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 704 | 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 | */ |
| 727 | static 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 Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 757 | * 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 | */ |
| 760 | static 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 Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 769 | ERROR("Internal error: failed to allocate buffer.\n"); |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 770 | 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 | */ |
| 781 | static 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 Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 799 | VB2_DEBUG("Found model <%s> setvars: %s\n", model.name, name); |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 800 | if (model_config_parse_setvars_file(&model, archive, name)) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 801 | ERROR("Invalid setvars file: %s\n", name); |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 802 | 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 Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 807 | VB2_DEBUG("Ignore non-exist EC image: %s\n", model.ec_image); |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 808 | free(model.ec_image); |
| 809 | model.ec_image = NULL; |
| 810 | } |
| 811 | if (model.pd_image && !archive_has_entry(archive, model.pd_image)) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 812 | VB2_DEBUG("Ignore non-exist PD image: %s\n", model.pd_image); |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 813 | free(model.pd_image); |
| 814 | model.pd_image = NULL; |
| 815 | } |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 816 | |
| 817 | /* Find patch files. */ |
| 818 | if (model.signature_id) |
| 819 | find_patches_for_model(&model, archive, model.signature_id); |
| 820 | |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 821 | return !manifest_add_model(manifest, &model); |
| 822 | } |
| 823 | |
Jack Rosenthal | 2407b67 | 2022-01-05 17:40:29 -0700 | [diff] [blame] | 824 | /** |
| 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 | */ |
| 834 | static 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 Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 848 | /* |
Hung-Te Lin | 92fe37c | 2018-10-15 14:57:34 +0800 | [diff] [blame] | 849 | * 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 | */ |
| 853 | const struct model_config *manifest_find_model(const struct manifest *manifest, |
| 854 | const char *model_name) |
| 855 | { |
Jack Rosenthal | 2407b67 | 2022-01-05 17:40:29 -0700 | [diff] [blame] | 856 | char *manifest_key = NULL; |
Hung-Te Lin | 92fe37c | 2018-10-15 14:57:34 +0800 | [diff] [blame] | 857 | const struct model_config *model = NULL; |
| 858 | int i; |
Jack Rosenthal | 2407b67 | 2022-01-05 17:40:29 -0700 | [diff] [blame] | 859 | int matched_index; |
Hung-Te Lin | 92fe37c | 2018-10-15 14:57:34 +0800 | [diff] [blame] | 860 | |
Hung-Te Lin | 3f02fc1 | 2019-03-11 12:14:22 +0800 | [diff] [blame] | 861 | /* |
| 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 Lin | 92fe37c | 2018-10-15 14:57:34 +0800 | [diff] [blame] | 868 | |
Hung-Te Lin | 3f02fc1 | 2019-03-11 12:14:22 +0800 | [diff] [blame] | 869 | if (!model_name) { |
Jack Rosenthal | 2407b67 | 2022-01-05 17:40:29 -0700 | [diff] [blame] | 870 | 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 Lin | f1144f4 | 2022-02-14 19:02:24 +0800 | [diff] [blame] | 877 | INFO("Identified the device using libcrosid, " |
| 878 | "matched chromeos-config index: %d, " |
| 879 | "manifest key (model): %s\n", |
| 880 | matched_index, manifest_key); |
Jack Rosenthal | 2407b67 | 2022-01-05 17:40:29 -0700 | [diff] [blame] | 881 | model_name = manifest_key; |
Hung-Te Lin | 92fe37c | 2018-10-15 14:57:34 +0800 | [diff] [blame] | 882 | } |
| 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 Lin | ba37ad2 | 2018-11-27 10:16:36 +0800 | [diff] [blame] | 888 | if (!model) { |
Jack Rosenthal | 2407b67 | 2022-01-05 17:40:29 -0700 | [diff] [blame] | 889 | ERROR("Unsupported model: '%s'.\n", model_name); |
Hung-Te Lin | ba37ad2 | 2018-11-27 10:16:36 +0800 | [diff] [blame] | 890 | |
| 891 | fprintf(stderr, |
Jack Rosenthal | 2407b67 | 2022-01-05 17:40:29 -0700 | [diff] [blame] | 892 | "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 Lin | ba37ad2 | 2018-11-27 10:16:36 +0800 | [diff] [blame] | 895 | |
| 896 | for (i = 0; i < manifest->num; i++) |
| 897 | fprintf(stderr, " %s", manifest->models[i].name); |
Jack Rosenthal | 2407b67 | 2022-01-05 17:40:29 -0700 | [diff] [blame] | 898 | 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 Lin | ba37ad2 | 2018-11-27 10:16:36 +0800 | [diff] [blame] | 906 | } |
| 907 | |
Hung-Te Lin | 92fe37c | 2018-10-15 14:57:34 +0800 | [diff] [blame] | 908 | |
Jack Rosenthal | 2407b67 | 2022-01-05 17:40:29 -0700 | [diff] [blame] | 909 | free(manifest_key); |
Hung-Te Lin | 92fe37c | 2018-10-15 14:57:34 +0800 | [diff] [blame] | 910 | return model; |
| 911 | } |
| 912 | |
| 913 | /* |
Hung-Te Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 914 | * Determines the signature ID to use for custom label. |
Hung-Te Lin | 9c06413 | 2019-03-05 08:24:47 +0800 | [diff] [blame] | 915 | * Returns the signature ID for looking up rootkey and vblock files. |
| 916 | * Caller must free the returned string. |
| 917 | */ |
| 918 | static char *resolve_signature_id(struct model_config *model, const char *image) |
| 919 | { |
| 920 | int is_unibuild = model->signature_id ? 1 : 0; |
Hung-Te Lin | 8f30263 | 2022-03-18 14:38:19 +0800 | [diff] [blame] | 921 | char *tag = vpd_get_value(image, VPD_CUSTOM_LABEL_TAG); |
Hung-Te Lin | 9c06413 | 2019-03-05 08:24:47 +0800 | [diff] [blame] | 922 | char *sig_id = NULL; |
| 923 | |
Hung-Te Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 924 | if (tag == NULL) |
Hung-Te Lin | 8f30263 | 2022-03-18 14:38:19 +0800 | [diff] [blame] | 925 | tag = vpd_get_value(image, VPD_CUSTOM_LABEL_TAG_LEGACY); |
Hung-Te Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 926 | |
| 927 | /* Unified build: $model.$tag, or $model (b/126800200). */ |
Hung-Te Lin | 9c06413 | 2019-03-05 08:24:47 +0800 | [diff] [blame] | 928 | if (is_unibuild) { |
Hung-Te Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 929 | if (!tag) { |
| 930 | WARN("No VPD '%s' set for custom label. " |
| 931 | "Use model name '%s' as default.\n", |
Hung-Te Lin | 8f30263 | 2022-03-18 14:38:19 +0800 | [diff] [blame] | 932 | VPD_CUSTOM_LABEL_TAG, model->name); |
Hung-Te Lin | 9c06413 | 2019-03-05 08:24:47 +0800 | [diff] [blame] | 933 | return strdup(model->name); |
| 934 | } |
| 935 | |
Hung-Te Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 936 | ASPRINTF(&sig_id, "%s-%s", model->name, tag); |
| 937 | free(tag); |
Hung-Te Lin | 9c06413 | 2019-03-05 08:24:47 +0800 | [diff] [blame] | 938 | return sig_id; |
| 939 | } |
| 940 | |
Hung-Te Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 941 | /* Non-Unibuild: Upper($tag), or Upper(${cid%%-*}). */ |
| 942 | if (!tag) { |
Hung-Te Lin | 9c06413 | 2019-03-05 08:24:47 +0800 | [diff] [blame] | 943 | 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 Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 949 | tag = cid; |
Hung-Te Lin | 9c06413 | 2019-03-05 08:24:47 +0800 | [diff] [blame] | 950 | } |
| 951 | } |
Hung-Te Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 952 | if (tag) |
| 953 | str_convert(tag, toupper); |
| 954 | return tag; |
Hung-Te Lin | 9c06413 | 2019-03-05 08:24:47 +0800 | [diff] [blame] | 955 | } |
| 956 | |
| 957 | /* |
Hung-Te Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 958 | * Applies custom label information to an existing model configuration. |
Hung-Te Lin | 4e06690 | 2018-10-15 15:33:47 +0800 | [diff] [blame] | 959 | * 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 Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 963 | int model_apply_custom_label( |
Hung-Te Lin | 4e06690 | 2018-10-15 15:33:47 +0800 | [diff] [blame] | 964 | 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 Lin | 9c06413 | 2019-03-05 08:24:47 +0800 | [diff] [blame] | 973 | sig_id = resolve_signature_id(model, image); |
Hung-Te Lin | 4e06690 | 2018-10-15 15:33:47 +0800 | [diff] [blame] | 974 | signature_id = sig_id; |
| 975 | } |
| 976 | |
Hung-Te Lin | 9c06413 | 2019-03-05 08:24:47 +0800 | [diff] [blame] | 977 | if (signature_id) { |
Hung-Te Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 978 | VB2_DEBUG("Find custom label patches by signature ID: '%s'.\n", |
Hung-Te Lin | 9c06413 | 2019-03-05 08:24:47 +0800 | [diff] [blame] | 979 | signature_id); |
| 980 | find_patches_for_model(model, archive, signature_id); |
| 981 | } else { |
| 982 | signature_id = ""; |
Hung-Te Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 983 | WARN("No VPD '%s' set for custom label - use default keys.\n", |
Hung-Te Lin | 8f30263 | 2022-03-18 14:38:19 +0800 | [diff] [blame] | 984 | VPD_CUSTOM_LABEL_TAG); |
Hung-Te Lin | 9c06413 | 2019-03-05 08:24:47 +0800 | [diff] [blame] | 985 | } |
Hung-Te Lin | 4e06690 | 2018-10-15 15:33:47 +0800 | [diff] [blame] | 986 | if (!model->patches.rootkey) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 987 | ERROR("No keys found for signature_id: '%s'\n", signature_id); |
Hung-Te Lin | 4e06690 | 2018-10-15 15:33:47 +0800 | [diff] [blame] | 988 | r = 1; |
| 989 | } else { |
Hung-Te Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 990 | INFO("Applied for custom label: %s\n", signature_id); |
Hung-Te Lin | 4e06690 | 2018-10-15 15:33:47 +0800 | [diff] [blame] | 991 | } |
| 992 | free(sig_id); |
| 993 | return r; |
| 994 | } |
| 995 | |
| 996 | /* |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 997 | * Creates a new manifest object by scanning files in archive. |
| 998 | * Returns the manifest on success, otherwise NULL for failure. |
| 999 | */ |
| 1000 | struct manifest *new_manifest_from_archive(struct archive *archive) |
| 1001 | { |
| 1002 | struct manifest manifest = {0}, *new_manifest; |
| 1003 | struct model_config model = {0}; |
Hung-Te Lin | 48d08d7 | 2018-11-09 09:41:03 +0800 | [diff] [blame] | 1004 | const char * const host_image_name = "image.bin", |
| 1005 | * const old_host_image_name = "bios.bin", |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 1006 | * 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 Lin | 48d08d7 | 2018-11-09 09:41:03 +0800 | [diff] [blame] | 1013 | const char *image_name = NULL; |
Hung-Te Lin | 1f2e477 | 2018-10-14 21:39:26 +0800 | [diff] [blame] | 1014 | struct firmware_image image = {0}; |
Hung-Te Lin | 48d08d7 | 2018-11-09 09:41:03 +0800 | [diff] [blame] | 1015 | |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 1016 | /* Try to load from current folder. */ |
Hung-Te Lin | 48d08d7 | 2018-11-09 09:41:03 +0800 | [diff] [blame] | 1017 | 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 Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 1022 | return 0; |
Hung-Te Lin | 48d08d7 | 2018-11-09 09:41:03 +0800 | [diff] [blame] | 1023 | |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 1024 | 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 Lin | 1f2e477 | 2018-10-14 21:39:26 +0800 | [diff] [blame] | 1029 | /* 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 Lin | 4e06690 | 2018-10-15 15:33:47 +0800 | [diff] [blame] | 1035 | str_convert(token, tolower); |
Hung-Te Lin | 1f2e477 | 2018-10-14 21:39:26 +0800 | [diff] [blame] | 1036 | model.name = strdup(token); |
| 1037 | } |
| 1038 | free_firmware_image(&image); |
| 1039 | } |
| 1040 | if (!model.name) |
| 1041 | model.name = strdup(DEFAULT_MODEL_NAME); |
Hung-Te Lin | 4e06690 | 2018-10-15 15:33:47 +0800 | [diff] [blame] | 1042 | if (manifest.has_keyset) |
Hung-Te Lin | 41cc9ec | 2022-03-07 09:39:37 +0800 | [diff] [blame] | 1043 | model.is_custom_label = 1; |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 1044 | manifest_add_model(&manifest, &model); |
| 1045 | manifest.default_model = manifest.num - 1; |
| 1046 | } |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 1047 | VB2_DEBUG("%d model(s) loaded.\n", manifest.num); |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 1048 | if (!manifest.num) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 1049 | ERROR("No valid configurations found from archive.\n"); |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 1050 | return NULL; |
| 1051 | } |
| 1052 | |
| 1053 | new_manifest = (struct manifest *)malloc(sizeof(manifest)); |
| 1054 | if (!new_manifest) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 1055 | ERROR("Internal error: memory allocation error.\n"); |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 1056 | 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. */ |
| 1063 | void 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 Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 1074 | free(model->patches.rootkey); |
| 1075 | free(model->patches.vblock_a); |
| 1076 | free(model->patches.vblock_b); |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 1077 | } |
| 1078 | free(manifest->models); |
| 1079 | free(manifest); |
| 1080 | } |
| 1081 | |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 1082 | static 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 Kitching | 582453d | 2019-10-07 15:28:43 +0800 | [diff] [blame] | 1090 | if (vb2_packed_key_looks_ok(key, size)) |
| 1091 | return "<Invalid key>"; |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 1092 | return packed_key_sha1_string(key); |
| 1093 | } |
| 1094 | |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 1095 | /* Prints the information of given image file in JSON format. */ |
| 1096 | static void print_json_image( |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 1097 | const char *name, const char *fpath, struct model_config *m, |
| 1098 | struct archive *archive, int indent, int is_host) |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 1099 | { |
| 1100 | struct firmware_image image = {0}; |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 1101 | const struct vb2_gbb_header *gbb = NULL; |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 1102 | if (!fpath) |
| 1103 | return; |
Jacob Garber | 26c3f19 | 2019-08-08 16:49:14 -0600 | [diff] [blame] | 1104 | if (load_firmware_image(&image, fpath, archive)) |
| 1105 | return; |
Isaac Lee | 0bffea9 | 2021-06-01 15:47:34 +0800 | [diff] [blame] | 1106 | if (!is_host) |
Hung-Te Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 1107 | printf(",\n"); |
| 1108 | printf("%*s\"%s\": { \"versions\": { \"ro\": \"%s\", \"rw\": \"%s\" },", |
| 1109 | indent, "", name, image.ro_version, image.rw_version_a); |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 1110 | indent += 2; |
Isaac Lee | 0bffea9 | 2021-06-01 15:47:34 +0800 | [diff] [blame] | 1111 | if (!is_host) { |
| 1112 | /* No extra information to be printed */ |
| 1113 | } else if (patch_image_by_model(&image, m, archive) != 0) { |
Julius Werner | 88a47ff | 2019-05-08 13:33:20 -0700 | [diff] [blame] | 1114 | ERROR("Failed to patch images by model: %s\n", m->name); |
Isaac Lee | 0bffea9 | 2021-06-01 15:47:34 +0800 | [diff] [blame] | 1115 | } else if (NULL != (gbb = find_gbb(&image))) { |
Hung-Te Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 1116 | 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 Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 1125 | free_firmware_image(&image); |
| 1126 | } |
| 1127 | |
| 1128 | /* Prints the information of objects in manifest (models and images) in JSON. */ |
| 1129 | void 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 Lin | c5cdf6f | 2018-10-09 12:00:10 +0800 | [diff] [blame] | 1139 | 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 Lin | 3e6397d | 2018-10-06 03:15:19 +0800 | [diff] [blame] | 1149 | 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 | } |