1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * EFI variable service via TEE 4 * 5 * Copyright (C) 2022 Linaro 6 */ 7 8 #include <linux/efi.h> 9 #include <linux/kernel.h> 10 #include <linux/slab.h> 11 #include <linux/tee.h> 12 #include <linux/tee_drv.h> 13 #include <linux/ucs2_string.h> 14 #include "mm_communication.h" 15 16 static struct efivars tee_efivars; 17 static struct efivar_operations tee_efivar_ops; 18 19 static size_t max_buffer_size; /* comm + var + func + data */ 20 static size_t max_payload_size; /* func + data */ 21 22 struct tee_stmm_efi_private { 23 struct tee_context *ctx; 24 u32 session; 25 struct device *dev; 26 }; 27 28 static struct tee_stmm_efi_private pvt_data; 29 30 /* UUID of the stmm PTA */ 31 static const struct tee_client_device_id tee_stmm_efi_id_table[] = { 32 {PTA_STMM_UUID}, 33 {} 34 }; 35 36 static int tee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) 37 { 38 /* currently only OP-TEE is supported as a communication path */ 39 if (ver->impl_id == TEE_IMPL_ID_OPTEE) 40 return 1; 41 else 42 return 0; 43 } 44 45 /** 46 * tee_mm_communicate() - Pass a buffer to StandaloneMM running in TEE 47 * 48 * @comm_buf: locally allocated communication buffer 49 * @dsize: buffer size 50 * Return: status code 51 */ 52 static efi_status_t tee_mm_communicate(void *comm_buf, size_t dsize) 53 { 54 size_t buf_size; 55 struct efi_mm_communicate_header *mm_hdr; 56 struct tee_ioctl_invoke_arg arg; 57 struct tee_param param[4]; 58 struct tee_shm *shm = NULL; 59 int rc; 60 61 if (!comm_buf) 62 return EFI_INVALID_PARAMETER; 63 64 mm_hdr = (struct efi_mm_communicate_header *)comm_buf; 65 buf_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t); 66 67 if (dsize != buf_size) 68 return EFI_INVALID_PARAMETER; 69 70 shm = tee_shm_register_kernel_buf(pvt_data.ctx, comm_buf, buf_size); 71 if (IS_ERR(shm)) { 72 dev_err(pvt_data.dev, "Unable to register shared memory\n"); 73 return EFI_UNSUPPORTED; 74 } 75 76 memset(&arg, 0, sizeof(arg)); 77 arg.func = PTA_STMM_CMD_COMMUNICATE; 78 arg.session = pvt_data.session; 79 arg.num_params = 4; 80 81 memset(param, 0, sizeof(param)); 82 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT; 83 param[0].u.memref.size = buf_size; 84 param[0].u.memref.shm = shm; 85 param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; 86 param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; 87 param[3].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; 88 89 rc = tee_client_invoke_func(pvt_data.ctx, &arg, param); 90 tee_shm_free(shm); 91 92 if (rc < 0 || arg.ret != 0) { 93 dev_err(pvt_data.dev, 94 "PTA_STMM_CMD_COMMUNICATE invoke error: 0x%x\n", arg.ret); 95 return EFI_DEVICE_ERROR; 96 } 97 98 switch (param[1].u.value.a) { 99 case ARM_SVC_SPM_RET_SUCCESS: 100 return EFI_SUCCESS; 101 102 case ARM_SVC_SPM_RET_INVALID_PARAMS: 103 return EFI_INVALID_PARAMETER; 104 105 case ARM_SVC_SPM_RET_DENIED: 106 return EFI_ACCESS_DENIED; 107 108 case ARM_SVC_SPM_RET_NO_MEMORY: 109 return EFI_OUT_OF_RESOURCES; 110 111 default: 112 return EFI_ACCESS_DENIED; 113 } 114 } 115 116 /** 117 * mm_communicate() - Adjust the communication buffer to StandAlonneMM and send 118 * it to TEE 119 * 120 * @comm_buf: locally allocated communication buffer, buffer should 121 * be enough big to have some headers and payload 122 * @payload_size: payload size 123 * Return: status code 124 */ 125 static efi_status_t mm_communicate(u8 *comm_buf, size_t payload_size) 126 { 127 size_t dsize; 128 efi_status_t ret; 129 struct efi_mm_communicate_header *mm_hdr; 130 struct smm_variable_communicate_header *var_hdr; 131 132 dsize = payload_size + MM_COMMUNICATE_HEADER_SIZE + 133 MM_VARIABLE_COMMUNICATE_SIZE; 134 mm_hdr = (struct efi_mm_communicate_header *)comm_buf; 135 var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data; 136 137 ret = tee_mm_communicate(comm_buf, dsize); 138 if (ret != EFI_SUCCESS) { 139 dev_err(pvt_data.dev, "%s failed!\n", __func__); 140 return ret; 141 } 142 143 return var_hdr->ret_status; 144 } 145 146 /** 147 * setup_mm_hdr() - Allocate a buffer for StandAloneMM and initialize the 148 * header data. 149 * 150 * @dptr: pointer address to store allocated buffer 151 * @payload_size: payload size 152 * @func: standAloneMM function number 153 * @ret: EFI return code 154 * Return: pointer to corresponding StandAloneMM function buffer or NULL 155 */ 156 static void *setup_mm_hdr(u8 **dptr, size_t payload_size, size_t func, 157 efi_status_t *ret) 158 { 159 const efi_guid_t mm_var_guid = EFI_MM_VARIABLE_GUID; 160 struct efi_mm_communicate_header *mm_hdr; 161 struct smm_variable_communicate_header *var_hdr; 162 u8 *comm_buf; 163 164 /* In the init function we initialize max_buffer_size with 165 * get_max_payload(). So skip the test if max_buffer_size is initialized 166 * StandAloneMM will perform similar checks and drop the buffer if it's 167 * too long 168 */ 169 if (max_buffer_size && 170 max_buffer_size < (MM_COMMUNICATE_HEADER_SIZE + 171 MM_VARIABLE_COMMUNICATE_SIZE + payload_size)) { 172 *ret = EFI_INVALID_PARAMETER; 173 return NULL; 174 } 175 176 comm_buf = kzalloc(MM_COMMUNICATE_HEADER_SIZE + 177 MM_VARIABLE_COMMUNICATE_SIZE + payload_size, 178 GFP_KERNEL); 179 if (!comm_buf) { 180 *ret = EFI_OUT_OF_RESOURCES; 181 return NULL; 182 } 183 184 mm_hdr = (struct efi_mm_communicate_header *)comm_buf; 185 memcpy(&mm_hdr->header_guid, &mm_var_guid, sizeof(mm_hdr->header_guid)); 186 mm_hdr->message_len = MM_VARIABLE_COMMUNICATE_SIZE + payload_size; 187 188 var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data; 189 var_hdr->function = func; 190 if (dptr) 191 *dptr = comm_buf; 192 *ret = EFI_SUCCESS; 193 194 return var_hdr->data; 195 } 196 197 /** 198 * get_max_payload() - Get variable payload size from StandAloneMM. 199 * 200 * @size: size of the variable in storage 201 * Return: status code 202 */ 203 static efi_status_t get_max_payload(size_t *size) 204 { 205 struct smm_variable_payload_size *var_payload = NULL; 206 size_t payload_size; 207 u8 *comm_buf = NULL; 208 efi_status_t ret; 209 210 if (!size) 211 return EFI_INVALID_PARAMETER; 212 213 payload_size = sizeof(*var_payload); 214 var_payload = setup_mm_hdr(&comm_buf, payload_size, 215 SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE, 216 &ret); 217 if (!var_payload) 218 return EFI_OUT_OF_RESOURCES; 219 220 ret = mm_communicate(comm_buf, payload_size); 221 if (ret != EFI_SUCCESS) 222 goto out; 223 224 /* Make sure the buffer is big enough for storing variables */ 225 if (var_payload->size < MM_VARIABLE_ACCESS_HEADER_SIZE + 0x20) { 226 ret = EFI_DEVICE_ERROR; 227 goto out; 228 } 229 *size = var_payload->size; 230 /* 231 * There seems to be a bug in EDK2 miscalculating the boundaries and 232 * size checks, so deduct 2 more bytes to fulfill this requirement. Fix 233 * it up here to ensure backwards compatibility with older versions 234 * (cf. StandaloneMmPkg/Drivers/StandaloneMmCpu/AArch64/EventHandle.c. 235 * sizeof (EFI_MM_COMMUNICATE_HEADER) instead the size minus the 236 * flexible array member). 237 * 238 * size is guaranteed to be > 2 due to checks on the beginning. 239 */ 240 *size -= 2; 241 out: 242 kfree(comm_buf); 243 return ret; 244 } 245 246 static efi_status_t get_property_int(u16 *name, size_t name_size, 247 const efi_guid_t *vendor, 248 struct var_check_property *var_property) 249 { 250 struct smm_variable_var_check_property *smm_property; 251 size_t payload_size; 252 u8 *comm_buf = NULL; 253 efi_status_t ret; 254 255 memset(var_property, 0, sizeof(*var_property)); 256 payload_size = sizeof(*smm_property) + name_size; 257 if (payload_size > max_payload_size) 258 return EFI_INVALID_PARAMETER; 259 260 smm_property = setup_mm_hdr( 261 &comm_buf, payload_size, 262 SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET, &ret); 263 if (!smm_property) 264 return EFI_OUT_OF_RESOURCES; 265 266 memcpy(&smm_property->guid, vendor, sizeof(smm_property->guid)); 267 smm_property->name_size = name_size; 268 memcpy(smm_property->name, name, name_size); 269 270 ret = mm_communicate(comm_buf, payload_size); 271 /* 272 * Currently only R/O property is supported in StMM. 273 * Variables that are not set to R/O will not set the property in StMM 274 * and the call will return EFI_NOT_FOUND. We are setting the 275 * properties to 0x0 so checking against that is enough for the 276 * EFI_NOT_FOUND case. 277 */ 278 if (ret == EFI_NOT_FOUND) 279 ret = EFI_SUCCESS; 280 if (ret != EFI_SUCCESS) 281 goto out; 282 memcpy(var_property, &smm_property->property, sizeof(*var_property)); 283 284 out: 285 kfree(comm_buf); 286 return ret; 287 } 288 289 static efi_status_t tee_get_variable(u16 *name, efi_guid_t *vendor, 290 u32 *attributes, unsigned long *data_size, 291 void *data) 292 { 293 struct var_check_property var_property; 294 struct smm_variable_access *var_acc; 295 size_t payload_size; 296 size_t name_size; 297 size_t tmp_dsize; 298 u8 *comm_buf = NULL; 299 efi_status_t ret; 300 301 if (!name || !vendor || !data_size) 302 return EFI_INVALID_PARAMETER; 303 304 name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16); 305 if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) 306 return EFI_INVALID_PARAMETER; 307 308 /* Trim output buffer size */ 309 tmp_dsize = *data_size; 310 if (name_size + tmp_dsize > 311 max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) { 312 tmp_dsize = max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE - 313 name_size; 314 } 315 316 payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + tmp_dsize; 317 var_acc = setup_mm_hdr(&comm_buf, payload_size, 318 SMM_VARIABLE_FUNCTION_GET_VARIABLE, &ret); 319 if (!var_acc) 320 return EFI_OUT_OF_RESOURCES; 321 322 /* Fill in contents */ 323 memcpy(&var_acc->guid, vendor, sizeof(var_acc->guid)); 324 var_acc->data_size = tmp_dsize; 325 var_acc->name_size = name_size; 326 var_acc->attr = attributes ? *attributes : 0; 327 memcpy(var_acc->name, name, name_size); 328 329 ret = mm_communicate(comm_buf, payload_size); 330 if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) 331 /* Update with reported data size for trimmed case */ 332 *data_size = var_acc->data_size; 333 if (ret != EFI_SUCCESS) 334 goto out; 335 336 ret = get_property_int(name, name_size, vendor, &var_property); 337 if (ret != EFI_SUCCESS) 338 goto out; 339 340 if (attributes) 341 *attributes = var_acc->attr; 342 343 if (!data) { 344 ret = EFI_INVALID_PARAMETER; 345 goto out; 346 } 347 memcpy(data, (u8 *)var_acc->name + var_acc->name_size, 348 var_acc->data_size); 349 out: 350 kfree(comm_buf); 351 return ret; 352 } 353 354 static efi_status_t tee_get_next_variable(unsigned long *name_size, 355 efi_char16_t *name, efi_guid_t *guid) 356 { 357 struct smm_variable_getnext *var_getnext; 358 size_t payload_size; 359 size_t out_name_size; 360 size_t in_name_size; 361 u8 *comm_buf = NULL; 362 efi_status_t ret; 363 364 if (!name_size || !name || !guid) 365 return EFI_INVALID_PARAMETER; 366 367 out_name_size = *name_size; 368 in_name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16); 369 370 if (out_name_size < in_name_size) 371 return EFI_INVALID_PARAMETER; 372 373 if (in_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) 374 return EFI_INVALID_PARAMETER; 375 376 /* Trim output buffer size */ 377 if (out_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) 378 out_name_size = 379 max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE; 380 381 payload_size = MM_VARIABLE_GET_NEXT_HEADER_SIZE + out_name_size; 382 var_getnext = setup_mm_hdr(&comm_buf, payload_size, 383 SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME, 384 &ret); 385 if (!var_getnext) 386 return EFI_OUT_OF_RESOURCES; 387 388 /* Fill in contents */ 389 memcpy(&var_getnext->guid, guid, sizeof(var_getnext->guid)); 390 var_getnext->name_size = out_name_size; 391 memcpy(var_getnext->name, name, in_name_size); 392 memset((u8 *)var_getnext->name + in_name_size, 0x0, 393 out_name_size - in_name_size); 394 395 ret = mm_communicate(comm_buf, payload_size); 396 if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) { 397 /* Update with reported data size for trimmed case */ 398 *name_size = var_getnext->name_size; 399 } 400 if (ret != EFI_SUCCESS) 401 goto out; 402 403 memcpy(guid, &var_getnext->guid, sizeof(*guid)); 404 memcpy(name, var_getnext->name, var_getnext->name_size); 405 406 out: 407 kfree(comm_buf); 408 return ret; 409 } 410 411 static efi_status_t tee_set_variable(efi_char16_t *name, efi_guid_t *vendor, 412 u32 attributes, unsigned long data_size, 413 void *data) 414 { 415 efi_status_t ret; 416 struct var_check_property var_property; 417 struct smm_variable_access *var_acc; 418 size_t payload_size; 419 size_t name_size; 420 u8 *comm_buf = NULL; 421 422 if (!name || name[0] == 0 || !vendor) 423 return EFI_INVALID_PARAMETER; 424 425 if (data_size > 0 && !data) 426 return EFI_INVALID_PARAMETER; 427 428 /* Check payload size */ 429 name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16); 430 payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size; 431 if (payload_size > max_payload_size) 432 return EFI_INVALID_PARAMETER; 433 434 /* 435 * Allocate the buffer early, before switching to RW (if needed) 436 * so we won't need to account for any failures in reading/setting 437 * the properties, if the allocation fails 438 */ 439 var_acc = setup_mm_hdr(&comm_buf, payload_size, 440 SMM_VARIABLE_FUNCTION_SET_VARIABLE, &ret); 441 if (!var_acc) 442 return EFI_OUT_OF_RESOURCES; 443 444 /* 445 * The API has the ability to override RO flags. If no RO check was 446 * requested switch the variable to RW for the duration of this call 447 */ 448 ret = get_property_int(name, name_size, vendor, &var_property); 449 if (ret != EFI_SUCCESS) { 450 dev_err(pvt_data.dev, "Getting variable property failed\n"); 451 goto out; 452 } 453 454 if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) { 455 ret = EFI_WRITE_PROTECTED; 456 goto out; 457 } 458 459 /* Fill in contents */ 460 memcpy(&var_acc->guid, vendor, sizeof(var_acc->guid)); 461 var_acc->data_size = data_size; 462 var_acc->name_size = name_size; 463 var_acc->attr = attributes; 464 memcpy(var_acc->name, name, name_size); 465 memcpy((u8 *)var_acc->name + name_size, data, data_size); 466 467 ret = mm_communicate(comm_buf, payload_size); 468 dev_dbg(pvt_data.dev, "Set Variable %s %d %lx\n", __FILE__, __LINE__, ret); 469 out: 470 kfree(comm_buf); 471 return ret; 472 } 473 474 static efi_status_t tee_set_variable_nonblocking(efi_char16_t *name, 475 efi_guid_t *vendor, 476 u32 attributes, 477 unsigned long data_size, 478 void *data) 479 { 480 return EFI_UNSUPPORTED; 481 } 482 483 static efi_status_t tee_query_variable_info(u32 attributes, 484 u64 *max_variable_storage_size, 485 u64 *remain_variable_storage_size, 486 u64 *max_variable_size) 487 { 488 struct smm_variable_query_info *mm_query_info; 489 size_t payload_size; 490 efi_status_t ret; 491 u8 *comm_buf; 492 493 payload_size = sizeof(*mm_query_info); 494 mm_query_info = setup_mm_hdr(&comm_buf, payload_size, 495 SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO, 496 &ret); 497 if (!mm_query_info) 498 return EFI_OUT_OF_RESOURCES; 499 500 mm_query_info->attr = attributes; 501 ret = mm_communicate(comm_buf, payload_size); 502 if (ret != EFI_SUCCESS) 503 goto out; 504 *max_variable_storage_size = mm_query_info->max_variable_storage; 505 *remain_variable_storage_size = 506 mm_query_info->remaining_variable_storage; 507 *max_variable_size = mm_query_info->max_variable_size; 508 509 out: 510 kfree(comm_buf); 511 return ret; 512 } 513 514 static void tee_stmm_efi_close_context(void *data) 515 { 516 tee_client_close_context(pvt_data.ctx); 517 } 518 519 static void tee_stmm_efi_close_session(void *data) 520 { 521 tee_client_close_session(pvt_data.ctx, pvt_data.session); 522 } 523 524 static void tee_stmm_restore_efivars_generic_ops(void) 525 { 526 efivars_unregister(&tee_efivars); 527 efivars_generic_ops_register(); 528 } 529 530 static int tee_stmm_efi_probe(struct device *dev) 531 { 532 struct tee_ioctl_open_session_arg sess_arg; 533 efi_status_t ret; 534 int rc; 535 536 pvt_data.ctx = tee_client_open_context(NULL, tee_ctx_match, NULL, NULL); 537 if (IS_ERR(pvt_data.ctx)) 538 return -ENODEV; 539 540 rc = devm_add_action_or_reset(dev, tee_stmm_efi_close_context, NULL); 541 if (rc) 542 return rc; 543 544 /* Open session with StMM PTA */ 545 memset(&sess_arg, 0, sizeof(sess_arg)); 546 export_uuid(sess_arg.uuid, &tee_stmm_efi_id_table[0].uuid); 547 rc = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL); 548 if ((rc < 0) || (sess_arg.ret != 0)) { 549 dev_err(dev, "tee_client_open_session failed, err: %x\n", 550 sess_arg.ret); 551 return -EINVAL; 552 } 553 pvt_data.session = sess_arg.session; 554 pvt_data.dev = dev; 555 rc = devm_add_action_or_reset(dev, tee_stmm_efi_close_session, NULL); 556 if (rc) 557 return rc; 558 559 ret = get_max_payload(&max_payload_size); 560 if (ret != EFI_SUCCESS) 561 return -EIO; 562 563 max_buffer_size = MM_COMMUNICATE_HEADER_SIZE + 564 MM_VARIABLE_COMMUNICATE_SIZE + 565 max_payload_size; 566 567 tee_efivar_ops.get_variable = tee_get_variable; 568 tee_efivar_ops.get_next_variable = tee_get_next_variable; 569 tee_efivar_ops.set_variable = tee_set_variable; 570 tee_efivar_ops.set_variable_nonblocking = tee_set_variable_nonblocking; 571 tee_efivar_ops.query_variable_store = efi_query_variable_store; 572 tee_efivar_ops.query_variable_info = tee_query_variable_info; 573 574 efivars_generic_ops_unregister(); 575 pr_info("Using TEE-based EFI runtime variable services\n"); 576 efivars_register(&tee_efivars, &tee_efivar_ops); 577 578 return 0; 579 } 580 581 static int tee_stmm_efi_remove(struct device *dev) 582 { 583 tee_stmm_restore_efivars_generic_ops(); 584 585 return 0; 586 } 587 588 MODULE_DEVICE_TABLE(tee, tee_stmm_efi_id_table); 589 590 static struct tee_client_driver tee_stmm_efi_driver = { 591 .id_table = tee_stmm_efi_id_table, 592 .driver = { 593 .name = "tee-stmm-efi", 594 .bus = &tee_bus_type, 595 .probe = tee_stmm_efi_probe, 596 .remove = tee_stmm_efi_remove, 597 }, 598 }; 599 600 static int __init tee_stmm_efi_mod_init(void) 601 { 602 return driver_register(&tee_stmm_efi_driver.driver); 603 } 604 605 static void __exit tee_stmm_efi_mod_exit(void) 606 { 607 driver_unregister(&tee_stmm_efi_driver.driver); 608 } 609 610 module_init(tee_stmm_efi_mod_init); 611 module_exit(tee_stmm_efi_mod_exit); 612 613 MODULE_LICENSE("GPL"); 614 MODULE_AUTHOR("Ilias Apalodimas <ilias.apalodimas@linaro.org>"); 615 MODULE_AUTHOR("Masahisa Kojima <masahisa.kojima@linaro.org>"); 616 MODULE_DESCRIPTION("TEE based EFI runtime variable service driver"); 617