blob: 04e55576a9f840ead205cd2f0ca0718723deb5d8 [file] [log] [blame]
Patrick Williams8dd68482022-10-04 07:57:18 -05001From b3c7d84dcde6ee1cbc13e10664d24ffa220f5fb3 Mon Sep 17 00:00:00 2001
2From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
3Date: Mon, 15 Aug 2022 15:12:49 +0100
4Subject: [PATCH 14/26] arm_ffa: introduce FF-A MM communication
5
6Add MM communication support using FF-A transport
7
8Access an SP's service through EFI MM communication protocol.
9
10This feature allows accessing MM partitions services through
11EFI MM communication protocol. MM partitions such as StandAlonneMM
12or smm-gateway secure partitions which reside in secure world.
13
14An MM shared buffer and a door bell event are used to exchange
15the data.
16
17The data is used by EFI services such as GetVariable()/SetVariable()
18and copied from the communication buffer to the MM shared buffer.
19
20The secure partition is notified about availability of data in the
21MM shared buffer by an FF-A message (door bell).
22
23On such event, MM SP can read the data and updates the MM shared
24buffer with the response data.
25
26The response data is copied back to the communication buffer and
27consumed by the EFI subsystem.
28
29FF-A driver private data is copied to EFI runtime section at
30ExitBootServices(). This garantees secure world partitions data are
31available at EFI runtime level.
32
33Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
34Signed-off-by: Gowtham Suresh Kumar <gowtham.sureshkumar@arm.com>
35Upstream-Status: Submitted [cover letter: https://lore.kernel.org/all/20220926101723.9965-1-abdellatif.elkhlifi@arm.com/]
36---
37
38Changelog:
39===============
40
41v4:
42
43* use the new FF-A driver interfaces
44* discover MM partitions at runtime
45* copy FF-A driver private data to EFI runtime section at
46 ExitBootServices()
47* drop use of FFA_ERR_STAT_SUCCESS error code
48* replace EFI_BUFFER_TOO_SMALL by EFI_OUT_OF_RESOURCES
49 in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore
50* revert the error log in mm_communicate() in case of failure
51* remove packed attribute from efi_mm_communicate_header and
52 smm_variable_communicate_header
53
54v2:
55
56* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
57
58v1:
59
60* introduce FF-A MM communication
61
62 arch/arm/cpu/armv8/cache.S | 16 ++
63 arch/arm/cpu/armv8/cache_v8.c | 3 +-
64 include/mm_communication.h | 7 +-
65 lib/efi_loader/Kconfig | 14 +-
66 lib/efi_loader/efi_boottime.c | 7 +
67 lib/efi_loader/efi_variable_tee.c | 261 +++++++++++++++++++++++++++++-
68 6 files changed, 299 insertions(+), 9 deletions(-)
69
70diff --git a/arch/arm/cpu/armv8/cache.S b/arch/arm/cpu/armv8/cache.S
71index d1cee23437..f69ef64ed6 100644
72--- a/arch/arm/cpu/armv8/cache.S
73+++ b/arch/arm/cpu/armv8/cache.S
74@@ -21,7 +21,11 @@
75 * x1: 0 clean & invalidate, 1 invalidate only
76 * x2~x9: clobbered
77 */
78+#ifdef CONFIG_EFI_LOADER
79+.pushsection .text.efi_runtime, "ax"
80+#else
81 .pushsection .text.__asm_dcache_level, "ax"
82+#endif
83 ENTRY(__asm_dcache_level)
84 lsl x12, x0, #1
85 msr csselr_el1, x12 /* select cache level */
86@@ -65,7 +69,11 @@ ENDPROC(__asm_dcache_level)
87 *
88 * flush or invalidate all data cache by SET/WAY.
89 */
90+#ifdef CONFIG_EFI_LOADER
91+.pushsection .text.efi_runtime, "ax"
92+#else
93 .pushsection .text.__asm_dcache_all, "ax"
94+#endif
95 ENTRY(__asm_dcache_all)
96 mov x1, x0
97 dsb sy
98@@ -109,7 +117,11 @@ ENTRY(__asm_flush_dcache_all)
99 ENDPROC(__asm_flush_dcache_all)
100 .popsection
101
102+#ifdef CONFIG_EFI_LOADER
103+.pushsection .text.efi_runtime, "ax"
104+#else
105 .pushsection .text.__asm_invalidate_dcache_all, "ax"
106+#endif
107 ENTRY(__asm_invalidate_dcache_all)
108 mov x0, #0x1
109 b __asm_dcache_all
110@@ -182,7 +194,11 @@ ENTRY(__asm_invalidate_icache_all)
111 ENDPROC(__asm_invalidate_icache_all)
112 .popsection
113
114+#ifdef CONFIG_EFI_LOADER
115+.pushsection .text.efi_runtime, "ax"
116+#else
117 .pushsection .text.__asm_invalidate_l3_dcache, "ax"
118+#endif
119 WEAK(__asm_invalidate_l3_dcache)
120 mov x0, #0 /* return status as success */
121 ret
122diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c
123index e4736e5643..afbc487fa1 100644
124--- a/arch/arm/cpu/armv8/cache_v8.c
125+++ b/arch/arm/cpu/armv8/cache_v8.c
126@@ -9,6 +9,7 @@
127
128 #include <common.h>
129 #include <cpu_func.h>
130+#include <efi_loader.h>
131 #include <hang.h>
132 #include <log.h>
133 #include <asm/cache.h>
134@@ -445,7 +446,7 @@ __weak void mmu_setup(void)
135 /*
136 * Performs a invalidation of the entire data cache at all levels
137 */
138-void invalidate_dcache_all(void)
139+void __efi_runtime invalidate_dcache_all(void)
140 {
141 __asm_invalidate_dcache_all();
142 __asm_invalidate_l3_dcache();
143diff --git a/include/mm_communication.h b/include/mm_communication.h
144index e65fbde60d..32dc5dbac8 100644
145--- a/include/mm_communication.h
146+++ b/include/mm_communication.h
147@@ -13,6 +13,9 @@
148
149 #include <part_efi.h>
150
151+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */
152+#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d"
153+
154 /*
155 * Interface to the pseudo Trusted Application (TA), which provides a
156 * communication channel with the Standalone MM (Management Mode)
157@@ -43,7 +46,7 @@
158 * To avoid confusion in interpreting frames, the communication buffer should
159 * always begin with efi_mm_communicate_header.
160 */
161-struct __packed efi_mm_communicate_header {
162+struct efi_mm_communicate_header {
163 efi_guid_t header_guid;
164 size_t message_len;
165 u8 data[];
166@@ -145,7 +148,7 @@ struct smm_variable_communicate_header {
167 * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
168 *
169 */
170-struct smm_variable_access {
171+struct __packed smm_variable_access {
172 efi_guid_t guid;
173 efi_uintn_t data_size;
174 efi_uintn_t name_size;
175diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
176index e3f2402d0e..2a6d70f862 100644
177--- a/lib/efi_loader/Kconfig
178+++ b/lib/efi_loader/Kconfig
179@@ -60,13 +60,23 @@ config EFI_VARIABLE_FILE_STORE
180 stored as file /ubootefi.var on the EFI system partition.
181
182 config EFI_MM_COMM_TEE
183- bool "UEFI variables storage service via OP-TEE"
184- depends on OPTEE
185+ bool "UEFI variables storage service via the trusted world"
186+ depends on OPTEE || ARM_FFA_TRANSPORT
187 help
188+ Allowing access to the MM SP services (SPs such as StandAlonneMM, smm-gateway).
189+ When using the u-boot OP-TEE driver, StandAlonneMM is supported.
190+ When using the u-boot FF-A driver any MM SP is supported.
191+
192 If OP-TEE is present and running StandAloneMM, dispatch all UEFI
193 variable related operations to that. The application will verify,
194 authenticate and store the variables on an RPMB.
195
196+ When ARM_FFA_TRANSPORT is used, dispatch all UEFI variable related
197+ operations to the MM SP running in the secure world.
198+ A door bell mechanism is used to notify the SP when there is data in the shared
199+ MM buffer. The data is copied by u-boot to the shared buffer before issuing
200+ the door bell event.
201+
202 config EFI_VARIABLE_NO_STORE
203 bool "Don't persist non-volatile UEFI variables"
204 help
205diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
206index 8fa9a58d76..cede7826bd 100644
207--- a/lib/efi_loader/efi_boottime.c
208+++ b/lib/efi_loader/efi_boottime.c
209@@ -2185,6 +2185,13 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
210 debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n");
211 #endif
212
213+#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE) && !CONFIG_IS_ENABLED(SANDBOX_FFA)
214+ if (ffa_copy_runtime_data())
215+ printf("ERROR: EFI: FFA: copying runtime data\n");
216+ else
217+ printf("INFO: EFI: FFA: runtime data copied\n");
218+#endif
219+
220 /* Patch out unsupported runtime function */
221 efi_runtime_detach();
222
223diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c
224index dfef18435d..7d9d577281 100644
225--- a/lib/efi_loader/efi_variable_tee.c
226+++ b/lib/efi_loader/efi_variable_tee.c
227@@ -15,6 +15,36 @@
228 #include <malloc.h>
229 #include <mm_communication.h>
230
231+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
232+
233+#include <arm_ffa.h>
234+#include <cpu_func.h>
235+#include <mapmem.h>
236+
237+#ifndef FFA_SHARED_MM_BUFFER_SIZE
238+#warning "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h"
239+#define FFA_SHARED_MM_BUFFER_SIZE 0
240+#endif
241+
242+#ifndef FFA_SHARED_MM_BUFFER_OFFSET
243+#warning "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h"
244+#define FFA_SHARED_MM_BUFFER_OFFSET 0
245+#endif
246+
247+#ifndef FFA_SHARED_MM_BUFFER_ADDR
248+#warning "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h"
249+#define FFA_SHARED_MM_BUFFER_ADDR 0
250+#endif
251+
252+/* MM return codes */
253+#define MM_SUCCESS (0)
254+
255+const char *mm_sp_svc_uuid = MM_SP_UUID;
256+
257+static __efi_runtime_data u16 mm_sp_id;
258+
259+#endif
260+
261 extern struct efi_var_file __efi_runtime_data *efi_var_buf;
262 static efi_uintn_t max_buffer_size; /* comm + var + func + data */
263 static efi_uintn_t max_payload_size; /* func + data */
264@@ -24,6 +54,7 @@ struct mm_connection {
265 u32 session;
266 };
267
268+#if (IS_ENABLED(CONFIG_OPTEE))
269 /**
270 * get_connection() - Retrieve OP-TEE session for a specific UUID.
271 *
272@@ -143,16 +174,227 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
273
274 return ret;
275 }
276+#endif
277+
278+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
279
280 /**
281- * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
282+ * ffa_notify_mm_sp() - Announce there is data in the shared buffer
283+ *
284+ * Notifies the MM partition in the trusted world that
285+ * data is available in the shared buffer.
286+ * This is a blocking call during which trusted world has exclusive access
287+ * to the MM shared buffer.
288+ *
289+ * Return:
290+ *
291+ * 0 on success
292+ */
293+static int __efi_runtime ffa_notify_mm_sp(void)
294+{
295+ struct ffa_send_direct_data msg = {0};
296+ int ret;
297+ int sp_event_ret = -1;
298+
299+ if (!ffa_bus_ops_get())
300+ return -EINVAL;
301+
302+ msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */
303+
304+ ret = ffa_bus_ops_get()->sync_send_receive(mm_sp_id, &msg);
305+ if (ret != 0)
306+ return ret;
307+
308+ sp_event_ret = msg.data0; /* x3 */
309+
310+ if (sp_event_ret == MM_SUCCESS)
311+ return 0;
312+
313+ /*
314+ * Failure to notify the MM SP
315+ */
316+
317+ return -EACCES;
318+}
319+
320+/**
321+ * ffa_discover_mm_sp_id() - Query the MM partition ID
322+ *
323+ * Use the FF-A driver to get the MM partition ID.
324+ * If multiple partitions are found, use the first one.
325+ * This is a boot time function.
326+ *
327+ * Return:
328+ *
329+ * 0 on success
330+ */
331+static int ffa_discover_mm_sp_id(void)
332+{
333+ u32 count = 0, size = 0;
334+ int ret;
335+ struct ffa_partition_info *parts_info;
336+
337+ if (!ffa_bus_ops_get())
338+ return -EINVAL;
339+
340+ /*
341+ * get from the driver the count of the SPs matching the UUID
342+ */
343+ ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &count, NULL);
344+ if (ret != 0) {
345+ log_err("EFI: Failure in querying partitions count (error code: %d)\n", ret);
346+ return ret;
347+ }
348+
349+ if (!count) {
350+ log_info("EFI: No MM partition found\n");
351+ return ret;
352+ }
353+
354+ /*
355+ * pre-allocate a buffer to be filled by the driver
356+ * with ffa_partition_info structs
357+ */
358+
359+ log_info("EFI: Pre-allocating %d partition(s) info structures\n", count);
360+
361+ parts_info = calloc(count, sizeof(struct ffa_partition_info));
362+ if (!parts_info)
363+ return -EINVAL;
364+
365+ size = count * sizeof(struct ffa_partition_info);
366+
367+ /*
368+ * ask the driver to fill the
369+ * buffer with the SPs info
370+ */
371+ ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &size, parts_info);
372+ if (ret != 0) {
373+ log_err("EFI: Failure in querying partition(s) info (error code: %d)\n", ret);
374+ free(parts_info);
375+ return ret;
376+ }
377+
378+ /*
379+ * MM SPs found , use the first one
380+ */
381+
382+ mm_sp_id = parts_info[0].id;
383+
384+ log_info("EFI: MM partition ID 0x%x\n", mm_sp_id);
385+
386+ free(parts_info);
387+
388+ return 0;
389+}
390+
391+/**
392+ * ffa_mm_communicate() - Exchange EFI services data with the MM partition using FF-A
393+ * @comm_buf: locally allocated communication buffer used for rx/tx
394+ * @dsize: communication buffer size
395+ *
396+ * Issues a door bell event to notify the MM partition (SP) running in OP-TEE
397+ * that there is data to read from the shared buffer.
398+ * Communication with the MM SP is performed using FF-A transport.
399+ * On the event, MM SP can read the data from the buffer and
400+ * update the MM shared buffer with response data.
401+ * The response data is copied back to the communication buffer.
402+ *
403+ * Return:
404+ *
405+ * EFI status code
406+ */
407+static efi_status_t __efi_runtime ffa_mm_communicate(void *comm_buf, ulong comm_buf_size)
408+{
409+ ulong tx_data_size;
410+ int ffa_ret;
411+ struct efi_mm_communicate_header *mm_hdr;
412+ void *virt_shared_buf;
413+
414+ if (!comm_buf)
415+ return EFI_INVALID_PARAMETER;
416+
417+ /* Discover MM partition ID at boot time */
418+ if (!mm_sp_id && ffa_discover_mm_sp_id() != 0) {
419+ log_err("EFI: Failure to discover MM partition ID at boot time\n");
420+ return EFI_UNSUPPORTED;
421+ }
422+
423+ mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
424+ tx_data_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t);
425+
426+ if (comm_buf_size != tx_data_size || tx_data_size > FFA_SHARED_MM_BUFFER_SIZE)
427+ return EFI_INVALID_PARAMETER;
428+
429+ /* Copy the data to the shared buffer */
430+
431+ virt_shared_buf = (void *)map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_ADDR, 0);
432+ efi_memcpy_runtime(virt_shared_buf, comm_buf, tx_data_size);
433+
434+ /*
435+ * The secure world might have cache disabled for
436+ * the device region used for shared buffer (which is the case for Optee).
437+ * In this case, the secure world reads the data from DRAM.
438+ * Let's flush the cache so the DRAM is updated with the latest data.
439+ */
440+ #ifdef CONFIG_ARM64
441+ invalidate_dcache_all();
442+ #endif
443+
444+ /* Announce there is data in the shared buffer */
445+
446+ ffa_ret = ffa_notify_mm_sp();
447+ if (ffa_ret)
448+ unmap_sysmem(virt_shared_buf);
449+
450+ switch (ffa_ret) {
451+ case 0:
452+ {
453+ ulong rx_data_size;
454+ /* Copy the MM SP response from the shared buffer to the communication buffer */
455+ rx_data_size = ((struct efi_mm_communicate_header *)virt_shared_buf)->message_len +
456+ sizeof(efi_guid_t) +
457+ sizeof(size_t);
458+
459+ if (rx_data_size > comm_buf_size) {
460+ unmap_sysmem(virt_shared_buf);
461+ return EFI_OUT_OF_RESOURCES;
462+ }
463+
464+ efi_memcpy_runtime(comm_buf, virt_shared_buf, rx_data_size);
465+ unmap_sysmem(virt_shared_buf);
466+
467+ return EFI_SUCCESS;
468+ }
469+ case -EINVAL:
470+ return EFI_DEVICE_ERROR;
471+ case -EPERM:
472+ return EFI_INVALID_PARAMETER;
473+ case -EACCES:
474+ return EFI_ACCESS_DENIED;
475+ case -EBUSY:
476+ return EFI_OUT_OF_RESOURCES;
477+ default:
478+ return EFI_ACCESS_DENIED;
479+ }
480+}
481+#endif
482+
483+/**
484+ * mm_communicate() - Adjust the communication buffer to the MM SP and send
485 * it to OP-TEE
486 *
487- * @comm_buf: locally allocted communcation buffer
488+ * @comm_buf: locally allocated communication buffer
489 * @dsize: buffer size
490+ *
491+ * The MM SP (also called partition) can be StandAlonneMM or smm-gateway.
492+ * The comm_buf format is the same for both partitions.
493+ * When using the u-boot OP-TEE driver, StandAlonneMM is supported.
494+ * When using the u-boot FF-A driver, StandAlonneMM and smm-gateway are supported.
495+ *
496 * Return: status code
497 */
498-static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize)
499+static efi_status_t __efi_runtime mm_communicate(u8 *comm_buf, efi_uintn_t dsize)
500 {
501 efi_status_t ret;
502 struct efi_mm_communicate_header *mm_hdr;
503@@ -162,7 +404,11 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize)
504 mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
505 var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
506
507+ #if (IS_ENABLED(CONFIG_OPTEE))
508 ret = optee_mm_communicate(comm_buf, dsize);
509+ #elif (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
510+ ret = ffa_mm_communicate(comm_buf, dsize);
511+ #endif
512 if (ret != EFI_SUCCESS) {
513 log_err("%s failed!\n", __func__);
514 return ret;
515@@ -258,6 +504,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size)
516 goto out;
517 }
518 *size = var_payload->size;
519+
520+ #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
521+ if (*size > FFA_SHARED_MM_BUFFER_SIZE)
522+ *size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE -
523+ MM_VARIABLE_COMMUNICATE_SIZE;
524+ #endif
525+
526 /*
527 * There seems to be a bug in EDK2 miscalculating the boundaries and
528 * size checks, so deduct 2 more bytes to fulfill this requirement. Fix
529@@ -697,7 +950,7 @@ void efi_variables_boot_exit_notify(void)
530 ret = EFI_NOT_FOUND;
531
532 if (ret != EFI_SUCCESS)
533- log_err("Unable to notify StMM for ExitBootServices\n");
534+ log_err("Unable to notify the MM partition for ExitBootServices\n");
535 free(comm_buf);
536
537 /*
538--
5392.17.1
540