1 /*
2 * Copyright (c) 2013-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_section.h"
30 #include "pt_block_cache.h"
31 #include "pt_image_section_cache.h"
32
33 #include "intel-pt.h"
34
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38
39
dupstr(const char * str)40 static char *dupstr(const char *str)
41 {
42 char *dup;
43 size_t len;
44
45 if (!str)
46 return NULL;
47
48 len = strlen(str);
49 dup = malloc(len + 1);
50 if (!dup)
51 return NULL;
52
53 return strcpy(dup, str);
54 }
55
pt_mk_section(const char * filename,uint64_t offset,uint64_t size)56 struct pt_section *pt_mk_section(const char *filename, uint64_t offset,
57 uint64_t size)
58 {
59 struct pt_section *section;
60 uint64_t fsize;
61 void *status;
62 int errcode;
63
64 errcode = pt_section_mk_status(&status, &fsize, filename);
65 if (errcode < 0)
66 return NULL;
67
68 /* Fail if the requested @offset lies beyond the end of @file. */
69 if (fsize <= offset)
70 goto out_status;
71
72 /* Truncate @size so the entire range lies within @file. */
73 fsize -= offset;
74 if (fsize < size)
75 size = fsize;
76
77 section = malloc(sizeof(*section));
78 if (!section)
79 goto out_status;
80
81 memset(section, 0, sizeof(*section));
82
83 section->filename = dupstr(filename);
84 section->status = status;
85 section->offset = offset;
86 section->size = size;
87 section->ucount = 1;
88
89 #if defined(FEATURE_THREADS)
90
91 errcode = mtx_init(§ion->lock, mtx_plain);
92 if (errcode != thrd_success) {
93 free(section->filename);
94 free(section);
95 goto out_status;
96 }
97
98 errcode = mtx_init(§ion->alock, mtx_plain);
99 if (errcode != thrd_success) {
100 mtx_destroy(§ion->lock);
101 free(section->filename);
102 free(section);
103 goto out_status;
104 }
105
106 #endif /* defined(FEATURE_THREADS) */
107
108 return section;
109
110 out_status:
111 free(status);
112 return NULL;
113 }
114
pt_section_lock(struct pt_section * section)115 int pt_section_lock(struct pt_section *section)
116 {
117 if (!section)
118 return -pte_internal;
119
120 #if defined(FEATURE_THREADS)
121 {
122 int errcode;
123
124 errcode = mtx_lock(§ion->lock);
125 if (errcode != thrd_success)
126 return -pte_bad_lock;
127 }
128 #endif /* defined(FEATURE_THREADS) */
129
130 return 0;
131 }
132
pt_section_unlock(struct pt_section * section)133 int pt_section_unlock(struct pt_section *section)
134 {
135 if (!section)
136 return -pte_internal;
137
138 #if defined(FEATURE_THREADS)
139 {
140 int errcode;
141
142 errcode = mtx_unlock(§ion->lock);
143 if (errcode != thrd_success)
144 return -pte_bad_lock;
145 }
146 #endif /* defined(FEATURE_THREADS) */
147
148 return 0;
149 }
150
pt_section_free(struct pt_section * section)151 static void pt_section_free(struct pt_section *section)
152 {
153 if (!section)
154 return;
155
156 #if defined(FEATURE_THREADS)
157
158 mtx_destroy(§ion->alock);
159 mtx_destroy(§ion->lock);
160
161 #endif /* defined(FEATURE_THREADS) */
162
163 free(section->filename);
164 free(section->status);
165 free(section);
166 }
167
pt_section_get(struct pt_section * section)168 int pt_section_get(struct pt_section *section)
169 {
170 uint16_t ucount;
171 int errcode;
172
173 if (!section)
174 return -pte_internal;
175
176 errcode = pt_section_lock(section);
177 if (errcode < 0)
178 return errcode;
179
180 ucount = section->ucount + 1;
181 if (!ucount) {
182 (void) pt_section_unlock(section);
183 return -pte_overflow;
184 }
185
186 section->ucount = ucount;
187
188 return pt_section_unlock(section);
189 }
190
pt_section_put(struct pt_section * section)191 int pt_section_put(struct pt_section *section)
192 {
193 uint16_t ucount, mcount;
194 int errcode;
195
196 if (!section)
197 return -pte_internal;
198
199 errcode = pt_section_lock(section);
200 if (errcode < 0)
201 return errcode;
202
203 mcount = section->mcount;
204 ucount = section->ucount;
205 if (ucount > 1) {
206 section->ucount = ucount - 1;
207 return pt_section_unlock(section);
208 }
209
210 errcode = pt_section_unlock(section);
211 if (errcode < 0)
212 return errcode;
213
214 if (!ucount || mcount)
215 return -pte_internal;
216
217 pt_section_free(section);
218 return 0;
219 }
220
pt_section_lock_attach(struct pt_section * section)221 static int pt_section_lock_attach(struct pt_section *section)
222 {
223 if (!section)
224 return -pte_internal;
225
226 #if defined(FEATURE_THREADS)
227 {
228 int errcode;
229
230 errcode = mtx_lock(§ion->alock);
231 if (errcode != thrd_success)
232 return -pte_bad_lock;
233 }
234 #endif /* defined(FEATURE_THREADS) */
235
236 return 0;
237 }
238
pt_section_unlock_attach(struct pt_section * section)239 static int pt_section_unlock_attach(struct pt_section *section)
240 {
241 if (!section)
242 return -pte_internal;
243
244 #if defined(FEATURE_THREADS)
245 {
246 int errcode;
247
248 errcode = mtx_unlock(§ion->alock);
249 if (errcode != thrd_success)
250 return -pte_bad_lock;
251 }
252 #endif /* defined(FEATURE_THREADS) */
253
254 return 0;
255 }
256
pt_section_attach(struct pt_section * section,struct pt_image_section_cache * iscache)257 int pt_section_attach(struct pt_section *section,
258 struct pt_image_section_cache *iscache)
259 {
260 uint16_t acount, ucount;
261 int errcode;
262
263 if (!section || !iscache)
264 return -pte_internal;
265
266 errcode = pt_section_lock_attach(section);
267 if (errcode < 0)
268 return errcode;
269
270 ucount = section->ucount;
271 acount = section->acount;
272 if (!acount) {
273 if (section->iscache || !ucount)
274 goto out_unlock;
275
276 section->iscache = iscache;
277 section->acount = 1;
278
279 return pt_section_unlock_attach(section);
280 }
281
282 acount += 1;
283 if (!acount) {
284 (void) pt_section_unlock_attach(section);
285 return -pte_overflow;
286 }
287
288 if (ucount < acount)
289 goto out_unlock;
290
291 if (section->iscache != iscache)
292 goto out_unlock;
293
294 section->acount = acount;
295
296 return pt_section_unlock_attach(section);
297
298 out_unlock:
299 (void) pt_section_unlock_attach(section);
300 return -pte_internal;
301 }
302
pt_section_detach(struct pt_section * section,struct pt_image_section_cache * iscache)303 int pt_section_detach(struct pt_section *section,
304 struct pt_image_section_cache *iscache)
305 {
306 uint16_t acount, ucount;
307 int errcode;
308
309 if (!section || !iscache)
310 return -pte_internal;
311
312 errcode = pt_section_lock_attach(section);
313 if (errcode < 0)
314 return errcode;
315
316 if (section->iscache != iscache)
317 goto out_unlock;
318
319 acount = section->acount;
320 if (!acount)
321 goto out_unlock;
322
323 acount -= 1;
324 ucount = section->ucount;
325 if (ucount < acount)
326 goto out_unlock;
327
328 section->acount = acount;
329 if (!acount)
330 section->iscache = NULL;
331
332 return pt_section_unlock_attach(section);
333
334 out_unlock:
335 (void) pt_section_unlock_attach(section);
336 return -pte_internal;
337 }
338
pt_section_filename(const struct pt_section * section)339 const char *pt_section_filename(const struct pt_section *section)
340 {
341 if (!section)
342 return NULL;
343
344 return section->filename;
345 }
346
pt_section_size(const struct pt_section * section)347 uint64_t pt_section_size(const struct pt_section *section)
348 {
349 if (!section)
350 return 0ull;
351
352 return section->size;
353 }
354
pt_section_bcache_memsize(const struct pt_section * section,uint64_t * psize)355 static int pt_section_bcache_memsize(const struct pt_section *section,
356 uint64_t *psize)
357 {
358 struct pt_block_cache *bcache;
359
360 if (!section || !psize)
361 return -pte_internal;
362
363 bcache = section->bcache;
364 if (!bcache) {
365 *psize = 0ull;
366 return 0;
367 }
368
369 *psize = sizeof(*bcache) +
370 (bcache->nentries * sizeof(struct pt_bcache_entry));
371
372 return 0;
373 }
374
pt_section_memsize_locked(const struct pt_section * section,uint64_t * psize)375 static int pt_section_memsize_locked(const struct pt_section *section,
376 uint64_t *psize)
377 {
378 uint64_t msize, bcsize;
379 int (*memsize)(const struct pt_section *section, uint64_t *size);
380 int errcode;
381
382 if (!section || !psize)
383 return -pte_internal;
384
385 memsize = section->memsize;
386 if (!memsize) {
387 if (section->mcount)
388 return -pte_internal;
389
390 *psize = 0ull;
391 return 0;
392 }
393
394 errcode = memsize(section, &msize);
395 if (errcode < 0)
396 return errcode;
397
398 errcode = pt_section_bcache_memsize(section, &bcsize);
399 if (errcode < 0)
400 return errcode;
401
402 *psize = msize + bcsize;
403
404 return 0;
405 }
406
pt_section_memsize(struct pt_section * section,uint64_t * size)407 int pt_section_memsize(struct pt_section *section, uint64_t *size)
408 {
409 int errcode, status;
410
411 errcode = pt_section_lock(section);
412 if (errcode < 0)
413 return errcode;
414
415 status = pt_section_memsize_locked(section, size);
416
417 errcode = pt_section_unlock(section);
418 if (errcode < 0)
419 return errcode;
420
421 return status;
422 }
423
pt_section_offset(const struct pt_section * section)424 uint64_t pt_section_offset(const struct pt_section *section)
425 {
426 if (!section)
427 return 0ull;
428
429 return section->offset;
430 }
431
pt_section_alloc_bcache(struct pt_section * section)432 int pt_section_alloc_bcache(struct pt_section *section)
433 {
434 struct pt_image_section_cache *iscache;
435 struct pt_block_cache *bcache;
436 uint64_t ssize, memsize;
437 uint32_t csize;
438 int errcode;
439
440 if (!section)
441 return -pte_internal;
442
443 if (!section->mcount)
444 return -pte_internal;
445
446 ssize = pt_section_size(section);
447 csize = (uint32_t) ssize;
448
449 if (csize != ssize)
450 return -pte_not_supported;
451
452 memsize = 0ull;
453
454 /* We need to take both the attach and the section lock in order to pair
455 * the block cache allocation and the resize notification.
456 *
457 * This allows map notifications in between but they only change the
458 * order of sections in the cache.
459 *
460 * The attach lock needs to be taken first.
461 */
462 errcode = pt_section_lock_attach(section);
463 if (errcode < 0)
464 return errcode;
465
466 errcode = pt_section_lock(section);
467 if (errcode < 0)
468 goto out_alock;
469
470 bcache = pt_section_bcache(section);
471 if (bcache) {
472 errcode = 0;
473 goto out_lock;
474 }
475
476 bcache = pt_bcache_alloc(csize);
477 if (!bcache) {
478 errcode = -pte_nomem;
479 goto out_lock;
480 }
481
482 /* Install the block cache. It will become visible and may be used
483 * immediately.
484 *
485 * If we fail later on, we leave the block cache and report the error to
486 * the allocating decoder thread.
487 */
488 section->bcache = bcache;
489
490 errcode = pt_section_memsize_locked(section, &memsize);
491 if (errcode < 0)
492 goto out_lock;
493
494 errcode = pt_section_unlock(section);
495 if (errcode < 0)
496 goto out_alock;
497
498 if (memsize) {
499 iscache = section->iscache;
500 if (iscache) {
501 errcode = pt_iscache_notify_resize(iscache, section,
502 memsize);
503 if (errcode < 0)
504 goto out_alock;
505 }
506 }
507
508 return pt_section_unlock_attach(section);
509
510
511 out_lock:
512 (void) pt_section_unlock(section);
513
514 out_alock:
515 (void) pt_section_unlock_attach(section);
516 return errcode;
517 }
518
pt_section_on_map_lock(struct pt_section * section)519 int pt_section_on_map_lock(struct pt_section *section)
520 {
521 struct pt_image_section_cache *iscache;
522 int errcode, status;
523
524 if (!section)
525 return -pte_internal;
526
527 errcode = pt_section_lock_attach(section);
528 if (errcode < 0)
529 return errcode;
530
531 iscache = section->iscache;
532 if (!iscache)
533 return pt_section_unlock_attach(section);
534
535 /* There is a potential deadlock when @section was unmapped again and
536 * @iscache tries to map it. This would cause this function to be
537 * re-entered while we're still holding the attach lock.
538 *
539 * This scenario is very unlikely, though, since our caller does not yet
540 * know whether pt_section_map() succeeded.
541 */
542 status = pt_iscache_notify_map(iscache, section);
543
544 errcode = pt_section_unlock_attach(section);
545 if (errcode < 0)
546 return errcode;
547
548 return status;
549 }
550
pt_section_map_share(struct pt_section * section)551 int pt_section_map_share(struct pt_section *section)
552 {
553 uint16_t mcount;
554 int errcode;
555
556 if (!section)
557 return -pte_internal;
558
559 errcode = pt_section_lock(section);
560 if (errcode < 0)
561 return errcode;
562
563 mcount = section->mcount;
564 if (!mcount) {
565 (void) pt_section_unlock(section);
566 return -pte_internal;
567 }
568
569 mcount += 1;
570 if (!mcount) {
571 (void) pt_section_unlock(section);
572 return -pte_overflow;
573 }
574
575 section->mcount = mcount;
576
577 return pt_section_unlock(section);
578 }
579
pt_section_unmap(struct pt_section * section)580 int pt_section_unmap(struct pt_section *section)
581 {
582 uint16_t mcount;
583 int errcode, status;
584
585 if (!section)
586 return -pte_internal;
587
588 errcode = pt_section_lock(section);
589 if (errcode < 0)
590 return errcode;
591
592 mcount = section->mcount;
593
594 errcode = -pte_nomap;
595 if (!mcount)
596 goto out_unlock;
597
598 section->mcount = mcount -= 1;
599 if (mcount)
600 return pt_section_unlock(section);
601
602 errcode = -pte_internal;
603 if (!section->unmap)
604 goto out_unlock;
605
606 status = section->unmap(section);
607
608 pt_bcache_free(section->bcache);
609 section->bcache = NULL;
610
611 errcode = pt_section_unlock(section);
612 if (errcode < 0)
613 return errcode;
614
615 return status;
616
617 out_unlock:
618 (void) pt_section_unlock(section);
619 return errcode;
620 }
621
pt_section_read(const struct pt_section * section,uint8_t * buffer,uint16_t size,uint64_t offset)622 int pt_section_read(const struct pt_section *section, uint8_t *buffer,
623 uint16_t size, uint64_t offset)
624 {
625 uint64_t limit, space;
626
627 if (!section)
628 return -pte_internal;
629
630 if (!section->read)
631 return -pte_nomap;
632
633 limit = section->size;
634 if (limit <= offset)
635 return -pte_nomap;
636
637 /* Truncate if we try to read past the end of the section. */
638 space = limit - offset;
639 if (space < size)
640 size = (uint16_t) space;
641
642 return section->read(section, buffer, size, offset);
643 }
644