1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2017, 2019, Linaro Limited 4 * Copyright (c) 2020, Arm Limited. 5 */ 6 7 /* 8 * Security properties of REE-FS TAs 9 * ================================= 10 * 11 * Authentication only 12 * ------------------- 13 * 14 * Required security properties: 15 * 1. Authentication and non-repudiation of a TA to Service Provider (SP). 16 * 2. Integrity of a TA. 17 * 18 * To satisfy (1) and (2), SP needs to sign TA and OP-TEE core needs to verify 19 * the signature using SP public key with computed hash of the TA. 20 * 21 * Authentication along with Confidentiality 22 * ----------------------------------------- 23 * 24 * Required security properties: 25 * 1. Authentication and non-repudiation of a TA to Service Provider (SP). 26 * 2. Confidentiality of a TA. 27 * 3. Integrity of an encrypted TA blob. 28 * 29 * To satisfy (1), SP needs to sign plain TA and OP-TEE core needs to verify the 30 * signature using SP public key with computed hash of the TA. 31 * 32 * To satisfy (2) and (3), SP needs to do authenticated encryption of TA and 33 * OP-TEE core needs to do authenticated decryption of TA to retrieve its 34 * contents. Here encryption provides the confidentiality of TA and MAC tag 35 * provides the integrity of encrypted TA blob. 36 */ 37 38 #include <assert.h> 39 #include <crypto/crypto.h> 40 #include <initcall.h> 41 #include <kernel/thread.h> 42 #include <kernel/ts_store.h> 43 #include <mm/core_memprot.h> 44 #include <mm/tee_mm.h> 45 #include <mm/mobj.h> 46 #include <optee_rpc_cmd.h> 47 #include <signed_hdr.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <tee_api_defines_extensions.h> 51 #include <tee_api_types.h> 52 #include <tee/tee_pobj.h> 53 #include <tee/tee_svc_storage.h> 54 #include <tee/tee_ta_enc_manager.h> 55 #include <tee/uuid.h> 56 #include <utee_defines.h> 57 58 struct ree_fs_ta_handle { 59 struct shdr *nw_ta; /* Non-secure (shared memory) */ 60 size_t nw_ta_size; 61 struct mobj *mobj; 62 size_t offs; 63 struct shdr *shdr; /* Verified secure copy of @nw_ta's signed header */ 64 void *hash_ctx; 65 void *enc_ctx; 66 struct shdr_bootstrap_ta *bs_hdr; 67 struct shdr_encrypted_ta *ehdr; 68 }; 69 70 struct ta_ver_db_hdr { 71 uint32_t db_version; 72 uint32_t nb_entries; 73 }; 74 75 static const char ta_ver_db_obj_id[] = "ta_ver.db"; 76 static struct mutex ta_ver_db_mutex = MUTEX_INITIALIZER; 77 78 /* 79 * Load a TA via RPC with UUID defined by input param @uuid. The virtual 80 * address of the raw TA binary is received in out parameter @ta. 81 */ 82 static TEE_Result rpc_load(const TEE_UUID *uuid, struct shdr **ta, 83 size_t *ta_size, struct mobj **mobj) 84 { 85 TEE_Result res; 86 struct thread_param params[2]; 87 88 if (!uuid || !ta || !mobj || !ta_size) 89 return TEE_ERROR_BAD_PARAMETERS; 90 91 memset(params, 0, sizeof(params)); 92 params[0].attr = THREAD_PARAM_ATTR_VALUE_IN; 93 tee_uuid_to_octets((void *)¶ms[0].u.value, uuid); 94 params[1].attr = THREAD_PARAM_ATTR_MEMREF_OUT; 95 96 res = thread_rpc_cmd(OPTEE_RPC_CMD_LOAD_TA, 2, params); 97 if (res != TEE_SUCCESS) 98 return res; 99 100 *mobj = thread_rpc_alloc_payload(params[1].u.memref.size); 101 if (!*mobj) 102 return TEE_ERROR_OUT_OF_MEMORY; 103 104 if ((*mobj)->size < params[1].u.memref.size) { 105 res = TEE_ERROR_SHORT_BUFFER; 106 goto exit; 107 } 108 109 *ta = mobj_get_va(*mobj, 0); 110 /* We don't expect NULL as thread_rpc_alloc_payload() was successful */ 111 assert(*ta); 112 *ta_size = params[1].u.memref.size; 113 114 params[0].attr = THREAD_PARAM_ATTR_VALUE_IN; 115 tee_uuid_to_octets((void *)¶ms[0].u.value, uuid); 116 params[1].attr = THREAD_PARAM_ATTR_MEMREF_OUT; 117 params[1].u.memref.offs = 0; 118 params[1].u.memref.mobj = *mobj; 119 120 res = thread_rpc_cmd(OPTEE_RPC_CMD_LOAD_TA, 2, params); 121 exit: 122 if (res != TEE_SUCCESS) 123 thread_rpc_free_payload(*mobj); 124 125 return res; 126 } 127 128 static TEE_Result ree_fs_ta_open(const TEE_UUID *uuid, 129 struct ts_store_handle **h) 130 { 131 struct ree_fs_ta_handle *handle; 132 struct shdr *shdr = NULL; 133 struct mobj *mobj = NULL; 134 void *hash_ctx = NULL; 135 struct shdr *ta = NULL; 136 size_t ta_size = 0; 137 TEE_Result res = TEE_SUCCESS; 138 size_t offs = 0; 139 struct shdr_bootstrap_ta *bs_hdr = NULL; 140 struct shdr_encrypted_ta *ehdr = NULL; 141 size_t shdr_sz = 0; 142 143 handle = calloc(1, sizeof(*handle)); 144 if (!handle) 145 return TEE_ERROR_OUT_OF_MEMORY; 146 147 /* Request TA from tee-supplicant */ 148 res = rpc_load(uuid, &ta, &ta_size, &mobj); 149 if (res != TEE_SUCCESS) 150 goto error; 151 152 /* Make secure copy of signed header */ 153 shdr = shdr_alloc_and_copy(ta, ta_size); 154 if (!shdr) { 155 res = TEE_ERROR_SECURITY; 156 goto error_free_payload; 157 } 158 159 /* Validate header signature */ 160 res = shdr_verify_signature(shdr); 161 if (res != TEE_SUCCESS) 162 goto error_free_payload; 163 if (shdr->img_type != SHDR_TA && shdr->img_type != SHDR_BOOTSTRAP_TA && 164 shdr->img_type != SHDR_ENCRYPTED_TA) { 165 res = TEE_ERROR_SECURITY; 166 goto error_free_payload; 167 } 168 169 /* 170 * Initialize a hash context and run the algorithm over the signed 171 * header (less the final file hash and its signature of course) 172 */ 173 res = crypto_hash_alloc_ctx(&hash_ctx, 174 TEE_DIGEST_HASH_TO_ALGO(shdr->algo)); 175 if (res != TEE_SUCCESS) 176 goto error_free_payload; 177 res = crypto_hash_init(hash_ctx); 178 if (res != TEE_SUCCESS) 179 goto error_free_hash; 180 res = crypto_hash_update(hash_ctx, (uint8_t *)shdr, sizeof(*shdr)); 181 if (res != TEE_SUCCESS) 182 goto error_free_hash; 183 shdr_sz = SHDR_GET_SIZE(shdr); 184 if (!shdr_sz) { 185 res = TEE_ERROR_SECURITY; 186 goto error_free_hash; 187 } 188 offs = shdr_sz; 189 190 if (shdr->img_type == SHDR_BOOTSTRAP_TA || 191 shdr->img_type == SHDR_ENCRYPTED_TA) { 192 TEE_UUID bs_uuid = { }; 193 size_t sz = shdr_sz; 194 195 if (ADD_OVERFLOW(sz, sizeof(*bs_hdr), &sz) || ta_size < sz) { 196 res = TEE_ERROR_SECURITY; 197 goto error_free_hash; 198 } 199 200 bs_hdr = malloc(sizeof(*bs_hdr)); 201 if (!bs_hdr) { 202 res = TEE_ERROR_OUT_OF_MEMORY; 203 goto error_free_hash; 204 } 205 206 memcpy(bs_hdr, (uint8_t *)ta + offs, sizeof(*bs_hdr)); 207 208 /* 209 * There's a check later that the UUID embedded inside the 210 * ELF is matching, but since we now have easy access to 211 * the expected uuid of the TA we check it a bit earlier 212 * here. 213 */ 214 tee_uuid_from_octets(&bs_uuid, bs_hdr->uuid); 215 if (memcmp(&bs_uuid, uuid, sizeof(TEE_UUID))) { 216 res = TEE_ERROR_SECURITY; 217 goto error_free_hash; 218 } 219 220 res = crypto_hash_update(hash_ctx, (uint8_t *)bs_hdr, 221 sizeof(*bs_hdr)); 222 if (res != TEE_SUCCESS) 223 goto error_free_hash; 224 offs += sizeof(*bs_hdr); 225 handle->bs_hdr = bs_hdr; 226 } 227 228 if (shdr->img_type == SHDR_ENCRYPTED_TA) { 229 struct shdr_encrypted_ta img_ehdr = { }; 230 size_t sz = shdr_sz; 231 size_t ehdr_sz = 0; 232 233 if (ADD_OVERFLOW(sz, sizeof(struct shdr_bootstrap_ta), &sz) || 234 ADD_OVERFLOW(sz, sizeof(img_ehdr), &sz) || 235 ta_size < sz) { 236 res = TEE_ERROR_SECURITY; 237 goto error_free_hash; 238 } 239 240 memcpy(&img_ehdr, ((uint8_t *)ta + offs), sizeof(img_ehdr)); 241 ehdr_sz = SHDR_ENC_GET_SIZE(&img_ehdr); 242 sz -= sizeof(img_ehdr); 243 if (!ehdr_sz || ADD_OVERFLOW(sz, ehdr_sz, &sz) || 244 ta_size < sz) { 245 res = TEE_ERROR_SECURITY; 246 goto error_free_hash; 247 } 248 249 250 ehdr = malloc(ehdr_sz); 251 if (!ehdr) { 252 res = TEE_ERROR_OUT_OF_MEMORY; 253 goto error_free_hash; 254 } 255 256 memcpy(ehdr, ((uint8_t *)ta + offs), ehdr_sz); 257 258 res = crypto_hash_update(hash_ctx, (uint8_t *)ehdr, ehdr_sz); 259 if (res != TEE_SUCCESS) 260 goto error_free_hash; 261 262 res = tee_ta_decrypt_init(&handle->enc_ctx, ehdr, 263 shdr->img_size); 264 if (res != TEE_SUCCESS) 265 goto error_free_hash; 266 267 offs += ehdr_sz; 268 handle->ehdr = ehdr; 269 } 270 271 if (ta_size != offs + shdr->img_size) { 272 res = TEE_ERROR_SECURITY; 273 goto error_free_hash; 274 } 275 276 handle->nw_ta = ta; 277 handle->nw_ta_size = ta_size; 278 handle->offs = offs; 279 handle->hash_ctx = hash_ctx; 280 handle->shdr = shdr; 281 handle->mobj = mobj; 282 *h = (struct ts_store_handle *)handle; 283 return TEE_SUCCESS; 284 285 error_free_hash: 286 crypto_hash_free_ctx(hash_ctx); 287 error_free_payload: 288 thread_rpc_free_payload(mobj); 289 error: 290 free(ehdr); 291 free(bs_hdr); 292 shdr_free(shdr); 293 free(handle); 294 return res; 295 } 296 297 static TEE_Result ree_fs_ta_get_size(const struct ts_store_handle *h, 298 size_t *size) 299 { 300 struct ree_fs_ta_handle *handle = (struct ree_fs_ta_handle *)h; 301 302 *size = handle->shdr->img_size; 303 return TEE_SUCCESS; 304 } 305 306 static TEE_Result ree_fs_ta_get_tag(const struct ts_store_handle *h, 307 uint8_t *tag, unsigned int *tag_len) 308 { 309 struct ree_fs_ta_handle *handle = (struct ree_fs_ta_handle *)h; 310 311 if (!tag || *tag_len < handle->shdr->hash_size) { 312 *tag_len = handle->shdr->hash_size; 313 return TEE_ERROR_SHORT_BUFFER; 314 } 315 *tag_len = handle->shdr->hash_size; 316 317 memcpy(tag, SHDR_GET_HASH(handle->shdr), handle->shdr->hash_size); 318 319 return TEE_SUCCESS; 320 } 321 322 static TEE_Result check_digest(struct ree_fs_ta_handle *h) 323 { 324 void *digest = NULL; 325 TEE_Result res; 326 327 digest = malloc(h->shdr->hash_size); 328 if (!digest) 329 return TEE_ERROR_OUT_OF_MEMORY; 330 res = crypto_hash_final(h->hash_ctx, digest, h->shdr->hash_size); 331 if (res != TEE_SUCCESS) { 332 res = TEE_ERROR_SECURITY; 333 goto out; 334 } 335 if (memcmp(digest, SHDR_GET_HASH(h->shdr), h->shdr->hash_size)) 336 res = TEE_ERROR_SECURITY; 337 out: 338 free(digest); 339 return res; 340 } 341 342 static TEE_Result check_update_version(struct shdr_bootstrap_ta *hdr) 343 { 344 struct shdr_bootstrap_ta hdr_entry = { }; 345 const struct tee_file_operations *ops = NULL; 346 struct tee_file_handle *fh = NULL; 347 TEE_Result res = TEE_SUCCESS; 348 bool entry_found = false; 349 size_t len = 0; 350 unsigned int i = 0; 351 struct ta_ver_db_hdr db_hdr = { }; 352 struct tee_pobj pobj = { 353 .obj_id = (void *)ta_ver_db_obj_id, 354 .obj_id_len = sizeof(ta_ver_db_obj_id) 355 }; 356 357 ops = tee_svc_storage_file_ops(TEE_STORAGE_PRIVATE); 358 if (!ops) 359 return TEE_SUCCESS; /* Compiled with no secure storage */ 360 361 mutex_lock(&ta_ver_db_mutex); 362 363 res = ops->open(&pobj, NULL, &fh); 364 if (res != TEE_SUCCESS && res != TEE_ERROR_ITEM_NOT_FOUND) 365 goto out; 366 367 if (res == TEE_ERROR_ITEM_NOT_FOUND) { 368 res = ops->create(&pobj, false, NULL, 0, NULL, 0, NULL, 0, &fh); 369 if (res != TEE_SUCCESS) 370 goto out; 371 372 res = ops->write(fh, 0, &db_hdr, sizeof(db_hdr)); 373 if (res != TEE_SUCCESS) 374 goto out; 375 } else { 376 len = sizeof(db_hdr); 377 378 res = ops->read(fh, 0, &db_hdr, &len); 379 if (res != TEE_SUCCESS) { 380 goto out; 381 } else if (len != sizeof(db_hdr)) { 382 res = TEE_ERROR_BAD_STATE; 383 goto out; 384 } 385 } 386 387 for (i = 0; i < db_hdr.nb_entries; i++) { 388 len = sizeof(hdr_entry); 389 390 res = ops->read(fh, sizeof(db_hdr) + (i * len), &hdr_entry, 391 &len); 392 if (res != TEE_SUCCESS) { 393 goto out; 394 } else if (len != sizeof(hdr_entry)) { 395 res = TEE_ERROR_BAD_STATE; 396 goto out; 397 } 398 399 if (!memcmp(hdr->uuid, hdr_entry.uuid, sizeof(TEE_UUID))) { 400 entry_found = true; 401 break; 402 } 403 } 404 405 if (entry_found) { 406 if (hdr_entry.ta_version > hdr->ta_version) { 407 res = TEE_ERROR_ACCESS_CONFLICT; 408 goto out; 409 } else if (hdr_entry.ta_version < hdr->ta_version) { 410 len = sizeof(*hdr); 411 res = ops->write(fh, sizeof(db_hdr) + (i * len), hdr, 412 len); 413 if (res != TEE_SUCCESS) 414 goto out; 415 } 416 } else { 417 len = sizeof(*hdr); 418 res = ops->write(fh, sizeof(db_hdr) + (db_hdr.nb_entries * len), 419 hdr, len); 420 if (res != TEE_SUCCESS) 421 goto out; 422 423 db_hdr.nb_entries++; 424 res = ops->write(fh, 0, &db_hdr, sizeof(db_hdr)); 425 if (res != TEE_SUCCESS) 426 goto out; 427 } 428 429 out: 430 ops->close(&fh); 431 mutex_unlock(&ta_ver_db_mutex); 432 return res; 433 } 434 435 static TEE_Result ree_fs_ta_read(struct ts_store_handle *h, void *data, 436 size_t len) 437 { 438 struct ree_fs_ta_handle *handle = (struct ree_fs_ta_handle *)h; 439 440 uint8_t *src = (uint8_t *)handle->nw_ta + handle->offs; 441 size_t next_offs = 0; 442 uint8_t *dst = src; 443 TEE_Result res = TEE_SUCCESS; 444 445 if (ADD_OVERFLOW(handle->offs, len, &next_offs) || 446 next_offs > handle->nw_ta_size) 447 return TEE_ERROR_BAD_PARAMETERS; 448 449 if (handle->shdr->img_type == SHDR_ENCRYPTED_TA) { 450 if (data) { 451 dst = data; /* Hash secure buffer */ 452 res = tee_ta_decrypt_update(handle->enc_ctx, dst, src, 453 len); 454 if (res != TEE_SUCCESS) 455 return TEE_ERROR_SECURITY; 456 } else { 457 size_t num_bytes = 0; 458 size_t b_size = MIN(1024U, len); 459 uint8_t *b = malloc(b_size); 460 461 if (!b) 462 return TEE_ERROR_OUT_OF_MEMORY; 463 464 dst = NULL; 465 while (num_bytes < len) { 466 size_t n = MIN(b_size, len - num_bytes); 467 468 res = tee_ta_decrypt_update(handle->enc_ctx, b, 469 src + num_bytes, n); 470 if (res) 471 break; 472 num_bytes += n; 473 474 res = crypto_hash_update(handle->hash_ctx, b, 475 n); 476 if (res) 477 break; 478 } 479 480 free(b); 481 if (res != TEE_SUCCESS) 482 return TEE_ERROR_SECURITY; 483 } 484 } else if (data) { 485 dst = data; /* Hash secure buffer (shm might be modified) */ 486 memcpy(dst, src, len); 487 } 488 489 if (dst) { 490 res = crypto_hash_update(handle->hash_ctx, dst, len); 491 if (res != TEE_SUCCESS) 492 return TEE_ERROR_SECURITY; 493 } 494 495 handle->offs = next_offs; 496 if (handle->offs == handle->nw_ta_size) { 497 if (handle->shdr->img_type == SHDR_ENCRYPTED_TA) { 498 /* 499 * Last read: time to finalize authenticated 500 * decryption. 501 */ 502 res = tee_ta_decrypt_final(handle->enc_ctx, 503 handle->ehdr, NULL, NULL, 0); 504 if (res != TEE_SUCCESS) 505 return TEE_ERROR_SECURITY; 506 } 507 /* 508 * Last read: time to check if our digest matches the expected 509 * one (from the signed header) 510 */ 511 res = check_digest(handle); 512 if (res != TEE_SUCCESS) 513 return res; 514 515 if (handle->bs_hdr) 516 res = check_update_version(handle->bs_hdr); 517 } 518 return res; 519 } 520 521 static void ree_fs_ta_close(struct ts_store_handle *h) 522 { 523 struct ree_fs_ta_handle *handle = (struct ree_fs_ta_handle *)h; 524 525 if (!handle) 526 return; 527 thread_rpc_free_payload(handle->mobj); 528 crypto_hash_free_ctx(handle->hash_ctx); 529 free(handle->shdr); 530 free(handle->ehdr); 531 free(handle->bs_hdr); 532 free(handle); 533 } 534 535 #ifndef CFG_REE_FS_TA_BUFFERED 536 REGISTER_TA_STORE(9) = { 537 .description = "REE", 538 .open = ree_fs_ta_open, 539 .get_size = ree_fs_ta_get_size, 540 .get_tag = ree_fs_ta_get_tag, 541 .read = ree_fs_ta_read, 542 .close = ree_fs_ta_close, 543 }; 544 #endif 545 546 #ifdef CFG_REE_FS_TA_BUFFERED 547 548 /* 549 * This is a wrapper around the "REE FS" TA store. 550 * The whole TA/library is read into a temporary buffer during .open(). This 551 * allows the binary to be authenticated before any data is read and processed 552 * by the upper layer (ELF loader). 553 */ 554 555 struct buf_ree_fs_ta_handle { 556 struct ts_store_handle *h; /* Note: a REE FS TA store handle */ 557 size_t ta_size; 558 tee_mm_entry_t *mm; 559 uint8_t *buf; 560 size_t offs; 561 uint8_t *tag; 562 unsigned int tag_len; 563 }; 564 565 static TEE_Result buf_ta_open(const TEE_UUID *uuid, 566 struct ts_store_handle **h) 567 { 568 struct buf_ree_fs_ta_handle *handle = NULL; 569 TEE_Result res = TEE_SUCCESS; 570 571 handle = calloc(1, sizeof(*handle)); 572 if (!handle) 573 return TEE_ERROR_OUT_OF_MEMORY; 574 res = ree_fs_ta_open(uuid, &handle->h); 575 if (res) 576 goto err2; 577 res = ree_fs_ta_get_size(handle->h, &handle->ta_size); 578 if (res) 579 goto err; 580 581 res = ree_fs_ta_get_tag(handle->h, NULL, &handle->tag_len); 582 if (res != TEE_ERROR_SHORT_BUFFER) { 583 res = TEE_ERROR_GENERIC; 584 goto err; 585 } 586 handle->tag = malloc(handle->tag_len); 587 if (!handle->tag) { 588 res = TEE_ERROR_OUT_OF_MEMORY; 589 goto err; 590 } 591 res = ree_fs_ta_get_tag(handle->h, handle->tag, &handle->tag_len); 592 if (res) 593 goto err; 594 595 handle->mm = tee_mm_alloc(&tee_mm_sec_ddr, handle->ta_size); 596 if (!handle->mm) { 597 res = TEE_ERROR_OUT_OF_MEMORY; 598 goto err; 599 } 600 handle->buf = phys_to_virt(tee_mm_get_smem(handle->mm), 601 MEM_AREA_TA_RAM, handle->ta_size); 602 if (!handle->buf) { 603 res = TEE_ERROR_OUT_OF_MEMORY; 604 goto err; 605 } 606 res = ree_fs_ta_read(handle->h, handle->buf, handle->ta_size); 607 if (res) 608 goto err; 609 *h = (struct ts_store_handle *)handle; 610 err: 611 ree_fs_ta_close(handle->h); 612 err2: 613 if (res) { 614 tee_mm_free(handle->mm); 615 free(handle->tag); 616 free(handle); 617 } 618 return res; 619 } 620 621 static TEE_Result buf_ta_get_size(const struct ts_store_handle *h, 622 size_t *size) 623 { 624 struct buf_ree_fs_ta_handle *handle = (struct buf_ree_fs_ta_handle *)h; 625 626 *size = handle->ta_size; 627 return TEE_SUCCESS; 628 } 629 630 static TEE_Result buf_ta_read(struct ts_store_handle *h, void *data, 631 size_t len) 632 { 633 struct buf_ree_fs_ta_handle *handle = (struct buf_ree_fs_ta_handle *)h; 634 uint8_t *src = handle->buf + handle->offs; 635 size_t next_offs = 0; 636 637 if (ADD_OVERFLOW(handle->offs, len, &next_offs) || 638 next_offs > handle->ta_size) 639 return TEE_ERROR_BAD_PARAMETERS; 640 641 if (data) 642 memcpy(data, src, len); 643 handle->offs = next_offs; 644 return TEE_SUCCESS; 645 } 646 647 static TEE_Result buf_ta_get_tag(const struct ts_store_handle *h, 648 uint8_t *tag, unsigned int *tag_len) 649 { 650 struct buf_ree_fs_ta_handle *handle = (struct buf_ree_fs_ta_handle *)h; 651 652 *tag_len = handle->tag_len; 653 if (!tag || *tag_len < handle->tag_len) 654 return TEE_ERROR_SHORT_BUFFER; 655 656 memcpy(tag, handle->tag, handle->tag_len); 657 658 return TEE_SUCCESS; 659 } 660 661 static void buf_ta_close(struct ts_store_handle *h) 662 { 663 struct buf_ree_fs_ta_handle *handle = (struct buf_ree_fs_ta_handle *)h; 664 665 if (!handle) 666 return; 667 tee_mm_free(handle->mm); 668 free(handle->tag); 669 free(handle); 670 } 671 672 REGISTER_TA_STORE(9) = { 673 .description = "REE [buffered]", 674 .open = buf_ta_open, 675 .get_size = buf_ta_get_size, 676 .get_tag = buf_ta_get_tag, 677 .read = buf_ta_read, 678 .close = buf_ta_close, 679 }; 680 681 #endif /* CFG_REE_FS_TA_BUFFERED */ 682