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