From: Abdellatif El Khlifi <[email protected]> Accept the FWU at ExitBootServices()
Provide the FWU_ACCEPT_IMAGE ABI and setup an event triggered on ExitBootServices(). This mechanism notifies Secure world that the system booted successfully by accepting the images in trial state. Also, add FWU_ARM_PSA_ACCEPT_IMAGES config to allow platforms to switch off image acceptance in ExitBootServices(). Signed-off-by: Abdellatif El Khlifi <[email protected]> Signed-off-by: Alex Chapman <[email protected]> Signed-off-by: David Hu <[email protected]> Cc: Sughosh Ganu <[email protected]> Cc: Tom Rini <[email protected]> Cc: Ilias Apalodimas <[email protected]> Cc: Simon Glass <[email protected]> Cc: Michal Simek <[email protected]> Cc: Marek Vasut <[email protected]> Cc: Casey Connolly <[email protected]> --- include/fwu_arm_psa.h | 23 ++++- lib/efi_loader/Kconfig | 8 ++ lib/efi_loader/fwu_arm_psa.c | 171 +++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+), 1 deletion(-) diff --git a/include/fwu_arm_psa.h b/include/fwu_arm_psa.h index 5b82a7b0fb2..1177ccea0db 100644 --- a/include/fwu_arm_psa.h +++ b/include/fwu_arm_psa.h @@ -90,9 +90,10 @@ enum fwu_abis { FWU_WRITE_STREAM = 20, FWU_READ_STREAM = 21, FWU_COMMIT = 22, + FWU_ACCEPT_IMAGE = 23, /* To be updated when adding new FWU IDs */ FWU_FIRST_ID = FWU_DISCOVER, /* Lowest number ID */ - FWU_LAST_ID = FWU_COMMIT, /* Highest number ID */ + FWU_LAST_ID = FWU_ACCEPT_IMAGE, /* Highest number ID */ }; enum fwu_abi_errcode { @@ -316,6 +317,26 @@ struct fwu_write_stream_resp { int status; } __packed; +/** + * struct fwu_accept_image_args - fwu_accept_image ABI arguments + * @function_id: fwu_accept_image service ID + * @reserved: Reserved, must be zero + * @image_type_guid: GUID of the image to be accepted + */ +struct fwu_accept_image_args { + u32 function_id; + u32 reserved; + efi_guid_t image_type_guid; +} __packed; + +/** + * struct fwu_accept_image_resp - fwu_accept_image ABI returns + * @status: The ABI return status + */ +struct fwu_accept_image_resp { + int status; +} __packed; + /* * FWU directory information structures */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 50302a03eae..09932a98d6e 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -395,6 +395,14 @@ config FWU_KEEP_FMP_HEADER Select this option if you do not want to strip the FMP header of the payload +config FWU_ARM_PSA_ACCEPT_IMAGES + bool "Accept images at EFI ExitBootServices() level" + depends on FWU_ARM_PSA + default y + help + Select this option if reaching ExitBootServices() level means the boot succeeded + This option will add a callback at ExitBootServices() that accepts all the images. + config FWU_BUFFER_PAGES int "Number of 4KB pages in the FWU shared buffer" depends on FWU_ARM_PSA diff --git a/lib/efi_loader/fwu_arm_psa.c b/lib/efi_loader/fwu_arm_psa.c index 40e0611adb9..c467922a6d8 100644 --- a/lib/efi_loader/fwu_arm_psa.c +++ b/lib/efi_loader/fwu_arm_psa.c @@ -115,6 +115,14 @@ static struct fwu_abi_errmap err_msg_map[FWU_ERRMAP_COUNT] = { "FWU_DENIED: The system is not in a Staging state", }, }, + [FWU_ID_TO_ERRMAP_ID(FWU_ACCEPT_IMAGE)] = { + { + [FWU_UNKNOWN] = + "FWU_UNKNOWN: Image with type=image_type_guid is not managed by the Update Agent", + [FWU_DENIED] = + "FWU_DENIED: The system has not booted with the active bank, or the image cannot be accepted before being activated", + }, + }, }; /** @@ -712,6 +720,35 @@ static int fwu_write_stream(u32 handle, const u8 *payload, u32 payload_size) return ret; } +/** + * fwu_accept() - fwu_accept_image ABI + * + * @guid: GUID of the image to be accepted + * + * Description: Sets the status of the firmware image, with a given GUID + * to "accepted" in the active firmware bank. + * + * Return: 0 on success. Otherwise, failure. + */ +static int fwu_accept(const efi_guid_t *guid) +{ + struct fwu_accept_image_args *args = g_fwu_buf; + struct fwu_accept_image_resp *resp = g_fwu_buf; + char *svc_name = "FWU_ACCEPT_IMAGE"; + + if (!guid) + return -EINVAL; + + /* Filling the arguments in the shared buffer */ + args->function_id = FWU_ACCEPT_IMAGE; + + guidcpy(&args->image_type_guid, guid); + + /* Executing the FWU ABI through the FF-A bus */ + return fwu_invoke_svc(args->function_id, svc_name, + sizeof(*args), sizeof(*resp), NULL); +} + /** * fwu_update_image() - Update an image * @@ -1079,6 +1116,117 @@ failure: return ret; } +/** + * fwu_one_image_accepted() - Accept one image in trial state + * + * @img_type_guid: Pointer to the img_type_guid. + * @image_number: Image number for logging purposes. + * + * Description: Invoke FWU accept image ABI to accept the image. + * + * Return: true on success, false on failure. + */ +static bool fwu_one_image_accepted(const efi_guid_t *img_type_guid, + u32 image_number) +{ + int fwu_ret; + + fwu_ret = fwu_accept(img_type_guid); + if (fwu_ret) { + log_err("FWU: Failed to accept image #%d\n", + image_number + 1); + return false; + } + + log_debug("FWU: Image #%d accepted\n", image_number + 1); + + return true; +} + +/** + * fwu_all_images_accepted() - Accept any pending firmware update images + * + * Description: Read the acceptance state of each image from the cached image + * directory. Then, accept the images which are not accepted yet. + * + * Return: true on success, false on failure. + */ +static bool fwu_all_images_accepted(void) +{ + u32 i; + bool accepted; + struct fwu_image_info_entry *entry; + + for (i = 0; i < g_fwu_cached_directory->num_images; i++) { + entry = &g_fwu_cached_directory->entries[i]; + if (entry->accepted) + continue; + + accepted = fwu_one_image_accepted(&entry->img_type_guid, i); + if (!accepted) + return false; + } + + return true; +} + +/** + * fwu_accept_notify_exit_boot_services() - ExitBootServices callback + * + * @event: callback event + * @context: callback context + * + * Description: Reaching ExitBootServices() level means the boot succeeded. + * So, accept all the images. + */ +static void EFIAPI fwu_accept_notify_exit_boot_services(struct efi_event *event, + void *context) +{ + efi_status_t efi_ret = EFI_SUCCESS; + bool all_accepted; + + EFI_ENTRY("%p, %p", event, context); + + if (g_in_trial) { + all_accepted = fwu_all_images_accepted(); + if (!all_accepted) { + efi_ret = EFI_ACCESS_DENIED; + goto out; + } + + } else { + log_info("FWU: ExitBootServices: Booting in regular state\n"); + } + +out: + EFI_EXIT(efi_ret); +} + +/** + * fwu_setup_accept_event() - Setup the FWU accept event + * + * Description: Create a FWU accept event triggered on ExitBootServices(). + * + * Return: 0 on success. Otherwise, failure. + */ +static int fwu_setup_accept_event(void) +{ + efi_status_t efi_ret; + struct efi_event *evt = NULL; + + efi_ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK, + fwu_accept_notify_exit_boot_services, NULL, + &efi_guid_event_group_exit_boot_services, + &evt); + if (efi_ret != EFI_SUCCESS) { + log_err("FWU: Cannot install accept event %p, err (%lu)\n", evt, + efi_ret); + return -EPERM; + } + + return 0; +} + /** * fwu_agent_init() - Setup the FWU agent * @@ -1092,6 +1240,7 @@ failure: int fwu_agent_init(void) { int ret; + int i; ret = uclass_first_device_err(UCLASS_FFA, &g_dev); if (ret) { @@ -1115,6 +1264,28 @@ int fwu_agent_init(void) if (ret) goto failure; + /* + * When booting from a bank, each image is expected to be either + * accepted or in a trial state. + */ + for (i = 0; i < g_fwu_cached_directory->num_images; i++) { + if (g_fwu_cached_directory->entries[i].accepted == 0) { + g_in_trial = true; + break; + } + } + + if (IS_ENABLED(CONFIG_FWU_ARM_PSA_ACCEPT_IMAGES)) { + ret = fwu_setup_accept_event(); + if (ret) + goto failure; + } + + if (g_in_trial) + log_info("FWU: System booting in Trial State\n"); + else + log_info("FWU: System booting in Regular State\n"); + g_fwu_initialized = true; return 0; -- 2.43.0
