blob: 3b82b416a85b4924f6666605a0cc070fc6eb682b [file] [log] [blame]
Patrick Williams92b42cb2022-09-03 06:53:57 -05001From ee7c0aee66db53b2372a3b4245a8754dceee804d Mon Sep 17 00:00:00 2001
Brad Bishopbec4ebc2022-08-03 09:55:16 -04002From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
3Date: Wed, 13 Oct 2021 17:51:44 +0100
Patrick Williams92b42cb2022-09-03 06:53:57 -05004Subject: [PATCH 08/24] arm_ffa: introducing MM communication with FF-A
Brad Bishopbec4ebc2022-08-03 09:55:16 -04005
6This commit allows to perform MM communication using FF-A transport.
7
8The MM SP (also called partition) can be StandAlonneMM or smm-gateway.
9Both partitions run in OP-TEE.
10
11When using the u-boot FF-A driver, StandAlonneMM and smm-gateway are
12supported.
13
14On EFI services such as GetVariable()/SetVariable(), the data
15is copied from the communication buffer to the MM shared buffer.
16
17Then, notifies the MM SP about data availability in the MM shared buffer.
18Communication with the MM SP is performed using FF-A transport.
19
20On such event, MM SP can read the data and updates the MM shared buffer
21with response data.
22
23The response data is copied back to the communication buffer.
24
25Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
26Signed-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
32diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
Patrick Williams92b42cb2022-09-03 06:53:57 -050033index e3f2402d0e8e..37131237af3f 100644
Brad Bishopbec4ebc2022-08-03 09:55:16 -040034--- a/lib/efi_loader/Kconfig
35+++ b/lib/efi_loader/Kconfig
Patrick Williams92b42cb2022-09-03 06:53:57 -050036@@ -60,13 +60,23 @@ config EFI_VARIABLE_FILE_STORE
Brad Bishopbec4ebc2022-08-03 09:55:16 -040037 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 Williams92b42cb2022-09-03 06:53:57 -050045+ the MM SP (also called partition) can be StandAlonneMM or smm-gateway.
Brad Bishopbec4ebc2022-08-03 09:55:16 -040046+ 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 Williams92b42cb2022-09-03 06:53:57 -050059 config EFI_VARIABLE_NO_STORE
60 bool "Don't persist non-volatile UEFI variables"
61 help
Brad Bishopbec4ebc2022-08-03 09:55:16 -040062diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c
63index 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 Williams92b42cb2022-09-03 06:53:57 -05003822.37.1
Brad Bishopbec4ebc2022-08-03 09:55:16 -0400383