From c1f9b056a3a7f15f33581a0cf6ae3bf5af828e7a Mon Sep 17 00:00:00 2001 From: Hung-Te Lin Date: Tue, 21 Aug 2018 18:57:19 +0800 Subject: futility: cmd_update: Implement updater logic "FULL UPDATE". The logic is same as --mode=factory or --mode=recovery,--wp=0 in legacy firmware updater. BUG=chromium:875551 TEST=make futil; futility update -i IMAGE tests/futility/run_test_scripts.sh $(pwd)/build/futility BRANCH=None Change-Id: Ifbfc4fb76f954483e779c8b508377d07561b67da Signed-off-by: Hung-Te Lin Reviewed-on: https://chromium-review.googlesource.com/1183651 Reviewed-by: Randall Spangler --- futility/cmd_update.c | 155 +++++++++++++++++++++++++++++++++++++++++- tests/futility/test_update.sh | 38 +++++++---- 2 files changed, 179 insertions(+), 14 deletions(-) diff --git a/futility/cmd_update.c b/futility/cmd_update.c index a3ca288f..a6727f05 100644 --- a/futility/cmd_update.c +++ b/futility/cmd_update.c @@ -270,10 +270,141 @@ static void free_image(struct firmware_image *image) memset(image, 0, sizeof(*image)); } +/* + * Emulates writing to firmware. + * Returns 0 if success, non-zero if error. + */ +static int emulate_write_firmware(const char *filename, + const struct firmware_image *image, + const char *section_name) +{ + struct firmware_image to_image = {0}; + struct firmware_section from, to; + int errorcnt = 0; + + from.data = image->data; + from.size = image->size; + + if (load_image(filename, &to_image)) { + Error("%s: Cannot load image from %s.\n", __FUNCTION__, + filename); + return -1; + } + + if (section_name) { + find_firmware_section(&from, image, section_name); + if (!from.data) { + Error("%s: No section %s in source image %s.\n", + __FUNCTION__, section_name, image->file_name); + errorcnt++; + } + find_firmware_section(&to, &to_image, section_name); + if (!to.data) { + Error("%s: No section %s in destination image %s.\n", + __FUNCTION__, section_name, filename); + errorcnt++; + } + } else if (image->size != to_image.size) { + Error("%s: Image size is different (%s:%d != %s:%d)\n", + __FUNCTION__, image->file_name, image->size, + to_image.file_name, to_image.size); + errorcnt++; + } else { + to.data = to_image.data; + to.size = to_image.size; + } + + if (!errorcnt) { + size_t to_write = Min(to.size, from.size); + + assert(from.data && to.data); + Debug("%s: Writing %d bytes\n", __FUNCTION__, to_write); + memcpy(to.data, from.data, to_write); + } + + if (!errorcnt && vb2_write_file( + filename, to_image.data, to_image.size)) { + Error("%s: Failed writing to file: %s\n", __FUNCTION__, + filename); + errorcnt++; + } + + free_image(&to_image); + return errorcnt; +} + +/* + * 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. + */ +static int write_firmware(struct updater_config *cfg, + const struct firmware_image *image, + const char *section_name) +{ + /* TODO(hungte) replace by mkstemp */ + const char *tmp_file = "/tmp/.fwupdate.write"; + const char *programmer = cfg->emulate ? image->emulation : + image->programmer; + + if (cfg->emulate) { + printf("%s: (emulation) %s %s from %s to %s.\n", + __FUNCTION__, + image->emulation ? "Writing" : "Skipped writing", + section_name ? section_name : "whole image", + image->file_name, programmer); + + if (!image->emulation) + return 0; + + /* + * TODO(hungte): Extract the real target from image->emulation, + * and allow to emulate writing with flashrom. + */ + return emulate_write_firmware( + cfg->image_current.file_name, image, + section_name); + + } + if (vb2_write_file(tmp_file, image->data, image->size) != VB2_SUCCESS) { + Error("%s: Cannot write temporary file for output: %s\n", + __FUNCTION__, tmp_file); + return -1; + } + return host_flashrom(FLASHROM_WRITE, tmp_file, programmer, 1, + section_name); +} + +/* + * Write a section from given firmware image to system firmware if possible. + * If section_name is NULL, write whole image. If the image has no data or if + * the section does not exist, ignore and return success. + * Returns 0 if success, non-zero if error. + */ +static int write_optional_firmware(struct updater_config *cfg, + const struct firmware_image *image, + const char *section_name) +{ + if (!image->data) { + Debug("%s: No data in <%s> image.\n", __FUNCTION__, + image->programmer); + return 0; + } + if (section_name && !firmware_section_exists(image, section_name)) { + Debug("%s: Image %s<%s> does not have section %s.\n", + __FUNCTION__, image->file_name, image->programmer, + section_name); + return 0; + } + + return write_firmware(cfg, image, section_name); +} + enum updater_error_codes { UPDATE_ERR_DONE, UPDATE_ERR_NO_IMAGE, UPDATE_ERR_SYSTEM_IMAGE, + UPDATE_ERR_WRITE_FIRMWARE, UPDATE_ERR_UNKNOWN, }; @@ -281,9 +412,31 @@ static const char * const updater_error_messages[] = { [UPDATE_ERR_DONE] = "Done (no error)", [UPDATE_ERR_NO_IMAGE] = "No image to update; try specify with -i.", [UPDATE_ERR_SYSTEM_IMAGE] = "Cannot load system active firmware.", + [UPDATE_ERR_WRITE_FIRMWARE] = "Failed writing firmware.", [UPDATE_ERR_UNKNOWN] = "Unknown error.", }; +/* + * The main updater for "Full update". + * This was also known as "--mode=factory" or "--mode=recovery, --wp=0" in + * legacy updater. + * Returns UPDATE_ERR_DONE if success, otherwise error. + */ +static enum updater_error_codes update_whole_firmware( + struct updater_config *cfg, + struct firmware_image *image_to) +{ + printf(">> FULL UPDATE: Updating whole firmware image(s), RO+RW.\n"); + + /* FMAP may be different so we should just update all. */ + if (write_firmware(cfg, image_to, NULL) || + write_optional_firmware(cfg, &cfg->ec_image, NULL) || + write_optional_firmware(cfg, &cfg->pd_image, NULL)) + return UPDATE_ERR_WRITE_FIRMWARE; + + return UPDATE_ERR_DONE; +} + /* * The main updater to update system firmware using the configuration parameter. * Returns UPDATE_ERR_DONE if success, otherwise failure. @@ -312,7 +465,7 @@ static enum updater_error_codes update_firmware(struct updater_config *cfg) image_from->file_name, image_from->ro_version, image_from->rw_version_a, image_from->rw_version_b); - return UPDATE_ERR_DONE; + return update_whole_firmware(cfg, image_to); } /* diff --git a/tests/futility/test_update.sh b/tests/futility/test_update.sh index c130a56c..7e2a142d 100755 --- a/tests/futility/test_update.sh +++ b/tests/futility/test_update.sh @@ -6,25 +6,37 @@ me=${0##*/} TMP="$me.tmp" -# Include /usr/sbin for flahsrom(8) -PATH=/usr/sbin:"${PATH}" - # Test data files LINK_BIOS="${SCRIPTDIR}/data/bios_link_mp.bin" PEPPY_BIOS="${SCRIPTDIR}/data/bios_peppy_mp.bin" -LINK_VERSION="Google_Link.2695.1.133" -PEPPY_VERSION="Google_Peppy.4389.89.0" # Work in scratch directory cd "$OUTDIR" set -o pipefail -# Prepare temporary files. -cp -f "${LINK_BIOS}" "${TMP}.emu" +# In all the test scenario, we want to test "updating from PEPPY to LINK". +TO_IMAGE=${TMP}.src.link +FROM_IMAGE=${TMP}.src.peppy +cp -f ${LINK_BIOS} ${TO_IMAGE} +cp -f ${PEPPY_BIOS} ${FROM_IMAGE} + +cp -f "${TO_IMAGE}" "${TMP}.expected.full" + +test_update() { + local test_name="$1" + local emu_src="$2" + local expected="$3" + local error_msg="${expected#!}" + local msg + + shift 3 + cp -f "${emu_src}" "${TMP}.emu" + echo "*** Test Item: ${test_name}" + "${FUTILITY}" update --emulate "${TMP}.emu" "$@" + cmp "${TMP}.emu" "${expected}" +} -# Test command execution. -versions="$("${FUTILITY}" update -i "${PEPPY_BIOS}" --emulate "${TMP}.emu" | - sed -n 's/.*(//; s/).*//p')" -test "${versions}" = \ -"RO:${PEPPY_VERSION}, RW/A:${PEPPY_VERSION}, RW/B:${PEPPY_VERSION} -RO:${LINK_VERSION}, RW/A:${LINK_VERSION}, RW/B:${LINK_VERSION}" +# Test Full update. +test_update "Full update" \ + "${FROM_IMAGE}" "${TMP}.expected.full" \ + -i "${TO_IMAGE}" -- cgit v1.2.1