1 /* 2 * Copyright (c) 2016-2018, Intel Corporation 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * * Redistributions of source code must retain the above copyright notice, 8 * this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright notice, 10 * this list of conditions and the following disclaimer in the documentation 11 * and/or other materials provided with the distribution. 12 * * Neither the name of Intel Corporation nor the names of its contributors 13 * may be used to endorse or promote products derived from this software 14 * without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "pt_image_section_cache.h" 30 #include "pt_section.h" 31 32 #include "intel-pt.h" 33 34 #include <stdlib.h> 35 36 37 static char *dupstr(const char *str) 38 { 39 char *dup; 40 size_t len; 41 42 if (!str) 43 return NULL; 44 45 len = strlen(str); 46 dup = malloc(len + 1); 47 if (!dup) 48 return NULL; 49 50 return strcpy(dup, str); 51 } 52 53 int pt_iscache_init(struct pt_image_section_cache *iscache, const char *name) 54 { 55 if (!iscache) 56 return -pte_internal; 57 58 memset(iscache, 0, sizeof(*iscache)); 59 iscache->limit = UINT64_MAX; 60 if (name) { 61 iscache->name = dupstr(name); 62 if (!iscache->name) 63 return -pte_nomem; 64 } 65 66 #if defined(FEATURE_THREADS) 67 { 68 int errcode; 69 70 errcode = mtx_init(&iscache->lock, mtx_plain); 71 if (errcode != thrd_success) 72 return -pte_bad_lock; 73 } 74 #endif /* defined(FEATURE_THREADS) */ 75 76 return 0; 77 } 78 79 void pt_iscache_fini(struct pt_image_section_cache *iscache) 80 { 81 if (!iscache) 82 return; 83 84 (void) pt_iscache_clear(iscache); 85 free(iscache->name); 86 87 #if defined(FEATURE_THREADS) 88 89 mtx_destroy(&iscache->lock); 90 91 #endif /* defined(FEATURE_THREADS) */ 92 } 93 94 static inline int pt_iscache_lock(struct pt_image_section_cache *iscache) 95 { 96 if (!iscache) 97 return -pte_internal; 98 99 #if defined(FEATURE_THREADS) 100 { 101 int errcode; 102 103 errcode = mtx_lock(&iscache->lock); 104 if (errcode != thrd_success) 105 return -pte_bad_lock; 106 } 107 #endif /* defined(FEATURE_THREADS) */ 108 109 return 0; 110 } 111 112 static inline int pt_iscache_unlock(struct pt_image_section_cache *iscache) 113 { 114 if (!iscache) 115 return -pte_internal; 116 117 #if defined(FEATURE_THREADS) 118 { 119 int errcode; 120 121 errcode = mtx_unlock(&iscache->lock); 122 if (errcode != thrd_success) 123 return -pte_bad_lock; 124 } 125 #endif /* defined(FEATURE_THREADS) */ 126 127 return 0; 128 } 129 130 static inline int isid_from_index(uint16_t index) 131 { 132 return index + 1; 133 } 134 135 static int pt_iscache_expand(struct pt_image_section_cache *iscache) 136 { 137 struct pt_iscache_entry *entries; 138 uint16_t capacity, target; 139 140 if (!iscache) 141 return -pte_internal; 142 143 capacity = iscache->capacity; 144 target = capacity + 8; 145 146 /* Check for overflows. */ 147 if (target < capacity) 148 return -pte_nomem; 149 150 entries = realloc(iscache->entries, target * sizeof(*entries)); 151 if (!entries) 152 return -pte_nomem; 153 154 iscache->capacity = target; 155 iscache->entries = entries; 156 return 0; 157 } 158 159 static int pt_iscache_find_locked(struct pt_image_section_cache *iscache, 160 const char *filename, uint64_t offset, 161 uint64_t size, uint64_t laddr) 162 { 163 uint16_t idx, end; 164 165 if (!iscache || !filename) 166 return -pte_internal; 167 168 end = iscache->size; 169 for (idx = 0; idx < end; ++idx) { 170 const struct pt_iscache_entry *entry; 171 const struct pt_section *section; 172 const char *sec_filename; 173 uint64_t sec_offset, sec_size; 174 175 entry = &iscache->entries[idx]; 176 177 /* We do not zero-initialize the array - a NULL check is 178 * pointless. 179 */ 180 section = entry->section; 181 sec_filename = pt_section_filename(section); 182 sec_offset = pt_section_offset(section); 183 sec_size = pt_section_size(section); 184 185 if (entry->laddr != laddr) 186 continue; 187 188 if (sec_offset != offset) 189 continue; 190 191 if (sec_size != size) 192 continue; 193 194 /* We should not have a section without a filename. */ 195 if (!sec_filename) 196 return -pte_internal; 197 198 if (strcmp(sec_filename, filename) != 0) 199 continue; 200 201 return isid_from_index(idx); 202 } 203 204 return 0; 205 } 206 207 static int pt_iscache_lru_free(struct pt_iscache_lru_entry *lru) 208 { 209 while (lru) { 210 struct pt_iscache_lru_entry *trash; 211 int errcode; 212 213 trash = lru; 214 lru = lru->next; 215 216 errcode = pt_section_unmap(trash->section); 217 if (errcode < 0) 218 return errcode; 219 220 free(trash); 221 } 222 223 return 0; 224 } 225 226 static int pt_iscache_lru_prune(struct pt_image_section_cache *iscache, 227 struct pt_iscache_lru_entry **tail) 228 { 229 struct pt_iscache_lru_entry *lru, **pnext; 230 uint64_t limit, used; 231 232 if (!iscache || !tail) 233 return -pte_internal; 234 235 limit = iscache->limit; 236 used = 0ull; 237 238 pnext = &iscache->lru; 239 for (lru = *pnext; lru; pnext = &lru->next, lru = *pnext) { 240 241 used += lru->size; 242 if (used <= limit) 243 continue; 244 245 /* The cache got too big; prune it starting from @lru. */ 246 iscache->used = used - lru->size; 247 *pnext = NULL; 248 *tail = lru; 249 250 return 0; 251 } 252 253 /* We shouldn't prune the cache unnecessarily. */ 254 return -pte_internal; 255 } 256 257 /* Add @section to the front of @iscache->lru. 258 * 259 * Returns a positive integer if we need to prune the cache. 260 * Returns zero if we don't need to prune the cache. 261 * Returns a negative pt_error_code otherwise. 262 */ 263 static int pt_isache_lru_new(struct pt_image_section_cache *iscache, 264 struct pt_section *section) 265 { 266 struct pt_iscache_lru_entry *lru; 267 uint64_t memsize, used, total, limit; 268 int errcode; 269 270 if (!iscache) 271 return -pte_internal; 272 273 errcode = pt_section_memsize(section, &memsize); 274 if (errcode < 0) 275 return errcode; 276 277 /* Don't try to add the section if it is too big. We'd prune it again 278 * together with all other sections in our cache. 279 */ 280 limit = iscache->limit; 281 if (limit < memsize) 282 return 0; 283 284 errcode = pt_section_map_share(section); 285 if (errcode < 0) 286 return errcode; 287 288 lru = malloc(sizeof(*lru)); 289 if (!lru) { 290 (void) pt_section_unmap(section); 291 return -pte_nomem; 292 } 293 294 lru->section = section; 295 lru->size = memsize; 296 297 lru->next = iscache->lru; 298 iscache->lru = lru; 299 300 used = iscache->used; 301 total = used + memsize; 302 if (total < used || total < memsize) 303 return -pte_overflow; 304 305 iscache->used = total; 306 307 return (limit < total) ? 1 : 0; 308 } 309 310 /* Add or move @section to the front of @iscache->lru. 311 * 312 * Returns a positive integer if we need to prune the cache. 313 * Returns zero if we don't need to prune the cache. 314 * Returns a negative pt_error_code otherwise. 315 */ 316 static int pt_iscache_lru_add(struct pt_image_section_cache *iscache, 317 struct pt_section *section) 318 { 319 struct pt_iscache_lru_entry *lru, **pnext; 320 321 if (!iscache) 322 return -pte_internal; 323 324 pnext = &iscache->lru; 325 for (lru = *pnext; lru; pnext = &lru->next, lru = *pnext) { 326 327 if (lru->section != section) 328 continue; 329 330 /* We found it in the cache. Move it to the front. */ 331 *pnext = lru->next; 332 lru->next = iscache->lru; 333 iscache->lru = lru; 334 335 return 0; 336 } 337 338 /* We didn't find it in the cache. Add it. */ 339 return pt_isache_lru_new(iscache, section); 340 } 341 342 343 /* Remove @section from @iscache->lru. 344 * 345 * Returns zero on success, a negative pt_error_code otherwise. 346 */ 347 static int pt_iscache_lru_remove(struct pt_image_section_cache *iscache, 348 const struct pt_section *section) 349 { 350 struct pt_iscache_lru_entry *lru, **pnext; 351 352 if (!iscache) 353 return -pte_internal; 354 355 pnext = &iscache->lru; 356 for (lru = *pnext; lru; pnext = &lru->next, lru = *pnext) { 357 358 if (lru->section != section) 359 continue; 360 361 /* We found it in the cache. Remove it. */ 362 *pnext = lru->next; 363 lru->next = NULL; 364 break; 365 } 366 367 return pt_iscache_lru_free(lru); 368 } 369 370 371 /* Add or move @section to the front of @iscache->lru and update its size. 372 * 373 * Returns a positive integer if we need to prune the cache. 374 * Returns zero if we don't need to prune the cache. 375 * Returns a negative pt_error_code otherwise. 376 */ 377 static int pt_iscache_lru_resize(struct pt_image_section_cache *iscache, 378 struct pt_section *section, uint64_t memsize) 379 { 380 struct pt_iscache_lru_entry *lru; 381 uint64_t oldsize, used; 382 int status; 383 384 if (!iscache) 385 return -pte_internal; 386 387 status = pt_iscache_lru_add(iscache, section); 388 if (status < 0) 389 return status; 390 391 lru = iscache->lru; 392 if (!lru) { 393 if (status) 394 return -pte_internal; 395 return 0; 396 } 397 398 /* If @section is cached, it must be first. 399 * 400 * We may choose not to cache it, though, e.g. if it is too big. 401 */ 402 if (lru->section != section) { 403 if (iscache->limit < memsize) 404 return 0; 405 406 return -pte_internal; 407 } 408 409 oldsize = lru->size; 410 lru->size = memsize; 411 412 /* If we need to prune anyway, we're done. */ 413 if (status) 414 return status; 415 416 used = iscache->used; 417 used -= oldsize; 418 used += memsize; 419 420 iscache->used = used; 421 422 return (iscache->limit < used) ? 1 : 0; 423 } 424 425 /* Clear @iscache->lru. 426 * 427 * Unlike other iscache_lru functions, the caller does not lock @iscache. 428 * 429 * Return zero on success, a negative pt_error_code otherwise. 430 */ 431 static int pt_iscache_lru_clear(struct pt_image_section_cache *iscache) 432 { 433 struct pt_iscache_lru_entry *lru; 434 int errcode; 435 436 errcode = pt_iscache_lock(iscache); 437 if (errcode < 0) 438 return errcode; 439 440 lru = iscache->lru; 441 iscache->lru = NULL; 442 iscache->used = 0ull; 443 444 errcode = pt_iscache_unlock(iscache); 445 if (errcode < 0) 446 return errcode; 447 448 return pt_iscache_lru_free(lru); 449 } 450 451 /* Search @iscache for a partial or exact match of @section loaded at @laddr and 452 * return the corresponding index or @iscache->size if no match is found. 453 * 454 * The caller must lock @iscache. 455 * 456 * Returns a non-zero index on success, a negative pt_error_code otherwise. 457 */ 458 static int 459 pt_iscache_find_section_locked(const struct pt_image_section_cache *iscache, 460 const char *filename, uint64_t offset, 461 uint64_t size, uint64_t laddr) 462 { 463 const struct pt_section *section; 464 uint16_t idx, end; 465 int match; 466 467 if (!iscache || !filename) 468 return -pte_internal; 469 470 section = NULL; 471 match = end = iscache->size; 472 for (idx = 0; idx < end; ++idx) { 473 const struct pt_iscache_entry *entry; 474 const struct pt_section *sec; 475 476 entry = &iscache->entries[idx]; 477 478 /* We do not zero-initialize the array - a NULL check is 479 * pointless. 480 */ 481 sec = entry->section; 482 483 /* Avoid redundant match checks. */ 484 if (sec != section) { 485 const char *sec_filename; 486 487 /* We don't have duplicates. Skip the check. */ 488 if (section) 489 continue; 490 491 if (offset != pt_section_offset(sec)) 492 continue; 493 494 if (size != pt_section_size(sec)) 495 continue; 496 497 sec_filename = pt_section_filename(sec); 498 if (!sec_filename) 499 return -pte_internal; 500 501 if (strcmp(filename, sec_filename) != 0) 502 continue; 503 504 /* Use the cached section instead. */ 505 section = sec; 506 match = idx; 507 } 508 509 /* If we didn't continue, @section == @sec and we have a match. 510 * 511 * If we also find a matching load address, we're done. 512 */ 513 if (laddr == entry->laddr) 514 return idx; 515 } 516 517 return match; 518 } 519 520 int pt_iscache_add(struct pt_image_section_cache *iscache, 521 struct pt_section *section, uint64_t laddr) 522 { 523 const char *filename; 524 uint64_t offset, size; 525 uint16_t idx; 526 int errcode; 527 528 if (!iscache || !section) 529 return -pte_internal; 530 531 /* We must have a filename for @section. */ 532 filename = pt_section_filename(section); 533 if (!filename) 534 return -pte_internal; 535 536 offset = pt_section_offset(section); 537 size = pt_section_size(section); 538 539 /* Adding a section is slightly complicated by a potential deadlock 540 * scenario: 541 * 542 * - in order to add a section, we need to attach to it, which 543 * requires taking the section's attach lock. 544 * 545 * - if we are already attached to it, we may receive on-map 546 * notifications, which will be sent while holding the attach lock 547 * and require taking the iscache lock. 548 * 549 * Hence we can't attach to a section while holding the iscache lock. 550 * 551 * 552 * We therefore attach to @section first and then lock @iscache. 553 * 554 * This opens a small window where an existing @section may be removed 555 * from @iscache and replaced by a new matching section. We would want 556 * to share that new section rather than adding a duplicate @section. 557 * 558 * After locking @iscache, we therefore check for existing matching 559 * sections and, if one is found, update @section. This involves 560 * detaching from @section and attaching to the existing section. 561 * 562 * And for this, we will have to temporarily unlock @iscache again. 563 */ 564 errcode = pt_section_get(section); 565 if (errcode < 0) 566 return errcode; 567 568 errcode = pt_section_attach(section, iscache); 569 if (errcode < 0) 570 goto out_put; 571 572 errcode = pt_iscache_lock(iscache); 573 if (errcode < 0) 574 goto out_detach; 575 576 /* We may need to repeat this step. 577 * 578 * Typically we don't and this takes only a single iteration. One 579 * scenario where we do repeat this is when adding a section with an 580 * out-of-bounds size. 581 * 582 * We will not find a matching section in pt_iscache_add_file() so we 583 * create a new section. This will have its size reduced to match the 584 * actual file size. 585 * 586 * For this reduced size, we may now find an existing section, and we 587 * will take another trip in the below loop. 588 */ 589 for (;;) { 590 const struct pt_iscache_entry *entry; 591 struct pt_section *sec; 592 int match; 593 594 /* Find an existing section matching @section that we'd share 595 * rather than adding @section. 596 */ 597 match = pt_iscache_find_section_locked(iscache, filename, 598 offset, size, laddr); 599 if (match < 0) { 600 errcode = match; 601 goto out_unlock_detach; 602 } 603 604 /* We're done if we have not found a matching section. */ 605 if (iscache->size <= match) 606 break; 607 608 entry = &iscache->entries[match]; 609 610 /* We're also done if we found the same section again. 611 * 612 * We further check for a perfect match. In that case, we don't 613 * need to insert anything, at all. 614 */ 615 sec = entry->section; 616 if (sec == section) { 617 if (entry->laddr == laddr) { 618 errcode = pt_iscache_unlock(iscache); 619 if (errcode < 0) 620 goto out_detach; 621 622 errcode = pt_section_detach(section, iscache); 623 if (errcode < 0) 624 goto out_lru; 625 626 errcode = pt_section_put(section); 627 if (errcode < 0) 628 return errcode; 629 630 return isid_from_index((uint16_t) match); 631 } 632 633 break; 634 } 635 636 /* We update @section to share the existing @sec. 637 * 638 * This requires detaching from @section, which, in turn, 639 * requires temporarily unlocking @iscache. 640 * 641 * We further need to remove @section from @iscache->lru. 642 */ 643 errcode = pt_section_get(sec); 644 if (errcode < 0) 645 goto out_unlock_detach; 646 647 errcode = pt_iscache_unlock(iscache); 648 if (errcode < 0) { 649 (void) pt_section_put(sec); 650 goto out_detach; 651 } 652 653 errcode = pt_section_detach(section, iscache); 654 if (errcode < 0) { 655 (void) pt_section_put(sec); 656 goto out_lru; 657 } 658 659 errcode = pt_section_attach(sec, iscache); 660 if (errcode < 0) { 661 (void) pt_section_put(sec); 662 goto out_lru; 663 } 664 665 errcode = pt_iscache_lock(iscache); 666 if (errcode < 0) { 667 (void) pt_section_put(section); 668 /* Complete the swap for cleanup. */ 669 section = sec; 670 goto out_detach; 671 } 672 673 /* We may have received on-map notifications for @section and we 674 * may have added @section to @iscache->lru. 675 * 676 * Since we're still holding a reference to it, no harm has been 677 * done. But we need to remove it before we drop our reference. 678 */ 679 errcode = pt_iscache_lru_remove(iscache, section); 680 if (errcode < 0) { 681 (void) pt_section_put(section); 682 /* Complete the swap for cleanup. */ 683 section = sec; 684 goto out_unlock_detach; 685 } 686 687 /* Drop the reference to @section. */ 688 errcode = pt_section_put(section); 689 if (errcode < 0) { 690 /* Complete the swap for cleanup. */ 691 section = sec; 692 goto out_unlock_detach; 693 } 694 695 /* Swap sections. 696 * 697 * We will try again in the next iteration. 698 */ 699 section = sec; 700 } 701 702 /* Expand the cache, if necessary. */ 703 if (iscache->capacity <= iscache->size) { 704 /* We must never exceed the capacity. */ 705 if (iscache->capacity < iscache->size) { 706 errcode = -pte_internal; 707 goto out_unlock_detach; 708 } 709 710 errcode = pt_iscache_expand(iscache); 711 if (errcode < 0) 712 goto out_unlock_detach; 713 714 /* Make sure it is big enough, now. */ 715 if (iscache->capacity <= iscache->size) { 716 errcode = -pte_internal; 717 goto out_unlock_detach; 718 } 719 } 720 721 /* Insert a new entry for @section at @laddr. 722 * 723 * This hands both attach and reference over to @iscache. We will 724 * detach and drop the reference again when the entry is removed. 725 */ 726 idx = iscache->size++; 727 728 iscache->entries[idx].section = section; 729 iscache->entries[idx].laddr = laddr; 730 731 errcode = pt_iscache_unlock(iscache); 732 if (errcode < 0) 733 return errcode; 734 735 return isid_from_index(idx); 736 737 out_unlock_detach: 738 (void) pt_iscache_unlock(iscache); 739 740 out_detach: 741 (void) pt_section_detach(section, iscache); 742 743 out_lru: 744 (void) pt_iscache_lru_clear(iscache); 745 746 out_put: 747 (void) pt_section_put(section); 748 749 return errcode; 750 } 751 752 int pt_iscache_find(struct pt_image_section_cache *iscache, 753 const char *filename, uint64_t offset, uint64_t size, 754 uint64_t laddr) 755 { 756 int errcode, isid; 757 758 errcode = pt_iscache_lock(iscache); 759 if (errcode < 0) 760 return errcode; 761 762 isid = pt_iscache_find_locked(iscache, filename, offset, size, laddr); 763 764 errcode = pt_iscache_unlock(iscache); 765 if (errcode < 0) 766 return errcode; 767 768 return isid; 769 } 770 771 int pt_iscache_lookup(struct pt_image_section_cache *iscache, 772 struct pt_section **section, uint64_t *laddr, int isid) 773 { 774 uint16_t index; 775 int errcode, status; 776 777 if (!iscache || !section || !laddr) 778 return -pte_internal; 779 780 if (isid <= 0) 781 return -pte_bad_image; 782 783 isid -= 1; 784 if (isid > UINT16_MAX) 785 return -pte_internal; 786 787 index = (uint16_t) isid; 788 789 errcode = pt_iscache_lock(iscache); 790 if (errcode < 0) 791 return errcode; 792 793 if (iscache->size <= index) 794 status = -pte_bad_image; 795 else { 796 const struct pt_iscache_entry *entry; 797 798 entry = &iscache->entries[index]; 799 *section = entry->section; 800 *laddr = entry->laddr; 801 802 status = pt_section_get(*section); 803 } 804 805 errcode = pt_iscache_unlock(iscache); 806 if (errcode < 0) 807 return errcode; 808 809 return status; 810 } 811 812 int pt_iscache_clear(struct pt_image_section_cache *iscache) 813 { 814 struct pt_iscache_lru_entry *lru; 815 struct pt_iscache_entry *entries; 816 uint16_t idx, end; 817 int errcode; 818 819 if (!iscache) 820 return -pte_internal; 821 822 errcode = pt_iscache_lock(iscache); 823 if (errcode < 0) 824 return errcode; 825 826 entries = iscache->entries; 827 end = iscache->size; 828 lru = iscache->lru; 829 830 iscache->entries = NULL; 831 iscache->capacity = 0; 832 iscache->size = 0; 833 iscache->lru = NULL; 834 iscache->used = 0ull; 835 836 errcode = pt_iscache_unlock(iscache); 837 if (errcode < 0) 838 return errcode; 839 840 errcode = pt_iscache_lru_free(lru); 841 if (errcode < 0) 842 return errcode; 843 844 for (idx = 0; idx < end; ++idx) { 845 struct pt_section *section; 846 847 section = entries[idx].section; 848 849 /* We do not zero-initialize the array - a NULL check is 850 * pointless. 851 */ 852 errcode = pt_section_detach(section, iscache); 853 if (errcode < 0) 854 return errcode; 855 856 errcode = pt_section_put(section); 857 if (errcode < 0) 858 return errcode; 859 } 860 861 free(entries); 862 return 0; 863 } 864 865 struct pt_image_section_cache *pt_iscache_alloc(const char *name) 866 { 867 struct pt_image_section_cache *iscache; 868 869 iscache = malloc(sizeof(*iscache)); 870 if (iscache) 871 pt_iscache_init(iscache, name); 872 873 return iscache; 874 } 875 876 void pt_iscache_free(struct pt_image_section_cache *iscache) 877 { 878 if (!iscache) 879 return; 880 881 pt_iscache_fini(iscache); 882 free(iscache); 883 } 884 885 int pt_iscache_set_limit(struct pt_image_section_cache *iscache, uint64_t limit) 886 { 887 struct pt_iscache_lru_entry *tail; 888 int errcode, status; 889 890 if (!iscache) 891 return -pte_invalid; 892 893 status = 0; 894 tail = NULL; 895 896 errcode = pt_iscache_lock(iscache); 897 if (errcode < 0) 898 return errcode; 899 900 iscache->limit = limit; 901 if (limit < iscache->used) 902 status = pt_iscache_lru_prune(iscache, &tail); 903 904 errcode = pt_iscache_unlock(iscache); 905 906 if (errcode < 0 || status < 0) 907 return (status < 0) ? status : errcode; 908 909 return pt_iscache_lru_free(tail); 910 } 911 912 const char *pt_iscache_name(const struct pt_image_section_cache *iscache) 913 { 914 if (!iscache) 915 return NULL; 916 917 return iscache->name; 918 } 919 920 int pt_iscache_add_file(struct pt_image_section_cache *iscache, 921 const char *filename, uint64_t offset, uint64_t size, 922 uint64_t vaddr) 923 { 924 struct pt_section *section; 925 int errcode, match, isid; 926 927 if (!iscache || !filename) 928 return -pte_invalid; 929 930 errcode = pt_iscache_lock(iscache); 931 if (errcode < 0) 932 return errcode; 933 934 match = pt_iscache_find_section_locked(iscache, filename, offset, 935 size, vaddr); 936 if (match < 0) { 937 (void) pt_iscache_unlock(iscache); 938 return match; 939 } 940 941 /* If we found a perfect match, we will share the existing entry. 942 * 943 * If we found a section, we need to grab a reference before we unlock. 944 * 945 * If we didn't find a matching section, we create a new section, which 946 * implicitly gives us a reference to it. 947 */ 948 if (match < iscache->size) { 949 const struct pt_iscache_entry *entry; 950 951 entry = &iscache->entries[match]; 952 if (entry->laddr == vaddr) { 953 errcode = pt_iscache_unlock(iscache); 954 if (errcode < 0) 955 return errcode; 956 957 return isid_from_index((uint16_t) match); 958 } 959 960 section = entry->section; 961 962 errcode = pt_section_get(section); 963 if (errcode < 0) { 964 (void) pt_iscache_unlock(iscache); 965 return errcode; 966 } 967 968 errcode = pt_iscache_unlock(iscache); 969 if (errcode < 0) { 970 (void) pt_section_put(section); 971 return errcode; 972 } 973 } else { 974 errcode = pt_iscache_unlock(iscache); 975 if (errcode < 0) 976 return errcode; 977 978 section = pt_mk_section(filename, offset, size); 979 if (!section) 980 return -pte_invalid; 981 } 982 983 /* We unlocked @iscache and hold a reference to @section. */ 984 isid = pt_iscache_add(iscache, section, vaddr); 985 986 /* We grab a reference when we add the section. Drop the one we 987 * obtained before. 988 */ 989 errcode = pt_section_put(section); 990 if (errcode < 0) 991 return errcode; 992 993 return isid; 994 } 995 996 997 int pt_iscache_read(struct pt_image_section_cache *iscache, uint8_t *buffer, 998 uint64_t size, int isid, uint64_t vaddr) 999 { 1000 struct pt_section *section; 1001 uint64_t laddr; 1002 int errcode, status; 1003 1004 if (!iscache || !buffer || !size) 1005 return -pte_invalid; 1006 1007 errcode = pt_iscache_lookup(iscache, §ion, &laddr, isid); 1008 if (errcode < 0) 1009 return errcode; 1010 1011 if (vaddr < laddr) { 1012 (void) pt_section_put(section); 1013 return -pte_nomap; 1014 } 1015 1016 vaddr -= laddr; 1017 1018 errcode = pt_section_map(section); 1019 if (errcode < 0) { 1020 (void) pt_section_put(section); 1021 return errcode; 1022 } 1023 1024 /* We truncate the read if it gets too big. The user is expected to 1025 * issue further reads for the remaining part. 1026 */ 1027 if (UINT16_MAX < size) 1028 size = UINT16_MAX; 1029 1030 status = pt_section_read(section, buffer, (uint16_t) size, vaddr); 1031 1032 errcode = pt_section_unmap(section); 1033 if (errcode < 0) { 1034 (void) pt_section_put(section); 1035 return errcode; 1036 } 1037 1038 errcode = pt_section_put(section); 1039 if (errcode < 0) 1040 return errcode; 1041 1042 return status; 1043 } 1044 1045 int pt_iscache_notify_map(struct pt_image_section_cache *iscache, 1046 struct pt_section *section) 1047 { 1048 struct pt_iscache_lru_entry *tail; 1049 int errcode, status; 1050 1051 tail = NULL; 1052 1053 errcode = pt_iscache_lock(iscache); 1054 if (errcode < 0) 1055 return errcode; 1056 1057 status = pt_iscache_lru_add(iscache, section); 1058 if (status > 0) 1059 status = pt_iscache_lru_prune(iscache, &tail); 1060 1061 errcode = pt_iscache_unlock(iscache); 1062 1063 if (errcode < 0 || status < 0) 1064 return (status < 0) ? status : errcode; 1065 1066 return pt_iscache_lru_free(tail); 1067 } 1068 1069 int pt_iscache_notify_resize(struct pt_image_section_cache *iscache, 1070 struct pt_section *section, uint64_t memsize) 1071 { 1072 struct pt_iscache_lru_entry *tail; 1073 int errcode, status; 1074 1075 tail = NULL; 1076 1077 errcode = pt_iscache_lock(iscache); 1078 if (errcode < 0) 1079 return errcode; 1080 1081 status = pt_iscache_lru_resize(iscache, section, memsize); 1082 if (status > 0) 1083 status = pt_iscache_lru_prune(iscache, &tail); 1084 1085 errcode = pt_iscache_unlock(iscache); 1086 1087 if (errcode < 0 || status < 0) 1088 return (status < 0) ? status : errcode; 1089 1090 return pt_iscache_lru_free(tail); 1091 } 1092