| From 652259af2f795a5d69c628ae7b1e79d33c234abd Mon Sep 17 00:00:00 2001 |
| From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> |
| Date: Thu, 11 Nov 2021 16:27:59 +0000 |
| Subject: [PATCH 11/24] efi: corstone1000: introduce EFI capsule update |
| |
| This commit provides capsule update feature for Corstone1000. |
| |
| This feature is available before and after ExitBootServices(). |
| |
| A platform specific capsule buffer is allocated. This buffer |
| is physically contiguous and allocated at the start of the DDR |
| memory after u-boot relocation to the end of DDR. |
| |
| The capsule buffer is shared between u-boot and the secure world. |
| On UpdateCapsule() , capsule data is copied to the buffer |
| and a buffer ready event is generated using FF-A transport. |
| |
| On ExitBootServices() a kernel started event is sent to the |
| SE Proxy FW update service. This event is generated on each boot. |
| |
| Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> |
| Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> |
| --- |
| include/configs/corstone1000.h | 18 +++++ |
| include/efi_loader.h | 4 +- |
| lib/efi_loader/efi_boottime.c | 47 ++++++++++++ |
| lib/efi_loader/efi_capsule.c | 135 ++++++++++++++++++++++++++++++++- |
| lib/efi_loader/efi_setup.c | 15 ++++ |
| 5 files changed, 215 insertions(+), 4 deletions(-) |
| |
| diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h |
| index afc9ccfc192b..a400cdef69d0 100644 |
| --- a/include/configs/corstone1000.h |
| +++ b/include/configs/corstone1000.h |
| @@ -14,6 +14,24 @@ |
| |
| #include <linux/sizes.h> |
| |
| +/* The SE Proxy partition ID*/ |
| +#define CORSTONE1000_SEPROXY_PART_ID (0x8002) |
| + |
| +/* Update service ID provided by the SE Proxy secure partition*/ |
| +#define CORSTONE1000_SEPROXY_UPDATE_SVC_ID (0x4) |
| + |
| +/* Notification events used with SE Proxy update service */ |
| +#define CORSTONE1000_BUFFER_READY_EVT (0x1) |
| +#define CORSTONE1000_KERNEL_STARTED_EVT (0x2) |
| + |
| +/* Size in 4KB pages of the EFI capsule buffer */ |
| +#define CORSTONE1000_CAPSULE_BUFFER_SIZE (8192) /* 32 MB */ |
| + |
| +/* Capsule GUID */ |
| +#define EFI_CORSTONE1000_CAPSULE_ID_GUID \ |
| + EFI_GUID(0x3a770ddc, 0x409b, 0x48b2, 0x81, 0x41, \ |
| + 0x93, 0xb7, 0xc6, 0x0b, 0x20, 0x9e) |
| + |
| /* MM SP UUID binary data (little-endian format) */ |
| #define MM_SP_UUID_DATA \ |
| 0xed, 0x32, 0xd5, 0x33, \ |
| diff --git a/include/efi_loader.h b/include/efi_loader.h |
| index 5b41985244e2..796419b69b40 100644 |
| --- a/include/efi_loader.h |
| +++ b/include/efi_loader.h |
| @@ -984,11 +984,11 @@ extern const struct efi_firmware_management_protocol efi_fmp_fit; |
| extern const struct efi_firmware_management_protocol efi_fmp_raw; |
| |
| /* Capsule update */ |
| -efi_status_t EFIAPI efi_update_capsule( |
| +efi_status_t __efi_runtime EFIAPI efi_update_capsule( |
| struct efi_capsule_header **capsule_header_array, |
| efi_uintn_t capsule_count, |
| u64 scatter_gather_list); |
| -efi_status_t EFIAPI efi_query_capsule_caps( |
| +efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps( |
| struct efi_capsule_header **capsule_header_array, |
| efi_uintn_t capsule_count, |
| u64 *maximum_capsule_size, |
| diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c |
| index c68d9ed4f0bd..f2b5c7834c01 100644 |
| --- a/lib/efi_loader/efi_boottime.c |
| +++ b/lib/efi_loader/efi_boottime.c |
| @@ -2095,6 +2095,44 @@ static void efi_exit_caches(void) |
| #endif |
| } |
| |
| +#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) |
| +/** |
| + * efi_corstone1000_kernel_started_event - notifies SE Proxy FW update service |
| + * |
| + * This function notifies the SE Proxy update service that the kernel has already started |
| + * |
| + * Return: |
| + * |
| + * 0: on success, otherwise failure |
| + */ |
| +static int efi_corstone1000_kernel_started_event(void) |
| +{ |
| + struct ffa_interface_data func_data = {0}; |
| + struct ffa_send_direct_data msg = {0}; |
| + u16 part_id = CORSTONE1000_SEPROXY_PART_ID; |
| + |
| + log_debug("[%s]\n", __func__); |
| + |
| + /* |
| + * telling the driver which partition to use |
| + */ |
| + func_data.data0_size = sizeof(part_id); |
| + func_data.data0 = &part_id; |
| + |
| + /* |
| + * setting the kernel started event arguments |
| + */ |
| + msg.a3 = CORSTONE1000_SEPROXY_UPDATE_SVC_ID; |
| + msg.a5 = CORSTONE1000_KERNEL_STARTED_EVT; |
| + |
| + func_data.data1_size = sizeof(msg); |
| + func_data.data1 = &msg; |
| + |
| + return ffa_helper_msg_send_direct_req(&func_data); |
| +} |
| + |
| +#endif |
| + |
| /** |
| * efi_exit_boot_services() - stop all boot services |
| * @image_handle: handle of the loaded image |
| @@ -2208,6 +2246,15 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, |
| /* Recalculate CRC32 */ |
| efi_update_table_header_crc32(&systab.hdr); |
| |
| +#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) |
| + /* Notifying SE Proxy FW update service */ |
| + ffa_ret = efi_corstone1000_kernel_started_event(); |
| + if (ffa_ret) |
| + debug("[efi_boottime][ERROR]: Failure to notify SE Proxy FW update service\n"); |
| + else |
| + debug("[efi_boottime][INFO]: SE Proxy FW update service notified\n"); |
| +#endif |
| + |
| /* Give the payload some time to boot */ |
| efi_set_watchdog(0); |
| WATCHDOG_RESET(); |
| diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c |
| index a6b98f066a0b..a0689ba912fc 100644 |
| --- a/lib/efi_loader/efi_capsule.c |
| +++ b/lib/efi_loader/efi_capsule.c |
| @@ -25,6 +25,14 @@ |
| #include <crypto/pkcs7_parser.h> |
| #include <linux/err.h> |
| |
| +#ifdef CONFIG_TARGET_CORSTONE1000 |
| +#include <arm_ffa_helper.h> |
| +#include <cpu_func.h> |
| + |
| +void *__efi_runtime_data corstone1000_capsule_buf; /* capsule shared buffer virtual address */ |
| +efi_guid_t corstone1000_capsule_guid = EFI_CORSTONE1000_CAPSULE_ID_GUID; |
| +#endif |
| + |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID; |
| @@ -512,6 +520,89 @@ static efi_status_t efi_capsule_update_firmware( |
| } |
| #endif /* CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT */ |
| |
| +#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) |
| + |
| +/** |
| + * efi_corstone1000_alloc_capsule_shared_buf - allocate capsule shared buffer |
| + * @capsule_image_size: The capsule data (header + payload) |
| + * |
| + * This function allocates the physically contiguous buffer shared between u-boot |
| + * and the secure world. On UpdateCapsule() capsule data is copied to the buffer |
| + * and a door bell event is generated. |
| + * The buffer is allocated at the start of the DDR memory after u-boot has been relocated |
| + * to the end of DDR. |
| + * |
| + * Return: |
| + * |
| + * 0: on success, otherwise failure |
| + */ |
| +efi_status_t efi_corstone1000_alloc_capsule_shared_buf(void) |
| +{ |
| + efi_status_t efi_ret; |
| + u64 ram_base = CONFIG_SYS_SDRAM_BASE; |
| + |
| + log_debug("[%s]\n", __func__); |
| + |
| + efi_ret = efi_allocate_pages(EFI_ALLOCATE_ADDRESS, |
| + EFI_RUNTIME_SERVICES_DATA, |
| + CORSTONE1000_CAPSULE_BUFFER_SIZE, |
| + &ram_base); |
| + |
| + if (efi_ret != EFI_SUCCESS) { |
| + corstone1000_capsule_buf = NULL; |
| + log_err("EFI: Corstone1000: Allocating capsule shared buffer error (%d)\n" |
| + , (int)efi_ret); |
| + return efi_ret; |
| + } |
| + |
| + log_info("EFI: Corstone1000: Capsule shared buffer at 0x%x , size %d pages\n" |
| + , (unsigned int)ram_base, |
| + CORSTONE1000_CAPSULE_BUFFER_SIZE); |
| + |
| + corstone1000_capsule_buf = (void *)map_sysmem((phys_addr_t)ram_base, 0); |
| + |
| + return EFI_SUCCESS; |
| +} |
| + |
| +/** |
| + * efi_corstone1000_buffer_ready_event - issue door bell event |
| + * @capsule_image_size: The capsule data (header + payload) |
| + * |
| + * This function notifies the SE Proxy update service that capsule data is available |
| + * in the capsule shared buffer. |
| + * |
| + * Return: |
| + * |
| + * 0: on success, otherwise failure |
| + */ |
| +static int __efi_runtime efi_corstone1000_buffer_ready_event(u32 capsule_image_size) |
| +{ |
| + struct ffa_interface_data func_data = {0}; |
| + struct ffa_send_direct_data msg = {0}; |
| + u16 part_id = CORSTONE1000_SEPROXY_PART_ID; |
| + |
| + log_debug("[%s]\n", __func__); |
| + |
| + /* |
| + * telling the driver which partition to use |
| + */ |
| + func_data.data0_size = sizeof(part_id); |
| + func_data.data0 = &part_id; |
| + |
| + /* |
| + * setting the buffer ready event arguments |
| + */ |
| + msg.a3 = CORSTONE1000_SEPROXY_UPDATE_SVC_ID; |
| + msg.a4 = capsule_image_size; |
| + msg.a5 = CORSTONE1000_BUFFER_READY_EVT; |
| + |
| + func_data.data1_size = sizeof(msg); |
| + func_data.data1 = &msg; |
| + |
| + return ffa_helper_msg_send_direct_req(&func_data); |
| +} |
| +#endif |
| + |
| /** |
| * efi_update_capsule() - process information from operating system |
| * @capsule_header_array: Array of virtual address pointers |
| @@ -525,7 +616,7 @@ static efi_status_t efi_capsule_update_firmware( |
| * |
| * Return: status code |
| */ |
| -efi_status_t EFIAPI efi_update_capsule( |
| +efi_status_t __efi_runtime EFIAPI efi_update_capsule( |
| struct efi_capsule_header **capsule_header_array, |
| efi_uintn_t capsule_count, |
| u64 scatter_gather_list) |
| @@ -542,6 +633,13 @@ efi_status_t EFIAPI efi_update_capsule( |
| goto out; |
| } |
| |
| +#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) |
| + if (capsule_count != 1 || !corstone1000_capsule_buf) { |
| + ret = EFI_INVALID_PARAMETER; |
| + goto out; |
| + } |
| +#endif |
| + |
| ret = EFI_SUCCESS; |
| for (i = 0, capsule = *capsule_header_array; i < capsule_count; |
| i++, capsule = *(++capsule_header_array)) { |
| @@ -554,6 +652,39 @@ efi_status_t EFIAPI efi_update_capsule( |
| |
| log_debug("Capsule[%d] (guid:%pUs)\n", |
| i, &capsule->capsule_guid); |
| + |
| +#if CONFIG_IS_ENABLED(TARGET_CORSTONE1000) |
| + if (guidcmp(&corstone1000_capsule_guid, &capsule->capsule_guid)) { |
| + ret = EFI_INVALID_PARAMETER; |
| + log_err("Corstone1000: Invalid capsule GUID\n"); |
| + goto out; |
| + } |
| + |
| + if (efi_size_in_pages(capsule->capsule_image_size) > |
| + CORSTONE1000_CAPSULE_BUFFER_SIZE) { |
| + log_err("Corstone1000: Capsule data size exceeds the shared buffer size\n"); |
| + ret = EFI_BUFFER_TOO_SMALL; |
| + goto out; |
| + } |
| + |
| + /* copy the data to the contiguous buffer */ |
| + efi_memcpy_runtime(corstone1000_capsule_buf, capsule, capsule->capsule_image_size); |
| + |
| + /* invalidate the data cache */ |
| + invalidate_dcache_all(); |
| + |
| + /* issue buffer ready event */ |
| + ret = efi_corstone1000_buffer_ready_event(capsule->capsule_image_size); |
| + if (ret) { |
| + log_err("EFI: Corstone1000: Buffer ready event error (%d)\n", (int)ret); |
| + ret = EFI_DEVICE_ERROR; |
| + } else { |
| + ret = EFI_SUCCESS; |
| + } |
| + |
| + goto out; |
| +#endif |
| + |
| if (!guidcmp(&capsule->capsule_guid, |
| &efi_guid_firmware_management_capsule_id)) { |
| ret = efi_capsule_update_firmware(capsule); |
| @@ -592,7 +723,7 @@ out: |
| * |
| * Return: status code |
| */ |
| -efi_status_t EFIAPI efi_query_capsule_caps( |
| +efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps( |
| struct efi_capsule_header **capsule_header_array, |
| efi_uintn_t capsule_count, |
| u64 *maximum_capsule_size, |
| diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c |
| index 492ecf4cb15c..bfd4687e10b5 100644 |
| --- a/lib/efi_loader/efi_setup.c |
| +++ b/lib/efi_loader/efi_setup.c |
| @@ -16,6 +16,13 @@ |
| |
| efi_status_t efi_obj_list_initialized = OBJ_LIST_NOT_INITIALIZED; |
| |
| +#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) |
| +/** |
| + * efi_corstone1000_alloc_capsule_shared_buf - allocate capsule shared buffer |
| + */ |
| +extern efi_status_t efi_corstone1000_alloc_capsule_shared_buf(void); |
| +#endif |
| + |
| /* |
| * Allow unaligned memory access. |
| * |
| @@ -128,6 +135,14 @@ static efi_status_t efi_init_capsule(void) |
| { |
| efi_status_t ret = EFI_SUCCESS; |
| |
| +#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) |
| + ret = efi_corstone1000_alloc_capsule_shared_buf(); |
| + if (ret != EFI_SUCCESS) { |
| + printf("EFI: Corstone-1000: cannot allocate caspsule shared buffer\n"); |
| + return ret; |
| + } |
| +#endif |
| + |
| if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_UPDATE)) { |
| ret = efi_set_variable_int(u"CapsuleMax", |
| &efi_guid_capsule_report, |
| -- |
| 2.37.1 |
| |