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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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