blob: ff90aa8e81c6559671e3314b2370283a28c6515a [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
18static efi_uintn_t max_buffer_size; /* comm + var + func + data */
19static efi_uintn_t max_payload_size; /* func + data */
20
21struct mm_connection {
22 struct udevice *tee;
23 u32 session;
24};
25
26/**
27 * get_connection() - Retrieve OP-TEE session for a specific UUID.
28 *
29 * @conn: session buffer to fill
30 * Return: status code
31 */
32static int get_connection(struct mm_connection *conn)
33{
34 static const struct tee_optee_ta_uuid uuid = PTA_STMM_UUID;
35 struct udevice *tee = NULL;
36 struct tee_open_session_arg arg;
37 int rc;
38
39 tee = tee_find_device(tee, NULL, NULL, NULL);
40 if (!tee)
41 return -ENODEV;
42
43 memset(&arg, 0, sizeof(arg));
44 tee_optee_ta_uuid_to_octets(arg.uuid, &uuid);
45 rc = tee_open_session(tee, &arg, 0, NULL);
46 if (!rc) {
47 conn->tee = tee;
48 conn->session = arg.session;
49 }
50
51 return rc;
52}
53
54/**
55 * optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE
56 *
57 * @comm_buf: locally allocted communcation buffer
58 * @dsize: buffer size
59 * Return: status code
60 */
61static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
62{
63 ulong buf_size;
64 efi_status_t ret;
65 struct efi_mm_communicate_header *mm_hdr;
66 struct mm_connection conn = { NULL, 0 };
67 struct tee_invoke_arg arg;
68 struct tee_param param[2];
69 struct tee_shm *shm = NULL;
70 int rc;
71
72 if (!comm_buf)
73 return EFI_INVALID_PARAMETER;
74
75 mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
76 buf_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t);
77
78 if (dsize != buf_size)
79 return EFI_INVALID_PARAMETER;
80
81 rc = get_connection(&conn);
82 if (rc) {
83 log_err("Unable to open OP-TEE session (err=%d)\n", rc);
84 return EFI_UNSUPPORTED;
85 }
86
87 if (tee_shm_register(conn.tee, comm_buf, buf_size, 0, &shm)) {
88 log_err("Unable to register shared memory\n");
89 return EFI_UNSUPPORTED;
90 }
91
92 memset(&arg, 0, sizeof(arg));
93 arg.func = PTA_STMM_CMDID_COMMUNICATE;
94 arg.session = conn.session;
95
96 memset(param, 0, sizeof(param));
97 param[0].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INOUT;
98 param[0].u.memref.size = buf_size;
99 param[0].u.memref.shm = shm;
100 param[1].attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT;
101
102 rc = tee_invoke_func(conn.tee, &arg, 2, param);
103 if (rc)
104 return EFI_INVALID_PARAMETER;
105 tee_shm_free(shm);
106 tee_close_session(conn.tee, conn.session);
107
108 switch (param[1].u.value.a) {
109 case ARM_SMC_MM_RET_SUCCESS:
110 ret = EFI_SUCCESS;
111 break;
112
113 case ARM_SMC_MM_RET_INVALID_PARAMS:
114 ret = EFI_INVALID_PARAMETER;
115 break;
116
117 case ARM_SMC_MM_RET_DENIED:
118 ret = EFI_ACCESS_DENIED;
119 break;
120
121 case ARM_SMC_MM_RET_NO_MEMORY:
122 ret = EFI_OUT_OF_RESOURCES;
123 break;
124
125 default:
126 ret = EFI_ACCESS_DENIED;
127 }
128
129 return ret;
130}
131
132/**
133 * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
134 * it to OP-TEE
135 *
136 * @comm_buf: locally allocted communcation buffer
137 * @dsize: buffer size
138 * Return: status code
139 */
140static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize)
141{
142 efi_status_t ret;
143 struct efi_mm_communicate_header *mm_hdr;
144 struct smm_variable_communicate_header *var_hdr;
145
146 dsize += MM_COMMUNICATE_HEADER_SIZE + MM_VARIABLE_COMMUNICATE_SIZE;
147 mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
148 var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
149
150 ret = optee_mm_communicate(comm_buf, dsize);
151 if (ret != EFI_SUCCESS) {
152 log_err("%s failed!\n", __func__);
153 return ret;
154 }
155
156 return var_hdr->ret_status;
157}
158
159/**
160 * setup_mm_hdr() - Allocate a buffer for StandAloneMM and initialize the
161 * header data.
162 *
163 * @dptr: pointer address of the corresponding StandAloneMM
164 * function
165 * @payload_size: buffer size
166 * @func: standAloneMM function number
167 * @ret: EFI return code
168 * Return: buffer or NULL
169 */
170static u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size,
171 efi_uintn_t func, efi_status_t *ret)
172{
173 const efi_guid_t mm_var_guid = EFI_MM_VARIABLE_GUID;
174 struct efi_mm_communicate_header *mm_hdr;
175 struct smm_variable_communicate_header *var_hdr;
176 u8 *comm_buf;
177
178 /* In the init function we initialize max_buffer_size with
179 * get_max_payload(). So skip the test if max_buffer_size is initialized
180 * StandAloneMM will perform similar checks and drop the buffer if it's
181 * too long
182 */
183 if (max_buffer_size && max_buffer_size <
184 (MM_COMMUNICATE_HEADER_SIZE +
185 MM_VARIABLE_COMMUNICATE_SIZE +
186 payload_size)) {
187 *ret = EFI_INVALID_PARAMETER;
188 return NULL;
189 }
190
191 comm_buf = calloc(1, MM_COMMUNICATE_HEADER_SIZE +
192 MM_VARIABLE_COMMUNICATE_SIZE +
193 payload_size);
194 if (!comm_buf) {
195 *ret = EFI_OUT_OF_RESOURCES;
196 return NULL;
197 }
198
199 mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
200 guidcpy(&mm_hdr->header_guid, &mm_var_guid);
201 mm_hdr->message_len = MM_VARIABLE_COMMUNICATE_SIZE + payload_size;
202
203 var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
204 var_hdr->function = func;
205 if (dptr)
206 *dptr = var_hdr->data;
207 *ret = EFI_SUCCESS;
208
209 return comm_buf;
210}
211
212/**
213 * get_max_payload() - Get variable payload size from StandAloneMM.
214 *
215 * @size: size of the variable in storage
216 * Return: status code
217 */
218efi_status_t EFIAPI get_max_payload(efi_uintn_t *size)
219{
220 struct smm_variable_payload_size *var_payload = NULL;
221 efi_uintn_t payload_size;
222 u8 *comm_buf = NULL;
223 efi_status_t ret;
224
225 if (!size) {
226 ret = EFI_INVALID_PARAMETER;
227 goto out;
228 }
229
230 payload_size = sizeof(*var_payload);
231 comm_buf = setup_mm_hdr((void **)&var_payload, payload_size,
232 SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE, &ret);
233 if (!comm_buf)
234 goto out;
235
236 ret = mm_communicate(comm_buf, payload_size);
237 if (ret != EFI_SUCCESS)
238 goto out;
239
240 *size = var_payload->size;
241
242out:
243 free(comm_buf);
244 return ret;
245}
246
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200247efi_status_t efi_get_variable_int(u16 *variable_name, const efi_guid_t *vendor,
248 u32 *attributes, efi_uintn_t *data_size,
249 void *data, u64 *timep)
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300250{
251 struct smm_variable_access *var_acc;
252 efi_uintn_t payload_size;
253 efi_uintn_t name_size;
254 efi_uintn_t tmp_dsize;
255 u8 *comm_buf = NULL;
256 efi_status_t ret;
257
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200258 if (!variable_name || !vendor || !data_size) {
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300259 ret = EFI_INVALID_PARAMETER;
260 goto out;
261 }
262
263 /* Check payload size */
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200264 name_size = u16_strsize(variable_name);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300265 if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
266 ret = EFI_INVALID_PARAMETER;
267 goto out;
268 }
269
270 /* Trim output buffer size */
271 tmp_dsize = *data_size;
272 if (name_size + tmp_dsize >
273 max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
274 tmp_dsize = max_payload_size -
275 MM_VARIABLE_ACCESS_HEADER_SIZE -
276 name_size;
277 }
278
279 /* Get communication buffer and initialize header */
280 payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + tmp_dsize;
281 comm_buf = setup_mm_hdr((void **)&var_acc, payload_size,
282 SMM_VARIABLE_FUNCTION_GET_VARIABLE, &ret);
283 if (!comm_buf)
284 goto out;
285
286 /* Fill in contents */
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200287 guidcpy(&var_acc->guid, vendor);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300288 var_acc->data_size = tmp_dsize;
289 var_acc->name_size = name_size;
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200290 var_acc->attr = attributes ? *attributes : 0;
291 memcpy(var_acc->name, variable_name, name_size);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300292
293 /* Communicate */
294 ret = mm_communicate(comm_buf, payload_size);
295 if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
296 /* Update with reported data size for trimmed case */
297 *data_size = var_acc->data_size;
298 }
299 if (ret != EFI_SUCCESS)
300 goto out;
301
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200302 if (attributes)
303 *attributes = var_acc->attr;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300304 if (data)
305 memcpy(data, (u8 *)var_acc->name + var_acc->name_size,
306 var_acc->data_size);
307 else
308 ret = EFI_INVALID_PARAMETER;
309
310out:
311 free(comm_buf);
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200312 return ret;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300313}
314
Heinrich Schuchardt276c61d2020-06-26 17:57:48 +0200315efi_status_t efi_get_next_variable_name_int(efi_uintn_t *variable_name_size,
316 u16 *variable_name,
317 efi_guid_t *guid)
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300318{
319 struct smm_variable_getnext *var_getnext;
320 efi_uintn_t payload_size;
321 efi_uintn_t out_name_size;
322 efi_uintn_t in_name_size;
323 efi_uintn_t tmp_dsize;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300324 u8 *comm_buf = NULL;
325 efi_status_t ret;
326
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300327 if (!variable_name_size || !variable_name || !guid) {
328 ret = EFI_INVALID_PARAMETER;
329 goto out;
330 }
331
332 out_name_size = *variable_name_size;
333 in_name_size = u16_strsize(variable_name);
334
335 if (out_name_size < in_name_size) {
336 ret = EFI_INVALID_PARAMETER;
337 goto out;
338 }
339
Ilias Apalodimas24105b82020-07-01 16:41:25 +0300340 if (in_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300341 ret = EFI_INVALID_PARAMETER;
342 goto out;
343 }
344
345 /* Trim output buffer size */
346 tmp_dsize = *variable_name_size;
Ilias Apalodimas24105b82020-07-01 16:41:25 +0300347 if (in_name_size + tmp_dsize >
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300348 max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
349 tmp_dsize = max_payload_size -
350 MM_VARIABLE_GET_NEXT_HEADER_SIZE -
Ilias Apalodimas24105b82020-07-01 16:41:25 +0300351 in_name_size;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300352 }
353
354 payload_size = MM_VARIABLE_GET_NEXT_HEADER_SIZE + out_name_size;
355 comm_buf = setup_mm_hdr((void **)&var_getnext, payload_size,
356 SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME,
357 &ret);
358 if (!comm_buf)
359 goto out;
360
361 /* Fill in contents */
362 guidcpy(&var_getnext->guid, guid);
363 var_getnext->name_size = out_name_size;
364 memcpy(var_getnext->name, variable_name, in_name_size);
365 memset((u8 *)var_getnext->name + in_name_size, 0x0,
366 out_name_size - in_name_size);
367
368 /* Communicate */
369 ret = mm_communicate(comm_buf, payload_size);
370 if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
371 /* Update with reported data size for trimmed case */
372 *variable_name_size = var_getnext->name_size;
373 }
374 if (ret != EFI_SUCCESS)
375 goto out;
376
377 guidcpy(guid, &var_getnext->guid);
378 memcpy(variable_name, (u8 *)var_getnext->name,
379 var_getnext->name_size);
380
381out:
382 free(comm_buf);
Heinrich Schuchardt276c61d2020-06-26 17:57:48 +0200383 return ret;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300384}
385
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200386efi_status_t efi_set_variable_int(u16 *variable_name, const efi_guid_t *vendor,
387 u32 attributes, efi_uintn_t data_size,
388 const void *data, bool ro_check)
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300389{
390 struct smm_variable_access *var_acc;
391 efi_uintn_t payload_size;
392 efi_uintn_t name_size;
393 u8 *comm_buf = NULL;
394 efi_status_t ret;
395
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200396 if (!variable_name || variable_name[0] == 0 || !vendor) {
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300397 ret = EFI_INVALID_PARAMETER;
398 goto out;
399 }
400 if (data_size > 0 && !data) {
401 ret = EFI_INVALID_PARAMETER;
402 goto out;
403 }
404
405 /* Check payload size */
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200406 name_size = u16_strsize(variable_name);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300407 payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size;
408 if (payload_size > max_payload_size) {
409 ret = EFI_INVALID_PARAMETER;
410 goto out;
411 }
412
413 /* Get communication buffer and initialize header */
414 comm_buf = setup_mm_hdr((void **)&var_acc, payload_size,
415 SMM_VARIABLE_FUNCTION_SET_VARIABLE, &ret);
416 if (!comm_buf)
417 goto out;
418
419 /* Fill in contents */
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200420 guidcpy(&var_acc->guid, vendor);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300421 var_acc->data_size = data_size;
422 var_acc->name_size = name_size;
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200423 var_acc->attr = attributes;
424 memcpy(var_acc->name, variable_name, name_size);
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300425 memcpy((u8 *)var_acc->name + name_size, data, data_size);
426
427 /* Communicate */
428 ret = mm_communicate(comm_buf, payload_size);
429
430out:
431 free(comm_buf);
Heinrich Schuchardt9827e842020-06-22 18:10:27 +0200432 return ret;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300433}
434
Heinrich Schuchardt276c61d2020-06-26 17:57:48 +0200435efi_status_t efi_query_variable_info_int(u32 attributes,
436 u64 *max_variable_storage_size,
437 u64 *remain_variable_storage_size,
438 u64 *max_variable_size)
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300439{
440 struct smm_variable_query_info *mm_query_info;
441 efi_uintn_t payload_size;
442 efi_status_t ret;
443 u8 *comm_buf;
444
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300445 payload_size = sizeof(*mm_query_info);
446 comm_buf = setup_mm_hdr((void **)&mm_query_info, payload_size,
447 SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO,
448 &ret);
449 if (!comm_buf)
450 goto out;
451
452 mm_query_info->attr = attributes;
453 ret = mm_communicate(comm_buf, payload_size);
454 if (ret != EFI_SUCCESS)
455 goto out;
456 *max_variable_storage_size = mm_query_info->max_variable_storage;
457 *remain_variable_storage_size =
458 mm_query_info->remaining_variable_storage;
459 *max_variable_size = mm_query_info->max_variable_size;
460
461out:
462 free(comm_buf);
Heinrich Schuchardt276c61d2020-06-26 17:57:48 +0200463 return ret;
Ilias Apalodimas77a364f2020-05-17 22:25:44 +0300464}
465
466/**
467 * efi_get_variable_runtime() - runtime implementation of GetVariable()
468 *
469 * @variable_name: name of the variable
470 * @guid: vendor GUID
471 * @attributes: attributes of the variable
472 * @data_size: size of the buffer to which the variable value is copied
473 * @data: buffer to which the variable value is copied
474 * Return: status code
475 */
476static efi_status_t __efi_runtime EFIAPI
477efi_get_variable_runtime(u16 *variable_name, const efi_guid_t *guid,
478 u32 *attributes, efi_uintn_t *data_size, void *data)
479{
480 return EFI_UNSUPPORTED;
481}
482
483/**
484 * efi_get_next_variable_name_runtime() - runtime implementation of
485 * GetNextVariable()
486 *
487 * @variable_name_size: size of variable_name buffer in byte
488 * @variable_name: name of uefi variable's name in u16
489 * @guid: vendor's guid
490 * Return: status code
491 */
492static efi_status_t __efi_runtime EFIAPI
493efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size,
494 u16 *variable_name, efi_guid_t *guid)
495{
496 return EFI_UNSUPPORTED;
497}
498
499/**
500 * efi_query_variable_info() - get information about EFI variables
501 *
502 * This function implements the QueryVariableInfo() runtime service.
503 *
504 * See the Unified Extensible Firmware Interface (UEFI) specification for
505 * details.
506 *
507 * @attributes: bitmask to select variables to be
508 * queried
509 * @maximum_variable_storage_size: maximum size of storage area for the
510 * selected variable types
511 * @remaining_variable_storage_size: remaining size of storage are for the
512 * selected variable types
513 * @maximum_variable_size: maximum size of a variable of the
514 * selected type
515 * Return: status code
516 */
517efi_status_t EFIAPI __efi_runtime
518efi_query_variable_info_runtime(u32 attributes, u64 *max_variable_storage_size,
519 u64 *remain_variable_storage_size,
520 u64 *max_variable_size)
521{
522 return EFI_UNSUPPORTED;
523}
524
525/**
526 * efi_set_variable_runtime() - runtime implementation of SetVariable()
527 *
528 * @variable_name: name of the variable
529 * @guid: vendor GUID
530 * @attributes: attributes of the variable
531 * @data_size: size of the buffer with the variable value
532 * @data: buffer with the variable value
533 * Return: status code
534 */
535static efi_status_t __efi_runtime EFIAPI
536efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *guid,
537 u32 attributes, efi_uintn_t data_size,
538 const void *data)
539{
540 return EFI_UNSUPPORTED;
541}
542
543/**
544 * efi_variables_boot_exit_notify() - notify ExitBootServices() is called
545 */
546void efi_variables_boot_exit_notify(void)
547{
548 u8 *comm_buf;
549 efi_status_t ret;
550
551 comm_buf = setup_mm_hdr(NULL, 0,
552 SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE, &ret);
553 if (comm_buf)
554 ret = mm_communicate(comm_buf, 0);
555 else
556 ret = EFI_NOT_FOUND;
557
558 if (ret != EFI_SUCCESS)
559 log_err("Unable to notify StMM for ExitBootServices\n");
560 free(comm_buf);
561
562 /* Update runtime service table */
563 efi_runtime_services.query_variable_info =
564 efi_query_variable_info_runtime;
565 efi_runtime_services.get_variable = efi_get_variable_runtime;
566 efi_runtime_services.get_next_variable_name =
567 efi_get_next_variable_name_runtime;
568 efi_runtime_services.set_variable = efi_set_variable_runtime;
569 efi_update_table_header_crc32(&efi_runtime_services.hdr);
570}
571
572/**
573 * efi_init_variables() - initialize variable services
574 *
575 * Return: status code
576 */
577efi_status_t efi_init_variables(void)
578{
579 efi_status_t ret;
580
581 ret = get_max_payload(&max_payload_size);
582 if (ret != EFI_SUCCESS)
583 return ret;
584
585 max_buffer_size = MM_COMMUNICATE_HEADER_SIZE +
586 MM_VARIABLE_COMMUNICATE_SIZE +
587 max_payload_size;
588
589 return EFI_SUCCESS;
590}