From 2f21f0f1928aa9e92fc9e69900eafaa9933f836c Mon Sep 17 00:00:00 2001 From: Hung-Te Lin Date: Thu, 28 Apr 2022 17:29:22 +0800 Subject: futility: updater: create a manifest from the signer config The signer_config.csv contains almost everything we need for mapping from models to image files so that can be an alternative when models/*/setvars.sh files are not available. This can be shared by /firmware in future. Verified by comparing the manifest.json from existing firmware updaters on the following boards: volteer (share target), sarien (no ec), octopus (custom-label), and hatch (share target). for BOARD in volteer sarien octopus hatch; do /build/$BOARD/usr/sbin/chromeos-firmwareupdate --unpack test/$BOARD jq -S . old rm -rf test/$BOARD/models futility update --manifest -a test/$BOARD | jq -S . >new diff new old >diff.$BOARD done # all diff.$BOARD are empty, e.g., no difference. BUG=b:230679721 TEST=make; run test BRANCH=None Change-Id: I17393c941d2577a2b1c5f00f60b25e98fa3f3a6f Signed-off-by: Hung-Te Lin Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/3612646 Reviewed-by: Julius Werner Commit-Queue: Yu-Ping Wu --- futility/updater_archive.c | 134 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 128 insertions(+), 6 deletions(-) diff --git a/futility/updater_archive.c b/futility/updater_archive.c index 58774fc4..4ed12aab 100644 --- a/futility/updater_archive.c +++ b/futility/updater_archive.c @@ -85,6 +85,7 @@ static const char * const SETVARS_IMAGE_MAIN = "IMAGE_MAIN", * const VPD_CUSTOMIZATION_ID = "customization_id", * const ENV_VAR_MODEL_DIR = "${MODEL_DIR}", * const PATH_STARTSWITH_KEYSET = "keyset/", + * const PATH_SIGNER_CONFIG = "signer_config.csv", * const PATH_ENDSWITH_SETVARS = "/setvars.sh"; struct archive { @@ -571,7 +572,7 @@ static int model_config_parse_setvars_file( uint8_t *data; uint32_t len; - char *ptr_line, *ptr_token; + char *ptr_line = NULL, *ptr_token = NULL; char *line, *k, *v; int valid = 0; @@ -867,6 +868,118 @@ static int manifest_scan_raw_entries(const char *name, void *arg) return !manifest_add_model(manifest, &model); } +/* Returns the matched model config from the manifest, or NULL if not found. */ +static struct model_config *manifest_get_model_config( + const struct manifest *manifest, const char *name) +{ + int i = 0; + + for (i = 0; i < manifest->num; i++) { + if (!strcmp(name, manifest->models[i].name)) + return &manifest->models[i]; + } + return NULL; +} + +/* + * Creates the manifest from the 'signer_config.csv' file. + * Returns 0 on success (loaded), otherwise failure. + */ +static int manifest_from_signer_config(struct manifest *manifest) +{ + struct archive *archive = manifest->archive; + uint32_t size; + uint8_t *data; + char *s, *tok_ptr = NULL; + + if (!archive_has_entry(archive, PATH_SIGNER_CONFIG)) + return -1; + + /* + * CSV format: model_name,firmware_image,key_id,ec_image + * + * Note the key_id is not signature_id and won't be used, and ec_image + * may be optional (for example sarien). + */ + + if (archive_read_file(archive, PATH_SIGNER_CONFIG, &data, &size,NULL)) { + ERROR("Failed reading: %s\n", PATH_SIGNER_CONFIG); + return -1; + } + + /* Skip headers. */ + s = strtok_r((char *)data, "\n", &tok_ptr); + if (!s || !strchr(s, ',')) { + ERROR("Invalid %s: missing header.\n", PATH_SIGNER_CONFIG); + free(data); + return -1; + } + + for (s = strtok_r(NULL, "\n", &tok_ptr); s != NULL; + s = strtok_r(NULL, "\n", &tok_ptr)) { + + struct model_config model = {0}; + int discard_model = 0; + + /* + * Both keyid (%3) and ec_image (%4) are optional so we want to + * read at least 2 fields. + */ + if (sscanf(s, "%m[^,],%m[^,],%*[^,],%m[^,]", + &model.name, &model.image, &model.ec_image) < 2) { + ERROR("Invalid entry(%s): %s\n", PATH_SIGNER_CONFIG, s); + discard_model = 1; + } else if (strchr(model.name, '-')) { + /* format: BaseModel-CustomLabel */ + char *tok_dash; + char *base_model; + struct model_config *base_model_config; + + VB2_DEBUG("Found custom-label: %s\n", model.name); + discard_model = 1; + base_model = strtok_r(model.name, "-", &tok_dash); + assert(base_model); + + /* + * Currently we assume the base model (e.g., base_model) + * is always listed before CL models in the CSV file - + * this is based on how the signerbot and the + * chromeos-config works today (validated on octopus). + */ + base_model_config = manifest_get_model_config( + manifest, base_model); + + if (!base_model_config) { + ERROR("Invalid CL-model: %s\n", base_model); + } else if (!base_model_config->is_custom_label) { + base_model_config->is_custom_label = 1; + /* + * Rewriting signature_id is not necessary, + * but in order to generate the same manifest + * from setvars, we want to temporarily use + * the special value. + */ + free(base_model_config->signature_id); + base_model_config->signature_id = strdup( + "sig-id-in-customization-id"); + } + } + + if (discard_model) { + free(model.name); + free(model.image); + free(model.ec_image); + continue; + } + + model.signature_id = strdup(model.name); + if (!manifest_add_model(manifest, &model)) + break; + } + free(data); + return 0; +} + /** * get_manifest_key() - Wrapper to get the firmware manifest key from crosid * @@ -927,10 +1040,8 @@ const struct model_config *manifest_find_model(const struct manifest *manifest, model_name = manifest_key; } - for (i = 0; !model && i < manifest->num; i++) { - if (strcmp(model_name, manifest->models[i].name) == 0) - model = &manifest->models[i]; - } + model = manifest_get_model_config(manifest, model_name); + if (!model) { ERROR("Unsupported model: '%s'.\n", model_name); @@ -1054,14 +1165,25 @@ struct manifest *new_manifest_from_archive(struct archive *archive) manifest.archive = archive; manifest.default_model = -1; + + VB2_DEBUG("Try to build a manifest from *%s\n", PATH_ENDSWITH_SETVARS); archive_walk(archive, &manifest, manifest_scan_entries); - if (manifest.num == 0) + + if (manifest.num == 0) { + VB2_DEBUG("Try to build a manifest from %s\n", + PATH_SIGNER_CONFIG); + manifest_from_signer_config(&manifest); + } + if (manifest.num == 0) { + VB2_DEBUG("Try to build a manifest from a */firmware folder\n"); archive_walk(archive, &manifest, manifest_scan_raw_entries); + } if (manifest.num == 0) { const char *image_name = NULL; struct firmware_image image = {0}; + VB2_DEBUG("Try to build a manifest from a simple folder\n"); /* Try to load from current folder. */ if (archive_has_entry(archive, old_host_image_name)) image_name = old_host_image_name; -- cgit v1.2.1