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