[PATCH v2 0/6] Miscellaneous FWU fixes

The following set of patches are miscellaneous fixes and some hardening of the FWU update logic.
Changes since V1: * Squash patch 1 and 2 in the earlier version into a single patch. * Have a single check for image descriptor offset, and have a log message mention that image descriptor mandatory. * Use a macro FWU_IMG_DESC_OFFSET for the image descriptor offset. * Use the enum names for primary and secondary partitions from fwu.h instead of defining new macros.
Sughosh Ganu (6): fwu: v2: try reading both copies of metadata fwu: v1: do a version check for the metadata fwu: check all images for transitioning out of Trial State fwu: add dependency checks for selecting FWU metadata version fwu: do not allow capsule processing on exceeding Trial Counter threshold fwu: print a message if empty capsule checks fail
include/fwu.h | 11 +++++ lib/efi_loader/efi_capsule.c | 11 +++-- lib/fwu_updates/Kconfig | 1 + lib/fwu_updates/fwu.c | 31 +++++++++++++- lib/fwu_updates/fwu_v1.c | 18 ++++++-- lib/fwu_updates/fwu_v2.c | 81 ++++++++++++++++++++---------------- 6 files changed, 108 insertions(+), 45 deletions(-)

In the version 2 of the FWU metadata, the metadata is broken into two parts, a top-level structure, which provides information on the total size of the structure among other things. Try reading the primary partition first, and if that fails, try reading the secondary partition. This will help in the scenario where the primary metadata partition has been corrupted, but the secondary partition is intact.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V1: * Squash patch 1 and 2 in the earlier version into a single patch. * Have a single check for image descriptor offset, and have a log message mention that image descriptor mandatory. * Use a macro FWU_IMG_DESC_OFFSET for the image descriptor offset. * Use the enum names for primary and secondary partitions from fwu.h instead of defining new macros.
lib/fwu_updates/fwu_v2.c | 78 +++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 36 deletions(-)
diff --git a/lib/fwu_updates/fwu_v2.c b/lib/fwu_updates/fwu_v2.c index 108bc9bb4a..a055a207bf 100644 --- a/lib/fwu_updates/fwu_v2.c +++ b/lib/fwu_updates/fwu_v2.c @@ -10,6 +10,9 @@ #include <linux/types.h>
#define FWU_MDATA_VERSION 0x2U +#define FWU_IMG_DESC_OFFSET 0x20U + +static struct fwu_mdata g_mdata;
static inline struct fwu_fw_store_desc *fwu_get_fw_desc(struct fwu_mdata *mdata) { @@ -58,24 +61,6 @@ static int fwu_mdata_sanity_checks(void) struct fwu_data *data = fwu_get_data(); struct fwu_mdata *mdata = data->fwu_mdata;
- if (mdata->version != FWU_MDATA_VERSION) { - log_err("FWU metadata version %u. Expected value of %u\n", - mdata->version, FWU_MDATA_VERSION); - return -EINVAL; - } - - if (!mdata->desc_offset) { - log_err("No image information provided with the Metadata. "); - log_err("Image information expected in the metadata\n"); - return -EINVAL; - } - - if (mdata->desc_offset != 0x20) { - log_err("Descriptor Offset(0x%x) in the FWU Metadata not equal to 0x20\n", - mdata->desc_offset); - return -EINVAL; - } - num_banks = fwu_get_fw_desc(mdata)->num_banks; num_images = fwu_get_fw_desc(mdata)->num_images;
@@ -127,6 +112,35 @@ static int fwu_trial_state_start(uint update_index) return 0; }
+static bool fwu_get_mdata_mandatory(uint part) +{ + int ret = 0; + struct udevice *fwu_dev = fwu_get_dev(); + + memset(&g_mdata, 0, sizeof(struct fwu_mdata)); + + ret = fwu_read_mdata(fwu_dev, &g_mdata, + part == PRIMARY_PART ? true : false, + sizeof(struct fwu_mdata)); + if (ret) + return false; + + if (g_mdata.version != FWU_MDATA_VERSION) { + log_err("FWU partition %u has metadata version %u. Expected value of %u\n", + part, g_mdata.version, FWU_MDATA_VERSION); + return false; + } + + if (g_mdata.desc_offset != FWU_IMG_DESC_OFFSET) { + log_err("Descriptor Offset(0x%x) in the FWU Metadata partition %u not equal to 0x20\n", + g_mdata.desc_offset, part); + log_err("Image information expected in the metadata\n"); + return false; + } + + return true; +} + /** * fwu_populate_mdata_image_info() - Populate the image information * of the metadata @@ -187,24 +201,14 @@ int fwu_state_machine_updates(bool trial_state, uint32_t update_index) */ int fwu_get_mdata_size(uint32_t *mdata_size) { - int ret = 0; - struct fwu_mdata mdata = { 0 }; struct fwu_data *data = fwu_get_data(); - struct udevice *fwu_dev = fwu_get_dev();
if (data->metadata_size) { *mdata_size = data->metadata_size; return 0; }
- ret = fwu_read_mdata(fwu_dev, &mdata, 1, - sizeof(struct fwu_mdata)); - if (ret) { - log_err("FWU metadata read failed\n"); - return ret; - } - - *mdata_size = mdata.metadata_size; + *mdata_size = g_mdata.metadata_size; if (!*mdata_size) return -EINVAL;
@@ -224,21 +228,23 @@ int fwu_get_mdata_size(uint32_t *mdata_size) int fwu_init(void) { int ret; - struct fwu_mdata mdata = { 0 }; - struct udevice *fwu_dev = fwu_get_dev();
/* * First we read only the top level structure * and get the size of the complete structure. + * Try reading the first partition first, if + * that does not work, try the secondary + * partition. The idea is, if one of the + * partitions is corrupted, it should be restored + * from the intact partition. */ - ret = fwu_read_mdata(fwu_dev, &mdata, 1, - sizeof(struct fwu_mdata)); - if (ret) { + if (!fwu_get_mdata_mandatory(PRIMARY_PART) && + !fwu_get_mdata_mandatory(SECONDARY_PART)) { log_err("FWU metadata read failed\n"); - return ret; + return -1; }
- ret = fwu_mdata_copies_allocate(mdata.metadata_size); + ret = fwu_mdata_copies_allocate(g_mdata.metadata_size); if (ret) return ret;

Do a sanity check that the version of the FWU metadata that has been read aligns with the version enabled in the image. This allows to indicate an early failure as part of the FWU module initialisation.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V1: None
lib/fwu_updates/fwu_v1.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/lib/fwu_updates/fwu_v1.c b/lib/fwu_updates/fwu_v1.c index efb8d51500..023e43728d 100644 --- a/lib/fwu_updates/fwu_v1.c +++ b/lib/fwu_updates/fwu_v1.c @@ -146,6 +146,7 @@ int fwu_init(void) { int ret; uint32_t mdata_size; + struct fwu_mdata mdata = {0};
fwu_get_mdata_size(&mdata_size);
@@ -157,10 +158,16 @@ int fwu_init(void) * Now read the entire structure, both copies, and * validate that the copies. */ - ret = fwu_get_mdata(NULL); + ret = fwu_get_mdata(&mdata); if (ret) return ret;
+ if (mdata.version != 0x1) { + log_err("FWU metadata version %u. Expected value of %u\n", + mdata.version, FWU_MDATA_VERSION); + return -EINVAL; + } + fwu_data_init();
return 0;

The platform transitions out of Trial State into the Regular State only when all the images in the update bank have been accepted. Check for this condition before transitioning out of Trial State.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Tested-by: Michal Simek michal.simek@amd.com --- Changes since V1: None
include/fwu.h | 11 +++++++++++ lib/fwu_updates/fwu.c | 25 +++++++++++++++++++++++++ lib/fwu_updates/fwu_v1.c | 9 ++++++--- lib/fwu_updates/fwu_v2.c | 3 +++ 4 files changed, 45 insertions(+), 3 deletions(-)
diff --git a/include/fwu.h b/include/fwu.h index 77ec65e618..70a5166f5e 100644 --- a/include/fwu.h +++ b/include/fwu.h @@ -417,4 +417,15 @@ int fwu_state_machine_updates(bool trial_state, uint32_t update_index); */ int fwu_init(void);
+/** + * fwu_bank_accepted() - Has the bank been accepted + * @data: Version agnostic FWU metadata information + * @bank: Update bank to check + * + * Check in the given bank if all the images have been accepted. + * + * Return: true if all images accepted, false otherwise + */ +bool fwu_bank_accepted(struct fwu_data *data, uint32_t bank); + #endif /* _FWU_H_ */ diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index c7fc8987be..a94a769a2b 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -28,6 +28,31 @@ enum { IMAGE_ACCEPT_CLEAR, };
+/** + * fwu_bank_accepted() - Has the bank been accepted + * @data: Version agnostic FWU metadata information + * @bank: Update bank to check + * + * Check in the given bank if all the images have been accepted. + * + * Return: true if all images accepted, false otherwise + */ +bool fwu_bank_accepted(struct fwu_data *data, uint32_t bank) +{ + u32 i; + struct fwu_image_entry *img_entry; + struct fwu_image_bank_info *img_bank_info; + + img_entry = &data->fwu_images[0]; + for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) { + img_bank_info = &img_entry[i].img_bank_info[bank]; + if (!img_bank_info->accepted) + return false; + } + + return true; +} + static int trial_counter_update(u16 *trial_state_ctr) { bool delete; diff --git a/lib/fwu_updates/fwu_v1.c b/lib/fwu_updates/fwu_v1.c index 023e43728d..c311a8857a 100644 --- a/lib/fwu_updates/fwu_v1.c +++ b/lib/fwu_updates/fwu_v1.c @@ -52,11 +52,14 @@ static void fwu_data_init(void) memcpy(dst_img_info, src_img_info, image_info_size); }
-static int fwu_trial_state_update(bool trial_state) +static int fwu_trial_state_update(bool trial_state, uint32_t bank) { int ret; struct fwu_data *data = fwu_get_data();
+ if (!trial_state && !fwu_bank_accepted(data, bank)) + return 0; + if (trial_state) { ret = fwu_trial_state_ctr_start(); if (ret) @@ -112,9 +115,9 @@ void fwu_populate_mdata_image_info(struct fwu_data *data) * Return: 0 if OK, -ve on error */ int fwu_state_machine_updates(bool trial_state, - __maybe_unused uint32_t update_index) + uint32_t update_index) { - return fwu_trial_state_update(trial_state); + return fwu_trial_state_update(trial_state, update_index); }
/** diff --git a/lib/fwu_updates/fwu_v2.c b/lib/fwu_updates/fwu_v2.c index a055a207bf..ce46904ff2 100644 --- a/lib/fwu_updates/fwu_v2.c +++ b/lib/fwu_updates/fwu_v2.c @@ -85,6 +85,9 @@ static int fwu_bank_state_update(bool trial_state, uint32_t bank) struct fwu_data *data = fwu_get_data(); struct fwu_mdata *mdata = data->fwu_mdata;
+ if (!trial_state && !fwu_bank_accepted(data, bank)) + return 0; + mdata->bank_state[bank] = data->bank_state[bank] = trial_state ? FWU_BANK_VALID : FWU_BANK_ACCEPTED;

The FWU code supports both versions of the FWU metadata, i.e. v1 and v2. A platform can then select one of the two versions through a config symbol. Put a dependency in the FWU metadata version selection config symbol to ensure that both versions of the metadata cannot be enabled.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Reviewed-by: Michal Simek michal.simek@amd.com --- Changes since V1: None
lib/fwu_updates/Kconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/lib/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig index 51b7fbbefd..a722107c12 100644 --- a/lib/fwu_updates/Kconfig +++ b/lib/fwu_updates/Kconfig @@ -40,6 +40,7 @@ config FWU_MDATA_V1
config FWU_MDATA_V2 bool "Enable support FWU Metadata version 2" + depends on !FWU_MDATA_V1 help The FWU specification supports two versions of the metadata structure. This option enables support for FWU

When in Trial State, the platform keeps a count of the number of times it has booted in the Trial State. Once the threshold of the maximum allowed count exceeds, the platform reverts to boot from a different bank on subsequent boot, thus coming out of the Trial State. It is expected that all the updated images would be accepted or rejected while the platform is in Trial State. Put in checks so that it is not possible to apply an empty capsule once the max Trial Count exceeds.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org Tested-by: Michal Simek michal.simek@amd.com --- Changes since V1: None
lib/fwu_updates/fwu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index a94a769a2b..7f085a0211 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -113,6 +113,8 @@ static int fwu_trial_count_update(void) ret = fwu_revert_boot_index(); if (ret) log_err("Unable to revert active_index\n"); + + trial_counter_update(NULL); ret = 1; } else { log_info("Trial State count: attempt %d out of %d\n", @@ -762,8 +764,8 @@ static int fwu_boottime_checks(void) return 0;
in_trial = in_trial_state(); - if (!in_trial || (ret = fwu_trial_count_update()) > 0) - ret = trial_counter_update(NULL); + + ret = in_trial ? fwu_trial_count_update() : trial_counter_update(NULL);
if (!ret) boottime_check = 1;

When dealing with processing of the empty capsule, the capsule gets applied only when the checks for the empty capsule pass. Print a message to highlight if empty capsule checks fail, and return an error value, similar to the normal capsules.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- Changes since V1: New patch
lib/efi_loader/efi_capsule.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index 635088f25a..22677f395b 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -563,9 +563,14 @@ static efi_status_t efi_capsule_update_firmware( bool fw_accept_os;
if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) { - if (fwu_empty_capsule_checks_pass() && - fwu_empty_capsule(capsule_data)) - return fwu_empty_capsule_process(capsule_data); + if (fwu_empty_capsule(capsule_data)) { + if (fwu_empty_capsule_checks_pass()) { + return fwu_empty_capsule_process(capsule_data); + } else { + log_err("FWU empty capsule checks failed. Cannot start update\n"); + return EFI_INVALID_PARAMETER; + } + }
if (!fwu_update_checks_pass()) { log_err("FWU checks failed. Cannot start update\n");

On 9/9/24 13:20, Sughosh Ganu wrote:
The following set of patches are miscellaneous fixes and some hardening of the FWU update logic.
Changes since V1:
- Squash patch 1 and 2 in the earlier version into a single patch.
- Have a single check for image descriptor offset, and have a log message mention that image descriptor mandatory.
- Use a macro FWU_IMG_DESC_OFFSET for the image descriptor offset.
- Use the enum names for primary and secondary partitions from fwu.h instead of defining new macros.
Sughosh Ganu (6): fwu: v2: try reading both copies of metadata fwu: v1: do a version check for the metadata fwu: check all images for transitioning out of Trial State fwu: add dependency checks for selecting FWU metadata version fwu: do not allow capsule processing on exceeding Trial Counter threshold fwu: print a message if empty capsule checks fail
include/fwu.h | 11 +++++ lib/efi_loader/efi_capsule.c | 11 +++-- lib/fwu_updates/Kconfig | 1 + lib/fwu_updates/fwu.c | 31 +++++++++++++- lib/fwu_updates/fwu_v1.c | 18 ++++++-- lib/fwu_updates/fwu_v2.c | 81 ++++++++++++++++++++---------------- 6 files changed, 108 insertions(+), 45 deletions(-)
Tested-by: Michal Simek michal.simek@amd.com
Thanks, Michal
participants (2)
-
Michal Simek
-
Sughosh Ganu