diff options
author | Hung-Te Lin <hungte@chromium.org> | 2019-11-20 15:15:02 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-11-22 22:09:21 +0000 |
commit | e8618380056e338d563501e2c9e03e9ff7102cc5 (patch) | |
tree | 76a524a91fd4011ee243006a811a6ffc94778e6a /futility | |
parent | a31ad0c3befcb93cc0e720c94919b203d5ff56ba (diff) | |
download | vboot-e8618380056e338d563501e2c9e03e9ff7102cc5.tar.gz |
futility: updater: move system-related utility functions to updater_utils
The firmware updater (updater.c) is bloated so we should move functions
that are not really related to 'updating logic' to a new file,
updater_utils.c.
Refactor only by moving functions (and renamed few functions), no
changes in updater logic.
BRANCH=none
BUG=chromium:1024401
TEST=make clean && make runtests
Change-Id: I98339c5c4a81845b36daf842c79625fa2389c7f0
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/1926009
Tested-by: Hung-Te Lin <hungte@chromium.org>
Reviewed-by: Joel Kitching <kitching@chromium.org>
Reviewed-by: Hung-Te Lin <hungte@chromium.org>
Commit-Queue: Hung-Te Lin <hungte@chromium.org>
Auto-Submit: Hung-Te Lin <hungte@chromium.org>
Diffstat (limited to 'futility')
-rw-r--r-- | futility/updater.c | 638 | ||||
-rw-r--r-- | futility/updater.h | 106 | ||||
-rw-r--r-- | futility/updater_quirks.c | 28 | ||||
-rw-r--r-- | futility/updater_utils.c | 641 | ||||
-rw-r--r-- | futility/updater_utils.h | 200 |
5 files changed, 853 insertions, 760 deletions
diff --git a/futility/updater.c b/futility/updater.c index c5a20c51..92020095 100644 --- a/futility/updater.c +++ b/futility/updater.c @@ -7,68 +7,25 @@ #include <assert.h> #include <ctype.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include "2common.h" #include "2rsa.h" #include "crossystem.h" -#include "fmap.h" #include "futility.h" #include "host_misc.h" #include "updater.h" -#include "utility.h" #include "util_misc.h" #include "vb2_common.h" -#include "vb2_struct.h" -#define COMMAND_BUFFER_SIZE 256 -#define RETURN_ON_FAILURE(x) do {int r = (x); if (r) return r;} while (0); -#define FLASHROM_OUTPUT_WP_PATTERN "write protect is " #define REMOVE_WP_URL "https://goo.gl/ces83U" -/* System environment values. */ -static const char * const FWACT_A = "A", - * const FWACT_B = "B", - * const STR_REV = "rev", - * const FLASHROM_OUTPUT_WP_ENABLED = - FLASHROM_OUTPUT_WP_PATTERN "enabled", - * const FLASHROM_OUTPUT_WP_DISABLED = - FLASHROM_OUTPUT_WP_PATTERN "disabled"; - -/* flashrom programmers. */ -static const char * const PROG_HOST = "host", - * const PROG_EC = "ec", - * const PROG_PD = "ec:dev=1"; - static const char ROOTKEY_HASH_DEV[] = "b11d74edd286c144e1135b49e7f0bc20cf041f10"; -enum wp_state { - WP_DISABLED, - WP_ENABLED, -}; - enum target_type { TARGET_SELF, TARGET_UPDATE, }; -enum active_slot { - SLOT_UNKNOWN = -1, - SLOT_A = 0, - SLOT_B, -}; - -enum flashrom_ops { - FLASHROM_READ, - FLASHROM_WRITE, - FLASHROM_WP_STATUS, -}; - enum rootkey_compat_result { ROOTKEY_COMPAT_OK, ROOTKEY_COMPAT_ERROR, @@ -77,286 +34,6 @@ enum rootkey_compat_result { }; /* - * Helper function to create a new temporary file. - * All files created will be removed by updater_remove_all_temp_files(). - * Returns the path of new file, or NULL on failure. - */ -const char *updater_create_temp_file(struct updater_config *cfg) -{ - struct tempfile *new_temp; - char new_path[] = P_tmpdir "/fwupdater.XXXXXX"; - int fd; - - fd = mkstemp(new_path); - if (fd < 0) { - ERROR("Failed to create new temp file in %s\n", new_path); - return NULL; - } - close(fd); - new_temp = (struct tempfile *)malloc(sizeof(*new_temp)); - if (new_temp) - new_temp->filepath = strdup(new_path); - if (!new_temp || !new_temp->filepath) { - remove(new_path); - free(new_temp); - ERROR("Failed to allocate buffer for new temp file.\n"); - return NULL; - } - VB2_DEBUG("Created new temporary file: %s.\n", new_path); - new_temp->next = cfg->tempfiles; - cfg->tempfiles = new_temp; - return new_temp->filepath; -} - -/* - * Helper function to remove all files created by create_temp_file(). - * This is intended to be called only once at end of program execution. - */ -static void updater_remove_all_temp_files(struct updater_config *cfg) -{ - struct tempfile *tempfiles = cfg->tempfiles; - while (tempfiles != NULL) { - struct tempfile *target = tempfiles; - VB2_DEBUG("Remove temporary file: %s.\n", target->filepath); - remove(target->filepath); - free(target->filepath); - tempfiles = target->next; - free(target); - } - cfg->tempfiles = NULL; -} - -/* - * Strip a string (usually from shell execution output) by removing all the - * trailing characters in pattern. If pattern is NULL, match by space type - * characters (space, new line, tab, ... etc). - */ -static void strip(char *s, const char *pattern) -{ - int len; - assert(s); - - len = strlen(s); - while (len-- > 0) { - if (pattern) { - if (!strchr(pattern, s[len])) - break; - } else { - if (!isascii(s[len]) || !isspace(s[len])) - break; - } - s[len] = '\0'; - } -} - -/* - * Executes a command on current host and returns stripped command output. - * If the command has failed (exit code is not zero), returns an empty string. - * The caller is responsible for releasing the returned string. - */ -char *host_shell(const char *command) -{ - /* Currently all commands we use do not have large output. */ - char buf[COMMAND_BUFFER_SIZE]; - - int result; - FILE *fp = popen(command, "r"); - - VB2_DEBUG("%s\n", command); - buf[0] = '\0'; - if (!fp) { - VB2_DEBUG("Execution error for %s.\n", command); - return strdup(buf); - } - - if (fgets(buf, sizeof(buf), fp)) - strip(buf, NULL); - result = pclose(fp); - if (!WIFEXITED(result) || WEXITSTATUS(result) != 0) { - VB2_DEBUG("Execution failure with exit code %d: %s\n", - WEXITSTATUS(result), command); - /* - * Discard all output if command failed, for example command - * syntax failure may lead to garbage in stdout. - */ - buf[0] = '\0'; - } - return strdup(buf); -} - - -/* An helper function to return "mainfw_act" system property. */ -static int host_get_mainfw_act(void) -{ - char buf[VB_MAX_STRING_PROPERTY]; - - if (!VbGetSystemPropertyString("mainfw_act", buf, sizeof(buf))) - return SLOT_UNKNOWN; - - if (strcmp(buf, FWACT_A) == 0) - return SLOT_A; - else if (strcmp(buf, FWACT_B) == 0) - return SLOT_B; - - return SLOT_UNKNOWN; -} - -/* A helper function to return the "tpm_fwver" system property. */ -static int host_get_tpm_fwver(void) -{ - return VbGetSystemPropertyInt("tpm_fwver"); -} - -/* A helper function to return the "hardware write protection" status. */ -static int host_get_wp_hw(void) -{ - /* wpsw refers to write protection 'switch', not 'software'. */ - int v = VbGetSystemPropertyInt("wpsw_cur"); - - /* wpsw_cur may be not available, especially in recovery mode. */ - if (v < 0) - v = VbGetSystemPropertyInt("wpsw_boot"); - - return v; -} - -/* A helper function to return "fw_vboot2" system property. */ -static int host_get_fw_vboot2(void) -{ - return VbGetSystemPropertyInt("fw_vboot2"); -} - -/* A help function to get $(mosys platform version). */ -static int host_get_platform_version(void) -{ - char *result = host_shell("mosys platform version"); - long rev = -1; - - /* Result should be 'revN' */ - if (strncmp(result, STR_REV, strlen(STR_REV)) == 0) - rev = strtol(result + strlen(STR_REV), NULL, 0); - - /* we should never have negative or extremely large versions, - * but clamp just to be sure - */ - if (rev < 0) - rev = 0; - if (rev > INT_MAX) - rev = INT_MAX; - - VB2_DEBUG("Raw data = [%s], parsed version is %ld\n", result, rev); - - free(result); - return rev; -} - -/* - * A helper function to invoke flashrom(8) command. - * Returns 0 if success, non-zero if error. - */ -static int host_flashrom(enum flashrom_ops op, const char *image_path, - const char *programmer, int verbose, - const char *section_name, const char *extra) -{ - char *command, *result; - const char *op_cmd, *dash_i = "-i", *postfix = ""; - int r; - - switch (verbose) { - case 0: - postfix = " >/dev/null 2>&1"; - break; - case 1: - break; - case 2: - postfix = "-V"; - break; - case 3: - postfix = "-V -V"; - break; - default: - postfix = "-V -V -V"; - break; - } - - if (!section_name || !*section_name) { - dash_i = ""; - section_name = ""; - } - - switch (op) { - case FLASHROM_READ: - op_cmd = "-r"; - assert(image_path); - break; - - case FLASHROM_WRITE: - op_cmd = "-w"; - assert(image_path); - break; - - case FLASHROM_WP_STATUS: - op_cmd = "--wp-status"; - assert(image_path == NULL); - image_path = ""; - /* grep is needed because host_shell only returns 1 line. */ - postfix = " 2>/dev/null | grep \"" \ - FLASHROM_OUTPUT_WP_PATTERN "\""; - break; - - default: - assert(0); - return -1; - } - - if (!extra) - extra = ""; - - /* TODO(hungte) In future we should link with flashrom directly. */ - ASPRINTF(&command, "flashrom %s %s -p %s %s %s %s %s", op_cmd, - image_path, programmer, dash_i, section_name, extra, - postfix); - - if (verbose) - INFO("Executing: %s\n", command); - - if (op != FLASHROM_WP_STATUS) { - r = system(command); - free(command); - if (r) - ERROR("Error code: %d\n", r); - return r; - } - - result = host_shell(command); - strip(result, NULL); - free(command); - VB2_DEBUG("wp-status: %s\n", result); - - if (strstr(result, FLASHROM_OUTPUT_WP_ENABLED)) - r = WP_ENABLED; - else if (strstr(result, FLASHROM_OUTPUT_WP_DISABLED)) - r = WP_DISABLED; - else - r = -1; - free(result); - return r; -} - -/* Helper function to return write protection status via given programmer. */ -static int host_get_wp(const char *programmer) -{ - return host_flashrom(FLASHROM_WP_STATUS, NULL, programmer, 0, NULL, - NULL); -} - -/* Helper function to return host software write protection status. */ -static int host_get_wp_sw(void) -{ - return host_get_wp(PROG_HOST); -} - -/* * Gets the system property by given type. * If the property was not loaded yet, invoke the property getter function * and cache the result. @@ -542,41 +219,6 @@ static int setup_config_quirks(const char *quirks, struct updater_config *cfg) } /* - * Finds a firmware section by given name in the firmware image. - * If successful, return zero and *section argument contains the address and - * size of the section; otherwise failure. - */ -int find_firmware_section(struct firmware_section *section, - const struct firmware_image *image, - const char *section_name) -{ - FmapAreaHeader *fah = NULL; - uint8_t *ptr; - - section->data = NULL; - section->size = 0; - ptr = fmap_find_by_name( - image->data, image->size, image->fmap_header, - section_name, &fah); - if (!ptr) - return -1; - section->data = (uint8_t *)ptr; - section->size = fah->area_size; - return 0; -} - -/* - * Returns true if the given FMAP section exists in the firmware image. - */ -static int firmware_section_exists(const struct firmware_image *image, - const char *section_name) -{ - struct firmware_section section; - find_firmware_section(§ion, image, section_name); - return section.data != NULL; -} - -/* * Checks if the section is filled with given character. * If section size is 0, return 0. If section is not empty, return non-zero if * the section is filled with same character c, otherwise 0. @@ -594,126 +236,6 @@ static int section_is_filled_with(const struct firmware_section *section, } /* - * Loads the firmware information from an FMAP section in loaded firmware image. - * The section should only contain ASCIIZ string as firmware version. - * If successful, the return value is zero and *version points to a newly - * allocated string as firmware version (caller must free it); otherwise - * failure. - */ -static int load_firmware_version(struct firmware_image *image, - const char *section_name, - char **version) -{ - struct firmware_section fwid; - find_firmware_section(&fwid, image, section_name); - if (fwid.size) { - *version = strndup((const char*)fwid.data, fwid.size); - /* - * For 'system current' images, the version string may contain - * invalid characters that we do want to strip. - */ - strip(*version, "\xff"); - return 0; - } - *version = strdup(""); - return -1; -} - -/* - * Loads a firmware image from file. - * If archive is provided and file_name is a relative path, read the file from - * archive. - * Returns 0 on success, otherwise failure. - */ -int load_firmware_image(struct firmware_image *image, const char *file_name, - struct archive *archive) -{ - if (!file_name) { - ERROR("No file name given\n"); - return -1; - } - - VB2_DEBUG("Load image file from %s...\n", file_name); - - if (!archive_has_entry(archive, file_name)) { - ERROR("Does not exist: %s\n", file_name); - return -1; - } - if (archive_read_file(archive, file_name, &image->data, &image->size, NULL) - != VB2_SUCCESS) { - ERROR("Failed to load %s\n", file_name); - return -1; - } - - VB2_DEBUG("Image size: %d\n", image->size); - assert(image->data); - image->file_name = strdup(file_name); - - image->fmap_header = fmap_find(image->data, image->size); - if (!image->fmap_header) { - ERROR("Invalid image file (missing FMAP): %s\n", file_name); - return -1; - } - - if (!firmware_section_exists(image, FMAP_RO_FRID)) { - ERROR("Does not look like VBoot firmware image: %s\n", - file_name); - return -1; - } - - load_firmware_version(image, FMAP_RO_FRID, &image->ro_version); - if (firmware_section_exists(image, FMAP_RW_FWID_A)) { - char **a = &image->rw_version_a, **b = &image->rw_version_b; - load_firmware_version(image, FMAP_RW_FWID_A, a); - load_firmware_version(image, FMAP_RW_FWID_B, b); - } else if (firmware_section_exists(image, FMAP_RW_FWID)) { - char **a = &image->rw_version_a, **b = &image->rw_version_b; - load_firmware_version(image, FMAP_RW_FWID, a); - load_firmware_version(image, FMAP_RW_FWID, b); - } else { - ERROR("Unsupported VBoot firmware (no RW ID): %s\n", file_name); - } - return 0; -} - -/* - * 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) -{ - const char *tmp_file = updater_create_temp_file(cfg); - - if (!tmp_file) - return -1; - RETURN_ON_FAILURE(host_flashrom( - FLASHROM_READ, tmp_file, image->programmer, - cfg->verbosity, NULL, NULL)); - return load_firmware_image(image, tmp_file, NULL); -} - -/* - * Frees the allocated resource from a firmware image object. - */ -void free_firmware_image(struct firmware_image *image) -{ - /* - * The programmer is not allocated by load_firmware_image and must be - * preserved explicitly. - */ - const char *programmer = image->programmer; - - free(image->data); - free(image->file_name); - free(image->ro_version); - free(image->rw_version_a); - free(image->rw_version_b); - memset(image, 0, sizeof(*image)); - image->programmer = programmer; -} - -/* * Decides which target in RW firmware to manipulate. * The `target` argument specifies if we want to know "the section to be * update" (TARGET_UPDATE), or "the (active) section * to check" (TARGET_SELF). @@ -854,42 +376,17 @@ static int write_firmware(struct updater_config *cfg, const struct firmware_image *image, const char *section_name) { - const char *tmp_file = updater_create_temp_file(cfg); - const char *tmp_diff_file = NULL; - const char *programmer = image->programmer; - char *extra = NULL; - int r; - - if (!tmp_file) - return -1; - if (cfg->emulation) { INFO("(emulation) Writing %s from %s to %s (emu=%s).\n", section_name ? section_name : "whole image", - image->file_name, programmer, cfg->emulation); + image->file_name, image->programmer, cfg->emulation); return emulate_write_firmware( cfg->emulation, image, section_name); } - if (vb2_write_file(tmp_file, image->data, image->size) != VB2_SUCCESS) { - ERROR("Cannot write temporary file for output: %s\n", tmp_file); - return -1; - } - if (cfg->fast_update && image == &cfg->image && cfg->image_current.data) - { - tmp_diff_file = updater_create_temp_file(cfg); - if (vb2_write_file(tmp_diff_file, cfg->image_current.data, - cfg->image_current.size) != VB2_SUCCESS) { - ERROR("Cannot write temporary file for diff image\n"); - return -1; - } - ASPRINTF(&extra, "--noverify --diff=%s", tmp_diff_file); - } - r = host_flashrom(FLASHROM_WRITE, tmp_file, programmer, - cfg->verbosity + 1, section_name, extra); - free(extra); - return r; + + return write_system_firmware(cfg, image, section_name); } /* @@ -950,54 +447,6 @@ static int write_optional_firmware(struct updater_config *cfg, } /* - * Preserves (copies) the given section (by name) from image_from to image_to. - * The offset may be different, and the section data will be directly copied. - * If the section does not exist on either images, return as failure. - * If the source section is larger, contents on destination be truncated. - * If the source section is smaller, the remaining area is not modified. - * Returns 0 if success, non-zero if error. - */ -int preserve_firmware_section(const struct firmware_image *image_from, - struct firmware_image *image_to, - const char *section_name) -{ - struct firmware_section from, to; - - find_firmware_section(&from, image_from, section_name); - find_firmware_section(&to, image_to, section_name); - if (!from.data || !to.data) { - VB2_DEBUG("Cannot find section %.*s: from=%p, to=%p\n", - FMAP_NAMELEN, section_name, from.data, to.data); - return -1; - } - if (from.size > to.size) { - WARN("Section %.*s is truncated after updated.\n", - FMAP_NAMELEN, section_name); - } - /* Use memmove in case if we need to deal with sections that overlap. */ - memmove(to.data, from.data, VB2_MIN(from.size, to.size)); - return 0; -} - -/* - * Finds the GBB (Google Binary Block) header on a given firmware image. - * Returns a pointer to valid GBB header, or NULL on not found. - */ -const struct vb2_gbb_header *find_gbb(const struct firmware_image *image) -{ - struct firmware_section section; - struct vb2_gbb_header *gbb_header; - - find_firmware_section(§ion, image, FMAP_RO_GBB); - gbb_header = (struct vb2_gbb_header *)section.data; - if (!futil_valid_gbb_header(gbb_header, section.size, NULL)) { - ERROR("Cannot find GBB in image: %s.\n", image->file_name); - return NULL; - } - return gbb_header; -} - -/* * Preserve the GBB contents from image_from to image_to. * HWID is always preserved, and flags are preserved only if preserve_flags set. * Returns 0 if success, otherwise -1 if GBB header can't be found or if HWID is @@ -1378,42 +827,6 @@ static enum rootkey_compat_result check_compatible_root_key( } /* - * Returns 1 if a given file (cbfs_entry_name) exists inside a particular CBFS - * section of an image file, otherwise 0. - */ -static int cbfs_file_exists(const char *image_file, - const char *section_name, - const char *cbfs_entry_name) -{ - char *cmd; - int r; - - ASPRINTF(&cmd, - "cbfstool '%s' print -r %s 2>/dev/null | grep -q '^%s '", - image_file, section_name, cbfs_entry_name); - r = system(cmd); - free(cmd); - return !r; -} - -static int cbfs_extract_file(const char *image_file, - const char *section_name, - const char *cbfs_entry_name, - const char *output_name) -{ - char *cmd; - int r; - - ASPRINTF(&cmd, - "cbfstool '%s' extract -r %s -n '%s' -f '%s' " - "2>/dev/null", image_file, section_name, cbfs_entry_name, - output_name); - r = system(cmd); - free(cmd); - return r; -} - -/* * Returns non-zero if the RW_LEGACY needs to be updated, otherwise 0. */ static int legacy_needs_update(struct updater_config *cfg) @@ -1542,13 +955,13 @@ static int is_ec_software_sync_enabled(struct updater_config *cfg) static int ec_ro_software_sync(struct updater_config *cfg) { const char *tmp_path = updater_create_temp_file(cfg); - const char *ec_ro_path = updater_create_temp_file(cfg); + const char *ec_ro_path; uint8_t *ec_ro_data; uint32_t ec_ro_len; int is_same_ec_ro; struct firmware_section ec_ro_sec; - if (!tmp_path || !ec_ro_path || + if (!tmp_path || vb2_write_file(tmp_path, cfg->image.data, cfg->image.size)) { ERROR("Failed to create temporary file for image contents.\n"); return 1; @@ -1563,7 +976,8 @@ static int ec_ro_software_sync(struct updater_config *cfg) ERROR("EC may need to update data outside EC RO Sync."); return 1; } - if (cbfs_extract_file(tmp_path, FMAP_RO_SECTION, "ecro", ec_ro_path) || + ec_ro_path = cbfs_extract_file(cfg, tmp_path, FMAP_RO_SECTION, "ecro"); + if (!ec_ro_path || !cbfs_file_exists(tmp_path, FMAP_RO_SECTION, "ecro.hash")) { INFO("No valid EC RO for software sync in AP firmware.\n"); return 1; @@ -1922,7 +1336,6 @@ enum updater_error_codes update_firmware(struct updater_config *cfg) */ struct updater_config *updater_new_config() { - struct system_property *props; struct updater_config *cfg = (struct updater_config *)calloc( 1, sizeof(struct updater_config)); if (!cfg) @@ -1934,44 +1347,13 @@ struct updater_config *updater_new_config() cfg->check_platform = 1; - props = cfg->system_properties; - props[SYS_PROP_MAINFW_ACT].getter = host_get_mainfw_act; - props[SYS_PROP_TPM_FWVER].getter = host_get_tpm_fwver; - props[SYS_PROP_FW_VBOOT2].getter = host_get_fw_vboot2; - props[SYS_PROP_PLATFORM_VER].getter = host_get_platform_version; - props[SYS_PROP_WP_HW].getter = host_get_wp_hw; - props[SYS_PROP_WP_SW].getter = host_get_wp_sw; - + init_system_properties(&cfg->system_properties[0], + ARRAY_SIZE(cfg->system_properties)); updater_register_quirks(cfg); return cfg; } /* - * Saves everything from stdin to given output file. - * Returns 0 on success, otherwise failure. - */ -static int save_from_stdin(const char *output) -{ - FILE *in = stdin, *out = fopen(output, "wb"); - char buffer[4096]; - size_t sz; - - assert(in); - if (!out) - return -1; - - while (!feof(in)) { - sz = fread(buffer, 1, sizeof(buffer), in); - if (fwrite(buffer, 1, sz, out) != sz) { - fclose(out); - return -1; - } - } - fclose(out); - return 0; -} - -/* * Setup quirks for updating current image. * * Quirks must be loaded after image loaded because we use image contents to @@ -2011,7 +1393,7 @@ static int updater_load_images(struct updater_config *cfg, INFO("Reading image from stdin...\n"); image = updater_create_temp_file(cfg); if (image) - errorcnt += !!save_from_stdin(image); + errorcnt += !!save_file_from_stdin(image); } errorcnt += !!load_firmware_image(&cfg->image, image, ar); if (!errorcnt) diff --git a/futility/updater.h b/futility/updater.h index ff2fc4f9..b74b67f0 100644 --- a/futility/updater.h +++ b/futility/updater.h @@ -8,13 +8,8 @@ #ifndef VBOOT_REFERENCE_FUTILITY_UPDATER_H_ #define VBOOT_REFERENCE_FUTILITY_UPDATER_H_ -#include <stdio.h> - -#include "fmap.h" #include "futility.h" - -#define ASPRINTF(strp, ...) do { if (asprintf(strp, __VA_ARGS__) >= 0) break; \ - ERROR("Failed to allocate memory, abort.\n"); exit(1); } while (0) +#include "updater_utils.h" /* FMAP section names. */ static const char * const FMAP_RO_FRID = "RO_FRID", @@ -32,37 +27,6 @@ static const char * const FMAP_RO_FRID = "RO_FRID", * const FMAP_SI_DESC = "SI_DESC", * const FMAP_SI_ME = "SI_ME"; -struct firmware_image { - const char *programmer; - uint32_t size; - uint8_t *data; - char *file_name; - char *ro_version, *rw_version_a, *rw_version_b; - FmapHeader *fmap_header; -}; - -struct firmware_section { - uint8_t *data; - size_t size; -}; - -struct system_property { - int (*getter)(void); - int value; - int initialized; -}; - -enum system_property_type { - SYS_PROP_MAINFW_ACT, - SYS_PROP_TPM_FWVER, - SYS_PROP_FW_VBOOT2, - SYS_PROP_PLATFORM_VER, - SYS_PROP_WP_HW, - SYS_PROP_WP_SW, - SYS_PROP_MAX -}; - -struct updater_config; struct quirk_entry { const char *name; const char *help; @@ -81,12 +45,6 @@ enum quirk_types { QUIRK_MAX, }; -struct tempfile { - char *filepath; - struct tempfile *next; -}; - -struct archive; struct updater_config { struct firmware_image image, image_current; struct firmware_image ec_image, pd_image; @@ -157,8 +115,6 @@ enum updater_error_codes { /* Messages explaining enum updater_error_codes. */ extern const char * const updater_error_messages[]; -struct updater_config; - /* * The main updater to update system firmware using the configuration parameter. * Returns UPDATE_ERR_DONE if success, otherwise failure. @@ -192,52 +148,6 @@ void updater_list_config_quirks(const struct updater_config *cfg); */ void updater_register_quirks(struct updater_config *cfg); -/* - * Helper function to create a new temporary file within updater's life cycle. - * Returns the path of new file, or NULL on failure. - */ -const char *updater_create_temp_file(struct updater_config *cfg); - -/* - * Finds a firmware section by given name in the firmware image. - * If successful, return zero and *section argument contains the address and - * size of the section; otherwise failure. - */ -int find_firmware_section(struct firmware_section *section, - const struct firmware_image *image, - const char *section_name); - -/* - * Preserves (copies) the given section (by name) from image_from to image_to. - * The offset may be different, and the section data will be directly copied. - * If the section does not exist on either images, return as failure. - * If the source section is larger, contents on destination be truncated. - * If the source section is smaller, the remaining area is not modified. - * Returns 0 if success, non-zero if error. - */ -int preserve_firmware_section(const struct firmware_image *image_from, - struct firmware_image *image_to, - const char *section_name); - -/* - * Loads a firmware image from file. - * If archive is provided and file_name is a relative path, read the file from - * archive. - * Returns 0 on success, otherwise failure. - */ -int load_firmware_image(struct firmware_image *image, const char *file_name, - struct archive *archive); - -/* - * 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); - -/* Frees the allocated resource from a firmware image object. */ -void free_firmware_image(struct firmware_image *image); - /* Gets the value (setting) of specified quirks from updater configuration. */ int get_config_quirk(enum quirk_types quirk, const struct updater_config *cfg); @@ -250,20 +160,6 @@ int get_system_property(enum system_property_type property_type, */ const char * const updater_get_default_quirks(struct updater_config *cfg); -/* - * Finds the GBB (Google Binary Block) header on a given firmware image. - * Returns a pointer to valid GBB header, or NULL on not found. - */ -struct vb2_gbb_header; -const struct vb2_gbb_header *find_gbb(const struct firmware_image *image); - -/* - * Executes a command on current host and returns stripped command output. - * If the command has failed (exit code is not zero), returns an empty string. - * The caller is responsible for releasing the returned string. - */ -char *host_shell(const char *command); - /* Functions from updater_archive.c */ /* diff --git a/futility/updater_quirks.c b/futility/updater_quirks.c index c14f55a7..9f037ea1 100644 --- a/futility/updater_quirks.c +++ b/futility/updater_quirks.c @@ -275,32 +275,6 @@ static int quirk_daisy_snow_dual_model(struct updater_config *cfg) } /* - * Extracts files from a CBFS on given region (section) of image_file. - * Returns the path to a temporary file on success, otherwise NULL. - */ -static const char *extract_cbfs_file(struct updater_config *cfg, - const char *image_file, - const char *cbfs_region, - const char *cbfs_name) -{ - const char *output = updater_create_temp_file(cfg); - char *command, *result; - - ASPRINTF(&command, "cbfstool \"%s\" extract -r %s -n \"%s\" " - "-f \"%s\" 2>&1", image_file, cbfs_region, - cbfs_name, output); - - result = host_shell(command); - free(command); - - if (!*result) - output = NULL; - - free(result); - return output; -} - -/* * Quirk to help preserving SMM store on devices without a dedicated "SMMSTORE" * FMAP section. These devices will store "smm_store" file in same CBFS where * the legacy boot loader lives (i.e, FMAP RW_LEGACY). @@ -319,7 +293,7 @@ static int quirk_eve_smm_store(struct updater_config *cfg) if (write_image(temp_image, &cfg->image_current) != VB2_SUCCESS) return -1; - old_store = extract_cbfs_file(cfg, temp_image, FMAP_RW_LEGACY, + old_store = cbfs_extract_file(cfg, temp_image, FMAP_RW_LEGACY, smm_store_name); if (!old_store) { VB2_DEBUG("cbfstool failure or SMM store not available. " diff --git a/futility/updater_utils.c b/futility/updater_utils.c new file mode 100644 index 00000000..2cfb1b0c --- /dev/null +++ b/futility/updater_utils.c @@ -0,0 +1,641 @@ +/* Copyright 2019 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. + * + * The utility functions for firmware updater. + */ + +#include <assert.h> +#include <limits.h> +#include <unistd.h> + +#include "2common.h" +#include "crossystem.h" +#include "host_misc.h" +#include "updater.h" + +#define COMMAND_BUFFER_SIZE 256 +#define FLASHROM_OUTPUT_WP_PATTERN "write protect is " + +enum flashrom_ops { + FLASHROM_READ, + FLASHROM_WRITE, + FLASHROM_WP_STATUS, +}; + +/* System environment values. */ +static const char * const STR_REV = "rev", + * const FLASHROM_OUTPUT_WP_ENABLED = + FLASHROM_OUTPUT_WP_PATTERN "enabled", + * const FLASHROM_OUTPUT_WP_DISABLED = + FLASHROM_OUTPUT_WP_PATTERN "disabled"; + +/* + * Strips a string (usually from shell execution output) by removing all the + * trailing characters in pattern. If pattern is NULL, match by space type + * characters (space, new line, tab, ... etc). + */ +void strip_string(char *s, const char *pattern) +{ + int len; + assert(s); + + len = strlen(s); + while (len-- > 0) { + if (pattern) { + if (!strchr(pattern, s[len])) + break; + } else { + if (!isascii(s[len]) || !isspace(s[len])) + break; + } + s[len] = '\0'; + } +} + +/* + * Saves everything from stdin to given output file. + * Returns 0 on success, otherwise failure. + */ +int save_file_from_stdin(const char *output) +{ + FILE *in = stdin, *out = fopen(output, "wb"); + char buffer[4096]; + size_t sz; + + assert(in); + if (!out) + return -1; + + while (!feof(in)) { + sz = fread(buffer, 1, sizeof(buffer), in); + if (fwrite(buffer, 1, sz, out) != sz) { + fclose(out); + return -1; + } + } + fclose(out); + return 0; +} + +/* + * Returns 1 if a given file (cbfs_entry_name) exists inside a particular CBFS + * section of an image file, otherwise 0. + */ +int cbfs_file_exists(const char *image_file, + const char *section_name, + const char *cbfs_entry_name) +{ + char *cmd; + int r; + + ASPRINTF(&cmd, + "cbfstool '%s' print -r %s 2>/dev/null | grep -q '^%s '", + image_file, section_name, cbfs_entry_name); + r = system(cmd); + free(cmd); + return !r; +} + +/* + * Extracts files from a CBFS on given region (section) of image_file. + * Returns the path to a temporary file on success, otherwise NULL. + */ +const char *cbfs_extract_file(struct updater_config *cfg, + const char *image_file, + const char *cbfs_region, + const char *cbfs_name) +{ + const char *output = updater_create_temp_file(cfg); + char *command, *result; + + if (!output) + return NULL; + + ASPRINTF(&command, "cbfstool \"%s\" extract -r %s -n \"%s\" " + "-f \"%s\" 2>&1", image_file, cbfs_region, + cbfs_name, output); + + result = host_shell(command); + free(command); + + if (!*result) + output = NULL; + + free(result); + return output; +} + +/* + * Loads the firmware information from an FMAP section in loaded firmware image. + * The section should only contain ASCIIZ string as firmware version. + * If successful, the return value is zero and *version points to a newly + * allocated string as firmware version (caller must free it); otherwise + * failure. + */ +static int load_firmware_version(struct firmware_image *image, + const char *section_name, + char **version) +{ + struct firmware_section fwid; + find_firmware_section(&fwid, image, section_name); + if (fwid.size) { + *version = strndup((const char*)fwid.data, fwid.size); + /* + * For 'system current' images, the version string may contain + * invalid characters that we do want to strip. + */ + strip_string(*version, "\xff"); + return 0; + } + *version = strdup(""); + return -1; +} + +/* + * Loads a firmware image from file. + * If archive is provided and file_name is a relative path, read the file from + * archive. + * Returns 0 on success, otherwise failure. + */ +int load_firmware_image(struct firmware_image *image, const char *file_name, + struct archive *archive) +{ + if (!file_name) { + ERROR("No file name given\n"); + return -1; + } + + VB2_DEBUG("Load image file from %s...\n", file_name); + + if (!archive_has_entry(archive, file_name)) { + ERROR("Does not exist: %s\n", file_name); + return -1; + } + if (archive_read_file(archive, file_name, &image->data, &image->size, + NULL) != VB2_SUCCESS) { + ERROR("Failed to load %s\n", file_name); + return -1; + } + + VB2_DEBUG("Image size: %d\n", image->size); + assert(image->data); + image->file_name = strdup(file_name); + + image->fmap_header = fmap_find(image->data, image->size); + if (!image->fmap_header) { + ERROR("Invalid image file (missing FMAP): %s\n", file_name); + return -1; + } + + if (!firmware_section_exists(image, FMAP_RO_FRID)) { + ERROR("Does not look like VBoot firmware image: %s\n", + file_name); + return -1; + } + + load_firmware_version(image, FMAP_RO_FRID, &image->ro_version); + if (firmware_section_exists(image, FMAP_RW_FWID_A)) { + char **a = &image->rw_version_a, **b = &image->rw_version_b; + load_firmware_version(image, FMAP_RW_FWID_A, a); + load_firmware_version(image, FMAP_RW_FWID_B, b); + } else if (firmware_section_exists(image, FMAP_RW_FWID)) { + char **a = &image->rw_version_a, **b = &image->rw_version_b; + load_firmware_version(image, FMAP_RW_FWID, a); + load_firmware_version(image, FMAP_RW_FWID, b); + } else { + ERROR("Unsupported VBoot firmware (no RW ID): %s\n", file_name); + } + return 0; +} + +/* + * Frees the allocated resource from a firmware image object. + */ +void free_firmware_image(struct firmware_image *image) +{ + /* + * The programmer is not allocated by load_firmware_image and must be + * preserved explicitly. + */ + const char *programmer = image->programmer; + + free(image->data); + free(image->file_name); + free(image->ro_version); + free(image->rw_version_a); + free(image->rw_version_b); + memset(image, 0, sizeof(*image)); + image->programmer = programmer; +} + +/* + * Finds a firmware section by given name in the firmware image. + * If successful, return zero and *section argument contains the address and + * size of the section; otherwise failure. + */ +int find_firmware_section(struct firmware_section *section, + const struct firmware_image *image, + const char *section_name) +{ + FmapAreaHeader *fah = NULL; + uint8_t *ptr; + + section->data = NULL; + section->size = 0; + ptr = fmap_find_by_name( + image->data, image->size, image->fmap_header, + section_name, &fah); + if (!ptr) + return -1; + section->data = (uint8_t *)ptr; + section->size = fah->area_size; + return 0; +} + +/* + * Returns true if the given FMAP section exists in the firmware image. + */ +int firmware_section_exists(const struct firmware_image *image, + const char *section_name) +{ + struct firmware_section section; + find_firmware_section(§ion, image, section_name); + return section.data != NULL; +} + +/* + * Preserves (copies) the given section (by name) from image_from to image_to. + * The offset may be different, and the section data will be directly copied. + * If the section does not exist on either images, return as failure. + * If the source section is larger, contents on destination be truncated. + * If the source section is smaller, the remaining area is not modified. + * Returns 0 if success, non-zero if error. + */ +int preserve_firmware_section(const struct firmware_image *image_from, + struct firmware_image *image_to, + const char *section_name) +{ + struct firmware_section from, to; + + find_firmware_section(&from, image_from, section_name); + find_firmware_section(&to, image_to, section_name); + if (!from.data || !to.data) { + VB2_DEBUG("Cannot find section %.*s: from=%p, to=%p\n", + FMAP_NAMELEN, section_name, from.data, to.data); + return -1; + } + if (from.size > to.size) { + WARN("Section %.*s is truncated after updated.\n", + FMAP_NAMELEN, section_name); + } + /* Use memmove in case if we need to deal with sections that overlap. */ + memmove(to.data, from.data, VB2_MIN(from.size, to.size)); + return 0; +} + +/* + * Finds the GBB (Google Binary Block) header on a given firmware image. + * Returns a pointer to valid GBB header, or NULL on not found. + */ +const struct vb2_gbb_header *find_gbb(const struct firmware_image *image) +{ + struct firmware_section section; + struct vb2_gbb_header *gbb_header; + + find_firmware_section(§ion, image, FMAP_RO_GBB); + gbb_header = (struct vb2_gbb_header *)section.data; + if (!futil_valid_gbb_header(gbb_header, section.size, NULL)) { + ERROR("Cannot find GBB in image: %s.\n", image->file_name); + return NULL; + } + return gbb_header; +} + +/* + * Executes a command on current host and returns stripped command output. + * If the command has failed (exit code is not zero), returns an empty string. + * The caller is responsible for releasing the returned string. + */ +char *host_shell(const char *command) +{ + /* Currently all commands we use do not have large output. */ + char buf[COMMAND_BUFFER_SIZE]; + + int result; + FILE *fp = popen(command, "r"); + + VB2_DEBUG("%s\n", command); + buf[0] = '\0'; + if (!fp) { + VB2_DEBUG("Execution error for %s.\n", command); + return strdup(buf); + } + + if (fgets(buf, sizeof(buf), fp)) + strip_string(buf, NULL); + result = pclose(fp); + if (!WIFEXITED(result) || WEXITSTATUS(result) != 0) { + VB2_DEBUG("Execution failure with exit code %d: %s\n", + WEXITSTATUS(result), command); + /* + * Discard all output if command failed, for example command + * syntax failure may lead to garbage in stdout. + */ + buf[0] = '\0'; + } + return strdup(buf); +} + + +/* An helper function to return "mainfw_act" system property. */ +static int host_get_mainfw_act(void) +{ + char buf[VB_MAX_STRING_PROPERTY]; + + if (!VbGetSystemPropertyString("mainfw_act", buf, sizeof(buf))) + return SLOT_UNKNOWN; + + if (strcmp(buf, FWACT_A) == 0) + return SLOT_A; + else if (strcmp(buf, FWACT_B) == 0) + return SLOT_B; + + return SLOT_UNKNOWN; +} + +/* A helper function to return the "tpm_fwver" system property. */ +static int host_get_tpm_fwver(void) +{ + return VbGetSystemPropertyInt("tpm_fwver"); +} + +/* A helper function to return the "hardware write protection" status. */ +static int host_get_wp_hw(void) +{ + /* wpsw refers to write protection 'switch', not 'software'. */ + int v = VbGetSystemPropertyInt("wpsw_cur"); + + /* wpsw_cur may be not available, especially in recovery mode. */ + if (v < 0) + v = VbGetSystemPropertyInt("wpsw_boot"); + + return v; +} + +/* A helper function to return "fw_vboot2" system property. */ +static int host_get_fw_vboot2(void) +{ + return VbGetSystemPropertyInt("fw_vboot2"); +} + +/* A help function to get $(mosys platform version). */ +static int host_get_platform_version(void) +{ + char *result = host_shell("mosys platform version"); + long rev = -1; + + /* Result should be 'revN' */ + if (strncmp(result, STR_REV, strlen(STR_REV)) == 0) + rev = strtol(result + strlen(STR_REV), NULL, 0); + + /* we should never have negative or extremely large versions, + * but clamp just to be sure + */ + if (rev < 0) + rev = 0; + if (rev > INT_MAX) + rev = INT_MAX; + + VB2_DEBUG("Raw data = [%s], parsed version is %ld\n", result, rev); + + free(result); + return rev; +} + +/* + * A helper function to invoke flashrom(8) command. + * Returns 0 if success, non-zero if error. + */ +static int host_flashrom(enum flashrom_ops op, const char *image_path, + const char *programmer, int verbose, + const char *section_name, const char *extra) +{ + char *command, *result; + const char *op_cmd, *dash_i = "-i", *postfix = ""; + int r; + + switch (verbose) { + case 0: + postfix = " >/dev/null 2>&1"; + break; + case 1: + break; + case 2: + postfix = "-V"; + break; + case 3: + postfix = "-V -V"; + break; + default: + postfix = "-V -V -V"; + break; + } + + if (!section_name || !*section_name) { + dash_i = ""; + section_name = ""; + } + + switch (op) { + case FLASHROM_READ: + op_cmd = "-r"; + assert(image_path); + break; + + case FLASHROM_WRITE: + op_cmd = "-w"; + assert(image_path); + break; + + case FLASHROM_WP_STATUS: + op_cmd = "--wp-status"; + assert(image_path == NULL); + image_path = ""; + /* grep is needed because host_shell only returns 1 line. */ + postfix = " 2>/dev/null | grep \"" \ + FLASHROM_OUTPUT_WP_PATTERN "\""; + break; + + default: + assert(0); + return -1; + } + + if (!extra) + extra = ""; + + /* TODO(hungte) In future we should link with flashrom directly. */ + ASPRINTF(&command, "flashrom %s %s -p %s %s %s %s %s", op_cmd, + image_path, programmer, dash_i, section_name, extra, + postfix); + + if (verbose) + INFO("Executing: %s\n", command); + + if (op != FLASHROM_WP_STATUS) { + r = system(command); + free(command); + if (r) + ERROR("Error code: %d\n", r); + return r; + } + + result = host_shell(command); + strip_string(result, NULL); + free(command); + VB2_DEBUG("wp-status: %s\n", result); + + if (strstr(result, FLASHROM_OUTPUT_WP_ENABLED)) + r = WP_ENABLED; + else if (strstr(result, FLASHROM_OUTPUT_WP_DISABLED)) + r = WP_DISABLED; + else + r = WP_ERROR; + free(result); + return r; +} + +/* Helper function to return write protection status via given programmer. */ +enum wp_state host_get_wp(const char *programmer) +{ + return host_flashrom(FLASHROM_WP_STATUS, NULL, programmer, 0, NULL, + NULL); +} + +/* Helper function to return host software write protection status. */ +static int host_get_wp_sw(void) +{ + return host_get_wp(PROG_HOST); +} + +/* + * 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 r; + const char *tmp_file = updater_create_temp_file(cfg); + + if (!tmp_file) + return -1; + + r = host_flashrom(FLASHROM_READ, tmp_file, image->programmer, + cfg->verbosity, NULL, NULL); + if (!r) + r = load_firmware_image(image, tmp_file, NULL); + return r; +} + +/* + * Writes a section from given firmware image to system firmware. + * If section_name is NULL, write whole image. + * Returns 0 if success, non-zero if error. + */ +int write_system_firmware(struct updater_config *cfg, + const struct firmware_image *image, + const char *section_name) +{ + const char *tmp_file = updater_create_temp_file(cfg); + const char *tmp_diff_file = NULL; + const char *programmer = image->programmer; + char *extra = NULL; + int r; + + if (!tmp_file) + return -1; + + if (vb2_write_file(tmp_file, image->data, image->size) != VB2_SUCCESS) { + ERROR("Cannot write temporary file for output: %s\n", tmp_file); + return -1; + } + if (cfg->fast_update && image == &cfg->image && cfg->image_current.data) + { + tmp_diff_file = updater_create_temp_file(cfg); + if (vb2_write_file(tmp_diff_file, cfg->image_current.data, + cfg->image_current.size) != VB2_SUCCESS) { + ERROR("Cannot write temporary file for diff image\n"); + return -1; + } + ASPRINTF(&extra, "--noverify --diff=%s", tmp_diff_file); + } + r = host_flashrom(FLASHROM_WRITE, tmp_file, programmer, + cfg->verbosity + 1, section_name, extra); + free(extra); + return r; +} + +/* Helper function to configure all properties. */ +void init_system_properties(struct system_property *props, int num) +{ + memset(props, 0, num * sizeof(*props)); + assert(num >= SYS_PROP_MAX); + props[SYS_PROP_MAINFW_ACT].getter = host_get_mainfw_act; + props[SYS_PROP_TPM_FWVER].getter = host_get_tpm_fwver; + props[SYS_PROP_FW_VBOOT2].getter = host_get_fw_vboot2; + props[SYS_PROP_PLATFORM_VER].getter = host_get_platform_version; + props[SYS_PROP_WP_HW].getter = host_get_wp_hw; + props[SYS_PROP_WP_SW].getter = host_get_wp_sw; +} + +/* + * Helper function to create a new temporary file. + * All files created will be removed by updater_remove_all_temp_files(). + * Returns the path of new file, or NULL on failure. + */ +const char *updater_create_temp_file(struct updater_config *cfg) +{ + struct tempfile *new_temp; + char new_path[] = P_tmpdir "/fwupdater.XXXXXX"; + int fd; + + fd = mkstemp(new_path); + if (fd < 0) { + ERROR("Failed to create new temp file in %s\n", new_path); + return NULL; + } + close(fd); + new_temp = (struct tempfile *)malloc(sizeof(*new_temp)); + if (new_temp) + new_temp->filepath = strdup(new_path); + if (!new_temp || !new_temp->filepath) { + remove(new_path); + free(new_temp); + ERROR("Failed to allocate buffer for new temp file.\n"); + return NULL; + } + VB2_DEBUG("Created new temporary file: %s.\n", new_path); + new_temp->next = cfg->tempfiles; + cfg->tempfiles = new_temp; + return new_temp->filepath; +} + +/* + * Helper function to remove all files created by create_temp_file(). + * This is intended to be called only once at end of program execution. + */ +void updater_remove_all_temp_files(struct updater_config *cfg) +{ + struct tempfile *tempfiles = cfg->tempfiles; + while (tempfiles != NULL) { + struct tempfile *target = tempfiles; + VB2_DEBUG("Remove temporary file: %s.\n", target->filepath); + remove(target->filepath); + free(target->filepath); + tempfiles = target->next; + free(target); + } + cfg->tempfiles = NULL; +} diff --git a/futility/updater_utils.h b/futility/updater_utils.h new file mode 100644 index 00000000..686b0888 --- /dev/null +++ b/futility/updater_utils.h @@ -0,0 +1,200 @@ +/* Copyright 2019 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. + * + * Utilities for firmware updater. + */ + +#ifndef VBOOT_REFERENCE_FUTILITY_UPDATER_UTILS_H_ +#define VBOOT_REFERENCE_FUTILITY_UPDATER_UTILS_H_ + +#include <stdio.h> +#include "fmap.h" + +#define ASPRINTF(strp, ...) do { if (asprintf(strp, __VA_ARGS__) >= 0) break; \ + ERROR("Failed to allocate memory, abort.\n"); exit(1); } while (0) + +/* Structure(s) declared in updater.h */ +struct updater_config; + +/* Structure(s) declared in updater_archive */ +struct archive; + +/* flashrom programmers. */ +static const char * const PROG_HOST = "host", + * const PROG_EC = "ec", + * const PROG_PD = "ec:dev=1"; + +/* Firmware slots */ +static const char * const FWACT_A = "A", + * const FWACT_B = "B"; + +enum active_slot { + SLOT_UNKNOWN = -1, + SLOT_A = 0, + SLOT_B, +}; + +/* Utilities for firmware images and (FMAP) sections */ +struct firmware_image { + const char *programmer; + uint32_t size; + uint8_t *data; + char *file_name; + char *ro_version, *rw_version_a, *rw_version_b; + FmapHeader *fmap_header; +}; + +/* + * Loads a firmware image from file. + * If archive is provided and file_name is a relative path, read the file from + * archive. + * Returns 0 on success, otherwise failure. + */ +int load_firmware_image(struct firmware_image *image, const char *file_name, + struct archive *archive); + +/* + * 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); + +/* + * Writes a section from given firmware image to system firmware. + * If section_name is NULL, write whole image. + * Returns 0 if success, non-zero if error. + */ +int write_system_firmware(struct updater_config *cfg, + const struct firmware_image *image, + const char *section_name); + +/* Frees the allocated resource from a firmware image object. */ +void free_firmware_image(struct firmware_image *image); + +struct firmware_section { + uint8_t *data; + size_t size; +}; + +/* + * Returns true if the given FMAP section exists in the firmware image. + */ +int firmware_section_exists(const struct firmware_image *image, + const char *section_name); + +/* + * Finds a firmware section by given name in the firmware image. + * If successful, return zero and *section argument contains the address and + * size of the section; otherwise failure. + */ +int find_firmware_section(struct firmware_section *section, + const struct firmware_image *image, + const char *section_name); + +/* + * Preserves (copies) the given section (by name) from image_from to image_to. + * The offset may be different, and the section data will be directly copied. + * If the section does not exist on either images, return as failure. + * If the source section is larger, contents on destination be truncated. + * If the source section is smaller, the remaining area is not modified. + * Returns 0 if success, non-zero if error. + */ +int preserve_firmware_section(const struct firmware_image *image_from, + struct firmware_image *image_to, + const char *section_name); + +/* + * Finds the GBB (Google Binary Block) header on a given firmware image. + * Returns a pointer to valid GBB header, or NULL on not found. + */ +struct vb2_gbb_header; +const struct vb2_gbb_header *find_gbb(const struct firmware_image *image); + +/* + * Strips a string (usually from shell execution output) by removing all the + * trailing characters in pattern. If pattern is NULL, match by space type + * characters (space, new line, tab, ... etc). + */ +void strip_string(char *s, const char *pattern); + +/* + * Saves everything from stdin to given output file. + * Returns 0 on success, otherwise failure. + */ +int save_file_from_stdin(const char *output); + +/* + * Executes a command on current host and returns stripped command output. + * If the command has failed (exit code is not zero), returns an empty string. + * The caller is responsible for releasing the returned string. + */ +char *host_shell(const char *command); + +enum wp_state { + WP_ERROR = -1, + WP_DISABLED = 0, + WP_ENABLED, +}; + +/* Helper function to return write protection status via given programmer. */ +enum wp_state host_get_wp(const char *programmer); + +/* + * Returns 1 if a given file (cbfs_entry_name) exists inside a particular CBFS + * section of an image file, otherwise 0. + */ +int cbfs_file_exists(const char *image_file, + const char *section_name, + const char *cbfs_entry_name); + +/* + * Extracts files from a CBFS on given region (section) of image_file. + * Returns the path to a temporary file on success, otherwise NULL. + */ +const char *cbfs_extract_file(struct updater_config *cfg, + const char *image_file, + const char *cbfs_region, + const char *cbfs_name); + +/* Utilities for accessing system properties */ +struct system_property { + int (*getter)(void); + int value; + int initialized; +}; + +enum system_property_type { + SYS_PROP_MAINFW_ACT, + SYS_PROP_TPM_FWVER, + SYS_PROP_FW_VBOOT2, + SYS_PROP_PLATFORM_VER, + SYS_PROP_WP_HW, + SYS_PROP_WP_SW, + SYS_PROP_MAX +}; + +/* Helper function to initialize system properties. */ +void init_system_properties(struct system_property *props, int num); + +/* Utilities for managing temporary files. */ +/* TODO(hungte) Change the functions below to take only tempfile as param. */ +struct tempfile { + char *filepath; + struct tempfile *next; +}; + +/* + * Helper function to create a new temporary file within updater's life cycle. + * Returns the path of new file, or NULL on failure. + */ +const char *updater_create_temp_file(struct updater_config *cfg); + +/* + * Helper function to remove all files created by create_temp_file(). + * This is intended to be called only once at end of program execution. + */ +void updater_remove_all_temp_files(struct updater_config *cfg); + +#endif /* VBOOT_REFERENCE_FUTILITY_UPDATER_UTILS_H_ */ |