blob: be6f3dfad46942e91c22f8aabeee5c841ea9a502 [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>
7 */
8
9#include <common.h>
10#include <efi.h>
11#include <efi_api.h>
12#include <efi_loader.h>
Heinrich Schuchardt9827e842020-06-22 18:10:27 +020013#include <efi_variable.h>
Ilias Apalodimas77a364f2020-05-17 22:25:44 +030014#include <tee.h>
15#include <malloc.h>
16#include <mm_communication.h>
17
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +030018#define OPTEE_PAGE_SIZE BIT(12)
19extern struct efi_var_file __efi_runtime_data *efi_var_buf;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +030020static efi_uintn_t max_buffer_size; /* comm + var + func + data */
21static efi_uintn_t max_payload_size; /* func + data */
22
23struct mm_connection {
24 struct udevice *tee;
25 u32 session;
26};
27
28/**
29 * get_connection() - Retrieve OP-TEE session for a specific UUID.
30 *
31 * @conn: session buffer to fill
32 * Return: status code
33 */
34static int get_connection(struct mm_connection *conn)
35{
36 static const struct tee_optee_ta_uuid uuid = PTA_STMM_UUID;
37 struct udevice *tee = NULL;
38 struct tee_open_session_arg arg;
39 int rc;
40
41 tee = tee_find_device(tee, NULL, NULL, NULL);
42 if (!tee)
43 return -ENODEV;
44
45 memset(&arg, 0, sizeof(arg));
46 tee_optee_ta_uuid_to_octets(arg.uuid, &uuid);
47 rc = tee_open_session(tee, &arg, 0, NULL);
48 if (!rc) {
49 conn->tee = tee;
50 conn->session = arg.session;
51 }
52
53 return rc;
54}
55
56/**
57 * optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE
58 *
59 * @comm_buf: locally allocted communcation buffer
60 * @dsize: buffer size
61 * Return: status code
62 */
63static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
64{
65 ulong buf_size;
66 efi_status_t ret;
67 struct efi_mm_communicate_header *mm_hdr;
68 struct mm_connection conn = { NULL, 0 };
69 struct tee_invoke_arg arg;
70 struct tee_param param[2];
71 struct tee_shm *shm = NULL;
72 int rc;
73
74 if (!comm_buf)
75 return EFI_INVALID_PARAMETER;
76
77 mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
78 buf_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t);
79
80 if (dsize != buf_size)
81 return EFI_INVALID_PARAMETER;
82
83 rc = get_connection(&conn);
84 if (rc) {
85 log_err("Unable to open OP-TEE session (err=%d)\n", rc);
86 return EFI_UNSUPPORTED;
87 }
88
89 if (tee_shm_register(conn.tee, comm_buf, buf_size, 0, &shm)) {
90 log_err("Unable to register shared memory\n");
91 return EFI_UNSUPPORTED;
92 }
93
94 memset(&arg, 0, sizeof(arg));
95 arg.func = PTA_STMM_CMDID_COMMUNICATE;
96 arg.session = conn.session;
97
98 memset(param, 0, sizeof(param));
99 param[0].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INOUT;
100 param[0].u.memref.size = buf_size;
101 param[0].u.memref.shm = shm;
102 param[1].attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT;
103
104 rc = tee_invoke_func(conn.tee, &arg, 2, param);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300105 tee_shm_free(shm);
106 tee_close_session(conn.tee, conn.session);
Ilias Apalodimasfe7c1fc2020-07-22 10:32:22 +0300107 if (rc || arg.ret != TEE_SUCCESS)
108 return EFI_DEVICE_ERROR;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300109
110 switch (param[1].u.value.a) {
Ilias Apalodimas128a3c52020-07-17 07:55:03 +0300111 case ARM_SVC_SPM_RET_SUCCESS:
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300112 ret = EFI_SUCCESS;
113 break;
114
Ilias Apalodimas128a3c52020-07-17 07:55:03 +0300115 case ARM_SVC_SPM_RET_INVALID_PARAMS:
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300116 ret = EFI_INVALID_PARAMETER;
117 break;
118
Ilias Apalodimas128a3c52020-07-17 07:55:03 +0300119 case ARM_SVC_SPM_RET_DENIED:
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300120 ret = EFI_ACCESS_DENIED;
121 break;
122
Ilias Apalodimas128a3c52020-07-17 07:55:03 +0300123 case ARM_SVC_SPM_RET_NO_MEMORY:
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300124 ret = EFI_OUT_OF_RESOURCES;
125 break;
126
127 default:
128 ret = EFI_ACCESS_DENIED;
129 }
130
131 return ret;
132}
133
134/**
135 * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
136 * it to OP-TEE
137 *
138 * @comm_buf: locally allocted communcation buffer
139 * @dsize: buffer size
140 * Return: status code
141 */
142static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize)
143{
144 efi_status_t ret;
145 struct efi_mm_communicate_header *mm_hdr;
146 struct smm_variable_communicate_header *var_hdr;
147
148 dsize += MM_COMMUNICATE_HEADER_SIZE + MM_VARIABLE_COMMUNICATE_SIZE;
149 mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
150 var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
151
152 ret = optee_mm_communicate(comm_buf, dsize);
153 if (ret != EFI_SUCCESS) {
154 log_err("%s failed!\n", __func__);
155 return ret;
156 }
157
158 return var_hdr->ret_status;
159}
160
161/**
162 * setup_mm_hdr() - Allocate a buffer for StandAloneMM and initialize the
163 * header data.
164 *
165 * @dptr: pointer address of the corresponding StandAloneMM
166 * function
167 * @payload_size: buffer size
168 * @func: standAloneMM function number
169 * @ret: EFI return code
170 * Return: buffer or NULL
171 */
172static u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size,
173 efi_uintn_t func, efi_status_t *ret)
174{
175 const efi_guid_t mm_var_guid = EFI_MM_VARIABLE_GUID;
176 struct efi_mm_communicate_header *mm_hdr;
177 struct smm_variable_communicate_header *var_hdr;
178 u8 *comm_buf;
179
180 /* In the init function we initialize max_buffer_size with
181 * get_max_payload(). So skip the test if max_buffer_size is initialized
182 * StandAloneMM will perform similar checks and drop the buffer if it's
183 * too long
184 */
185 if (max_buffer_size && max_buffer_size <
186 (MM_COMMUNICATE_HEADER_SIZE +
187 MM_VARIABLE_COMMUNICATE_SIZE +
188 payload_size)) {
189 *ret = EFI_INVALID_PARAMETER;
190 return NULL;
191 }
192
193 comm_buf = calloc(1, MM_COMMUNICATE_HEADER_SIZE +
194 MM_VARIABLE_COMMUNICATE_SIZE +
195 payload_size);
196 if (!comm_buf) {
197 *ret = EFI_OUT_OF_RESOURCES;
198 return NULL;
199 }
200
201 mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
202 guidcpy(&mm_hdr->header_guid, &mm_var_guid);
203 mm_hdr->message_len = MM_VARIABLE_COMMUNICATE_SIZE + payload_size;
204
205 var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
206 var_hdr->function = func;
207 if (dptr)
208 *dptr = var_hdr->data;
209 *ret = EFI_SUCCESS;
210
211 return comm_buf;
212}
213
214/**
215 * get_max_payload() - Get variable payload size from StandAloneMM.
216 *
217 * @size: size of the variable in storage
218 * Return: status code
219 */
220efi_status_t EFIAPI get_max_payload(efi_uintn_t *size)
221{
222 struct smm_variable_payload_size *var_payload = NULL;
223 efi_uintn_t payload_size;
224 u8 *comm_buf = NULL;
225 efi_status_t ret;
226
227 if (!size) {
228 ret = EFI_INVALID_PARAMETER;
229 goto out;
230 }
231
232 payload_size = sizeof(*var_payload);
233 comm_buf = setup_mm_hdr((void **)&var_payload, payload_size,
234 SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE, &ret);
235 if (!comm_buf)
236 goto out;
237
238 ret = mm_communicate(comm_buf, payload_size);
239 if (ret != EFI_SUCCESS)
240 goto out;
241
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300242 /* Make sure the buffer is big enough for storing variables */
243 if (var_payload->size < MM_VARIABLE_ACCESS_HEADER_SIZE + 0x20) {
244 ret = EFI_DEVICE_ERROR;
245 goto out;
246 }
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300247 *size = var_payload->size;
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300248 /*
249 * Although the max payload is configurable on StMM, we only share a
250 * single page from OP-TEE for the non-secure buffer used to communicate
251 * with StMM. Since OP-TEE will reject to map anything bigger than that,
252 * make sure we are in bounds.
253 */
254 if (*size > OPTEE_PAGE_SIZE)
255 *size = OPTEE_PAGE_SIZE - MM_COMMUNICATE_HEADER_SIZE -
256 MM_VARIABLE_COMMUNICATE_SIZE;
257 /*
258 * There seems to be a bug in EDK2 miscalculating the boundaries and
259 * size checks, so deduct 2 more bytes to fulfill this requirement. Fix
260 * it up here to ensure backwards compatibility with older versions
261 * (cf. StandaloneMmPkg/Drivers/StandaloneMmCpu/AArch64/EventHandle.c.
262 * sizeof (EFI_MM_COMMUNICATE_HEADER) instead the size minus the
263 * flexible array member).
264 *
265 * size is guaranteed to be > 2 due to checks on the beginning.
266 */
267 *size -= 2;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300268out:
269 free(comm_buf);
270 return ret;
271}
272
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300273/*
274 * StMM can store internal attributes and properties for variables, i.e enabling
275 * R/O variables
276 */
277static efi_status_t set_property_int(u16 *variable_name, efi_uintn_t name_size,
278 const efi_guid_t *vendor,
279 struct var_check_property *var_property)
280{
281 struct smm_variable_var_check_property *smm_property;
282 efi_uintn_t payload_size;
283 u8 *comm_buf = NULL;
284 efi_status_t ret;
285
286 payload_size = sizeof(*smm_property) + name_size;
287 if (payload_size > max_payload_size) {
288 ret = EFI_INVALID_PARAMETER;
289 goto out;
290 }
291 comm_buf = setup_mm_hdr((void **)&smm_property, payload_size,
292 SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET,
293 &ret);
294 if (!comm_buf)
295 goto out;
296
297 guidcpy(&smm_property->guid, vendor);
298 smm_property->name_size = name_size;
299 memcpy(&smm_property->property, var_property,
300 sizeof(smm_property->property));
301 memcpy(smm_property->name, variable_name, name_size);
302
303 ret = mm_communicate(comm_buf, payload_size);
304
305out:
306 free(comm_buf);
307 return ret;
308}
309
310static efi_status_t get_property_int(u16 *variable_name, efi_uintn_t name_size,
311 const efi_guid_t *vendor,
312 struct var_check_property *var_property)
313{
314 struct smm_variable_var_check_property *smm_property;
315 efi_uintn_t payload_size;
316 u8 *comm_buf = NULL;
317 efi_status_t ret;
318
319 memset(var_property, 0, sizeof(*var_property));
320 payload_size = sizeof(*smm_property) + name_size;
321 if (payload_size > max_payload_size) {
322 ret = EFI_INVALID_PARAMETER;
323 goto out;
324 }
325 comm_buf = setup_mm_hdr((void **)&smm_property, payload_size,
326 SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET,
327 &ret);
328 if (!comm_buf)
329 goto out;
330
331 guidcpy(&smm_property->guid, vendor);
332 smm_property->name_size = name_size;
333 memcpy(smm_property->name, variable_name, name_size);
334
335 ret = mm_communicate(comm_buf, payload_size);
336 /*
337 * Currently only R/O property is supported in StMM.
338 * Variables that are not set to R/O will not set the property in StMM
339 * and the call will return EFI_NOT_FOUND. We are setting the
340 * properties to 0x0 so checking against that is enough for the
341 * EFI_NOT_FOUND case.
342 */
343 if (ret == EFI_NOT_FOUND)
344 ret = EFI_SUCCESS;
345 if (ret != EFI_SUCCESS)
346 goto out;
347 memcpy(var_property, &smm_property->property, sizeof(*var_property));
348
349out:
350 free(comm_buf);
351 return ret;
352}
353
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200354efi_status_t efi_get_variable_int(u16 *variable_name, const efi_guid_t *vendor,
355 u32 *attributes, efi_uintn_t *data_size,
356 void *data, u64 *timep)
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300357{
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300358 struct var_check_property var_property;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300359 struct smm_variable_access *var_acc;
360 efi_uintn_t payload_size;
361 efi_uintn_t name_size;
362 efi_uintn_t tmp_dsize;
363 u8 *comm_buf = NULL;
364 efi_status_t ret;
365
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200366 if (!variable_name || !vendor || !data_size) {
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300367 ret = EFI_INVALID_PARAMETER;
368 goto out;
369 }
370
371 /* Check payload size */
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200372 name_size = u16_strsize(variable_name);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300373 if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
374 ret = EFI_INVALID_PARAMETER;
375 goto out;
376 }
377
378 /* Trim output buffer size */
379 tmp_dsize = *data_size;
380 if (name_size + tmp_dsize >
381 max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
382 tmp_dsize = max_payload_size -
383 MM_VARIABLE_ACCESS_HEADER_SIZE -
384 name_size;
385 }
386
387 /* Get communication buffer and initialize header */
388 payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + tmp_dsize;
389 comm_buf = setup_mm_hdr((void **)&var_acc, payload_size,
390 SMM_VARIABLE_FUNCTION_GET_VARIABLE, &ret);
391 if (!comm_buf)
392 goto out;
393
394 /* Fill in contents */
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200395 guidcpy(&var_acc->guid, vendor);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300396 var_acc->data_size = tmp_dsize;
397 var_acc->name_size = name_size;
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200398 var_acc->attr = attributes ? *attributes : 0;
399 memcpy(var_acc->name, variable_name, name_size);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300400
401 /* Communicate */
402 ret = mm_communicate(comm_buf, payload_size);
403 if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
404 /* Update with reported data size for trimmed case */
405 *data_size = var_acc->data_size;
406 }
407 if (ret != EFI_SUCCESS)
408 goto out;
409
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300410 ret = get_property_int(variable_name, name_size, vendor, &var_property);
411 if (ret != EFI_SUCCESS)
412 goto out;
413
414 if (attributes) {
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200415 *attributes = var_acc->attr;
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300416 if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY)
417 *attributes |= EFI_VARIABLE_READ_ONLY;
418 }
419
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300420 if (data)
421 memcpy(data, (u8 *)var_acc->name + var_acc->name_size,
422 var_acc->data_size);
423 else
424 ret = EFI_INVALID_PARAMETER;
425
426out:
427 free(comm_buf);
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200428 return ret;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300429}
430
Heinrich Schuchardt276c61d2020-06-26 17:57:48 +0200431efi_status_t efi_get_next_variable_name_int(efi_uintn_t *variable_name_size,
432 u16 *variable_name,
433 efi_guid_t *guid)
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300434{
435 struct smm_variable_getnext *var_getnext;
436 efi_uintn_t payload_size;
437 efi_uintn_t out_name_size;
438 efi_uintn_t in_name_size;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300439 u8 *comm_buf = NULL;
440 efi_status_t ret;
441
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300442 if (!variable_name_size || !variable_name || !guid) {
443 ret = EFI_INVALID_PARAMETER;
444 goto out;
445 }
446
447 out_name_size = *variable_name_size;
448 in_name_size = u16_strsize(variable_name);
449
450 if (out_name_size < in_name_size) {
451 ret = EFI_INVALID_PARAMETER;
452 goto out;
453 }
454
Ilias Apalodimas24105b82020-07-01 16:41:25 +0300455 if (in_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300456 ret = EFI_INVALID_PARAMETER;
457 goto out;
458 }
459
460 /* Trim output buffer size */
Ilias Apalodimas91b218d2020-07-22 01:50:37 +0300461 if (out_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE)
462 out_name_size = max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300463
464 payload_size = MM_VARIABLE_GET_NEXT_HEADER_SIZE + out_name_size;
465 comm_buf = setup_mm_hdr((void **)&var_getnext, payload_size,
466 SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME,
467 &ret);
468 if (!comm_buf)
469 goto out;
470
471 /* Fill in contents */
472 guidcpy(&var_getnext->guid, guid);
473 var_getnext->name_size = out_name_size;
474 memcpy(var_getnext->name, variable_name, in_name_size);
475 memset((u8 *)var_getnext->name + in_name_size, 0x0,
476 out_name_size - in_name_size);
477
478 /* Communicate */
479 ret = mm_communicate(comm_buf, payload_size);
480 if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
481 /* Update with reported data size for trimmed case */
482 *variable_name_size = var_getnext->name_size;
483 }
484 if (ret != EFI_SUCCESS)
485 goto out;
486
487 guidcpy(guid, &var_getnext->guid);
Ilias Apalodimas91b218d2020-07-22 01:50:37 +0300488 memcpy(variable_name, var_getnext->name, var_getnext->name_size);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300489
490out:
491 free(comm_buf);
Heinrich Schuchardt276c61d2020-06-26 17:57:48 +0200492 return ret;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300493}
494
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200495efi_status_t efi_set_variable_int(u16 *variable_name, const efi_guid_t *vendor,
496 u32 attributes, efi_uintn_t data_size,
497 const void *data, bool ro_check)
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300498{
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300499 efi_status_t ret, alt_ret = EFI_SUCCESS;
500 struct var_check_property var_property;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300501 struct smm_variable_access *var_acc;
502 efi_uintn_t payload_size;
503 efi_uintn_t name_size;
504 u8 *comm_buf = NULL;
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300505 bool ro;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300506
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200507 if (!variable_name || variable_name[0] == 0 || !vendor) {
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300508 ret = EFI_INVALID_PARAMETER;
509 goto out;
510 }
511 if (data_size > 0 && !data) {
512 ret = EFI_INVALID_PARAMETER;
513 goto out;
514 }
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300515 /* Check payload size */
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200516 name_size = u16_strsize(variable_name);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300517 payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size;
518 if (payload_size > max_payload_size) {
519 ret = EFI_INVALID_PARAMETER;
520 goto out;
521 }
522
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300523 /*
524 * Allocate the buffer early, before switching to RW (if needed)
525 * so we won't need to account for any failures in reading/setting
526 * the properties, if the allocation fails
527 */
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300528 comm_buf = setup_mm_hdr((void **)&var_acc, payload_size,
529 SMM_VARIABLE_FUNCTION_SET_VARIABLE, &ret);
530 if (!comm_buf)
531 goto out;
532
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300533 ro = !!(attributes & EFI_VARIABLE_READ_ONLY);
534 attributes &= EFI_VARIABLE_MASK;
535
536 /*
537 * The API has the ability to override RO flags. If no RO check was
538 * requested switch the variable to RW for the duration of this call
539 */
540 ret = get_property_int(variable_name, name_size, vendor,
541 &var_property);
542 if (ret != EFI_SUCCESS)
543 goto out;
544
545 if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) {
546 /* Bypass r/o check */
547 if (!ro_check) {
548 var_property.property &= ~VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY;
549 ret = set_property_int(variable_name, name_size, vendor, &var_property);
550 if (ret != EFI_SUCCESS)
551 goto out;
552 } else {
553 ret = EFI_WRITE_PROTECTED;
554 goto out;
555 }
556 }
557
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300558 /* Fill in contents */
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200559 guidcpy(&var_acc->guid, vendor);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300560 var_acc->data_size = data_size;
561 var_acc->name_size = name_size;
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200562 var_acc->attr = attributes;
563 memcpy(var_acc->name, variable_name, name_size);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300564 memcpy((u8 *)var_acc->name + name_size, data, data_size);
565
566 /* Communicate */
567 ret = mm_communicate(comm_buf, payload_size);
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300568 if (ret != EFI_SUCCESS)
569 alt_ret = ret;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300570
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300571 if (ro && !(var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY)) {
572 var_property.revision = VAR_CHECK_VARIABLE_PROPERTY_REVISION;
573 var_property.property |= VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY;
574 var_property.attributes = attributes;
575 var_property.minsize = 1;
576 var_property.maxsize = var_acc->data_size;
577 ret = set_property_int(variable_name, name_size, vendor, &var_property);
578 }
Heinrich Schuchardt730e2292020-07-14 08:14:08 +0200579
580 if (alt_ret != EFI_SUCCESS)
581 goto out;
582
583 if (!u16_strcmp(variable_name, L"PK"))
584 alt_ret = efi_init_secure_state();
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300585out:
586 free(comm_buf);
Ilias Apalodimas5f1bce92020-07-09 23:00:40 +0300587 return alt_ret == EFI_SUCCESS ? ret : alt_ret;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300588}
589
Heinrich Schuchardt276c61d2020-06-26 17:57:48 +0200590efi_status_t efi_query_variable_info_int(u32 attributes,
591 u64 *max_variable_storage_size,
592 u64 *remain_variable_storage_size,
593 u64 *max_variable_size)
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300594{
595 struct smm_variable_query_info *mm_query_info;
596 efi_uintn_t payload_size;
597 efi_status_t ret;
598 u8 *comm_buf;
599
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300600 payload_size = sizeof(*mm_query_info);
601 comm_buf = setup_mm_hdr((void **)&mm_query_info, payload_size,
602 SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO,
603 &ret);
604 if (!comm_buf)
605 goto out;
606
607 mm_query_info->attr = attributes;
608 ret = mm_communicate(comm_buf, payload_size);
609 if (ret != EFI_SUCCESS)
610 goto out;
611 *max_variable_storage_size = mm_query_info->max_variable_storage;
612 *remain_variable_storage_size =
613 mm_query_info->remaining_variable_storage;
614 *max_variable_size = mm_query_info->max_variable_size;
615
616out:
617 free(comm_buf);
Heinrich Schuchardt276c61d2020-06-26 17:57:48 +0200618 return ret;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300619}
620
621/**
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300622 * efi_query_variable_info() - get information about EFI variables
623 *
624 * This function implements the QueryVariableInfo() runtime service.
625 *
626 * See the Unified Extensible Firmware Interface (UEFI) specification for
627 * details.
628 *
629 * @attributes: bitmask to select variables to be
630 * queried
631 * @maximum_variable_storage_size: maximum size of storage area for the
632 * selected variable types
633 * @remaining_variable_storage_size: remaining size of storage are for the
634 * selected variable types
635 * @maximum_variable_size: maximum size of a variable of the
636 * selected type
637 * Return: status code
638 */
639efi_status_t EFIAPI __efi_runtime
640efi_query_variable_info_runtime(u32 attributes, u64 *max_variable_storage_size,
641 u64 *remain_variable_storage_size,
642 u64 *max_variable_size)
643{
644 return EFI_UNSUPPORTED;
645}
646
647/**
648 * efi_set_variable_runtime() - runtime implementation of SetVariable()
649 *
650 * @variable_name: name of the variable
651 * @guid: vendor GUID
652 * @attributes: attributes of the variable
653 * @data_size: size of the buffer with the variable value
654 * @data: buffer with the variable value
655 * Return: status code
656 */
657static efi_status_t __efi_runtime EFIAPI
658efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *guid,
659 u32 attributes, efi_uintn_t data_size,
660 const void *data)
661{
662 return EFI_UNSUPPORTED;
663}
664
665/**
666 * efi_variables_boot_exit_notify() - notify ExitBootServices() is called
667 */
668void efi_variables_boot_exit_notify(void)
669{
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300670 efi_status_t ret;
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300671 u8 *comm_buf;
672 loff_t len;
673 struct efi_var_file *var_buf;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300674
675 comm_buf = setup_mm_hdr(NULL, 0,
676 SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE, &ret);
677 if (comm_buf)
678 ret = mm_communicate(comm_buf, 0);
679 else
680 ret = EFI_NOT_FOUND;
681
682 if (ret != EFI_SUCCESS)
683 log_err("Unable to notify StMM for ExitBootServices\n");
684 free(comm_buf);
685
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300686 /*
687 * Populate the list for runtime variables.
688 * asking EFI_VARIABLE_RUNTIME_ACCESS is redundant, since
689 * efi_var_mem_notify_exit_boot_services will clean those, but that's fine
690 */
691 ret = efi_var_collect(&var_buf, &len, EFI_VARIABLE_RUNTIME_ACCESS);
692 if (ret != EFI_SUCCESS)
693 log_err("Can't populate EFI variables. No runtime variables will be available\n");
694 else
695 memcpy(efi_var_buf, var_buf, len);
696 free(var_buf);
697
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300698 /* Update runtime service table */
699 efi_runtime_services.query_variable_info =
700 efi_query_variable_info_runtime;
701 efi_runtime_services.get_variable = efi_get_variable_runtime;
702 efi_runtime_services.get_next_variable_name =
703 efi_get_next_variable_name_runtime;
704 efi_runtime_services.set_variable = efi_set_variable_runtime;
705 efi_update_table_header_crc32(&efi_runtime_services.hdr);
706}
707
708/**
709 * efi_init_variables() - initialize variable services
710 *
711 * Return: status code
712 */
713efi_status_t efi_init_variables(void)
714{
715 efi_status_t ret;
716
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300717 /* Create a cached copy of the variables that will be enabled on ExitBootServices() */
718 ret = efi_var_mem_init();
719 if (ret != EFI_SUCCESS)
720 return ret;
721
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300722 ret = get_max_payload(&max_payload_size);
723 if (ret != EFI_SUCCESS)
724 return ret;
725
726 max_buffer_size = MM_COMMUNICATE_HEADER_SIZE +
727 MM_VARIABLE_COMMUNICATE_SIZE +
728 max_payload_size;
729
Heinrich Schuchardt730e2292020-07-14 08:14:08 +0200730 ret = efi_init_secure_state();
731 if (ret != EFI_SUCCESS)
732 return ret;
733
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300734 return EFI_SUCCESS;
735}