diff options
-rw-r--r-- | futility/cmd_update.c | 11 | ||||
-rw-r--r-- | futility/updater.c | 91 | ||||
-rw-r--r-- | futility/updater.h | 23 | ||||
-rw-r--r-- | futility/updater_archive.c | 50 | ||||
-rwxr-xr-x | tests/futility/models/link/setvars.sh | 14 | ||||
-rwxr-xr-x | tests/futility/models/peppy/setvars.sh | 14 | ||||
-rwxr-xr-x | tests/futility/test_update.sh | 38 |
7 files changed, 206 insertions, 35 deletions
diff --git a/futility/cmd_update.c b/futility/cmd_update.c index f382b9ca..ce81660d 100644 --- a/futility/cmd_update.c +++ b/futility/cmd_update.c @@ -26,6 +26,7 @@ static struct option const long_opts[] = { {"quirks", 1, NULL, 'f'}, {"list-quirks", 0, NULL, 'L'}, {"mode", 1, NULL, 'm'}, + {"model", 1, NULL, 'M'}, {"manifest", 0, NULL, 'A'}, {"factory", 0, NULL, 'Y'}, {"force", 0, NULL, 'F'}, @@ -64,6 +65,7 @@ static void print_help(int argc, char *argv[]) "Debugging and testing options:\n" " --wp=1|0 \tSpecify write protection status\n" " --emulate=FILE \tEmulate system firmware using file\n" + " --model=MODEL \tOverride system model for images\n" " --sys_props=LIST\tList of system properties to override\n" "-d, --debug \tPrint debugging messages\n" "-v, --verbose \tPrint verbose messages\n" @@ -75,7 +77,7 @@ static int do_update(int argc, char *argv[]) { struct updater_config *cfg; struct updater_config_arguments args = {0}; - int i, errorcnt = 0; + int i, errorcnt = 0, do_update = 1; fprintf(stderr, ">> Firmware updater started.\n"); cfg = updater_new_config(); @@ -108,6 +110,9 @@ static int do_update(int argc, char *argv[]) case 'm': args.mode = optarg; break; + case 'M': + args.model = optarg; + break; case 'A': args.do_manifest = 1; break; @@ -160,8 +165,8 @@ static int do_update(int argc, char *argv[]) Error("Unexpected arguments.\n"); } if (!errorcnt) - errorcnt += updater_setup_config(cfg, &args); - if (!errorcnt && !args.do_manifest) { + errorcnt += updater_setup_config(cfg, &args, &do_update); + if (!errorcnt && do_update) { int r = update_firmware(cfg); if (r != UPDATE_ERR_DONE) { r = Min(r, UPDATE_ERR_UNKNOWN); diff --git a/futility/updater.c b/futility/updater.c index 5cd29f4a..17b12a2b 100644 --- a/futility/updater.c +++ b/futility/updater.c @@ -630,7 +630,8 @@ int load_firmware_image(struct firmware_image *image, const char *file_name, * Loads the active system firmware image (usually from SPI flash chip). * Returns 0 if success, non-zero if error. */ -int load_system_firmware(struct updater_config *cfg, struct firmware_image *image) +int load_system_firmware(struct updater_config *cfg, + struct firmware_image *image) { const char *tmp_file = updater_create_temp_file(cfg); @@ -1667,17 +1668,47 @@ static int updater_load_images(struct updater_config *cfg, } /* + * Setup what the updater has to do against an archive. + * Returns number of failures, or 0 on success. + */ +static int updater_setup_archive( + struct updater_config *cfg, + const struct updater_config_arguments *arg, + struct manifest *manifest) +{ + int errorcnt = 0; + struct archive *ar = cfg->archive; + const struct model_config *model; + + if (arg->do_manifest) { + assert(!arg->image); + print_json_manifest(manifest); + /* No additional error. */ + return errorcnt; + } + + model = manifest_find_model(manifest, arg->model); + if (!model) + return ++errorcnt; + + errorcnt += updater_load_images( + cfg, model->image, model->ec_image, model->pd_image); + errorcnt += patch_image_by_model(&cfg->image, model, ar); + return errorcnt; +} + +/* * Helper function to setup an allocated updater_config object. * Returns number of failures, or 0 on success. */ int updater_setup_config(struct updater_config *cfg, - const struct updater_config_arguments *arg) + const struct updater_config_arguments *arg, + int *do_update) { int errorcnt = 0; int check_single_image = 0, check_wp_disabled = 0; const char *default_quirks = NULL; const char *archive_path = arg->archive; - struct manifest *manifest = NULL; /* Setup values that may change output or decision of other argument. */ cfg->verbosity = arg->verbosity; @@ -1686,9 +1717,17 @@ int updater_setup_config(struct updater_config *cfg, cfg->force_update = 1; /* Check incompatible options and return early. */ - if (arg->do_manifest && !arg->archive) { - ERROR("Manifest is only available for archive."); - return ++errorcnt; + if (arg->do_manifest) { + if (!!arg->archive == !!arg->image) { + ERROR("--manifest needs either -a or -i"); + return ++errorcnt; + } + if (arg->archive && (arg->ec_image || arg->pd_image)) { + ERROR("--manifest for archive (-a) does not accept " + "additional images (--ec_image, --pd_image)."); + return ++errorcnt; + } + *do_update = 0; } /* Setup update mode. */ @@ -1740,6 +1779,11 @@ int updater_setup_config(struct updater_config *cfg, errorcnt += !!load_firmware_image( &cfg->image_current, arg->emulation, NULL); } + + /* Always load images specified from command line directly. */ + errorcnt += updater_load_images( + cfg, arg->image, arg->ec_image, arg->pd_image); + if (!archive_path) archive_path = "."; cfg->archive = archive_open(archive_path); @@ -1748,17 +1792,30 @@ int updater_setup_config(struct updater_config *cfg, return ++errorcnt; } - errorcnt += updater_load_images( - cfg, arg->image, arg->ec_image, arg->pd_image); - - if (arg->do_manifest) { - manifest = new_manifest_from_archive(cfg->archive); - if (!manifest) { - ERROR("Failure in archive: %s", archive_path); - return ++errorcnt; + /* Load images from archive. */ + if (arg->archive) { + struct manifest *m = new_manifest_from_archive(cfg->archive); + if (m) { + errorcnt += updater_setup_archive(cfg, arg, m); + delete_manifest(m); + } else { + ERROR("Failure in archive: %s", arg->archive); + ++errorcnt; } - print_json_manifest(manifest); - return errorcnt; + } else if (arg->do_manifest) { + char name[] = "default"; + struct model_config model = { + .name = name, + .image = arg->image, + .ec_image = arg->ec_image, + .pd_image = arg->pd_image, + }; + struct manifest manifest = { + .num = 1, + .models = &model, + }; + assert(model.image); + print_json_manifest(&manifest); } /* @@ -1781,8 +1838,6 @@ int updater_setup_config(struct updater_config *cfg, errorcnt++; ERROR("Factory mode needs WP disabled."); } - if (manifest) - delete_manifest(manifest); return errorcnt; } diff --git a/futility/updater.h b/futility/updater.h index 21bdbdb5..c1405663 100644 --- a/futility/updater.h +++ b/futility/updater.h @@ -135,10 +135,10 @@ struct model_config { struct manifest { int num; - int default_model; - int has_keyset; struct model_config *models; struct archive *archive; + int default_model; + int has_keyset; }; enum updater_error_codes { @@ -183,7 +183,8 @@ void updater_delete_config(struct updater_config *cfg); * Returns number of failures, or 0 on success. */ int updater_setup_config(struct updater_config *cfg, - const struct updater_config_arguments *arg); + const struct updater_config_arguments *arg, + int *do_update); /* Prints the name and description from all supported quirks. */ void updater_list_config_quirks(const struct updater_config *cfg); @@ -307,4 +308,20 @@ void delete_manifest(struct manifest *manifest); /* Prints the information of objects in manifest (models and images) in JSON. */ void print_json_manifest(const struct manifest *manifest); +/* + * Modifies a firmware image from patch information specified in model config. + * Returns 0 on success, otherwise number of failures. + */ +int patch_image_by_model( + struct firmware_image *image, const struct model_config *model, + struct archive *archive); + +/* + * Finds the existing model_config from manifest that best matches current + * system (as defined by model_name). + * Returns a model_config from manifest, or NULL if not found. + */ +const struct model_config *manifest_find_model(const struct manifest *manifest, + const char *model_name); + #endif /* VBOOT_REFERENCE_FUTILITY_UPDATER_H_ */ diff --git a/futility/updater_archive.c b/futility/updater_archive.c index 86d70e75..e08f736a 100644 --- a/futility/updater_archive.c +++ b/futility/updater_archive.c @@ -65,6 +65,7 @@ static const char * const SETVARS_IMAGE_MAIN = "IMAGE_MAIN", * const DIR_KEYSET = "keyset", * const DIR_MODELS = "models", * const DEFAULT_MODEL_NAME = "default", + * const ENV_VAR_MODEL_DIR = "${MODEL_DIR}", * const PATH_STARTSWITH_KEYSET = "keyset/", * const PATH_ENDSWITH_SERVARS = "/setvars.sh"; @@ -418,6 +419,9 @@ static int model_config_parse_setvars_file( for (line = strtok_r((char *)data, "\n\r", &ptr_line); line; line = strtok_r(NULL, "\n\r", &ptr_line)) { + char *expand_path = NULL; + int found_valid = 1; + /* Format: KEY="value" */ k = strtok_r(line, "=", &ptr_token); if (!k) @@ -426,6 +430,12 @@ static int model_config_parse_setvars_file( if (!v) continue; + /* Some legacy updaters may be still using ${MODEL_DIR}. */ + if (str_startswith(v, ENV_VAR_MODEL_DIR)) { + ASPRINTF(&expand_path, "%s%s%s", "models/", cfg->name, + v + strlen(ENV_VAR_MODEL_DIR)); + } + if (strcmp(k, SETVARS_IMAGE_MAIN) == 0) cfg->image = strdup(v); else if (strcmp(k, SETVARS_IMAGE_EC) == 0) @@ -435,8 +445,9 @@ static int model_config_parse_setvars_file( else if (strcmp(k, SETVARS_SIGNATURE_ID) == 0) cfg->signature_id = strdup(v); else - continue; - valid++; + found_valid = 0; + free(expand_path); + valid += found_valid; } free(data); return valid == 0; @@ -524,7 +535,7 @@ static int apply_key_file( * Modifies a firmware image from patch information specified in model config. * Returns 0 on success, otherwise number of failures. */ -static int patch_image_by_model( +int patch_image_by_model( struct firmware_image *image, const struct model_config *model, struct archive *archive) { @@ -646,6 +657,39 @@ static int manifest_scan_entries(const char *name, void *arg) } /* + * Finds the existing model_config from manifest that best matches current + * system (as defined by model_name). + * Returns a model_config from manifest, or NULL if not found. + */ +const struct model_config *manifest_find_model(const struct manifest *manifest, + const char *model_name) +{ + char *sys_model_name = NULL; + const struct model_config *model = NULL; + int i; + + /* Match if the manifest has only one package without signature. */ + if (manifest->num == 1 && !manifest->models[0].signature_id) + model = &manifest->models[0]; + + if (!model && !model_name) { + sys_model_name = host_shell("mosys platform name"); + DEBUG("System model name: '%s'", sys_model_name); + model_name = sys_model_name; + } + + for (i = 0; !model && i < manifest->num; i++) { + if (strcmp(model_name, manifest->models[i].name) == 0) + model = &manifest->models[i]; + } + if (!model) + ERROR("Model '%s' is not defined in manifest.", model_name); + + free(sys_model_name); + return model; +} + +/* * Creates a new manifest object by scanning files in archive. * Returns the manifest on success, otherwise NULL for failure. */ diff --git a/tests/futility/models/link/setvars.sh b/tests/futility/models/link/setvars.sh new file mode 100755 index 00000000..19180998 --- /dev/null +++ b/tests/futility/models/link/setvars.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# Copyright 2017 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This is a template file which provides settings for firmware update of a +# particular model. The pack_firmware.py script uses this to create a working +# setvars-model.sh script. + +# Image and key files for model link +IMAGE_MAIN="images/bios_link.bin" +IMAGE_EC="images/ec_link.bin" +IMAGE_PD="" +SIGNATURE_ID="link" diff --git a/tests/futility/models/peppy/setvars.sh b/tests/futility/models/peppy/setvars.sh new file mode 100755 index 00000000..855e815a --- /dev/null +++ b/tests/futility/models/peppy/setvars.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# Copyright 2017 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This is a template file which provides settings for firmware update of a +# particular model. The pack_firmware.py script uses this to create a working +# setvars-model.sh script. + +# Image and key files for model peppy +IMAGE_MAIN="images/bios_peppy.bin" +IMAGE_EC="images/ec_peppy.bin" +IMAGE_PD="" +SIGNATURE_ID="peppy" diff --git a/tests/futility/test_update.sh b/tests/futility/test_update.sh index 6bb627c8..32fe921e 100755 --- a/tests/futility/test_update.sh +++ b/tests/futility/test_update.sh @@ -294,17 +294,39 @@ test_update "Full update (--quirks min_platform_version)" \ --quirks min_platform_version=3 \ -i "${TO_IMAGE}" --wp=0 --sys_props 0,0x10001,1,3 -mkdir -p "${TMP}.archive" -cp -f "${LINK_BIOS}" "${TMP}.archive/bios.bin" -cp -f "${TO_IMAGE}" "${TMP}.archive/image_in_archive" -test_update "Full update (--archive)" \ - "${FROM_IMAGE}" "${TMP}.expected.full" \ - -a "${TMP}.archive" \ - -i "image_in_archive" --wp=0 --sys_props 0,0x10001,1,3 +# Test archive and manifest. +A="${TMP}.archive" +mkdir -p "${A}" +cp -f "${LINK_BIOS}" "${A}/bios.bin" echo "TEST: Manifest (--manifest)" -${FUTILITY} update -a "${TMP}.archive" --manifest >"${TMP}.json.out" +${FUTILITY} update -a "${A}" --manifest >"${TMP}.json.out" cmp "${TMP}.json.out" "${SCRIPTDIR}/link.manifest.json" +cp -f "${TO_IMAGE}" "${A}/bios.bin" +test_update "Full update (--archive, single package)" \ + "${FROM_IMAGE}" "${TMP}.expected.full" \ + -a "${A}" --wp=0 --sys_props 0,0x10001,1,3 + +rm -f "${A}/bios.bin" +cp -r "${SCRIPTDIR}/models" "${A}/" +mkdir -p "${A}/images" +cp -f "${PEPPY_BIOS}" "${A}/images/bios_peppy.bin" +cp -f "${LINK_BIOS}" "${A}/images/bios_link.bin" + +cp -f "${PEPPY_BIOS}" "${FROM_IMAGE}.ap" +cp -f "${LINK_BIOS}" "${FROM_IMAGE}.al" +patch_file ${FROM_IMAGE}.ap FW_MAIN_A 0 "corrupted" +patch_file ${FROM_IMAGE}.al FW_MAIN_A 0 "corrupted" +test_update "Full update (--archive, model=link)" \ + "${FROM_IMAGE}.al" "${LINK_BIOS}" \ + -a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --model=link +test_update "Full update (--archive, model=peppy)" \ + "${FROM_IMAGE}.ap" "${PEPPY_BIOS}" \ + -a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --model=peppy +test_update "Full update (--archive, model=unknown)" \ + "${FROM_IMAGE}.ap" "!Model 'unknown' is not defined" \ + -a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --model=unknown + # Test special programmer if type flashrom >/dev/null 2>&1; then echo "TEST: Full update (dummy programmer)" |