summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHung-Te Lin <hungte@chromium.org>2019-11-20 15:15:02 +0800
committerCommit Bot <commit-bot@chromium.org>2020-01-05 08:51:16 +0000
commitf17876b8846f9e5272fe1eef2987da79c4243666 (patch)
treef1f93775ae55ef447b10791a808a112461801145
parent2673e4cf122e8345bfade469aebad6cf1bbbc6f5 (diff)
downloadvboot-f17876b8846f9e5272fe1eef2987da79c4243666.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> (cherry picked from commit e8618380056e338d563501e2c9e03e9ff7102cc5) Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/1987578 Reviewed-by: Shelley Chen <shchen@chromium.org> Commit-Queue: Shelley Chen <shchen@chromium.org> Tested-by: Shelley Chen <shchen@chromium.org>
-rw-r--r--Makefile1
-rw-r--r--futility/updater.c638
-rw-r--r--futility/updater.h106
-rw-r--r--futility/updater_quirks.c28
-rw-r--r--futility/updater_utils.c641
-rw-r--r--futility/updater_utils.h200
6 files changed, 854 insertions, 760 deletions
diff --git a/Makefile b/Makefile
index f8a8dbd6..6721347d 100644
--- a/Makefile
+++ b/Makefile
@@ -653,6 +653,7 @@ FUTIL_SRCS = \
futility/updater.c \
futility/updater_archive.c \
futility/updater_quirks.c \
+ futility/updater_utils.c \
futility/vb1_helper.c \
futility/vb2_helper.c
diff --git a/futility/updater.c b/futility/updater.c
index 9f8ef52e..4383aaa6 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(&section, 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(&section, 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)
@@ -1537,13 +950,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;
@@ -1558,7 +971,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;
@@ -1917,7 +1331,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)
@@ -1929,44 +1342,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
@@ -2006,7 +1388,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 2f5603e2..2f7040ec 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(&section, 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(&section, 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_ */