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