Patrick Williams | 92b42cb | 2022-09-03 06:53:57 -0500 | [diff] [blame^] | 1 | From ee7c0aee66db53b2372a3b4245a8754dceee804d Mon Sep 17 00:00:00 2001 |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 2 | From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> |
| 3 | Date: Wed, 13 Oct 2021 17:51:44 +0100 |
Patrick Williams | 92b42cb | 2022-09-03 06:53:57 -0500 | [diff] [blame^] | 4 | Subject: [PATCH 08/24] arm_ffa: introducing MM communication with FF-A |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 5 | |
| 6 | This commit allows to perform MM communication using FF-A transport. |
| 7 | |
| 8 | The MM SP (also called partition) can be StandAlonneMM or smm-gateway. |
| 9 | Both partitions run in OP-TEE. |
| 10 | |
| 11 | When using the u-boot FF-A driver, StandAlonneMM and smm-gateway are |
| 12 | supported. |
| 13 | |
| 14 | On EFI services such as GetVariable()/SetVariable(), the data |
| 15 | is copied from the communication buffer to the MM shared buffer. |
| 16 | |
| 17 | Then, notifies the MM SP about data availability in the MM shared buffer. |
| 18 | Communication with the MM SP is performed using FF-A transport. |
| 19 | |
| 20 | On such event, MM SP can read the data and updates the MM shared buffer |
| 21 | with response data. |
| 22 | |
| 23 | The response data is copied back to the communication buffer. |
| 24 | |
| 25 | Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> |
| 26 | Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> |
| 27 | --- |
| 28 | lib/efi_loader/Kconfig | 14 +- |
| 29 | lib/efi_loader/efi_variable_tee.c | 265 +++++++++++++++++++++++++++++- |
| 30 | 2 files changed, 273 insertions(+), 6 deletions(-) |
| 31 | |
| 32 | diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig |
Patrick Williams | 92b42cb | 2022-09-03 06:53:57 -0500 | [diff] [blame^] | 33 | index e3f2402d0e8e..37131237af3f 100644 |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 34 | --- a/lib/efi_loader/Kconfig |
| 35 | +++ b/lib/efi_loader/Kconfig |
Patrick Williams | 92b42cb | 2022-09-03 06:53:57 -0500 | [diff] [blame^] | 36 | @@ -60,13 +60,23 @@ config EFI_VARIABLE_FILE_STORE |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 37 | stored as file /ubootefi.var on the EFI system partition. |
| 38 | |
| 39 | config EFI_MM_COMM_TEE |
| 40 | - bool "UEFI variables storage service via OP-TEE" |
| 41 | - depends on OPTEE |
| 42 | + bool "UEFI variables storage service via the trusted world" |
| 43 | + depends on OPTEE || ARM_FFA_TRANSPORT |
| 44 | help |
Patrick Williams | 92b42cb | 2022-09-03 06:53:57 -0500 | [diff] [blame^] | 45 | + the MM SP (also called partition) can be StandAlonneMM or smm-gateway. |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 46 | + When using the u-boot OP-TEE driver, StandAlonneMM is supported. |
| 47 | + When using the u-boot FF-A driver, StandAlonneMM and smm-gateway are supported. |
| 48 | + |
| 49 | If OP-TEE is present and running StandAloneMM, dispatch all UEFI |
| 50 | variable related operations to that. The application will verify, |
| 51 | authenticate and store the variables on an RPMB. |
| 52 | |
| 53 | + When ARM_FFA_TRANSPORT is used, dispatch all UEFI variable related |
| 54 | + operations to the MM SP running under Optee in the trusted world. |
| 55 | + A door bell mechanism is used to notify the SP when there is data in the shared |
| 56 | + MM buffer. The data is copied by u-boot to thea shared buffer before issuing |
| 57 | + the door bell event. |
| 58 | + |
Patrick Williams | 92b42cb | 2022-09-03 06:53:57 -0500 | [diff] [blame^] | 59 | config EFI_VARIABLE_NO_STORE |
| 60 | bool "Don't persist non-volatile UEFI variables" |
| 61 | help |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 62 | diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c |
| 63 | index dfef18435dfa..9cb8cfb9c779 100644 |
| 64 | --- a/lib/efi_loader/efi_variable_tee.c |
| 65 | +++ b/lib/efi_loader/efi_variable_tee.c |
| 66 | @@ -15,6 +15,28 @@ |
| 67 | #include <malloc.h> |
| 68 | #include <mm_communication.h> |
| 69 | |
| 70 | +#if (IS_ENABLED(CONFIG_OPTEE)) |
| 71 | +#define OPTEE_PAGE_SIZE BIT(12) |
| 72 | +#endif |
| 73 | + |
| 74 | +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) |
| 75 | + |
| 76 | +#include <arm_ffa_helper.h> |
| 77 | +#include <mapmem.h> |
| 78 | + |
| 79 | +/* MM return codes */ |
| 80 | +#define MM_SUCCESS (0) |
| 81 | + |
| 82 | +#define ARM_SVC_ID_SP_EVENT_COMPLETE_AARCH64 (0xC4000061) |
| 83 | +#define ARM_SVC_ID_SP_EVENT_COMPLETE ARM_SVC_ID_SP_EVENT_COMPLETE_AARCH64 |
| 84 | + |
| 85 | +/* MM_SP_UUID_DATA defined by the platform */ |
| 86 | +union ffa_partition_uuid mm_sp_svc_uuid = {.bytes = {MM_SP_UUID_DATA}}; |
| 87 | + |
| 88 | +static u16 __efi_runtime_data mm_sp_id; |
| 89 | + |
| 90 | +#endif |
| 91 | + |
| 92 | extern struct efi_var_file __efi_runtime_data *efi_var_buf; |
| 93 | static efi_uintn_t max_buffer_size; /* comm + var + func + data */ |
| 94 | static efi_uintn_t max_payload_size; /* func + data */ |
| 95 | @@ -24,6 +46,7 @@ struct mm_connection { |
| 96 | u32 session; |
| 97 | }; |
| 98 | |
| 99 | +#if (IS_ENABLED(CONFIG_OPTEE)) |
| 100 | /** |
| 101 | * get_connection() - Retrieve OP-TEE session for a specific UUID. |
| 102 | * |
| 103 | @@ -143,16 +166,229 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) |
| 104 | |
| 105 | return ret; |
| 106 | } |
| 107 | +#endif |
| 108 | + |
| 109 | +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) |
| 110 | + |
| 111 | +/** |
| 112 | + * ffa_notify_mm_sp() - Announce there is data in the shared buffer |
| 113 | + * |
| 114 | + * Notifies the MM partition in the trusted world that |
| 115 | + * data is available in the shared buffer. |
| 116 | + * This is a blocking call during which trusted world has exclusive access |
| 117 | + * to the MM shared buffer. |
| 118 | + * |
| 119 | + * Return: |
| 120 | + * |
| 121 | + * 0 on success |
| 122 | + */ |
| 123 | +static int __efi_runtime ffa_notify_mm_sp(void) |
| 124 | +{ |
| 125 | + struct ffa_interface_data func_data = {0}; |
| 126 | + struct ffa_send_direct_data msg = {0}; |
| 127 | + int ret; |
| 128 | + u32 sp_event_complete; |
| 129 | + int sp_event_ret; |
| 130 | + |
| 131 | + func_data.data0_size = sizeof(mm_sp_id); |
| 132 | + func_data.data0 = &mm_sp_id; |
| 133 | + |
| 134 | + msg.a3 = FFA_SHARED_MM_BUFFER_ADDR; |
| 135 | + msg.a4 = FFA_SHARED_MM_BUFFER_SIZE; |
| 136 | + func_data.data1_size = sizeof(msg); |
| 137 | + func_data.data1 = &msg; |
| 138 | + |
| 139 | + ret = ffa_helper_msg_send_direct_req(&func_data); |
| 140 | + if (ret != FFA_ERR_STAT_SUCCESS) { |
| 141 | + log_err("EFI: Failure to notify the MM SP , FF-A error (%d)\n", ret); |
| 142 | + return ret; |
| 143 | + } |
| 144 | + |
| 145 | + sp_event_complete = msg.a3; |
| 146 | + sp_event_ret = (int)msg.a4; |
| 147 | + |
| 148 | + if (sp_event_complete == ARM_SVC_ID_SP_EVENT_COMPLETE && sp_event_ret == MM_SUCCESS) |
| 149 | + return 0; |
| 150 | + |
| 151 | + log_err("EFI: Failure to notify the MM SP (0x%x , %d)\n", |
| 152 | + sp_event_complete, |
| 153 | + sp_event_ret); |
| 154 | + |
| 155 | + return -EACCES; |
| 156 | +} |
| 157 | + |
| 158 | +/** |
| 159 | + * ffa_discover_mm_sp_id() - Query the MM partition ID |
| 160 | + * |
| 161 | + * Use the FF-A driver to get the MM partition ID. |
| 162 | + * If multiple partitions are found, use the first one |
| 163 | + * |
| 164 | + * Return: |
| 165 | + * |
| 166 | + * 0 on success |
| 167 | + */ |
| 168 | +static int __efi_runtime ffa_discover_mm_sp_id(void) |
| 169 | +{ |
| 170 | + struct ffa_interface_data func_data = {0}; |
| 171 | + u32 count = 0; |
| 172 | + int ret; |
| 173 | + struct ffa_partition_info *parts_info; |
| 174 | + |
| 175 | + /* |
| 176 | + * get from the driver the count of the SPs matching the UUID |
| 177 | + */ |
| 178 | + func_data.data0_size = sizeof(mm_sp_svc_uuid); |
| 179 | + func_data.data0 = &mm_sp_svc_uuid; |
| 180 | + func_data.data1_size = sizeof(count); |
| 181 | + func_data.data1 = &count; |
| 182 | + |
| 183 | + ret = ffa_helper_get_partitions_info(&func_data); |
| 184 | + if (ret != FFA_ERR_STAT_SUCCESS) { |
| 185 | + log_err("EFI: Failure in querying partitions count (error code: %d)\n", ret); |
| 186 | + return ret; |
| 187 | + } |
| 188 | + |
| 189 | + if (!count) { |
| 190 | + log_info("EFI: No MM partition found\n"); |
| 191 | + return ret; |
| 192 | + } |
| 193 | + |
| 194 | + /* |
| 195 | + * pre-allocate a buffer to be filled by the driver |
| 196 | + * with ffa_partition_info structs |
| 197 | + */ |
| 198 | + |
| 199 | + parts_info = calloc(count, sizeof(struct ffa_partition_info)); |
| 200 | + if (!parts_info) |
| 201 | + return -EINVAL; |
| 202 | + |
| 203 | + log_info("EFI: Pre-allocating %d partition(s) info structures\n", count); |
| 204 | + |
| 205 | + func_data.data1_size = count * |
| 206 | + sizeof(struct ffa_partition_info); |
| 207 | + func_data.data1 = parts_info; |
| 208 | + |
| 209 | + /* |
| 210 | + * ask the driver to fill the |
| 211 | + * buffer with the SPs info |
| 212 | + */ |
| 213 | + ret = ffa_helper_get_partitions_info(&func_data); |
| 214 | + if (ret != FFA_ERR_STAT_SUCCESS) { |
| 215 | + log_err("EFI: Failure in querying partition(s) info (error code: %d)\n", ret); |
| 216 | + free(parts_info); |
| 217 | + return ret; |
| 218 | + } |
| 219 | + |
| 220 | + /* |
| 221 | + * MM SPs found , use the first one |
| 222 | + */ |
| 223 | + |
| 224 | + mm_sp_id = parts_info[0].id; |
| 225 | + |
| 226 | + log_info("EFI: MM partition ID 0x%x\n", mm_sp_id); |
| 227 | + |
| 228 | + free(parts_info); |
| 229 | + |
| 230 | + return 0; |
| 231 | +} |
| 232 | |
| 233 | /** |
| 234 | - * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send |
| 235 | + * ffa_mm_communicate() - Exchange EFI services data with the MM partition using FF-A |
| 236 | + * @comm_buf: locally allocated communication buffer used for for rx/tx |
| 237 | + * @dsize: communication buffer size |
| 238 | + * |
| 239 | + * Issues a door bell event to notify the MM partition (SP) running in OP-TEE |
| 240 | + * that there is data to read from the shared buffer. |
| 241 | + * Communication with the MM SP is performed using FF-A transport. |
| 242 | + * On the event, MM SP can read the data from the buffer and |
| 243 | + * update the MM shared buffer with response data. |
| 244 | + * The response data is copied back to the communication buffer. |
| 245 | + * |
| 246 | + * Return: |
| 247 | + * |
| 248 | + * EFI status code |
| 249 | + */ |
| 250 | +static efi_status_t __efi_runtime ffa_mm_communicate(void *comm_buf, ulong comm_buf_size) |
| 251 | +{ |
| 252 | + ulong tx_data_size; |
| 253 | + int ffa_ret; |
| 254 | + struct efi_mm_communicate_header *mm_hdr; |
| 255 | + void *virt_shared_buf; |
| 256 | + |
| 257 | + if (!comm_buf) |
| 258 | + return EFI_INVALID_PARAMETER; |
| 259 | + |
| 260 | + /* Discover MM partition ID */ |
| 261 | + if (!mm_sp_id && ffa_discover_mm_sp_id() != FFA_ERR_STAT_SUCCESS) { |
| 262 | + log_err("EFI: Failure to discover MM partition ID\n"); |
| 263 | + return EFI_UNSUPPORTED; |
| 264 | + } |
| 265 | + |
| 266 | + mm_hdr = (struct efi_mm_communicate_header *)comm_buf; |
| 267 | + tx_data_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t); |
| 268 | + |
| 269 | + if (comm_buf_size != tx_data_size || tx_data_size > FFA_SHARED_MM_BUFFER_SIZE) |
| 270 | + return EFI_INVALID_PARAMETER; |
| 271 | + |
| 272 | + /* Copy the data to the shared buffer */ |
| 273 | + |
| 274 | + virt_shared_buf = (void *)map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_ADDR, 0); |
| 275 | + efi_memcpy_runtime(virt_shared_buf, comm_buf, tx_data_size); |
| 276 | + |
| 277 | + /* Announce there is data in the shared buffer */ |
| 278 | + |
| 279 | + ffa_ret = ffa_notify_mm_sp(); |
| 280 | + if (ffa_ret) |
| 281 | + unmap_sysmem(virt_shared_buf); |
| 282 | + |
| 283 | + switch (ffa_ret) { |
| 284 | + case 0: |
| 285 | + { |
| 286 | + ulong rx_data_size; |
| 287 | + /* Copy the MM SP response from the shared buffer to the communication buffer */ |
| 288 | + rx_data_size = ((struct efi_mm_communicate_header *)virt_shared_buf)->message_len + |
| 289 | + sizeof(efi_guid_t) + |
| 290 | + sizeof(size_t); |
| 291 | + |
| 292 | + if (rx_data_size > comm_buf_size) { |
| 293 | + unmap_sysmem(virt_shared_buf); |
| 294 | + return EFI_OUT_OF_RESOURCES; |
| 295 | + } |
| 296 | + |
| 297 | + efi_memcpy_runtime(comm_buf, virt_shared_buf, rx_data_size); |
| 298 | + unmap_sysmem(virt_shared_buf); |
| 299 | + |
| 300 | + return EFI_SUCCESS; |
| 301 | + } |
| 302 | + case -EINVAL: |
| 303 | + return EFI_DEVICE_ERROR; |
| 304 | + case -EPERM: |
| 305 | + return EFI_INVALID_PARAMETER; |
| 306 | + case -EACCES: |
| 307 | + return EFI_ACCESS_DENIED; |
| 308 | + case -EBUSY: |
| 309 | + return EFI_OUT_OF_RESOURCES; |
| 310 | + default: |
| 311 | + return EFI_ACCESS_DENIED; |
| 312 | + } |
| 313 | +} |
| 314 | +#endif |
| 315 | + |
| 316 | +/** |
| 317 | + * mm_communicate() - Adjust the communication buffer to the MM SP and send |
| 318 | * it to OP-TEE |
| 319 | * |
| 320 | - * @comm_buf: locally allocted communcation buffer |
| 321 | + * @comm_buf: locally allocted communication buffer |
| 322 | * @dsize: buffer size |
| 323 | + * |
| 324 | + * The MM SP (also called partition) can be StandAlonneMM or smm-gateway. |
| 325 | + * The comm_buf format is the same for both partitions. |
| 326 | + * When using the u-boot OP-TEE driver, StandAlonneMM is supported. |
| 327 | + * When using the u-boot FF-A driver, StandAlonneMM and smm-gateway are supported. |
| 328 | + * |
| 329 | * Return: status code |
| 330 | */ |
| 331 | -static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) |
| 332 | +static efi_status_t __efi_runtime mm_communicate(u8 *comm_buf, efi_uintn_t dsize) |
| 333 | { |
| 334 | efi_status_t ret; |
| 335 | struct efi_mm_communicate_header *mm_hdr; |
| 336 | @@ -162,7 +398,11 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) |
| 337 | mm_hdr = (struct efi_mm_communicate_header *)comm_buf; |
| 338 | var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data; |
| 339 | |
| 340 | + #if (IS_ENABLED(CONFIG_OPTEE)) |
| 341 | ret = optee_mm_communicate(comm_buf, dsize); |
| 342 | + #elif (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) |
| 343 | + ret = ffa_mm_communicate(comm_buf, dsize); |
| 344 | + #endif |
| 345 | if (ret != EFI_SUCCESS) { |
| 346 | log_err("%s failed!\n", __func__); |
| 347 | return ret; |
| 348 | @@ -258,6 +498,23 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) |
| 349 | goto out; |
| 350 | } |
| 351 | *size = var_payload->size; |
| 352 | + |
| 353 | + #if (IS_ENABLED(CONFIG_OPTEE)) |
| 354 | + /* |
| 355 | + * Although the max payload is configurable on StMM, we only share a |
| 356 | + * single page from OP-TEE for the non-secure buffer used to communicate |
| 357 | + * with StMM. Since OP-TEE will reject to map anything bigger than that, |
| 358 | + * make sure we are in bounds. |
| 359 | + */ |
| 360 | + if (*size > OPTEE_PAGE_SIZE) |
| 361 | + *size = OPTEE_PAGE_SIZE - MM_COMMUNICATE_HEADER_SIZE - |
| 362 | + MM_VARIABLE_COMMUNICATE_SIZE; |
| 363 | + #elif (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) |
| 364 | + if (*size > FFA_SHARED_MM_BUFFER_SIZE) |
| 365 | + *size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE - |
| 366 | + MM_VARIABLE_COMMUNICATE_SIZE; |
| 367 | + #endif |
| 368 | + |
| 369 | /* |
| 370 | * There seems to be a bug in EDK2 miscalculating the boundaries and |
| 371 | * size checks, so deduct 2 more bytes to fulfill this requirement. Fix |
| 372 | @@ -697,7 +954,7 @@ void efi_variables_boot_exit_notify(void) |
| 373 | ret = EFI_NOT_FOUND; |
| 374 | |
| 375 | if (ret != EFI_SUCCESS) |
| 376 | - log_err("Unable to notify StMM for ExitBootServices\n"); |
| 377 | + log_err("Unable to notify the MM partition for ExitBootServices\n"); |
| 378 | free(comm_buf); |
| 379 | |
| 380 | /* |
| 381 | -- |
Patrick Williams | 92b42cb | 2022-09-03 06:53:57 -0500 | [diff] [blame^] | 382 | 2.37.1 |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 383 | |