efi_loader: Implement EFI variable handling via OP-TEE
[oweals/u-boot.git] / lib / efi_loader / efi_variable_tee.c
1 // 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>
13 #include <tee.h>
14 #include <malloc.h>
15 #include <mm_communication.h>
16
17 static efi_uintn_t max_buffer_size;     /* comm + var + func + data */
18 static efi_uintn_t max_payload_size;    /* func + data */
19
20 struct mm_connection {
21         struct udevice *tee;
22         u32 session;
23 };
24
25 /**
26  * get_connection() - Retrieve OP-TEE session for a specific UUID.
27  *
28  * @conn:   session buffer to fill
29  * Return:  status code
30  */
31 static int get_connection(struct mm_connection *conn)
32 {
33         static const struct tee_optee_ta_uuid uuid = PTA_STMM_UUID;
34         struct udevice *tee = NULL;
35         struct tee_open_session_arg arg;
36         int rc;
37
38         tee = tee_find_device(tee, NULL, NULL, NULL);
39         if (!tee)
40                 return -ENODEV;
41
42         memset(&arg, 0, sizeof(arg));
43         tee_optee_ta_uuid_to_octets(arg.uuid, &uuid);
44         rc = tee_open_session(tee, &arg, 0, NULL);
45         if (!rc) {
46                 conn->tee = tee;
47                 conn->session = arg.session;
48         }
49
50         return rc;
51 }
52
53 /**
54  * optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE
55  *
56  * @comm_buf:           locally allocted communcation buffer
57  * @dsize:              buffer size
58  * Return:              status code
59  */
60 static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
61 {
62         ulong buf_size;
63         efi_status_t ret;
64         struct efi_mm_communicate_header *mm_hdr;
65         struct mm_connection conn = { NULL, 0 };
66         struct tee_invoke_arg arg;
67         struct tee_param param[2];
68         struct tee_shm *shm = NULL;
69         int rc;
70
71         if (!comm_buf)
72                 return EFI_INVALID_PARAMETER;
73
74         mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
75         buf_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t);
76
77         if (dsize != buf_size)
78                 return EFI_INVALID_PARAMETER;
79
80         rc = get_connection(&conn);
81         if (rc) {
82                 log_err("Unable to open OP-TEE session (err=%d)\n", rc);
83                 return EFI_UNSUPPORTED;
84         }
85
86         if (tee_shm_register(conn.tee, comm_buf, buf_size, 0, &shm)) {
87                 log_err("Unable to register shared memory\n");
88                 return EFI_UNSUPPORTED;
89         }
90
91         memset(&arg, 0, sizeof(arg));
92         arg.func = PTA_STMM_CMDID_COMMUNICATE;
93         arg.session = conn.session;
94
95         memset(param, 0, sizeof(param));
96         param[0].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INOUT;
97         param[0].u.memref.size = buf_size;
98         param[0].u.memref.shm = shm;
99         param[1].attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT;
100
101         rc = tee_invoke_func(conn.tee, &arg, 2, param);
102         if (rc)
103                 return EFI_INVALID_PARAMETER;
104         tee_shm_free(shm);
105         tee_close_session(conn.tee, conn.session);
106
107         switch (param[1].u.value.a) {
108         case ARM_SMC_MM_RET_SUCCESS:
109                 ret = EFI_SUCCESS;
110                 break;
111
112         case ARM_SMC_MM_RET_INVALID_PARAMS:
113                 ret = EFI_INVALID_PARAMETER;
114                 break;
115
116         case ARM_SMC_MM_RET_DENIED:
117                 ret = EFI_ACCESS_DENIED;
118                 break;
119
120         case ARM_SMC_MM_RET_NO_MEMORY:
121                 ret = EFI_OUT_OF_RESOURCES;
122                 break;
123
124         default:
125                 ret = EFI_ACCESS_DENIED;
126         }
127
128         return ret;
129 }
130
131 /**
132  * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
133  * it to OP-TEE
134  *
135  * @comm_buf:           locally allocted communcation buffer
136  * @dsize:              buffer size
137  * Return:              status code
138  */
139 static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize)
140 {
141         efi_status_t ret;
142         struct efi_mm_communicate_header *mm_hdr;
143         struct smm_variable_communicate_header *var_hdr;
144
145         dsize += MM_COMMUNICATE_HEADER_SIZE + MM_VARIABLE_COMMUNICATE_SIZE;
146         mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
147         var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
148
149         ret = optee_mm_communicate(comm_buf, dsize);
150         if (ret != EFI_SUCCESS) {
151                 log_err("%s failed!\n", __func__);
152                 return ret;
153         }
154
155         return var_hdr->ret_status;
156 }
157
158 /**
159  * setup_mm_hdr() -     Allocate a buffer for StandAloneMM and initialize the
160  *                      header data.
161  *
162  * @dptr:               pointer address of the corresponding StandAloneMM
163  *                      function
164  * @payload_size:       buffer size
165  * @func:               standAloneMM function number
166  * @ret:                EFI return code
167  * Return:              buffer or NULL
168  */
169 static u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size,
170                         efi_uintn_t func, efi_status_t *ret)
171 {
172         const efi_guid_t mm_var_guid = EFI_MM_VARIABLE_GUID;
173         struct efi_mm_communicate_header *mm_hdr;
174         struct smm_variable_communicate_header *var_hdr;
175         u8 *comm_buf;
176
177         /* In the init function we initialize max_buffer_size with
178          * get_max_payload(). So skip the test if max_buffer_size is initialized
179          * StandAloneMM will perform similar checks and drop the buffer if it's
180          * too long
181          */
182         if (max_buffer_size && max_buffer_size <
183                         (MM_COMMUNICATE_HEADER_SIZE +
184                          MM_VARIABLE_COMMUNICATE_SIZE +
185                          payload_size)) {
186                 *ret = EFI_INVALID_PARAMETER;
187                 return NULL;
188         }
189
190         comm_buf = calloc(1, MM_COMMUNICATE_HEADER_SIZE +
191                           MM_VARIABLE_COMMUNICATE_SIZE +
192                           payload_size);
193         if (!comm_buf) {
194                 *ret = EFI_OUT_OF_RESOURCES;
195                 return NULL;
196         }
197
198         mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
199         guidcpy(&mm_hdr->header_guid, &mm_var_guid);
200         mm_hdr->message_len = MM_VARIABLE_COMMUNICATE_SIZE + payload_size;
201
202         var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
203         var_hdr->function = func;
204         if (dptr)
205                 *dptr = var_hdr->data;
206         *ret = EFI_SUCCESS;
207
208         return comm_buf;
209 }
210
211 /**
212  * get_max_payload() - Get variable payload size from StandAloneMM.
213  *
214  * @size:    size of the variable in storage
215  * Return:   status code
216  */
217 efi_status_t EFIAPI get_max_payload(efi_uintn_t *size)
218 {
219         struct smm_variable_payload_size *var_payload = NULL;
220         efi_uintn_t payload_size;
221         u8 *comm_buf = NULL;
222         efi_status_t ret;
223
224         if (!size) {
225                 ret = EFI_INVALID_PARAMETER;
226                 goto out;
227         }
228
229         payload_size = sizeof(*var_payload);
230         comm_buf = setup_mm_hdr((void **)&var_payload, payload_size,
231                                 SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE, &ret);
232         if (!comm_buf)
233                 goto out;
234
235         ret = mm_communicate(comm_buf, payload_size);
236         if (ret != EFI_SUCCESS)
237                 goto out;
238
239         *size = var_payload->size;
240
241 out:
242         free(comm_buf);
243         return ret;
244 }
245
246 /**
247  * efi_get_variable() - retrieve value of a UEFI variable
248  *
249  * This function implements the GetVariable runtime service.
250  *
251  * See the Unified Extensible Firmware Interface (UEFI) specification for
252  * details.
253  *
254  * @name:               name of the variable
255  * @guid:               vendor GUID
256  * @attr:               attributes of the variable
257  * @data_size:          size of the buffer to which the variable value is copied
258  * @data:               buffer to which the variable value is copied
259  * Return:              status code
260  */
261 efi_status_t EFIAPI efi_get_variable(u16 *name, const efi_guid_t *guid,
262                                      u32 *attr, efi_uintn_t *data_size,
263                                      void *data)
264 {
265         struct smm_variable_access *var_acc;
266         efi_uintn_t payload_size;
267         efi_uintn_t name_size;
268         efi_uintn_t tmp_dsize;
269         u8 *comm_buf = NULL;
270         efi_status_t ret;
271
272         EFI_ENTRY("\"%ls\" %pUl %p %p %p", name, guid, attr, data_size, data);
273
274         if (!name || !guid || !data_size) {
275                 ret = EFI_INVALID_PARAMETER;
276                 goto out;
277         }
278
279         /* Check payload size */
280         name_size = u16_strsize(name);
281         if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
282                 ret = EFI_INVALID_PARAMETER;
283                 goto out;
284         }
285
286         /* Trim output buffer size */
287         tmp_dsize = *data_size;
288         if (name_size + tmp_dsize >
289                         max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
290                 tmp_dsize = max_payload_size -
291                                 MM_VARIABLE_ACCESS_HEADER_SIZE -
292                                 name_size;
293         }
294
295         /* Get communication buffer and initialize header */
296         payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + tmp_dsize;
297         comm_buf = setup_mm_hdr((void **)&var_acc, payload_size,
298                                 SMM_VARIABLE_FUNCTION_GET_VARIABLE, &ret);
299         if (!comm_buf)
300                 goto out;
301
302         /* Fill in contents */
303         guidcpy(&var_acc->guid, guid);
304         var_acc->data_size = tmp_dsize;
305         var_acc->name_size = name_size;
306         var_acc->attr = attr ? *attr : 0;
307         memcpy(var_acc->name, name, name_size);
308
309         /* Communicate */
310         ret = mm_communicate(comm_buf, payload_size);
311         if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
312                 /* Update with reported data size for trimmed case */
313                 *data_size = var_acc->data_size;
314         }
315         if (ret != EFI_SUCCESS)
316                 goto out;
317
318         if (attr)
319                 *attr = var_acc->attr;
320         if (data)
321                 memcpy(data, (u8 *)var_acc->name + var_acc->name_size,
322                        var_acc->data_size);
323         else
324                 ret = EFI_INVALID_PARAMETER;
325
326 out:
327         free(comm_buf);
328         return EFI_EXIT(ret);
329 }
330
331 /**
332  * efi_get_next_variable_name() - enumerate the current variable names
333  *
334  * @variable_name_size: size of variable_name buffer in bytes
335  * @variable_name:      name of uefi variable's name in u16
336  * @guid:               vendor's guid
337  *
338  * This function implements the GetNextVariableName service.
339  *
340  * See the Unified Extensible Firmware Interface (UEFI) specification for
341  * details.
342  *
343  * Return: status code
344  */
345 efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size,
346                                                u16 *variable_name,
347                                                efi_guid_t *guid)
348 {
349         struct smm_variable_getnext *var_getnext;
350         efi_uintn_t payload_size;
351         efi_uintn_t out_name_size;
352         efi_uintn_t in_name_size;
353         efi_uintn_t tmp_dsize;
354         efi_uintn_t name_size;
355         u8 *comm_buf = NULL;
356         efi_status_t ret;
357
358         EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, guid);
359
360         if (!variable_name_size || !variable_name || !guid) {
361                 ret = EFI_INVALID_PARAMETER;
362                 goto out;
363         }
364
365         out_name_size = *variable_name_size;
366         in_name_size = u16_strsize(variable_name);
367
368         if (out_name_size < in_name_size) {
369                 ret = EFI_INVALID_PARAMETER;
370                 goto out;
371         }
372
373         name_size = u16_strsize(variable_name);
374         if (name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
375                 ret = EFI_INVALID_PARAMETER;
376                 goto out;
377         }
378
379         /* Trim output buffer size */
380         tmp_dsize = *variable_name_size;
381         if (name_size + tmp_dsize >
382                         max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
383                 tmp_dsize = max_payload_size -
384                                 MM_VARIABLE_GET_NEXT_HEADER_SIZE -
385                                 name_size;
386         }
387
388         payload_size = MM_VARIABLE_GET_NEXT_HEADER_SIZE + out_name_size;
389         comm_buf = setup_mm_hdr((void **)&var_getnext, payload_size,
390                                 SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME,
391                                 &ret);
392         if (!comm_buf)
393                 goto out;
394
395         /* Fill in contents */
396         guidcpy(&var_getnext->guid, guid);
397         var_getnext->name_size = out_name_size;
398         memcpy(var_getnext->name, variable_name, in_name_size);
399         memset((u8 *)var_getnext->name + in_name_size, 0x0,
400                out_name_size - in_name_size);
401
402         /* Communicate */
403         ret = mm_communicate(comm_buf, payload_size);
404         if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
405                 /* Update with reported data size for trimmed case */
406                 *variable_name_size = var_getnext->name_size;
407         }
408         if (ret != EFI_SUCCESS)
409                 goto out;
410
411         guidcpy(guid, &var_getnext->guid);
412         memcpy(variable_name, (u8 *)var_getnext->name,
413                var_getnext->name_size);
414
415 out:
416         free(comm_buf);
417         return EFI_EXIT(ret);
418 }
419
420 /**
421  * efi_set_variable() - set value of a UEFI variable
422  *
423  * This function implements the SetVariable runtime service.
424  *
425  * See the Unified Extensible Firmware Interface (UEFI) specification for
426  * details.
427  *
428  * @name:               name of the variable
429  * @guid:               vendor GUID
430  * @attr:               attributes of the variable
431  * @data_size:          size of the buffer with the variable value
432  * @data:               buffer with the variable value
433  * Return:              status code
434  */
435 efi_status_t EFIAPI efi_set_variable(u16 *name, const efi_guid_t *guid,
436                                      u32 attr, efi_uintn_t data_size,
437                                      const void *data)
438 {
439         struct smm_variable_access *var_acc;
440         efi_uintn_t payload_size;
441         efi_uintn_t name_size;
442         u8 *comm_buf = NULL;
443         efi_status_t ret;
444
445         EFI_ENTRY("\"%ls\" %pUl %x %zu %p", name, guid, attr, data_size, data);
446
447         if (!name || name[0] == 0 || !guid) {
448                 ret = EFI_INVALID_PARAMETER;
449                 goto out;
450         }
451         if (data_size > 0 && !data) {
452                 ret = EFI_INVALID_PARAMETER;
453                 goto out;
454         }
455
456         /* Check payload size */
457         name_size = u16_strsize(name);
458         payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size;
459         if (payload_size > max_payload_size) {
460                 ret = EFI_INVALID_PARAMETER;
461                 goto out;
462         }
463
464         /* Get communication buffer and initialize header */
465         comm_buf = setup_mm_hdr((void **)&var_acc, payload_size,
466                                 SMM_VARIABLE_FUNCTION_SET_VARIABLE, &ret);
467         if (!comm_buf)
468                 goto out;
469
470         /* Fill in contents */
471         guidcpy(&var_acc->guid, guid);
472         var_acc->data_size = data_size;
473         var_acc->name_size = name_size;
474         var_acc->attr = attr;
475         memcpy(var_acc->name, name, name_size);
476         memcpy((u8 *)var_acc->name + name_size, data, data_size);
477
478         /* Communicate */
479         ret = mm_communicate(comm_buf, payload_size);
480
481 out:
482         free(comm_buf);
483         return EFI_EXIT(ret);
484 }
485
486 /**
487  * efi_query_variable_info() - get information about EFI variables
488  *
489  * This function implements the QueryVariableInfo() runtime service.
490  *
491  * See the Unified Extensible Firmware Interface (UEFI) specification for
492  * details.
493  *
494  * @attributes:                         bitmask to select variables to be
495  *                                      queried
496  * @maximum_variable_storage_size:      maximum size of storage area for the
497  *                                      selected variable types
498  * @remaining_variable_storage_size:    remaining size of storage are for the
499  *                                      selected variable types
500  * @maximum_variable_size:              maximum size of a variable of the
501  *                                      selected type
502  * Returns:                             status code
503  */
504 efi_status_t EFIAPI __efi_runtime
505 efi_query_variable_info(u32 attributes, u64 *max_variable_storage_size,
506                         u64 *remain_variable_storage_size,
507                         u64 *max_variable_size)
508 {
509         struct smm_variable_query_info *mm_query_info;
510         efi_uintn_t payload_size;
511         efi_status_t ret;
512         u8 *comm_buf;
513
514         EFI_ENTRY("%x %p %p %p", attributes, max_variable_storage_size,
515                   remain_variable_storage_size, max_variable_size);
516
517         payload_size = sizeof(*mm_query_info);
518         comm_buf = setup_mm_hdr((void **)&mm_query_info, payload_size,
519                                 SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO,
520                                 &ret);
521         if (!comm_buf)
522                 goto out;
523
524         mm_query_info->attr = attributes;
525         ret = mm_communicate(comm_buf, payload_size);
526         if (ret != EFI_SUCCESS)
527                 goto out;
528         *max_variable_storage_size = mm_query_info->max_variable_storage;
529         *remain_variable_storage_size =
530                         mm_query_info->remaining_variable_storage;
531         *max_variable_size = mm_query_info->max_variable_size;
532
533 out:
534         free(comm_buf);
535         return EFI_EXIT(ret);
536 }
537
538 /**
539  * efi_get_variable_runtime() - runtime implementation of GetVariable()
540  *
541  * @variable_name:      name of the variable
542  * @guid:               vendor GUID
543  * @attributes:         attributes of the variable
544  * @data_size:          size of the buffer to which the variable value is copied
545  * @data:               buffer to which the variable value is copied
546  * Return:              status code
547  */
548 static efi_status_t __efi_runtime EFIAPI
549 efi_get_variable_runtime(u16 *variable_name, const efi_guid_t *guid,
550                          u32 *attributes, efi_uintn_t *data_size, void *data)
551 {
552         return EFI_UNSUPPORTED;
553 }
554
555 /**
556  * efi_get_next_variable_name_runtime() - runtime implementation of
557  *                                        GetNextVariable()
558  *
559  * @variable_name_size: size of variable_name buffer in byte
560  * @variable_name:      name of uefi variable's name in u16
561  * @guid:               vendor's guid
562  * Return:              status code
563  */
564 static efi_status_t __efi_runtime EFIAPI
565 efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size,
566                                    u16 *variable_name, efi_guid_t *guid)
567 {
568         return EFI_UNSUPPORTED;
569 }
570
571 /**
572  * efi_query_variable_info() - get information about EFI variables
573  *
574  * This function implements the QueryVariableInfo() runtime service.
575  *
576  * See the Unified Extensible Firmware Interface (UEFI) specification for
577  * details.
578  *
579  * @attributes:                         bitmask to select variables to be
580  *                                      queried
581  * @maximum_variable_storage_size:      maximum size of storage area for the
582  *                                      selected variable types
583  * @remaining_variable_storage_size:    remaining size of storage are for the
584  *                                      selected variable types
585  * @maximum_variable_size:              maximum size of a variable of the
586  *                                      selected type
587  * Return:                              status code
588  */
589 efi_status_t EFIAPI __efi_runtime
590 efi_query_variable_info_runtime(u32 attributes, u64 *max_variable_storage_size,
591                                 u64 *remain_variable_storage_size,
592                                 u64 *max_variable_size)
593 {
594         return EFI_UNSUPPORTED;
595 }
596
597 /**
598  * efi_set_variable_runtime() - runtime implementation of SetVariable()
599  *
600  * @variable_name:      name of the variable
601  * @guid:               vendor GUID
602  * @attributes:         attributes of the variable
603  * @data_size:          size of the buffer with the variable value
604  * @data:               buffer with the variable value
605  * Return:              status code
606  */
607 static efi_status_t __efi_runtime EFIAPI
608 efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *guid,
609                          u32 attributes, efi_uintn_t data_size,
610                          const void *data)
611 {
612         return EFI_UNSUPPORTED;
613 }
614
615 /**
616  * efi_variables_boot_exit_notify() - notify ExitBootServices() is called
617  */
618 void efi_variables_boot_exit_notify(void)
619 {
620         u8 *comm_buf;
621         efi_status_t ret;
622
623         comm_buf = setup_mm_hdr(NULL, 0,
624                                 SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE, &ret);
625         if (comm_buf)
626                 ret = mm_communicate(comm_buf, 0);
627         else
628                 ret = EFI_NOT_FOUND;
629
630         if (ret != EFI_SUCCESS)
631                 log_err("Unable to notify StMM for ExitBootServices\n");
632         free(comm_buf);
633
634         /* Update runtime service table */
635         efi_runtime_services.query_variable_info =
636                         efi_query_variable_info_runtime;
637         efi_runtime_services.get_variable = efi_get_variable_runtime;
638         efi_runtime_services.get_next_variable_name =
639                         efi_get_next_variable_name_runtime;
640         efi_runtime_services.set_variable = efi_set_variable_runtime;
641         efi_update_table_header_crc32(&efi_runtime_services.hdr);
642 }
643
644 /**
645  * efi_init_variables() - initialize variable services
646  *
647  * Return:      status code
648  */
649 efi_status_t efi_init_variables(void)
650 {
651         efi_status_t ret;
652
653         ret = get_max_payload(&max_payload_size);
654         if (ret != EFI_SUCCESS)
655                 return ret;
656
657         max_buffer_size = MM_COMMUNICATE_HEADER_SIZE +
658                           MM_VARIABLE_COMMUNICATE_SIZE +
659                           max_payload_size;
660
661         return EFI_SUCCESS;
662 }