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>
diff --git a/Makefile b/Makefile
index f8a8dbd..6721347 100644
--- a/Makefile
+++ b/Makefile
@@ -653,6 +653,7 @@
 	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 9f8ef52..4383aaa 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 @@
 };
 
 /*
- * 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 @@
 }
 
 /*
- * 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 @@
 }
 
 /*
- * 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 @@
 			  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 @@
 }
 
 /*
- * 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 @@
 }
 
 /*
- * 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 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 @@
 		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 @@
  */
 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 @@
 
 	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 @@
 			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 2f5603e..2f7040e 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 @@
 		  * 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 @@
 	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 @@
 /* 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_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 @@
  */
 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 c14f55a..9f037ea 100644
--- a/futility/updater_quirks.c
+++ b/futility/updater_quirks.c
@@ -275,32 +275,6 @@
 }
 
 /*
- * 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 @@
 	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 0000000..2cfb1b0
--- /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 0000000..686b088
--- /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_ */