"Das U-Boot" Source Tree
at master 1014 lines 26 kB view raw
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 * 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> 11 */ 12 13#define LOG_CATEGORY LOGC_EFI 14 15#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) 16#include <arm_ffa.h> 17#endif 18#include <cpu_func.h> 19#include <dm.h> 20#include <efi.h> 21#include <efi_api.h> 22#include <efi_loader.h> 23#include <efi_variable.h> 24#include <malloc.h> 25#include <mapmem.h> 26#include <mm_communication.h> 27#include <tee.h> 28 29#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) 30/* MM return codes */ 31#define MM_SUCCESS (0) 32#define MM_NOT_SUPPORTED (-1) 33#define MM_INVALID_PARAMETER (-2) 34#define MM_DENIED (-3) 35#define MM_NO_MEMORY (-5) 36 37static const char *mm_sp_svc_uuid = MM_SP_UUID; 38static u16 mm_sp_id; 39#endif 40 41extern struct efi_var_file __efi_runtime_data *efi_var_buf; 42static efi_uintn_t max_buffer_size; /* comm + var + func + data */ 43static efi_uintn_t max_payload_size; /* func + data */ 44static const u16 __efi_runtime_rodata pk[] = u"PK"; 45 46struct mm_connection { 47 struct udevice *tee; 48 u32 session; 49}; 50 51/** 52 * get_connection() - Retrieve OP-TEE session for a specific UUID. 53 * 54 * @conn: session buffer to fill 55 * Return: status code 56 */ 57static int get_connection(struct mm_connection *conn) 58{ 59 static const struct tee_optee_ta_uuid uuid = PTA_STMM_UUID; 60 struct udevice *tee = NULL; 61 struct tee_open_session_arg arg; 62 int rc = -ENODEV; 63 64 tee = tee_find_device(tee, NULL, NULL, NULL); 65 if (!tee) 66 goto out; 67 68 memset(&arg, 0, sizeof(arg)); 69 tee_optee_ta_uuid_to_octets(arg.uuid, &uuid); 70 rc = tee_open_session(tee, &arg, 0, NULL); 71 if (rc) 72 goto out; 73 74 /* Check the internal OP-TEE result */ 75 if (arg.ret != TEE_SUCCESS) { 76 rc = -EIO; 77 goto out; 78 } 79 80 conn->tee = tee; 81 conn->session = arg.session; 82 83 return 0; 84out: 85 return rc; 86} 87 88/** 89 * optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE 90 * 91 * @comm_buf: locally allocted communcation buffer 92 * @dsize: buffer size 93 * Return: status code 94 */ 95static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) 96{ 97 ulong buf_size; 98 efi_status_t ret; 99 struct efi_mm_communicate_header *mm_hdr; 100 struct mm_connection conn = { NULL, 0 }; 101 struct tee_invoke_arg arg; 102 struct tee_param param[2]; 103 struct tee_shm *shm = NULL; 104 int rc; 105 106 if (!comm_buf) 107 return EFI_INVALID_PARAMETER; 108 109 mm_hdr = (struct efi_mm_communicate_header *)comm_buf; 110 buf_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t); 111 112 if (dsize != buf_size) 113 return EFI_INVALID_PARAMETER; 114 115 rc = get_connection(&conn); 116 if (rc) { 117 log_err("Unable to open OP-TEE session (err=%d)\n", rc); 118 return EFI_UNSUPPORTED; 119 } 120 121 if (tee_shm_register(conn.tee, comm_buf, buf_size, 0, &shm)) { 122 log_err("Unable to register shared memory\n"); 123 tee_close_session(conn.tee, conn.session); 124 return EFI_UNSUPPORTED; 125 } 126 127 memset(&arg, 0, sizeof(arg)); 128 arg.func = PTA_STMM_CMDID_COMMUNICATE; 129 arg.session = conn.session; 130 131 memset(param, 0, sizeof(param)); 132 param[0].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INOUT; 133 param[0].u.memref.size = buf_size; 134 param[0].u.memref.shm = shm; 135 param[1].attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT; 136 137 rc = tee_invoke_func(conn.tee, &arg, 2, param); 138 tee_shm_free(shm); 139 tee_close_session(conn.tee, conn.session); 140 if (rc) 141 return EFI_DEVICE_ERROR; 142 if (arg.ret == TEE_ERROR_EXCESS_DATA) 143 log_err("Variable payload too large\n"); 144 if (arg.ret != TEE_SUCCESS) 145 return EFI_DEVICE_ERROR; 146 147 switch (param[1].u.value.a) { 148 case ARM_SVC_SPM_RET_SUCCESS: 149 ret = EFI_SUCCESS; 150 break; 151 152 case ARM_SVC_SPM_RET_INVALID_PARAMS: 153 ret = EFI_INVALID_PARAMETER; 154 break; 155 156 case ARM_SVC_SPM_RET_DENIED: 157 ret = EFI_ACCESS_DENIED; 158 break; 159 160 case ARM_SVC_SPM_RET_NO_MEMORY: 161 ret = EFI_OUT_OF_RESOURCES; 162 break; 163 164 default: 165 ret = EFI_ACCESS_DENIED; 166 } 167 168 return ret; 169} 170 171#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) 172/** 173 * ffa_notify_mm_sp() - Announce there is data in the shared buffer 174 * 175 * Notify the MM partition in the trusted world that 176 * data is available in the shared buffer. 177 * This is a blocking call during which trusted world has exclusive access 178 * to the MM shared buffer. 179 * 180 * Return: 181 * 182 * 0 on success 183 */ 184static int ffa_notify_mm_sp(void) 185{ 186 struct ffa_send_direct_data msg = {0}; 187 int ret; 188 int sp_event_ret; 189 struct udevice *dev; 190 191 ret = uclass_first_device_err(UCLASS_FFA, &dev); 192 if (ret) { 193 log_err("EFI: Cannot find FF-A bus device, notify MM SP failure\n"); 194 return ret; 195 } 196 197 msg.data0 = CONFIG_FFA_SHARED_MM_BUF_OFFSET; /* x3 */ 198 199 ret = ffa_sync_send_receive(dev, mm_sp_id, &msg, 1); 200 if (ret) 201 return ret; 202 203 sp_event_ret = msg.data0; /* x3 */ 204 205 switch (sp_event_ret) { 206 case MM_SUCCESS: 207 ret = 0; 208 break; 209 case MM_NOT_SUPPORTED: 210 ret = -EINVAL; 211 break; 212 case MM_INVALID_PARAMETER: 213 ret = -EPERM; 214 break; 215 case MM_DENIED: 216 ret = -EACCES; 217 break; 218 case MM_NO_MEMORY: 219 ret = -EBUSY; 220 break; 221 default: 222 ret = -EACCES; 223 } 224 225 return ret; 226} 227 228/** 229 * ffa_discover_mm_sp_id() - Query the MM partition ID 230 * 231 * Use the FF-A driver to get the MM partition ID. 232 * If multiple partitions are found, use the first one. 233 * This is a boot time function. 234 * 235 * Return: 236 * 237 * 0 on success 238 */ 239static int ffa_discover_mm_sp_id(void) 240{ 241 u32 count = 0; 242 int ret; 243 struct ffa_partition_desc *descs; 244 struct udevice *dev; 245 246 ret = uclass_first_device_err(UCLASS_FFA, &dev); 247 if (ret) { 248 log_err("EFI: Cannot find FF-A bus device, MM SP discovery failure\n"); 249 return ret; 250 } 251 252 /* Ask the driver to fill the buffer with the SPs info */ 253 ret = ffa_partition_info_get(dev, mm_sp_svc_uuid, &count, &descs); 254 if (ret) { 255 log_err("EFI: Failure in querying SPs info (%d), MM SP discovery failure\n", ret); 256 return ret; 257 } 258 259 /* MM SPs found , use the first one */ 260 261 mm_sp_id = descs[0].info.id; 262 263 log_info("EFI: MM partition ID 0x%x\n", mm_sp_id); 264 265 return 0; 266} 267 268/** 269 * ffa_mm_communicate() - Exchange EFI services data with the MM partition using FF-A 270 * @comm_buf: locally allocated communication buffer used for rx/tx 271 * @dsize: communication buffer size 272 * 273 * Issue a door bell event to notify the MM partition (SP) running in OP-TEE 274 * that there is data to read from the shared buffer. 275 * Communication with the MM SP is performed using FF-A transport. 276 * On the event, MM SP can read the data from the buffer and 277 * update the MM shared buffer with response data. 278 * The response data is copied back to the communication buffer. 279 * 280 * Return: 281 * 282 * EFI status code 283 */ 284static efi_status_t ffa_mm_communicate(void *comm_buf, ulong comm_buf_size) 285{ 286 ulong tx_data_size; 287 int ffa_ret; 288 efi_status_t efi_ret; 289 struct efi_mm_communicate_header *mm_hdr; 290 void *virt_shared_buf; 291 292 if (!comm_buf) 293 return EFI_INVALID_PARAMETER; 294 295 /* Discover MM partition ID at boot time */ 296 if (!mm_sp_id && ffa_discover_mm_sp_id()) { 297 log_err("EFI: Failure to discover MM SP ID at boot time, FF-A MM comms failure\n"); 298 return EFI_UNSUPPORTED; 299 } 300 301 mm_hdr = (struct efi_mm_communicate_header *)comm_buf; 302 tx_data_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t); 303 304 if (comm_buf_size != tx_data_size || tx_data_size > CONFIG_FFA_SHARED_MM_BUF_SIZE) 305 return EFI_INVALID_PARAMETER; 306 307 /* Copy the data to the shared buffer */ 308 309 virt_shared_buf = map_sysmem((phys_addr_t)CONFIG_FFA_SHARED_MM_BUF_ADDR, 0); 310 memcpy(virt_shared_buf, comm_buf, tx_data_size); 311 312 /* 313 * The secure world might have cache disabled for 314 * the device region used for shared buffer (which is the case for Optee). 315 * In this case, the secure world reads the data from DRAM. 316 * Let's flush the cache so the DRAM is updated with the latest data. 317 */ 318#ifdef CONFIG_ARM64 319 invalidate_dcache_all(); 320#endif 321 322 /* Announce there is data in the shared buffer */ 323 324 ffa_ret = ffa_notify_mm_sp(); 325 326 switch (ffa_ret) { 327 case 0: { 328 ulong rx_data_size; 329 /* Copy the MM SP response from the shared buffer to the communication buffer */ 330 rx_data_size = ((struct efi_mm_communicate_header *)virt_shared_buf)->message_len + 331 sizeof(efi_guid_t) + 332 sizeof(size_t); 333 334 if (rx_data_size > comm_buf_size) { 335 efi_ret = EFI_OUT_OF_RESOURCES; 336 break; 337 } 338 339 memcpy(comm_buf, virt_shared_buf, rx_data_size); 340 efi_ret = EFI_SUCCESS; 341 break; 342 } 343 case -EINVAL: 344 efi_ret = EFI_DEVICE_ERROR; 345 break; 346 case -EPERM: 347 efi_ret = EFI_INVALID_PARAMETER; 348 break; 349 case -EACCES: 350 efi_ret = EFI_ACCESS_DENIED; 351 break; 352 case -EBUSY: 353 efi_ret = EFI_OUT_OF_RESOURCES; 354 break; 355 default: 356 efi_ret = EFI_ACCESS_DENIED; 357 } 358 359 unmap_sysmem(virt_shared_buf); 360 return efi_ret; 361} 362 363/** 364 * get_mm_comms() - detect the available MM transport 365 * 366 * Make sure the FF-A bus is probed successfully 367 * which means FF-A communication with secure world works and ready 368 * for use. 369 * 370 * If FF-A bus is not ready, use OPTEE comms. 371 * 372 * Return: 373 * 374 * MM_COMMS_FFA or MM_COMMS_OPTEE 375 */ 376static enum mm_comms_select get_mm_comms(void) 377{ 378 struct udevice *dev; 379 int ret; 380 381 ret = uclass_first_device_err(UCLASS_FFA, &dev); 382 if (ret) { 383 log_debug("EFI: Cannot find FF-A bus device, trying Optee comms\n"); 384 return MM_COMMS_OPTEE; 385 } 386 387 return MM_COMMS_FFA; 388} 389#endif 390 391/** 392 * mm_communicate() - Adjust the communication buffer to the MM SP and send 393 * it to OP-TEE 394 * 395 * @comm_buf: locally allocated communication buffer 396 * @dsize: buffer size 397 * 398 * The SP (also called partition) can be any MM SP such as StandAlonneMM or smm-gateway. 399 * The comm_buf format is the same for both partitions. 400 * When using the u-boot OP-TEE driver, StandAlonneMM is supported. 401 * When using the u-boot FF-A driver, any MM SP is supported. 402 * 403 * Return: status code 404 */ 405static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 406{ 407 efi_status_t ret; 408 struct efi_mm_communicate_header *mm_hdr; 409 struct smm_variable_communicate_header *var_hdr; 410#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) 411 enum mm_comms_select mm_comms; 412#endif 413 414 dsize += MM_COMMUNICATE_HEADER_SIZE + MM_VARIABLE_COMMUNICATE_SIZE; 415 mm_hdr = (struct efi_mm_communicate_header *)comm_buf; 416 var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data; 417 418#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) 419 mm_comms = get_mm_comms(); 420 if (mm_comms == MM_COMMS_FFA) 421 ret = ffa_mm_communicate(comm_buf, dsize); 422 else 423 ret = optee_mm_communicate(comm_buf, dsize); 424#else 425 ret = optee_mm_communicate(comm_buf, dsize); 426#endif 427 428 if (ret != EFI_SUCCESS) { 429 log_err("%s failed!\n", __func__); 430 return ret; 431 } 432 433 return var_hdr->ret_status; 434} 435 436/** 437 * setup_mm_hdr() - Allocate a buffer for StandAloneMM and initialize the 438 * header data. 439 * 440 * @dptr: pointer address of the corresponding StandAloneMM 441 * function 442 * @payload_size: buffer size 443 * @func: standAloneMM function number 444 * @ret: EFI return code 445 * Return: buffer or NULL 446 */ 447static u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size, 448 efi_uintn_t func, efi_status_t *ret) 449{ 450 const efi_guid_t mm_var_guid = EFI_MM_VARIABLE_GUID; 451 struct efi_mm_communicate_header *mm_hdr; 452 struct smm_variable_communicate_header *var_hdr; 453 u8 *comm_buf; 454 455 /* In the init function we initialize max_buffer_size with 456 * get_max_payload(). So skip the test if max_buffer_size is initialized 457 * StandAloneMM will perform similar checks and drop the buffer if it's 458 * too long 459 */ 460 if (max_buffer_size && max_buffer_size < 461 (MM_COMMUNICATE_HEADER_SIZE + 462 MM_VARIABLE_COMMUNICATE_SIZE + 463 payload_size)) { 464 *ret = EFI_INVALID_PARAMETER; 465 return NULL; 466 } 467 468 comm_buf = calloc(1, MM_COMMUNICATE_HEADER_SIZE + 469 MM_VARIABLE_COMMUNICATE_SIZE + 470 payload_size); 471 if (!comm_buf) { 472 *ret = EFI_OUT_OF_RESOURCES; 473 return NULL; 474 } 475 476 mm_hdr = (struct efi_mm_communicate_header *)comm_buf; 477 guidcpy(&mm_hdr->header_guid, &mm_var_guid); 478 mm_hdr->message_len = MM_VARIABLE_COMMUNICATE_SIZE + payload_size; 479 480 var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data; 481 var_hdr->function = func; 482 if (dptr) 483 *dptr = var_hdr->data; 484 *ret = EFI_SUCCESS; 485 486 return comm_buf; 487} 488 489/** 490 * get_max_payload() - Get variable payload size from StandAloneMM. 491 * 492 * @size: size of the variable in storage 493 * Return: status code 494 */ 495efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) 496{ 497 struct smm_variable_payload_size *var_payload = NULL; 498 efi_uintn_t payload_size; 499 u8 *comm_buf = NULL; 500 efi_status_t ret; 501 502 if (!size) { 503 ret = EFI_INVALID_PARAMETER; 504 goto out; 505 } 506 507 payload_size = sizeof(*var_payload); 508 comm_buf = setup_mm_hdr((void **)&var_payload, payload_size, 509 SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE, &ret); 510 if (!comm_buf) 511 goto out; 512 513 ret = mm_communicate(comm_buf, payload_size); 514 if (ret != EFI_SUCCESS) 515 goto out; 516 517 /* Make sure the buffer is big enough for storing variables */ 518 if (var_payload->size < MM_VARIABLE_ACCESS_HEADER_SIZE + 0x20) { 519 ret = EFI_DEVICE_ERROR; 520 goto out; 521 } 522 *size = var_payload->size; 523 /* 524 * There seems to be a bug in EDK2 miscalculating the boundaries and 525 * size checks, so deduct 2 more bytes to fulfill this requirement. Fix 526 * it up here to ensure backwards compatibility with older versions 527 * (cf. StandaloneMmPkg/Drivers/StandaloneMmCpu/AArch64/EventHandle.c. 528 * sizeof (EFI_MM_COMMUNICATE_HEADER) instead the size minus the 529 * flexible array member). 530 * 531 * size is guaranteed to be > 2 due to checks on the beginning. 532 */ 533 *size -= 2; 534out: 535 free(comm_buf); 536 return ret; 537} 538 539/* 540 * StMM can store internal attributes and properties for variables, i.e enabling 541 * R/O variables 542 */ 543static efi_status_t set_property_int(const u16 *variable_name, 544 efi_uintn_t name_size, 545 const efi_guid_t *vendor, 546 struct var_check_property *var_property) 547{ 548 struct smm_variable_var_check_property *smm_property; 549 efi_uintn_t payload_size; 550 u8 *comm_buf = NULL; 551 efi_status_t ret; 552 553 payload_size = sizeof(*smm_property) + name_size; 554 if (payload_size > max_payload_size) { 555 ret = EFI_INVALID_PARAMETER; 556 goto out; 557 } 558 comm_buf = setup_mm_hdr((void **)&smm_property, payload_size, 559 SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET, 560 &ret); 561 if (!comm_buf) 562 goto out; 563 564 guidcpy(&smm_property->guid, vendor); 565 smm_property->name_size = name_size; 566 memcpy(&smm_property->property, var_property, 567 sizeof(smm_property->property)); 568 memcpy(smm_property->name, variable_name, name_size); 569 570 ret = mm_communicate(comm_buf, payload_size); 571 572out: 573 free(comm_buf); 574 return ret; 575} 576 577static efi_status_t get_property_int(const u16 *variable_name, 578 efi_uintn_t name_size, 579 const efi_guid_t *vendor, 580 struct var_check_property *var_property) 581{ 582 struct smm_variable_var_check_property *smm_property; 583 efi_uintn_t payload_size; 584 u8 *comm_buf = NULL; 585 efi_status_t ret; 586 587 memset(var_property, 0, sizeof(*var_property)); 588 payload_size = sizeof(*smm_property) + name_size; 589 if (payload_size > max_payload_size) { 590 ret = EFI_INVALID_PARAMETER; 591 goto out; 592 } 593 comm_buf = setup_mm_hdr((void **)&smm_property, payload_size, 594 SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET, 595 &ret); 596 if (!comm_buf) 597 goto out; 598 599 guidcpy(&smm_property->guid, vendor); 600 smm_property->name_size = name_size; 601 memcpy(smm_property->name, variable_name, name_size); 602 603 ret = mm_communicate(comm_buf, payload_size); 604 /* 605 * Currently only R/O property is supported in StMM. 606 * Variables that are not set to R/O will not set the property in StMM 607 * and the call will return EFI_NOT_FOUND. We are setting the 608 * properties to 0x0 so checking against that is enough for the 609 * EFI_NOT_FOUND case. 610 */ 611 if (ret == EFI_NOT_FOUND) 612 ret = EFI_SUCCESS; 613 if (ret != EFI_SUCCESS) 614 goto out; 615 memcpy(var_property, &smm_property->property, sizeof(*var_property)); 616 617out: 618 free(comm_buf); 619 return ret; 620} 621 622efi_status_t efi_get_variable_int(const u16 *variable_name, 623 const efi_guid_t *vendor, 624 u32 *attributes, efi_uintn_t *data_size, 625 void *data, u64 *timep) 626{ 627 struct var_check_property var_property; 628 struct smm_variable_access *var_acc; 629 efi_uintn_t payload_size; 630 efi_uintn_t name_size; 631 efi_uintn_t tmp_dsize; 632 u8 *comm_buf = NULL; 633 efi_status_t ret, tmp; 634 635 if (!variable_name || !vendor || !data_size) { 636 ret = EFI_INVALID_PARAMETER; 637 goto out; 638 } 639 640 /* Check payload size */ 641 name_size = u16_strsize(variable_name); 642 if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) { 643 ret = EFI_INVALID_PARAMETER; 644 goto out; 645 } 646 647 /* Trim output buffer size */ 648 tmp_dsize = *data_size; 649 if (name_size + tmp_dsize > 650 max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) { 651 tmp_dsize = max_payload_size - 652 MM_VARIABLE_ACCESS_HEADER_SIZE - 653 name_size; 654 } 655 656 /* Get communication buffer and initialize header */ 657 payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + tmp_dsize; 658 comm_buf = setup_mm_hdr((void **)&var_acc, payload_size, 659 SMM_VARIABLE_FUNCTION_GET_VARIABLE, &ret); 660 if (!comm_buf) 661 goto out; 662 663 /* Fill in contents */ 664 guidcpy(&var_acc->guid, vendor); 665 var_acc->data_size = tmp_dsize; 666 var_acc->name_size = name_size; 667 var_acc->attr = attributes ? *attributes : 0; 668 memcpy(var_acc->name, variable_name, name_size); 669 670 /* Communicate */ 671 ret = mm_communicate(comm_buf, payload_size); 672 if (ret != EFI_SUCCESS && ret != EFI_BUFFER_TOO_SMALL) 673 goto out; 674 675 /* Update with reported data size for trimmed case */ 676 *data_size = var_acc->data_size; 677 /* 678 * UEFI > 2.7 needs the attributes set even if the buffer is 679 * smaller 680 */ 681 if (attributes) { 682 tmp = get_property_int(variable_name, name_size, vendor, 683 &var_property); 684 if (tmp != EFI_SUCCESS) { 685 ret = tmp; 686 goto out; 687 } 688 *attributes = var_acc->attr; 689 if (var_property.property & 690 VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) 691 *attributes |= EFI_VARIABLE_READ_ONLY; 692 } 693 694 /* return if ret is EFI_BUFFER_TOO_SMALL */ 695 if (ret != EFI_SUCCESS) 696 goto out; 697 698 if (data) 699 memcpy(data, (u8 *)var_acc->name + var_acc->name_size, 700 var_acc->data_size); 701 else 702 ret = EFI_INVALID_PARAMETER; 703 704out: 705 free(comm_buf); 706 return ret; 707} 708 709efi_status_t efi_get_next_variable_name_int(efi_uintn_t *variable_name_size, 710 u16 *variable_name, 711 efi_guid_t *guid) 712{ 713 struct smm_variable_getnext *var_getnext; 714 efi_uintn_t payload_size; 715 efi_uintn_t out_name_size; 716 efi_uintn_t in_name_size; 717 u8 *comm_buf = NULL; 718 efi_status_t ret; 719 720 if (!variable_name_size || !variable_name || !guid) { 721 ret = EFI_INVALID_PARAMETER; 722 goto out; 723 } 724 725 out_name_size = *variable_name_size; 726 in_name_size = u16_strsize(variable_name); 727 728 if (out_name_size < in_name_size) { 729 ret = EFI_INVALID_PARAMETER; 730 goto out; 731 } 732 733 if (in_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) { 734 ret = EFI_INVALID_PARAMETER; 735 goto out; 736 } 737 738 /* Trim output buffer size */ 739 if (out_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) 740 out_name_size = max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE; 741 742 payload_size = MM_VARIABLE_GET_NEXT_HEADER_SIZE + out_name_size; 743 comm_buf = setup_mm_hdr((void **)&var_getnext, payload_size, 744 SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME, 745 &ret); 746 if (!comm_buf) 747 goto out; 748 749 /* Fill in contents */ 750 guidcpy(&var_getnext->guid, guid); 751 var_getnext->name_size = out_name_size; 752 memcpy(var_getnext->name, variable_name, in_name_size); 753 memset((u8 *)var_getnext->name + in_name_size, 0x0, 754 out_name_size - in_name_size); 755 756 /* Communicate */ 757 ret = mm_communicate(comm_buf, payload_size); 758 if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) { 759 /* Update with reported data size for trimmed case */ 760 *variable_name_size = var_getnext->name_size; 761 } 762 if (ret != EFI_SUCCESS) 763 goto out; 764 765 guidcpy(guid, &var_getnext->guid); 766 memcpy(variable_name, var_getnext->name, var_getnext->name_size); 767 768out: 769 free(comm_buf); 770 return ret; 771} 772 773efi_status_t efi_set_variable_int(const u16 *variable_name, 774 const efi_guid_t *vendor, u32 attributes, 775 efi_uintn_t data_size, const void *data, 776 bool ro_check) 777{ 778 efi_status_t ret, alt_ret = EFI_SUCCESS; 779 struct var_check_property var_property; 780 struct smm_variable_access *var_acc; 781 efi_uintn_t payload_size; 782 efi_uintn_t name_size; 783 u8 *comm_buf = NULL; 784 bool ro; 785 786 if (!variable_name || variable_name[0] == 0 || !vendor) { 787 ret = EFI_INVALID_PARAMETER; 788 goto out; 789 } 790 if (data_size > 0 && !data) { 791 ret = EFI_INVALID_PARAMETER; 792 goto out; 793 } 794 /* Check payload size */ 795 name_size = u16_strsize(variable_name); 796 payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size; 797 if (payload_size > max_payload_size) { 798 ret = EFI_INVALID_PARAMETER; 799 goto out; 800 } 801 802 /* 803 * Allocate the buffer early, before switching to RW (if needed) 804 * so we won't need to account for any failures in reading/setting 805 * the properties, if the allocation fails 806 */ 807 comm_buf = setup_mm_hdr((void **)&var_acc, payload_size, 808 SMM_VARIABLE_FUNCTION_SET_VARIABLE, &ret); 809 if (!comm_buf) 810 goto out; 811 812 ro = !!(attributes & EFI_VARIABLE_READ_ONLY); 813 attributes &= EFI_VARIABLE_MASK; 814 815 /* 816 * The API has the ability to override RO flags. If no RO check was 817 * requested switch the variable to RW for the duration of this call 818 */ 819 ret = get_property_int(variable_name, name_size, vendor, 820 &var_property); 821 if (ret != EFI_SUCCESS) 822 goto out; 823 824 if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) { 825 /* Bypass r/o check */ 826 if (!ro_check) { 827 var_property.property &= ~VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY; 828 ret = set_property_int(variable_name, name_size, vendor, &var_property); 829 if (ret != EFI_SUCCESS) 830 goto out; 831 } else { 832 ret = EFI_WRITE_PROTECTED; 833 goto out; 834 } 835 } 836 837 /* Fill in contents */ 838 guidcpy(&var_acc->guid, vendor); 839 var_acc->data_size = data_size; 840 var_acc->name_size = name_size; 841 var_acc->attr = attributes; 842 memcpy(var_acc->name, variable_name, name_size); 843 memcpy((u8 *)var_acc->name + name_size, data, data_size); 844 845 /* Communicate */ 846 ret = mm_communicate(comm_buf, payload_size); 847 if (ret != EFI_SUCCESS) 848 alt_ret = ret; 849 850 if (ro && !(var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY)) { 851 var_property.revision = VAR_CHECK_VARIABLE_PROPERTY_REVISION; 852 var_property.property |= VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY; 853 var_property.attributes = attributes; 854 var_property.minsize = 1; 855 var_property.maxsize = var_acc->data_size; 856 ret = set_property_int(variable_name, name_size, vendor, &var_property); 857 } 858 859 if (alt_ret != EFI_SUCCESS) 860 goto out; 861 862 if (!u16_strcmp(variable_name, pk)) 863 alt_ret = efi_init_secure_state(); 864out: 865 free(comm_buf); 866 return alt_ret == EFI_SUCCESS ? ret : alt_ret; 867} 868 869efi_status_t efi_query_variable_info_int(u32 attributes, 870 u64 *max_variable_storage_size, 871 u64 *remain_variable_storage_size, 872 u64 *max_variable_size) 873{ 874 struct smm_variable_query_info *mm_query_info; 875 efi_uintn_t payload_size; 876 efi_status_t ret; 877 u8 *comm_buf; 878 879 if (!max_variable_storage_size || 880 !remain_variable_storage_size || 881 !max_variable_size || !attributes) 882 return EFI_INVALID_PARAMETER; 883 884 payload_size = sizeof(*mm_query_info); 885 comm_buf = setup_mm_hdr((void **)&mm_query_info, payload_size, 886 SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO, 887 &ret); 888 if (!comm_buf) 889 goto out; 890 891 mm_query_info->attr = attributes; 892 ret = mm_communicate(comm_buf, payload_size); 893 if (ret != EFI_SUCCESS) 894 goto out; 895 *max_variable_storage_size = mm_query_info->max_variable_storage; 896 *remain_variable_storage_size = 897 mm_query_info->remaining_variable_storage; 898 *max_variable_size = mm_query_info->max_variable_size; 899 900out: 901 free(comm_buf); 902 return ret; 903} 904 905/** 906 * efi_query_variable_info() - get information about EFI variables 907 * 908 * This function implements the QueryVariableInfo() runtime service. 909 * 910 * See the Unified Extensible Firmware Interface (UEFI) specification for 911 * details. 912 * 913 * @attributes: bitmask to select variables to be 914 * queried 915 * @maximum_variable_storage_size: maximum size of storage area for the 916 * selected variable types 917 * @remaining_variable_storage_size: remaining size of storage are for the 918 * selected variable types 919 * @maximum_variable_size: maximum size of a variable of the 920 * selected type 921 * Return: status code 922 */ 923efi_status_t EFIAPI __efi_runtime 924efi_query_variable_info_runtime(u32 attributes, u64 *max_variable_storage_size, 925 u64 *remain_variable_storage_size, 926 u64 *max_variable_size) 927{ 928 return EFI_UNSUPPORTED; 929} 930 931/** 932 * efi_set_variable_runtime() - runtime implementation of SetVariable() 933 * 934 * @variable_name: name of the variable 935 * @guid: vendor GUID 936 * @attributes: attributes of the variable 937 * @data_size: size of the buffer with the variable value 938 * @data: buffer with the variable value 939 * Return: status code 940 */ 941static efi_status_t __efi_runtime EFIAPI 942efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *guid, 943 u32 attributes, efi_uintn_t data_size, 944 const void *data) 945{ 946 return EFI_UNSUPPORTED; 947} 948 949/** 950 * efi_variables_boot_exit_notify() - notify ExitBootServices() is called 951 */ 952void efi_variables_boot_exit_notify(void) 953{ 954 efi_status_t ret; 955 u8 *comm_buf; 956 loff_t len; 957 struct efi_var_file *var_buf; 958 959 comm_buf = setup_mm_hdr(NULL, 0, 960 SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE, &ret); 961 if (comm_buf) 962 ret = mm_communicate(comm_buf, 0); 963 else 964 ret = EFI_NOT_FOUND; 965 966 if (ret != EFI_SUCCESS) 967 log_err("Unable to notify the MM partition for ExitBootServices\n"); 968 free(comm_buf); 969 970 ret = efi_var_collect(&var_buf, &len, EFI_VARIABLE_RUNTIME_ACCESS); 971 if (ret != EFI_SUCCESS) 972 log_err("Can't populate EFI variables. No runtime variables will be available\n"); 973 else 974 efi_var_buf_update(var_buf); 975 free(var_buf); 976 977 /* Update runtime service table */ 978 efi_runtime_services.query_variable_info = 979 efi_query_variable_info_runtime; 980 efi_runtime_services.get_variable = efi_get_variable_runtime; 981 efi_runtime_services.get_next_variable_name = 982 efi_get_next_variable_name_runtime; 983 efi_runtime_services.set_variable = efi_set_variable_runtime; 984 efi_update_table_header_crc32(&efi_runtime_services.hdr); 985} 986 987/** 988 * efi_init_variables() - initialize variable services 989 * 990 * Return: status code 991 */ 992efi_status_t efi_init_variables(void) 993{ 994 efi_status_t ret; 995 996 /* Create a cached copy of the variables that will be enabled on ExitBootServices() */ 997 ret = efi_var_mem_init(); 998 if (ret != EFI_SUCCESS) 999 return ret; 1000 1001 ret = get_max_payload(&max_payload_size); 1002 if (ret != EFI_SUCCESS) 1003 return ret; 1004 1005 max_buffer_size = MM_COMMUNICATE_HEADER_SIZE + 1006 MM_VARIABLE_COMMUNICATE_SIZE + 1007 max_payload_size; 1008 1009 ret = efi_init_secure_state(); 1010 if (ret != EFI_SUCCESS) 1011 return ret; 1012 1013 return EFI_SUCCESS; 1014}