blob: 09d03c0eee7877d4cec05d4b9f5458274586866c [file] [log] [blame]
Ilias Apalodimas77a364f2020-05-17 22:25:44 +03001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * EFI variable service via OP-TEE
4 *
5 * Copyright (C) 2019 Linaro Ltd. <sughosh.ganu@linaro.org>
6 * Copyright (C) 2019 Linaro Ltd. <ilias.apalodimas@linaro.org>
Abdellatif El Khlifi431c7b52023-08-04 14:33:44 +01007 * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
8 *
9 * Authors:
10 * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
Ilias Apalodimas77a364f2020-05-17 22:25:44 +030011 */
12
13#include <common.h>
Abdellatif El Khlifi431c7b52023-08-04 14:33:44 +010014#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT)
15#include <arm_ffa.h>
16#endif
17#include <cpu_func.h>
18#include <dm.h>
Ilias Apalodimas77a364f2020-05-17 22:25:44 +030019#include <efi.h>
20#include <efi_api.h>
21#include <efi_loader.h>
Heinrich Schuchardt9827e842020-06-22 18:10:27 +020022#include <efi_variable.h>
Ilias Apalodimas77a364f2020-05-17 22:25:44 +030023#include <malloc.h>
Abdellatif El Khlifi431c7b52023-08-04 14:33:44 +010024#include <mapmem.h>
Ilias Apalodimas77a364f2020-05-17 22:25:44 +030025#include <mm_communication.h>
Abdellatif El Khlifi431c7b52023-08-04 14:33:44 +010026#include <tee.h>
27
28#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT)
29/* MM return codes */
30#define MM_SUCCESS (0)
31#define MM_NOT_SUPPORTED (-1)
32#define MM_INVALID_PARAMETER (-2)
33#define MM_DENIED (-3)
34#define MM_NO_MEMORY (-5)
35
36static const char *mm_sp_svc_uuid = MM_SP_UUID;
37static u16 mm_sp_id;
38#endif
Ilias Apalodimas77a364f2020-05-17 22:25:44 +030039
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +030040extern struct efi_var_file __efi_runtime_data *efi_var_buf;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +030041static efi_uintn_t max_buffer_size; /* comm + var + func + data */
42static efi_uintn_t max_payload_size; /* func + data */
43
44struct mm_connection {
45 struct udevice *tee;
46 u32 session;
47};
48
49/**
50 * get_connection() - Retrieve OP-TEE session for a specific UUID.
51 *
52 * @conn: session buffer to fill
53 * Return: status code
54 */
55static int get_connection(struct mm_connection *conn)
56{
57 static const struct tee_optee_ta_uuid uuid = PTA_STMM_UUID;
58 struct udevice *tee = NULL;
59 struct tee_open_session_arg arg;
Ilias Apalodimas555e05d2020-12-23 13:25:00 +020060 int rc = -ENODEV;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +030061
62 tee = tee_find_device(tee, NULL, NULL, NULL);
63 if (!tee)
Ilias Apalodimas555e05d2020-12-23 13:25:00 +020064 goto out;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +030065
66 memset(&arg, 0, sizeof(arg));
67 tee_optee_ta_uuid_to_octets(arg.uuid, &uuid);
68 rc = tee_open_session(tee, &arg, 0, NULL);
Ilias Apalodimas555e05d2020-12-23 13:25:00 +020069 if (rc)
70 goto out;
71
72 /* Check the internal OP-TEE result */
73 if (arg.ret != TEE_SUCCESS) {
74 rc = -EIO;
75 goto out;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +030076 }
77
Ilias Apalodimas555e05d2020-12-23 13:25:00 +020078 conn->tee = tee;
79 conn->session = arg.session;
80
81 return 0;
82out:
Ilias Apalodimas77a364f2020-05-17 22:25:44 +030083 return rc;
84}
85
86/**
87 * optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE
88 *
89 * @comm_buf: locally allocted communcation buffer
90 * @dsize: buffer size
91 * Return: status code
92 */
93static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
94{
95 ulong buf_size;
96 efi_status_t ret;
97 struct efi_mm_communicate_header *mm_hdr;
98 struct mm_connection conn = { NULL, 0 };
99 struct tee_invoke_arg arg;
100 struct tee_param param[2];
101 struct tee_shm *shm = NULL;
102 int rc;
103
104 if (!comm_buf)
105 return EFI_INVALID_PARAMETER;
106
107 mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
108 buf_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t);
109
110 if (dsize != buf_size)
111 return EFI_INVALID_PARAMETER;
112
113 rc = get_connection(&conn);
114 if (rc) {
115 log_err("Unable to open OP-TEE session (err=%d)\n", rc);
116 return EFI_UNSUPPORTED;
117 }
118
119 if (tee_shm_register(conn.tee, comm_buf, buf_size, 0, &shm)) {
120 log_err("Unable to register shared memory\n");
Ilias Apalodimas555e05d2020-12-23 13:25:00 +0200121 tee_close_session(conn.tee, conn.session);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300122 return EFI_UNSUPPORTED;
123 }
124
125 memset(&arg, 0, sizeof(arg));
126 arg.func = PTA_STMM_CMDID_COMMUNICATE;
127 arg.session = conn.session;
128
129 memset(param, 0, sizeof(param));
130 param[0].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INOUT;
131 param[0].u.memref.size = buf_size;
132 param[0].u.memref.shm = shm;
133 param[1].attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT;
134
135 rc = tee_invoke_func(conn.tee, &arg, 2, param);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300136 tee_shm_free(shm);
137 tee_close_session(conn.tee, conn.session);
Ilias Apalodimas599ac592021-12-24 10:08:41 +0200138 if (rc)
139 return EFI_DEVICE_ERROR;
140 if (arg.ret == TEE_ERROR_EXCESS_DATA)
141 log_err("Variable payload too large\n");
142 if (arg.ret != TEE_SUCCESS)
Ilias Apalodimasfe7c1fc2020-07-22 10:32:22 +0300143 return EFI_DEVICE_ERROR;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300144
145 switch (param[1].u.value.a) {
Ilias Apalodimas128a3c52020-07-17 07:55:03 +0300146 case ARM_SVC_SPM_RET_SUCCESS:
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300147 ret = EFI_SUCCESS;
148 break;
149
Ilias Apalodimas128a3c52020-07-17 07:55:03 +0300150 case ARM_SVC_SPM_RET_INVALID_PARAMS:
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300151 ret = EFI_INVALID_PARAMETER;
152 break;
153
Ilias Apalodimas128a3c52020-07-17 07:55:03 +0300154 case ARM_SVC_SPM_RET_DENIED:
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300155 ret = EFI_ACCESS_DENIED;
156 break;
157
Ilias Apalodimas128a3c52020-07-17 07:55:03 +0300158 case ARM_SVC_SPM_RET_NO_MEMORY:
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300159 ret = EFI_OUT_OF_RESOURCES;
160 break;
161
162 default:
163 ret = EFI_ACCESS_DENIED;
164 }
165
166 return ret;
167}
168
Abdellatif El Khlifi431c7b52023-08-04 14:33:44 +0100169#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT)
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300170/**
Abdellatif El Khlifi431c7b52023-08-04 14:33:44 +0100171 * ffa_notify_mm_sp() - Announce there is data in the shared buffer
172 *
173 * Notify the MM partition in the trusted world that
174 * data is available in the shared buffer.
175 * This is a blocking call during which trusted world has exclusive access
176 * to the MM shared buffer.
177 *
178 * Return:
179 *
180 * 0 on success
181 */
182static int ffa_notify_mm_sp(void)
183{
184 struct ffa_send_direct_data msg = {0};
185 int ret;
186 int sp_event_ret;
187 struct udevice *dev;
188
189 ret = uclass_first_device_err(UCLASS_FFA, &dev);
190 if (ret) {
191 log_err("EFI: Cannot find FF-A bus device, notify MM SP failure\n");
192 return ret;
193 }
194
195 msg.data0 = CONFIG_FFA_SHARED_MM_BUF_OFFSET; /* x3 */
196
197 ret = ffa_sync_send_receive(dev, mm_sp_id, &msg, 1);
198 if (ret)
199 return ret;
200
201 sp_event_ret = msg.data0; /* x3 */
202
203 switch (sp_event_ret) {
204 case MM_SUCCESS:
205 ret = 0;
206 break;
207 case MM_NOT_SUPPORTED:
208 ret = -EINVAL;
209 break;
210 case MM_INVALID_PARAMETER:
211 ret = -EPERM;
212 break;
213 case MM_DENIED:
214 ret = -EACCES;
215 break;
216 case MM_NO_MEMORY:
217 ret = -EBUSY;
218 break;
219 default:
220 ret = -EACCES;
221 }
222
223 return ret;
224}
225
226/**
227 * ffa_discover_mm_sp_id() - Query the MM partition ID
228 *
229 * Use the FF-A driver to get the MM partition ID.
230 * If multiple partitions are found, use the first one.
231 * This is a boot time function.
232 *
233 * Return:
234 *
235 * 0 on success
236 */
237static int ffa_discover_mm_sp_id(void)
238{
239 u32 count = 0;
240 int ret;
241 struct ffa_partition_desc *descs;
242 struct udevice *dev;
243
244 ret = uclass_first_device_err(UCLASS_FFA, &dev);
245 if (ret) {
246 log_err("EFI: Cannot find FF-A bus device, MM SP discovery failure\n");
247 return ret;
248 }
249
250 /* Ask the driver to fill the buffer with the SPs info */
251 ret = ffa_partition_info_get(dev, mm_sp_svc_uuid, &count, &descs);
252 if (ret) {
253 log_err("EFI: Failure in querying SPs info (%d), MM SP discovery failure\n", ret);
254 return ret;
255 }
256
257 /* MM SPs found , use the first one */
258
259 mm_sp_id = descs[0].info.id;
260
261 log_info("EFI: MM partition ID 0x%x\n", mm_sp_id);
262
263 return 0;
264}
265
266/**
267 * ffa_mm_communicate() - Exchange EFI services data with the MM partition using FF-A
268 * @comm_buf: locally allocated communication buffer used for rx/tx
269 * @dsize: communication buffer size
270 *
271 * Issue a door bell event to notify the MM partition (SP) running in OP-TEE
272 * that there is data to read from the shared buffer.
273 * Communication with the MM SP is performed using FF-A transport.
274 * On the event, MM SP can read the data from the buffer and
275 * update the MM shared buffer with response data.
276 * The response data is copied back to the communication buffer.
277 *
278 * Return:
279 *
280 * EFI status code
281 */
282static efi_status_t ffa_mm_communicate(void *comm_buf, ulong comm_buf_size)
283{
284 ulong tx_data_size;
285 int ffa_ret;
286 efi_status_t efi_ret;
287 struct efi_mm_communicate_header *mm_hdr;
288 void *virt_shared_buf;
289
290 if (!comm_buf)
291 return EFI_INVALID_PARAMETER;
292
293 /* Discover MM partition ID at boot time */
294 if (!mm_sp_id && ffa_discover_mm_sp_id()) {
295 log_err("EFI: Failure to discover MM SP ID at boot time, FF-A MM comms failure\n");
296 return EFI_UNSUPPORTED;
297 }
298
299 mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
300 tx_data_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t);
301
302 if (comm_buf_size != tx_data_size || tx_data_size > CONFIG_FFA_SHARED_MM_BUF_SIZE)
303 return EFI_INVALID_PARAMETER;
304
305 /* Copy the data to the shared buffer */
306
307 virt_shared_buf = map_sysmem((phys_addr_t)CONFIG_FFA_SHARED_MM_BUF_ADDR, 0);
308 memcpy(virt_shared_buf, comm_buf, tx_data_size);
309
310 /*
311 * The secure world might have cache disabled for
312 * the device region used for shared buffer (which is the case for Optee).
313 * In this case, the secure world reads the data from DRAM.
314 * Let's flush the cache so the DRAM is updated with the latest data.
315 */
316#ifdef CONFIG_ARM64
317 invalidate_dcache_all();
318#endif
319
320 /* Announce there is data in the shared buffer */
321
322 ffa_ret = ffa_notify_mm_sp();
323
324 switch (ffa_ret) {
325 case 0: {
326 ulong rx_data_size;
327 /* Copy the MM SP response from the shared buffer to the communication buffer */
328 rx_data_size = ((struct efi_mm_communicate_header *)virt_shared_buf)->message_len +
329 sizeof(efi_guid_t) +
330 sizeof(size_t);
331
332 if (rx_data_size > comm_buf_size) {
333 efi_ret = EFI_OUT_OF_RESOURCES;
334 break;
335 }
336
337 memcpy(comm_buf, virt_shared_buf, rx_data_size);
338 efi_ret = EFI_SUCCESS;
339 break;
340 }
341 case -EINVAL:
342 efi_ret = EFI_DEVICE_ERROR;
343 break;
344 case -EPERM:
345 efi_ret = EFI_INVALID_PARAMETER;
346 break;
347 case -EACCES:
348 efi_ret = EFI_ACCESS_DENIED;
349 break;
350 case -EBUSY:
351 efi_ret = EFI_OUT_OF_RESOURCES;
352 break;
353 default:
354 efi_ret = EFI_ACCESS_DENIED;
355 }
356
357 unmap_sysmem(virt_shared_buf);
358 return efi_ret;
359}
360
361/**
362 * get_mm_comms() - detect the available MM transport
363 *
364 * Make sure the FF-A bus is probed successfully
365 * which means FF-A communication with secure world works and ready
366 * for use.
367 *
368 * If FF-A bus is not ready, use OPTEE comms.
369 *
370 * Return:
371 *
372 * MM_COMMS_FFA or MM_COMMS_OPTEE
373 */
374static enum mm_comms_select get_mm_comms(void)
375{
376 struct udevice *dev;
377 int ret;
378
379 ret = uclass_first_device_err(UCLASS_FFA, &dev);
380 if (ret) {
381 log_debug("EFI: Cannot find FF-A bus device, trying Optee comms\n");
382 return MM_COMMS_OPTEE;
383 }
384
385 return MM_COMMS_FFA;
386}
387#endif
388
389/**
390 * mm_communicate() - Adjust the communication buffer to the MM SP and send
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300391 * it to OP-TEE
392 *
Abdellatif El Khlifi431c7b52023-08-04 14:33:44 +0100393 * @comm_buf: locally allocated communication buffer
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300394 * @dsize: buffer size
Abdellatif El Khlifi431c7b52023-08-04 14:33:44 +0100395 *
396 * The SP (also called partition) can be any MM SP such as StandAlonneMM or smm-gateway.
397 * The comm_buf format is the same for both partitions.
398 * When using the u-boot OP-TEE driver, StandAlonneMM is supported.
399 * When using the u-boot FF-A driver, any MM SP is supported.
400 *
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300401 * Return: status code
402 */
403static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize)
404{
405 efi_status_t ret;
406 struct efi_mm_communicate_header *mm_hdr;
407 struct smm_variable_communicate_header *var_hdr;
Abdellatif El Khlifi431c7b52023-08-04 14:33:44 +0100408#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT)
409 enum mm_comms_select mm_comms;
410#endif
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300411
412 dsize += MM_COMMUNICATE_HEADER_SIZE + MM_VARIABLE_COMMUNICATE_SIZE;
413 mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
414 var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
415
Abdellatif El Khlifi431c7b52023-08-04 14:33:44 +0100416#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT)
417 mm_comms = get_mm_comms();
418 if (mm_comms == MM_COMMS_FFA)
419 ret = ffa_mm_communicate(comm_buf, dsize);
420 else
421 ret = optee_mm_communicate(comm_buf, dsize);
422#else
423 ret = optee_mm_communicate(comm_buf, dsize);
424#endif
425
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300426 if (ret != EFI_SUCCESS) {
427 log_err("%s failed!\n", __func__);
428 return ret;
429 }
430
431 return var_hdr->ret_status;
432}
433
434/**
435 * setup_mm_hdr() - Allocate a buffer for StandAloneMM and initialize the
436 * header data.
437 *
438 * @dptr: pointer address of the corresponding StandAloneMM
439 * function
440 * @payload_size: buffer size
441 * @func: standAloneMM function number
442 * @ret: EFI return code
443 * Return: buffer or NULL
444 */
445static u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size,
446 efi_uintn_t func, efi_status_t *ret)
447{
448 const efi_guid_t mm_var_guid = EFI_MM_VARIABLE_GUID;
449 struct efi_mm_communicate_header *mm_hdr;
450 struct smm_variable_communicate_header *var_hdr;
451 u8 *comm_buf;
452
453 /* In the init function we initialize max_buffer_size with
454 * get_max_payload(). So skip the test if max_buffer_size is initialized
455 * StandAloneMM will perform similar checks and drop the buffer if it's
456 * too long
457 */
458 if (max_buffer_size && max_buffer_size <
459 (MM_COMMUNICATE_HEADER_SIZE +
460 MM_VARIABLE_COMMUNICATE_SIZE +
461 payload_size)) {
462 *ret = EFI_INVALID_PARAMETER;
463 return NULL;
464 }
465
466 comm_buf = calloc(1, MM_COMMUNICATE_HEADER_SIZE +
467 MM_VARIABLE_COMMUNICATE_SIZE +
468 payload_size);
469 if (!comm_buf) {
470 *ret = EFI_OUT_OF_RESOURCES;
471 return NULL;
472 }
473
474 mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
475 guidcpy(&mm_hdr->header_guid, &mm_var_guid);
476 mm_hdr->message_len = MM_VARIABLE_COMMUNICATE_SIZE + payload_size;
477
478 var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
479 var_hdr->function = func;
480 if (dptr)
481 *dptr = var_hdr->data;
482 *ret = EFI_SUCCESS;
483
484 return comm_buf;
485}
486
487/**
488 * get_max_payload() - Get variable payload size from StandAloneMM.
489 *
490 * @size: size of the variable in storage
491 * Return: status code
492 */
493efi_status_t EFIAPI get_max_payload(efi_uintn_t *size)
494{
495 struct smm_variable_payload_size *var_payload = NULL;
496 efi_uintn_t payload_size;
497 u8 *comm_buf = NULL;
498 efi_status_t ret;
499
500 if (!size) {
501 ret = EFI_INVALID_PARAMETER;
502 goto out;
503 }
504
505 payload_size = sizeof(*var_payload);
506 comm_buf = setup_mm_hdr((void **)&var_payload, payload_size,
507 SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE, &ret);
508 if (!comm_buf)
509 goto out;
510
511 ret = mm_communicate(comm_buf, payload_size);
512 if (ret != EFI_SUCCESS)
513 goto out;
514
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300515 /* Make sure the buffer is big enough for storing variables */
516 if (var_payload->size < MM_VARIABLE_ACCESS_HEADER_SIZE + 0x20) {
517 ret = EFI_DEVICE_ERROR;
518 goto out;
519 }
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300520 *size = var_payload->size;
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300521 /*
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300522 * There seems to be a bug in EDK2 miscalculating the boundaries and
523 * size checks, so deduct 2 more bytes to fulfill this requirement. Fix
524 * it up here to ensure backwards compatibility with older versions
525 * (cf. StandaloneMmPkg/Drivers/StandaloneMmCpu/AArch64/EventHandle.c.
526 * sizeof (EFI_MM_COMMUNICATE_HEADER) instead the size minus the
527 * flexible array member).
528 *
529 * size is guaranteed to be > 2 due to checks on the beginning.
530 */
531 *size -= 2;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300532out:
533 free(comm_buf);
534 return ret;
535}
536
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300537/*
538 * StMM can store internal attributes and properties for variables, i.e enabling
539 * R/O variables
540 */
Heinrich Schuchardt1ad2f0d2021-09-09 07:12:14 +0200541static efi_status_t set_property_int(const u16 *variable_name,
542 efi_uintn_t name_size,
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300543 const efi_guid_t *vendor,
544 struct var_check_property *var_property)
545{
546 struct smm_variable_var_check_property *smm_property;
547 efi_uintn_t payload_size;
548 u8 *comm_buf = NULL;
549 efi_status_t ret;
550
551 payload_size = sizeof(*smm_property) + name_size;
552 if (payload_size > max_payload_size) {
553 ret = EFI_INVALID_PARAMETER;
554 goto out;
555 }
556 comm_buf = setup_mm_hdr((void **)&smm_property, payload_size,
557 SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET,
558 &ret);
559 if (!comm_buf)
560 goto out;
561
562 guidcpy(&smm_property->guid, vendor);
563 smm_property->name_size = name_size;
564 memcpy(&smm_property->property, var_property,
565 sizeof(smm_property->property));
566 memcpy(smm_property->name, variable_name, name_size);
567
568 ret = mm_communicate(comm_buf, payload_size);
569
570out:
571 free(comm_buf);
572 return ret;
573}
574
Heinrich Schuchardt1ad2f0d2021-09-09 07:12:14 +0200575static efi_status_t get_property_int(const u16 *variable_name,
576 efi_uintn_t name_size,
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300577 const efi_guid_t *vendor,
578 struct var_check_property *var_property)
579{
580 struct smm_variable_var_check_property *smm_property;
581 efi_uintn_t payload_size;
582 u8 *comm_buf = NULL;
583 efi_status_t ret;
584
585 memset(var_property, 0, sizeof(*var_property));
586 payload_size = sizeof(*smm_property) + name_size;
587 if (payload_size > max_payload_size) {
588 ret = EFI_INVALID_PARAMETER;
589 goto out;
590 }
591 comm_buf = setup_mm_hdr((void **)&smm_property, payload_size,
592 SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET,
593 &ret);
594 if (!comm_buf)
595 goto out;
596
597 guidcpy(&smm_property->guid, vendor);
598 smm_property->name_size = name_size;
599 memcpy(smm_property->name, variable_name, name_size);
600
601 ret = mm_communicate(comm_buf, payload_size);
602 /*
603 * Currently only R/O property is supported in StMM.
604 * Variables that are not set to R/O will not set the property in StMM
605 * and the call will return EFI_NOT_FOUND. We are setting the
606 * properties to 0x0 so checking against that is enough for the
607 * EFI_NOT_FOUND case.
608 */
609 if (ret == EFI_NOT_FOUND)
610 ret = EFI_SUCCESS;
611 if (ret != EFI_SUCCESS)
612 goto out;
613 memcpy(var_property, &smm_property->property, sizeof(*var_property));
614
615out:
616 free(comm_buf);
617 return ret;
618}
619
Heinrich Schuchardt1ad2f0d2021-09-09 07:12:14 +0200620efi_status_t efi_get_variable_int(const u16 *variable_name,
621 const efi_guid_t *vendor,
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200622 u32 *attributes, efi_uintn_t *data_size,
623 void *data, u64 *timep)
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300624{
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300625 struct var_check_property var_property;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300626 struct smm_variable_access *var_acc;
627 efi_uintn_t payload_size;
628 efi_uintn_t name_size;
629 efi_uintn_t tmp_dsize;
630 u8 *comm_buf = NULL;
Ilias Apalodimasa378f962022-03-16 17:13:37 +0200631 efi_status_t ret, tmp;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300632
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200633 if (!variable_name || !vendor || !data_size) {
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300634 ret = EFI_INVALID_PARAMETER;
635 goto out;
636 }
637
638 /* Check payload size */
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200639 name_size = u16_strsize(variable_name);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300640 if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
641 ret = EFI_INVALID_PARAMETER;
642 goto out;
643 }
644
645 /* Trim output buffer size */
646 tmp_dsize = *data_size;
647 if (name_size + tmp_dsize >
648 max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
649 tmp_dsize = max_payload_size -
650 MM_VARIABLE_ACCESS_HEADER_SIZE -
651 name_size;
652 }
653
654 /* Get communication buffer and initialize header */
655 payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + tmp_dsize;
656 comm_buf = setup_mm_hdr((void **)&var_acc, payload_size,
657 SMM_VARIABLE_FUNCTION_GET_VARIABLE, &ret);
658 if (!comm_buf)
659 goto out;
660
661 /* Fill in contents */
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200662 guidcpy(&var_acc->guid, vendor);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300663 var_acc->data_size = tmp_dsize;
664 var_acc->name_size = name_size;
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200665 var_acc->attr = attributes ? *attributes : 0;
666 memcpy(var_acc->name, variable_name, name_size);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300667
668 /* Communicate */
669 ret = mm_communicate(comm_buf, payload_size);
Ilias Apalodimasa378f962022-03-16 17:13:37 +0200670 if (ret != EFI_SUCCESS && ret != EFI_BUFFER_TOO_SMALL)
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300671 goto out;
672
Ilias Apalodimasa378f962022-03-16 17:13:37 +0200673 /* Update with reported data size for trimmed case */
674 *data_size = var_acc->data_size;
675 /*
676 * UEFI > 2.7 needs the attributes set even if the buffer is
677 * smaller
678 */
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300679 if (attributes) {
Ilias Apalodimasa378f962022-03-16 17:13:37 +0200680 tmp = get_property_int(variable_name, name_size, vendor,
681 &var_property);
682 if (tmp != EFI_SUCCESS) {
683 ret = tmp;
684 goto out;
685 }
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200686 *attributes = var_acc->attr;
Ilias Apalodimasa378f962022-03-16 17:13:37 +0200687 if (var_property.property &
688 VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY)
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300689 *attributes |= EFI_VARIABLE_READ_ONLY;
690 }
691
Ilias Apalodimasa378f962022-03-16 17:13:37 +0200692 /* return if ret is EFI_BUFFER_TOO_SMALL */
693 if (ret != EFI_SUCCESS)
694 goto out;
695
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300696 if (data)
697 memcpy(data, (u8 *)var_acc->name + var_acc->name_size,
698 var_acc->data_size);
699 else
700 ret = EFI_INVALID_PARAMETER;
701
702out:
703 free(comm_buf);
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200704 return ret;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300705}
706
Heinrich Schuchardt276c61d2020-06-26 17:57:48 +0200707efi_status_t efi_get_next_variable_name_int(efi_uintn_t *variable_name_size,
708 u16 *variable_name,
709 efi_guid_t *guid)
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300710{
711 struct smm_variable_getnext *var_getnext;
712 efi_uintn_t payload_size;
713 efi_uintn_t out_name_size;
714 efi_uintn_t in_name_size;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300715 u8 *comm_buf = NULL;
716 efi_status_t ret;
717
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300718 if (!variable_name_size || !variable_name || !guid) {
719 ret = EFI_INVALID_PARAMETER;
720 goto out;
721 }
722
723 out_name_size = *variable_name_size;
724 in_name_size = u16_strsize(variable_name);
725
726 if (out_name_size < in_name_size) {
727 ret = EFI_INVALID_PARAMETER;
728 goto out;
729 }
730
Ilias Apalodimas24105b82020-07-01 16:41:25 +0300731 if (in_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300732 ret = EFI_INVALID_PARAMETER;
733 goto out;
734 }
735
736 /* Trim output buffer size */
Ilias Apalodimas91b218d2020-07-22 01:50:37 +0300737 if (out_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE)
738 out_name_size = max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300739
740 payload_size = MM_VARIABLE_GET_NEXT_HEADER_SIZE + out_name_size;
741 comm_buf = setup_mm_hdr((void **)&var_getnext, payload_size,
742 SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME,
743 &ret);
744 if (!comm_buf)
745 goto out;
746
747 /* Fill in contents */
748 guidcpy(&var_getnext->guid, guid);
749 var_getnext->name_size = out_name_size;
750 memcpy(var_getnext->name, variable_name, in_name_size);
751 memset((u8 *)var_getnext->name + in_name_size, 0x0,
752 out_name_size - in_name_size);
753
754 /* Communicate */
755 ret = mm_communicate(comm_buf, payload_size);
756 if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
757 /* Update with reported data size for trimmed case */
758 *variable_name_size = var_getnext->name_size;
759 }
760 if (ret != EFI_SUCCESS)
761 goto out;
762
763 guidcpy(guid, &var_getnext->guid);
Ilias Apalodimas91b218d2020-07-22 01:50:37 +0300764 memcpy(variable_name, var_getnext->name, var_getnext->name_size);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300765
766out:
767 free(comm_buf);
Heinrich Schuchardt276c61d2020-06-26 17:57:48 +0200768 return ret;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300769}
770
Heinrich Schuchardt1ad2f0d2021-09-09 07:12:14 +0200771efi_status_t efi_set_variable_int(const u16 *variable_name,
772 const efi_guid_t *vendor, u32 attributes,
773 efi_uintn_t data_size, const void *data,
774 bool ro_check)
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300775{
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300776 efi_status_t ret, alt_ret = EFI_SUCCESS;
777 struct var_check_property var_property;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300778 struct smm_variable_access *var_acc;
779 efi_uintn_t payload_size;
780 efi_uintn_t name_size;
781 u8 *comm_buf = NULL;
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300782 bool ro;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300783
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200784 if (!variable_name || variable_name[0] == 0 || !vendor) {
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300785 ret = EFI_INVALID_PARAMETER;
786 goto out;
787 }
788 if (data_size > 0 && !data) {
789 ret = EFI_INVALID_PARAMETER;
790 goto out;
791 }
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300792 /* Check payload size */
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200793 name_size = u16_strsize(variable_name);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300794 payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size;
795 if (payload_size > max_payload_size) {
796 ret = EFI_INVALID_PARAMETER;
797 goto out;
798 }
799
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300800 /*
801 * Allocate the buffer early, before switching to RW (if needed)
802 * so we won't need to account for any failures in reading/setting
803 * the properties, if the allocation fails
804 */
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300805 comm_buf = setup_mm_hdr((void **)&var_acc, payload_size,
806 SMM_VARIABLE_FUNCTION_SET_VARIABLE, &ret);
807 if (!comm_buf)
808 goto out;
809
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300810 ro = !!(attributes & EFI_VARIABLE_READ_ONLY);
811 attributes &= EFI_VARIABLE_MASK;
812
813 /*
814 * The API has the ability to override RO flags. If no RO check was
815 * requested switch the variable to RW for the duration of this call
816 */
817 ret = get_property_int(variable_name, name_size, vendor,
818 &var_property);
819 if (ret != EFI_SUCCESS)
820 goto out;
821
822 if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) {
823 /* Bypass r/o check */
824 if (!ro_check) {
825 var_property.property &= ~VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY;
826 ret = set_property_int(variable_name, name_size, vendor, &var_property);
827 if (ret != EFI_SUCCESS)
828 goto out;
829 } else {
830 ret = EFI_WRITE_PROTECTED;
831 goto out;
832 }
833 }
834
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300835 /* Fill in contents */
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200836 guidcpy(&var_acc->guid, vendor);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300837 var_acc->data_size = data_size;
838 var_acc->name_size = name_size;
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200839 var_acc->attr = attributes;
840 memcpy(var_acc->name, variable_name, name_size);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300841 memcpy((u8 *)var_acc->name + name_size, data, data_size);
842
843 /* Communicate */
844 ret = mm_communicate(comm_buf, payload_size);
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300845 if (ret != EFI_SUCCESS)
846 alt_ret = ret;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300847
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300848 if (ro && !(var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY)) {
849 var_property.revision = VAR_CHECK_VARIABLE_PROPERTY_REVISION;
850 var_property.property |= VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY;
851 var_property.attributes = attributes;
852 var_property.minsize = 1;
853 var_property.maxsize = var_acc->data_size;
854 ret = set_property_int(variable_name, name_size, vendor, &var_property);
855 }
Heinrich Schuchardt730e2292020-07-14 08:14:08 +0200856
857 if (alt_ret != EFI_SUCCESS)
858 goto out;
859
Simon Glass90975372022-01-23 12:55:12 -0700860 if (!u16_strcmp(variable_name, u"PK"))
Heinrich Schuchardt730e2292020-07-14 08:14:08 +0200861 alt_ret = efi_init_secure_state();
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300862out:
863 free(comm_buf);
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300864 return alt_ret == EFI_SUCCESS ? ret : alt_ret;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300865}
866
Heinrich Schuchardt276c61d2020-06-26 17:57:48 +0200867efi_status_t efi_query_variable_info_int(u32 attributes,
868 u64 *max_variable_storage_size,
869 u64 *remain_variable_storage_size,
870 u64 *max_variable_size)
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300871{
872 struct smm_variable_query_info *mm_query_info;
873 efi_uintn_t payload_size;
874 efi_status_t ret;
875 u8 *comm_buf;
876
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300877 payload_size = sizeof(*mm_query_info);
878 comm_buf = setup_mm_hdr((void **)&mm_query_info, payload_size,
879 SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO,
880 &ret);
881 if (!comm_buf)
882 goto out;
883
884 mm_query_info->attr = attributes;
885 ret = mm_communicate(comm_buf, payload_size);
886 if (ret != EFI_SUCCESS)
887 goto out;
888 *max_variable_storage_size = mm_query_info->max_variable_storage;
889 *remain_variable_storage_size =
890 mm_query_info->remaining_variable_storage;
891 *max_variable_size = mm_query_info->max_variable_size;
892
893out:
894 free(comm_buf);
Heinrich Schuchardt276c61d2020-06-26 17:57:48 +0200895 return ret;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300896}
897
898/**
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300899 * efi_query_variable_info() - get information about EFI variables
900 *
901 * This function implements the QueryVariableInfo() runtime service.
902 *
903 * See the Unified Extensible Firmware Interface (UEFI) specification for
904 * details.
905 *
906 * @attributes: bitmask to select variables to be
907 * queried
908 * @maximum_variable_storage_size: maximum size of storage area for the
909 * selected variable types
910 * @remaining_variable_storage_size: remaining size of storage are for the
911 * selected variable types
912 * @maximum_variable_size: maximum size of a variable of the
913 * selected type
914 * Return: status code
915 */
916efi_status_t EFIAPI __efi_runtime
917efi_query_variable_info_runtime(u32 attributes, u64 *max_variable_storage_size,
918 u64 *remain_variable_storage_size,
919 u64 *max_variable_size)
920{
921 return EFI_UNSUPPORTED;
922}
923
924/**
925 * efi_set_variable_runtime() - runtime implementation of SetVariable()
926 *
927 * @variable_name: name of the variable
928 * @guid: vendor GUID
929 * @attributes: attributes of the variable
930 * @data_size: size of the buffer with the variable value
931 * @data: buffer with the variable value
932 * Return: status code
933 */
934static efi_status_t __efi_runtime EFIAPI
935efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *guid,
936 u32 attributes, efi_uintn_t data_size,
937 const void *data)
938{
939 return EFI_UNSUPPORTED;
940}
941
942/**
943 * efi_variables_boot_exit_notify() - notify ExitBootServices() is called
944 */
945void efi_variables_boot_exit_notify(void)
946{
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300947 efi_status_t ret;
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300948 u8 *comm_buf;
949 loff_t len;
950 struct efi_var_file *var_buf;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300951
952 comm_buf = setup_mm_hdr(NULL, 0,
953 SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE, &ret);
954 if (comm_buf)
955 ret = mm_communicate(comm_buf, 0);
956 else
957 ret = EFI_NOT_FOUND;
958
959 if (ret != EFI_SUCCESS)
Abdellatif El Khlifi431c7b52023-08-04 14:33:44 +0100960 log_err("Unable to notify the MM partition for ExitBootServices\n");
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300961 free(comm_buf);
962
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300963 /*
964 * Populate the list for runtime variables.
965 * asking EFI_VARIABLE_RUNTIME_ACCESS is redundant, since
966 * efi_var_mem_notify_exit_boot_services will clean those, but that's fine
967 */
968 ret = efi_var_collect(&var_buf, &len, EFI_VARIABLE_RUNTIME_ACCESS);
969 if (ret != EFI_SUCCESS)
970 log_err("Can't populate EFI variables. No runtime variables will be available\n");
971 else
Ilias Apalodimas33521442021-01-16 17:28:04 +0200972 efi_var_buf_update(var_buf);
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300973 free(var_buf);
974
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300975 /* Update runtime service table */
976 efi_runtime_services.query_variable_info =
977 efi_query_variable_info_runtime;
978 efi_runtime_services.get_variable = efi_get_variable_runtime;
979 efi_runtime_services.get_next_variable_name =
980 efi_get_next_variable_name_runtime;
981 efi_runtime_services.set_variable = efi_set_variable_runtime;
982 efi_update_table_header_crc32(&efi_runtime_services.hdr);
983}
984
985/**
986 * efi_init_variables() - initialize variable services
987 *
988 * Return: status code
989 */
990efi_status_t efi_init_variables(void)
991{
992 efi_status_t ret;
993
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300994 /* Create a cached copy of the variables that will be enabled on ExitBootServices() */
995 ret = efi_var_mem_init();
996 if (ret != EFI_SUCCESS)
997 return ret;
998
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300999 ret = get_max_payload(&max_payload_size);
1000 if (ret != EFI_SUCCESS)
1001 return ret;
1002
1003 max_buffer_size = MM_COMMUNICATE_HEADER_SIZE +
1004 MM_VARIABLE_COMMUNICATE_SIZE +
1005 max_payload_size;
1006
Heinrich Schuchardt730e2292020-07-14 08:14:08 +02001007 ret = efi_init_secure_state();
1008 if (ret != EFI_SUCCESS)
1009 return ret;
1010
Ilias Apalodimas77a364f2020-05-17 22:25:44 +03001011 return EFI_SUCCESS;
1012}