The last_attempt_version and last_attempt_status fields in struct
fmp_state were carried but never populated, so the ESRT last-attempt
information exposed to the OS stayed at its boot-time defaults
regardless of what happened during an update.

Add the plumbing to maintain them: a helper to record the attempted
version and status, a mapping from internal efi_status_t codes to the
UEFI LAST_ATTEMPT_STATUS_* values, and reads/writes of the two fields
in efi_firmware_fill_version_info() and efi_firmware_set_fmp_state_var().
fw_version is now only advanced on a successful update so a failed
attempt no longer overwrites the recorded good version.

Signed-off-by: Balaji Selvanathan <[email protected]>
---
 lib/efi_loader/efi_firmware.c | 74 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 68 insertions(+), 6 deletions(-)

diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
index b41969c70fd..cbe8bcd7c64 100644
--- a/lib/efi_loader/efi_firmware.c
+++ b/lib/efi_loader/efi_firmware.c
@@ -52,9 +52,9 @@ struct fmp_payload_header {
  */
 struct fmp_state {
        u32 fw_version;
-       u32 lowest_supported_version; /* not used */
-       u32 last_attempt_version; /* not used */
-       u32 last_attempt_status; /* not used */
+       u32 lowest_supported_version; /* not used - read from DTB for security 
*/
+       u32 last_attempt_version;     /* used for esrt tracking */
+       u32 last_attempt_status;      /* used for esrt tracking */
 };
 
 /**
@@ -190,6 +190,49 @@ static void efi_firmware_get_lsv_from_dtb(u8 image_index,
        }
 }
 
+/**
+ * efi_firmware_set_last_attempt - set last attempt information
+ * @state:             Pointer to fmp state
+ * @attempt_version:   Version that was attempted
+ * @attempt_status:    Status of the attempt
+ *
+ * Set the last attempt version and status in the fmp_state structure.
+ */
+static void efi_firmware_set_last_attempt(struct fmp_state *state,
+                                         u32 attempt_version,
+                                         u32 attempt_status)
+{
+       state->last_attempt_version = attempt_version;
+       state->last_attempt_status = attempt_status;
+}
+
+/**
+ * efi_firmware_map_error_to_status - map internal errors to UEFI status codes
+ * @error:     Internal error code
+ *
+ * Map U-Boot internal error codes to UEFI-compliant last attempt status codes.
+ *
+ * Return: UEFI last attempt status code
+ */
+static u32 efi_firmware_map_error_to_status(efi_status_t error)
+{
+       switch (error) {
+       case EFI_SUCCESS:
+               return LAST_ATTEMPT_STATUS_SUCCESS;
+       case EFI_OUT_OF_RESOURCES:
+               return LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
+       case EFI_INVALID_PARAMETER:
+               return LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;
+       case EFI_SECURITY_VIOLATION:
+               return LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR;
+       case EFI_UNSUPPORTED:
+               return LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
+       case EFI_DEVICE_ERROR:
+       default:
+               return LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;
+       }
+}
+
 /**
  * efi_firmware_fill_version_info - fill the version information
  * @image_info:                Image information
@@ -237,8 +280,15 @@ void efi_firmware_fill_version_info(struct 
efi_firmware_image_descriptor *image_
 
        ret = efi_get_variable_int(varname, &fw_array->image_type_id,
                                   NULL, &size, var_state, NULL);
-       if (ret == EFI_SUCCESS && expected_size == size)
+       if (ret == EFI_SUCCESS && expected_size == size) {
                image_info->version = var_state[active_index].fw_version;
+               image_info->last_attempt_version = 
var_state[active_index].last_attempt_version;
+               image_info->last_attempt_status = 
var_state[active_index].last_attempt_status;
+       } else {
+               /* Default values if no previous state exists */
+               image_info->last_attempt_version = 0;
+               image_info->last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
+       }
 
        free(var_state);
 }
@@ -427,6 +477,10 @@ efi_status_t efi_firmware_capsule_authenticate(const void 
**p_image,
  * @image_index:       image index
  *
  * Update the FmpStateXXXX variable with the firmware update state.
+ * On successful update (last_attempt_status == LAST_ATTEMPT_STATUS_SUCCESS),
+ * updates fw_version to the new version.
+ * On failed update, preserves the old fw_version.
+ * Always updates last_attempt_version and last_attempt_status.
  *
  * Return:             status code
  */
@@ -471,11 +525,19 @@ efi_status_t efi_firmware_set_fmp_state_var(struct 
fmp_state *state, u8 image_in
                memset(var_state, 0, num_banks * sizeof(*var_state));
 
        /*
-        * Only the fw_version is set here.
+        * Set fw_version and last attempt information.
         * lowest_supported_version in FmpState variable is ignored since
         * it can be tampered if the file based EFI variable storage is used.
+        *
+        * Only update fw_version if the update succeeded.
+        * On failure, preserve the old fw_version to maintain accurate ESRT 
state.
         */
-       var_state[update_bank].fw_version = state->fw_version;
+       if (state->last_attempt_status == LAST_ATTEMPT_STATUS_SUCCESS)
+               var_state[update_bank].fw_version = state->fw_version;
+       /* else: keep existing fw_version (don't update on failure) */
+
+       var_state[update_bank].last_attempt_version = 
state->last_attempt_version;
+       var_state[update_bank].last_attempt_status = state->last_attempt_status;
 
        size = num_banks * sizeof(*var_state);
        ret = efi_set_variable_int(varname, image_type_id,

-- 
2.34.1

Reply via email to