1 /* 2 * Copyright (c) 2012-2013 Apple Computer, Inc. All Rights Reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29 #define IOKIT_ENABLE_SHARED_PTR 30 31 #include <IOKit/IOKernelReportStructs.h> 32 #include <IOKit/IOKernelReporters.h> 33 #include "IOReporterDefs.h" 34 35 #include <string.h> 36 #include <sys/param.h> 37 #include <IOKit/IORegistryEntry.h> 38 39 #define super OSObject 40 OSDefineMetaClassAndStructors(IOReporter, OSObject); 41 42 static OSSharedPtr<const OSSymbol> gIOReportNoChannelName; 43 44 // * We might someday want an IOReportManager (vs. these static funcs) 45 46 /**************************************/ 47 /*** STATIC METHODS ***/ 48 /**************************************/ 49 IOReturn 50 IOReporter::configureAllReports(OSSet *reporters, 51 IOReportChannelList *channelList, 52 IOReportConfigureAction action, 53 void *result, 54 void *destination) 55 { 56 IOReturn rval = kIOReturnError; 57 OSSharedPtr<OSCollectionIterator> iterator; 58 59 if (reporters == NULL || channelList == NULL || result == NULL) { 60 rval = kIOReturnBadArgument; 61 goto finish; 62 } 63 64 switch (action) { 65 case kIOReportGetDimensions: 66 case kIOReportEnable: 67 case kIOReportDisable: 68 { 69 OSObject * object; 70 iterator = OSCollectionIterator::withCollection(reporters); 71 72 while ((object = iterator->getNextObject())) { 73 IOReporter *rep = OSDynamicCast(IOReporter, object); 74 75 if (rep) { 76 (void)rep->configureReport(channelList, action, result, destination); 77 } else { 78 rval = kIOReturnUnsupported; // kIOReturnNotFound? 79 goto finish; 80 } 81 } 82 83 break; 84 } 85 86 case kIOReportTraceOnChange: 87 case kIOReportNotifyHubOnChange: 88 default: 89 rval = kIOReturnUnsupported; 90 goto finish; 91 } 92 93 rval = kIOReturnSuccess; 94 95 finish: 96 return rval; 97 } 98 99 // the duplication in these functions almost makes one want Objective-C SEL* ;) 100 IOReturn 101 IOReporter::updateAllReports(OSSet *reporters, 102 IOReportChannelList *channelList, 103 IOReportConfigureAction action, 104 void *result, 105 void *destination) 106 { 107 IOReturn rval = kIOReturnError; 108 OSSharedPtr<OSCollectionIterator> iterator; 109 110 if (reporters == NULL || 111 channelList == NULL || 112 result == NULL || 113 destination == NULL) { 114 rval = kIOReturnBadArgument; 115 goto finish; 116 } 117 118 switch (action) { 119 case kIOReportCopyChannelData: 120 { 121 OSObject * object; 122 iterator = OSCollectionIterator::withCollection(reporters); 123 124 while ((object = iterator->getNextObject())) { 125 IOReporter *rep = OSDynamicCast(IOReporter, object); 126 127 if (rep) { 128 (void)rep->updateReport(channelList, action, result, destination); 129 } else { 130 rval = kIOReturnUnsupported; // kIOReturnNotFound? 131 goto finish; 132 } 133 } 134 135 break; 136 } 137 138 case kIOReportTraceChannelData: 139 default: 140 rval = kIOReturnUnsupported; 141 goto finish; 142 } 143 144 rval = kIOReturnSuccess; 145 146 finish: 147 return rval; 148 } 149 150 151 /**************************************/ 152 /*** COMMON INIT METHODS ***/ 153 /**************************************/ 154 155 bool 156 IOReporter::init(IOService *reportingService, 157 IOReportChannelType channelType, 158 IOReportUnit unit) 159 { 160 bool success = false; 161 162 // ::free() relies on these being initialized 163 _reporterLock = NULL; 164 _configLock = NULL; 165 _elements = NULL; 166 _enableCounts = NULL; 167 _channelNames = nullptr; 168 169 if (channelType.report_format == kIOReportInvalidFormat) { 170 IORLOG("init ERROR: Channel Type ill-defined"); 171 goto finish; 172 } 173 174 _driver_id = reportingService->getRegistryEntryID(); 175 if (_driver_id == 0) { 176 IORLOG("init() ERROR: no registry ID"); 177 goto finish; 178 } 179 180 if (!super::init()) { 181 return false; 182 } 183 184 if (channelType.nelements > INT16_MAX) { 185 return false; 186 } 187 _channelDimension = channelType.nelements; 188 _channelType = channelType; 189 // FIXME: need to look up dynamically 190 if (unit == kIOReportUnitHWTicks) { 191 #if defined(__arm64__) 192 unit = kIOReportUnit24MHzTicks; 193 #elif defined(__i386__) || defined(__x86_64__) 194 // Most, but not all Macs use 1GHz 195 unit = kIOReportUnit1GHzTicks; 196 #else 197 #error kIOReportUnitHWTicks not defined 198 #endif 199 } 200 _unit = unit; 201 202 // Allocate a reporter (data) lock 203 _reporterLock = IOSimpleLockAlloc(); 204 if (!_reporterLock) { 205 goto finish; 206 } 207 _reporterIsLocked = false; 208 209 // Allocate a config lock 210 _configLock = IOLockAlloc(); 211 if (!_configLock) { 212 goto finish; 213 } 214 _reporterConfigIsLocked = false; 215 216 // Allocate channel names array 217 _channelNames = OSArray::withCapacity(1); 218 if (!_channelNames) { 219 goto finish; 220 } 221 222 // success 223 success = true; 224 225 finish: 226 return success; 227 } 228 229 230 /*******************************/ 231 /*** PUBLIC METHODS ***/ 232 /*******************************/ 233 234 void 235 IOReporter::initialize(void) 236 { 237 gIOReportNoChannelName = OSSymbol::withCString("_NO_NAME_4"); 238 } 239 240 // init() [possibly via init*()] must be called before free() 241 // to ensure that _<var> = NULL 242 void 243 IOReporter::free(void) 244 { 245 if (_configLock) { 246 IOLockFree(_configLock); 247 } 248 if (_reporterLock) { 249 IOSimpleLockFree(_reporterLock); 250 } 251 252 if (_elements) { 253 PREFL_MEMOP_PANIC(_nElements, IOReportElement); 254 IOFreeData(_elements, (size_t)_nElements * sizeof(IOReportElement)); 255 } 256 if (_enableCounts) { 257 PREFL_MEMOP_PANIC(_nChannels, int); 258 IOFreeData(_enableCounts, (size_t)_nChannels * sizeof(int)); 259 } 260 261 super::free(); 262 } 263 264 /* 265 #define TESTALLOC() do { \ 266 * void *tbuf; \ 267 * tbuf = IOMalloc(10); \ 268 * IOFree(tbuf, 10); \ 269 * IORLOG("%s:%d - _reporterIsLocked = %d & allocation successful", \ 270 * __PRETTY_FUNCTION__, __LINE__, _reporterIsLocked); \ 271 * } while (0); 272 */ 273 IOReturn 274 IOReporter::addChannel(uint64_t channelID, 275 const char *channelName /* = NULL */) 276 { 277 IOReturn res = kIOReturnError, kerr; 278 OSSharedPtr<const OSSymbol> symChannelName; 279 int oldNChannels, newNChannels = 0, freeNChannels = 0; 280 281 IORLOG("IOReporter::addChannel %llx", channelID); 282 283 // protect instance variables (but not contents) 284 lockReporterConfig(); 285 286 // FIXME: Check if any channel is already present and return error 287 288 // addChannel() always adds one channel 289 oldNChannels = _nChannels; 290 if (oldNChannels < 0 || oldNChannels > INT_MAX - 1) { 291 res = kIOReturnOverrun; 292 goto finish; 293 } 294 newNChannels = oldNChannels + 1; 295 freeNChannels = newNChannels; // until swap success 296 297 // Expand addChannel()-specific data structure 298 if (_channelNames->ensureCapacity((unsigned)newNChannels) < 299 (unsigned)newNChannels) { 300 res = kIOReturnNoMemory; goto finish; 301 } 302 if (channelName) { 303 symChannelName = OSSymbol::withCString(channelName); 304 if (!symChannelName) { 305 res = kIOReturnNoMemory; goto finish; 306 } 307 } else { 308 // grab a reference to our shared global 309 symChannelName = gIOReportNoChannelName; 310 } 311 312 // allocate new buffers into _swap* variables 313 if ((kerr = handleSwapPrepare(newNChannels))) { 314 // on error, channels are *not* swapped 315 res = kerr; goto finish; 316 } 317 318 // exchange main and _swap* buffers with buffer contents protected 319 // IOReporter::handleAddChannelSwap() also increments _nElements, etc 320 lockReporter(); 321 res = handleAddChannelSwap(channelID, symChannelName.get()); 322 unlockReporter(); 323 // On failure, handleAddChannelSwap() leaves *new* buffers in _swap*. 324 // On success, it's the old buffers, so we put the right size in here. 325 if (res == kIOReturnSuccess) { 326 freeNChannels = oldNChannels; 327 } 328 329 finish: 330 // free up not-in-use buffers (tracked by _swap*) 331 handleSwapCleanup(freeNChannels); 332 333 unlockReporterConfig(); 334 335 return res; 336 } 337 338 339 OSSharedPtr<IOReportLegendEntry> 340 IOReporter::createLegend(void) 341 { 342 OSSharedPtr<IOReportLegendEntry> legendEntry; 343 344 lockReporterConfig(); 345 346 legendEntry = handleCreateLegend(); 347 348 unlockReporterConfig(); 349 350 return legendEntry; 351 } 352 353 354 IOReturn 355 IOReporter::configureReport(IOReportChannelList *channelList, 356 IOReportConfigureAction action, 357 void *result, 358 void *destination) 359 { 360 IOReturn res = kIOReturnError; 361 362 lockReporterConfig(); 363 364 res = handleConfigureReport(channelList, action, result, destination); 365 366 unlockReporterConfig(); 367 368 return res; 369 } 370 371 372 IOReturn 373 IOReporter::updateReport(IOReportChannelList *channelList, 374 IOReportConfigureAction action, 375 void *result, 376 void *destination) 377 { 378 IOReturn res = kIOReturnError; 379 380 lockReporter(); 381 382 res = handleUpdateReport(channelList, action, result, destination); 383 384 unlockReporter(); 385 386 return res; 387 } 388 389 390 /*******************************/ 391 /*** PROTECTED METHODS ***/ 392 /*******************************/ 393 394 395 void 396 IOReporter::lockReporter() 397 { 398 _interruptState = IOSimpleLockLockDisableInterrupt(_reporterLock); 399 _reporterIsLocked = true; 400 } 401 402 403 void 404 IOReporter::unlockReporter() 405 { 406 _reporterIsLocked = false; 407 IOSimpleLockUnlockEnableInterrupt(_reporterLock, _interruptState); 408 } 409 410 void 411 IOReporter::lockReporterConfig() 412 { 413 IOLockLock(_configLock); 414 _reporterConfigIsLocked = true; 415 } 416 417 void 418 IOReporter::unlockReporterConfig() 419 { 420 _reporterConfigIsLocked = false; 421 IOLockUnlock(_configLock); 422 } 423 424 425 IOReturn 426 IOReporter::handleSwapPrepare(int newNChannels) 427 { 428 IOReturn res = kIOReturnError; 429 int newNElements; 430 size_t newElementsSize, newECSize; 431 432 // analyzer appeasement 433 newElementsSize = newECSize = 0; 434 435 //IORLOG("IOReporter::handleSwapPrepare"); 436 437 IOREPORTER_CHECK_CONFIG_LOCK(); 438 439 if (newNChannels < _nChannels) { 440 panic("%s doesn't support shrinking", __func__); 441 } 442 if (newNChannels <= 0 || _channelDimension <= 0) { 443 res = kIOReturnUnderrun; 444 goto finish; 445 } 446 if (_swapElements || _swapEnableCounts) { 447 panic("IOReporter::_swap* already in use"); 448 } 449 450 // calculate the number of elements given #ch & the dimension of each 451 if (newNChannels < 0 || newNChannels > INT_MAX / _channelDimension) { 452 res = kIOReturnOverrun; 453 goto finish; 454 } 455 newNElements = newNChannels * _channelDimension; 456 457 // Allocate memory for the new array of report elements 458 PREFL_MEMOP_FAIL(newNElements, IOReportElement); 459 newElementsSize = (size_t)newNElements * sizeof(IOReportElement); 460 _swapElements = (IOReportElement *)IOMallocZeroData(newElementsSize); 461 if (_swapElements == NULL) { 462 res = kIOReturnNoMemory; goto finish; 463 } 464 465 // Allocate memory for the new array of channel watch counts 466 PREFL_MEMOP_FAIL(newNChannels, int); 467 newECSize = (size_t)newNChannels * sizeof(int); 468 _swapEnableCounts = (int *)IOMallocZeroData(newECSize); 469 if (_swapEnableCounts == NULL) { 470 res = kIOReturnNoMemory; goto finish; 471 } 472 473 // success 474 res = kIOReturnSuccess; 475 476 finish: 477 if (res) { 478 if (_swapElements) { 479 IOFreeData(_swapElements, newElementsSize); 480 _swapElements = NULL; 481 } 482 if (_swapEnableCounts) { 483 IOFreeData(_swapEnableCounts, newECSize); 484 _swapEnableCounts = NULL; 485 } 486 } 487 488 return res; 489 } 490 491 492 IOReturn 493 IOReporter::handleAddChannelSwap(uint64_t channel_id, 494 const OSSymbol *symChannelName) 495 { 496 IOReturn res = kIOReturnError; 497 int cnt; 498 int *tmpWatchCounts = NULL; 499 IOReportElement *tmpElements = NULL; 500 bool swapComplete = false; 501 502 //IORLOG("IOReporter::handleSwap"); 503 504 IOREPORTER_CHECK_CONFIG_LOCK(); 505 IOREPORTER_CHECK_LOCK(); 506 507 if (!_swapElements || !_swapEnableCounts) { 508 IORLOG("IOReporter::handleSwap ERROR swap variables uninitialized!"); 509 goto finish; 510 } 511 512 // Copy any existing elements to the new location 513 //IORLOG("handleSwap (base) -> copying %u elements over...", _nChannels); 514 if (_elements) { 515 PREFL_MEMOP_PANIC(_nElements, IOReportElement); 516 memcpy(_swapElements, _elements, 517 (size_t)_nElements * sizeof(IOReportElement)); 518 519 PREFL_MEMOP_PANIC(_nElements, int); 520 memcpy(_swapEnableCounts, _enableCounts, 521 (size_t)_nChannels * sizeof(int)); 522 } 523 524 // Update principal instance variables, keep old buffers for cleanup 525 tmpElements = _elements; 526 _elements = _swapElements; 527 _swapElements = tmpElements; 528 529 tmpWatchCounts = _enableCounts; 530 _enableCounts = _swapEnableCounts; 531 _swapEnableCounts = tmpWatchCounts; 532 533 swapComplete = true; 534 535 // but _nChannels & _nElements is still the old (one smaller) size 536 537 // Initialize new element metadata (existing elements copied above) 538 for (cnt = 0; cnt < _channelDimension; cnt++) { 539 _elements[_nElements + cnt].channel_id = channel_id; 540 _elements[_nElements + cnt].provider_id = _driver_id; 541 _elements[_nElements + cnt].channel_type = _channelType; 542 _elements[_nElements + cnt].channel_type.element_idx = ((int16_t) cnt); 543 544 //IOREPORTER_DEBUG_ELEMENT(_swapNElements + cnt); 545 } 546 547 // Store a channel name at the end 548 if (!_channelNames->setObject((unsigned)_nChannels, symChannelName)) { 549 // Should never happen because we ensured capacity in addChannel() 550 res = kIOReturnNoMemory; 551 goto finish; 552 } 553 554 // And update the metadata: addChannel() always adds just one channel 555 _nChannels += 1; 556 _nElements += _channelDimension; 557 558 // success 559 res = kIOReturnSuccess; 560 561 finish: 562 if (res && swapComplete) { 563 // unswap so new buffers get cleaned up instead of old 564 tmpElements = _elements; 565 _elements = _swapElements; 566 _swapElements = tmpElements; 567 568 tmpWatchCounts = _enableCounts; 569 _enableCounts = _swapEnableCounts; 570 _swapEnableCounts = tmpWatchCounts; 571 } 572 return res; 573 } 574 575 void 576 IOReporter::handleSwapCleanup(int swapNChannels) 577 { 578 int swapNElements; 579 580 if (!_channelDimension || swapNChannels > INT_MAX / _channelDimension) { 581 panic("%s - can't free %d channels of dimension %d", __func__, 582 swapNChannels, _channelDimension); 583 } 584 swapNElements = swapNChannels * _channelDimension; 585 586 IOREPORTER_CHECK_CONFIG_LOCK(); 587 588 // release buffers no longer used after swapping 589 if (_swapElements) { 590 PREFL_MEMOP_PANIC(swapNElements, IOReportElement); 591 IOFreeData(_swapElements, (size_t)swapNElements * sizeof(IOReportElement)); 592 _swapElements = NULL; 593 } 594 if (_swapEnableCounts) { 595 PREFL_MEMOP_PANIC(swapNChannels, int); 596 IOFreeData(_swapEnableCounts, (size_t)swapNChannels * sizeof(int)); 597 _swapEnableCounts = NULL; 598 } 599 } 600 601 602 // The reporter wants to know if its channels have observers. 603 // Eventually we'll add some sort of bool ::anyChannelsInUse() which 604 // clients can use to cull unused reporters after configureReport(disable). 605 IOReturn 606 IOReporter::handleConfigureReport(IOReportChannelList *channelList, 607 IOReportConfigureAction action, 608 void *result, 609 void *destination) 610 { 611 IOReturn res = kIOReturnError; 612 int channel_index = 0; 613 uint32_t chIdx; 614 int *nElements, *nChannels; 615 616 // Check on channelList and result because used below 617 if (!channelList || !result) { 618 goto finish; 619 } 620 621 //IORLOG("IOReporter::configureReport action %u for %u channels", 622 // action, channelList->nchannels); 623 624 // Make sure channel is present, increase matching watch count, 'result' 625 for (chIdx = 0; chIdx < channelList->nchannels; chIdx++) { 626 if (getChannelIndex(channelList->channels[chIdx].channel_id, 627 &channel_index) == kIOReturnSuccess) { 628 // IORLOG("reporter %p recognizes channel %lld", this, channelList->channels[chIdx].channel_id); 629 630 switch (action) { 631 case kIOReportEnable: 632 nChannels = (int*)result; 633 _enabled++; 634 _enableCounts[channel_index]++; 635 (*nChannels)++; 636 break; 637 638 case kIOReportDisable: 639 nChannels = (int*)result; 640 _enabled--; 641 _enableCounts[channel_index]--; 642 (*nChannels)++; 643 break; 644 645 case kIOReportGetDimensions: 646 nElements = (int *)result; 647 *nElements += _channelDimension; 648 break; 649 650 default: 651 IORLOG("ERROR configureReport unknown action!"); 652 break; 653 } 654 } 655 } 656 657 // success 658 res = kIOReturnSuccess; 659 660 finish: 661 return res; 662 } 663 664 static const uint32_t UNLOCK_PERIOD = 1 << 5; 665 static_assert(powerof2(UNLOCK_PERIOD), 666 "unlock period not a power of 2: period check must be efficient"); 667 static const uint32_t UNLOCK_PERIOD_MASK = UNLOCK_PERIOD - 1; 668 669 IOReturn 670 IOReporter::handleUpdateReport(IOReportChannelList *channelList, 671 IOReportConfigureAction action, 672 void *result, 673 void *destination) 674 { 675 IOReturn res = kIOReturnError; 676 int *nElements = (int *)result; 677 int channel_index = 0; 678 uint32_t chIdx; 679 IOBufferMemoryDescriptor *dest; 680 681 if (!channelList || !result || !destination) { 682 goto finish; 683 } 684 685 dest = OSDynamicCast(IOBufferMemoryDescriptor, (OSObject *)destination); 686 if (dest == NULL) { 687 // Invalid destination 688 res = kIOReturnBadArgument; 689 goto finish; 690 } 691 692 if (!_enabled) { 693 goto finish; 694 } 695 696 for (chIdx = 0; chIdx < channelList->nchannels; chIdx++) { 697 // Drop the lock periodically to allow reporters to emit data, as they 698 // might be running in interrupt context. 699 // 700 // This is safe because the spinlock is really only protecting the 701 // element array, any other changes to the reporter will need to 702 // take the configuration lock. Keeping the lock between channels 703 // isn't protecting us from anything, because there's no API to 704 // update multiple channels atomically anyway. 705 if ((chIdx & UNLOCK_PERIOD_MASK) == UNLOCK_PERIOD_MASK) { 706 unlockReporter(); 707 delay(1); 708 lockReporter(); 709 } 710 if (getChannelIndex(channelList->channels[chIdx].channel_id, 711 &channel_index) == kIOReturnSuccess) { 712 //IORLOG("%s - found channel_id %llx @ index %d", __func__, 713 // channelList->channels[chIdx].channel_id, 714 // channel_index); 715 716 switch (action) { 717 case kIOReportCopyChannelData: 718 res = updateChannelValues(channel_index); 719 if (res) { 720 IORLOG("ERROR: updateChannelValues() failed: %x", res); 721 goto finish; 722 } 723 724 res = updateReportChannel(channel_index, nElements, dest); 725 if (res) { 726 IORLOG("ERROR: updateReportChannel() failed: %x", res); 727 goto finish; 728 } 729 break; 730 731 default: 732 IORLOG("ERROR updateReport unknown action!"); 733 res = kIOReturnError; 734 goto finish; 735 } 736 } 737 } 738 739 // success 740 res = kIOReturnSuccess; 741 742 finish: 743 return res; 744 } 745 746 747 OSSharedPtr<IOReportLegendEntry> 748 IOReporter::handleCreateLegend(void) 749 { 750 OSSharedPtr<IOReportLegendEntry> legendEntry = nullptr; 751 OSSharedPtr<OSArray> channelIDs; 752 753 channelIDs = copyChannelIDs(); 754 755 if (channelIDs) { 756 legendEntry = IOReporter::legendWith(channelIDs.get(), _channelNames.get(), _channelType, _unit); 757 } 758 759 return legendEntry; 760 } 761 762 763 IOReturn 764 IOReporter::setElementValues(int element_index, 765 IOReportElementValues *values, 766 uint64_t record_time /* = 0 */) 767 { 768 IOReturn res = kIOReturnError; 769 770 IOREPORTER_CHECK_LOCK(); 771 772 if (record_time == 0) { 773 record_time = mach_absolute_time(); 774 } 775 776 if (element_index >= _nElements || values == NULL) { 777 res = kIOReturnBadArgument; 778 goto finish; 779 } 780 781 memcpy(&_elements[element_index].values, values, sizeof(IOReportElementValues)); 782 783 _elements[element_index].timestamp = record_time; 784 785 //IOREPORTER_DEBUG_ELEMENT(index); 786 787 res = kIOReturnSuccess; 788 789 finish: 790 return res; 791 } 792 793 794 const IOReportElementValues* 795 IOReporter::getElementValues(int element_index) 796 { 797 IOReportElementValues *elementValues = NULL; 798 799 IOREPORTER_CHECK_LOCK(); 800 801 if (element_index < 0 || element_index >= _nElements) { 802 IORLOG("ERROR getElementValues out of bounds!"); 803 goto finish; 804 } 805 806 elementValues = &_elements[element_index].values; 807 808 finish: 809 return elementValues; 810 } 811 812 813 IOReturn 814 IOReporter::updateChannelValues(int channel_index) 815 { 816 return kIOReturnSuccess; 817 } 818 819 820 IOReturn 821 IOReporter::updateReportChannel(int channel_index, 822 int *nElements, 823 IOBufferMemoryDescriptor *destination) 824 { 825 IOReturn res = kIOReturnError; 826 int start_element_idx, chElems; 827 size_t size2cpy; 828 829 res = kIOReturnBadArgument; 830 if (!nElements || !destination) { 831 goto finish; 832 } 833 if (channel_index > _nChannels) { 834 goto finish; 835 } 836 837 IOREPORTER_CHECK_LOCK(); 838 839 res = kIOReturnOverrun; 840 841 start_element_idx = channel_index * _channelDimension; 842 if (start_element_idx >= _nElements) { 843 goto finish; 844 } 845 846 chElems = _elements[start_element_idx].channel_type.nelements; 847 848 // make sure we don't go beyond the end of _elements[_nElements-1] 849 if (start_element_idx + chElems > _nElements) { 850 goto finish; 851 } 852 853 PREFL_MEMOP_FAIL(chElems, IOReportElement); 854 size2cpy = (size_t)chElems * sizeof(IOReportElement); 855 856 // make sure there's space in the destination 857 if (size2cpy > (destination->getCapacity() - destination->getLength())) { 858 IORLOG("CRITICAL ERROR: Report Buffer Overflow (buffer cap %luB, length %luB, size2cpy %luB", 859 (unsigned long)destination->getCapacity(), 860 (unsigned long)destination->getLength(), 861 (unsigned long)size2cpy); 862 goto finish; 863 } 864 865 destination->appendBytes(&_elements[start_element_idx], size2cpy); 866 *nElements += chElems; 867 868 res = kIOReturnSuccess; 869 870 finish: 871 return res; 872 } 873 874 875 IOReturn 876 IOReporter::copyElementValues(int element_index, 877 IOReportElementValues *elementValues) 878 { 879 IOReturn res = kIOReturnError; 880 881 if (!elementValues) { 882 goto finish; 883 } 884 885 IOREPORTER_CHECK_LOCK(); 886 887 if (element_index >= _nElements) { 888 IORLOG("ERROR getElementValues out of bounds!"); 889 res = kIOReturnBadArgument; 890 goto finish; 891 } 892 893 memcpy(elementValues, &_elements[element_index].values, sizeof(IOReportElementValues)); 894 res = kIOReturnSuccess; 895 896 finish: 897 return res; 898 } 899 900 901 IOReturn 902 IOReporter::getFirstElementIndex(uint64_t channel_id, 903 int *index) 904 { 905 IOReturn res = kIOReturnError; 906 int channel_index = 0, element_index = 0; 907 908 if (!index) { 909 goto finish; 910 } 911 912 res = getChannelIndices(channel_id, &channel_index, &element_index); 913 914 if (res == kIOReturnSuccess) { 915 *index = element_index; 916 } 917 918 finish: 919 return res; 920 } 921 922 923 IOReturn 924 IOReporter::getChannelIndex(uint64_t channel_id, 925 int *index) 926 { 927 IOReturn res = kIOReturnError; 928 int channel_index = 0, element_index = 0; 929 930 if (!index) { 931 goto finish; 932 } 933 934 res = getChannelIndices(channel_id, &channel_index, &element_index); 935 936 if (res == kIOReturnSuccess) { 937 *index = channel_index; 938 } 939 940 finish: 941 return res; 942 } 943 944 945 IOReturn 946 IOReporter::getChannelIndices(uint64_t channel_id, 947 int *channel_index, 948 int *element_index) 949 { 950 IOReturn res = kIOReturnNotFound; 951 int chIdx, elemIdx; 952 953 if (!channel_index || !element_index) { 954 goto finish; 955 } 956 957 for (chIdx = 0; chIdx < _nChannels; chIdx++) { 958 elemIdx = chIdx * _channelDimension; 959 if (elemIdx >= _nElements) { 960 IORLOG("ERROR getChannelIndices out of bounds!"); 961 res = kIOReturnOverrun; 962 goto finish; 963 } 964 965 if (channel_id == _elements[elemIdx].channel_id) { 966 // The channel index does not care about the depth of elements... 967 *channel_index = chIdx; 968 *element_index = elemIdx; 969 970 res = kIOReturnSuccess; 971 goto finish; 972 } 973 } 974 975 finish: 976 return res; 977 } 978 979 /********************************/ 980 /*** PRIVATE METHODS ***/ 981 /********************************/ 982 983 984 // copyChannelIDs relies on the caller to take lock 985 OSSharedPtr<OSArray> 986 IOReporter::copyChannelIDs() 987 { 988 int cnt, cnt2; 989 OSSharedPtr<OSArray> channelIDs; 990 OSSharedPtr<OSNumber> tmpNum; 991 992 channelIDs = OSArray::withCapacity((unsigned)_nChannels); 993 994 if (!channelIDs) { 995 return nullptr; 996 } 997 998 for (cnt = 0; cnt < _nChannels; cnt++) { 999 cnt2 = cnt * _channelDimension; 1000 1001 // Encapsulate the Channel ID in OSNumber 1002 tmpNum = OSNumber::withNumber(_elements[cnt2].channel_id, 64); 1003 if (!tmpNum) { 1004 IORLOG("ERROR: Could not create array of channelIDs"); 1005 return nullptr; 1006 } 1007 1008 channelIDs->setObject((unsigned)cnt, tmpNum.get()); 1009 tmpNum.reset(); 1010 } 1011 1012 return channelIDs; 1013 } 1014 1015 1016 // DO NOT REMOVE THIS METHOD WHICH IS THE MAIN LEGEND CREATION FUNCTION 1017 /*static */ OSPtr<IOReportLegendEntry> 1018 IOReporter::legendWith(OSArray *channelIDs, 1019 OSArray *channelNames, 1020 IOReportChannelType channelType, 1021 IOReportUnit unit) 1022 { 1023 unsigned int cnt, chCnt; 1024 uint64_t type64; 1025 OSSharedPtr<OSNumber> tmpNum; 1026 const OSSymbol *tmpSymbol; 1027 OSSharedPtr<OSArray> channelLegendArray; 1028 OSSharedPtr<OSArray> tmpChannelArray; 1029 OSSharedPtr<OSDictionary> channelInfoDict; 1030 OSSharedPtr<IOReportLegendEntry> legendEntry = nullptr; 1031 1032 // No need to check validity of channelNames because param is optional 1033 if (!channelIDs) { 1034 goto finish; 1035 } 1036 chCnt = channelIDs->getCount(); 1037 1038 channelLegendArray = OSArray::withCapacity(chCnt); 1039 1040 for (cnt = 0; cnt < chCnt; cnt++) { 1041 tmpChannelArray = OSArray::withCapacity(3); 1042 1043 // Encapsulate the Channel ID in OSNumber 1044 tmpChannelArray->setObject(kIOReportChannelIDIdx, channelIDs->getObject(cnt)); 1045 1046 // Encapsulate the Channel Type in OSNumber 1047 memcpy(&type64, &channelType, sizeof(type64)); 1048 tmpNum = OSNumber::withNumber(type64, 64); 1049 if (!tmpNum) { 1050 goto finish; 1051 } 1052 tmpChannelArray->setObject(kIOReportChannelTypeIdx, tmpNum.get()); 1053 tmpNum.reset(); 1054 1055 // Encapsulate the Channel Name in OSSymbol 1056 // Use channelNames if provided 1057 if (channelNames != NULL) { 1058 tmpSymbol = OSDynamicCast(OSSymbol, channelNames->getObject(cnt)); 1059 if (tmpSymbol && tmpSymbol != gIOReportNoChannelName) { 1060 tmpChannelArray->setObject(kIOReportChannelNameIdx, tmpSymbol); 1061 } // Else, skip and leave name field empty 1062 } 1063 1064 channelLegendArray->setObject(cnt, tmpChannelArray.get()); 1065 tmpChannelArray.reset(); 1066 } 1067 1068 // Stuff the legend entry only if we have channels... 1069 if (channelLegendArray->getCount() != 0) { 1070 channelInfoDict = OSDictionary::withCapacity(1); 1071 1072 if (!channelInfoDict) { 1073 goto finish; 1074 } 1075 1076 tmpNum = OSNumber::withNumber(unit, 64); 1077 if (tmpNum) { 1078 channelInfoDict->setObject(kIOReportLegendUnitKey, tmpNum.get()); 1079 } 1080 1081 legendEntry = OSDictionary::withCapacity(1); 1082 1083 if (legendEntry) { 1084 legendEntry->setObject(kIOReportLegendChannelsKey, channelLegendArray.get()); 1085 legendEntry->setObject(kIOReportLegendInfoKey, channelInfoDict.get()); 1086 } 1087 } 1088 1089 finish: 1090 return legendEntry; 1091 } 1092 1093 /*static */ OSPtr<IOReportLegendEntry> 1094 IOReporter::legendWith(const uint64_t *channelIDs, 1095 const char **channelNames, 1096 int channelCount, 1097 IOReportChannelType channelType, 1098 IOReportUnit unit) 1099 { 1100 OSSharedPtr<OSArray> channelIDsArray; 1101 OSSharedPtr<OSArray> channelNamesArray; 1102 OSSharedPtr<OSNumber> channelID; 1103 OSSharedPtr<const OSSymbol> channelName; 1104 int cnt; 1105 1106 channelIDsArray = OSArray::withCapacity(channelCount); 1107 channelNamesArray = OSArray::withCapacity(channelCount); 1108 if (!channelIDsArray || !channelNamesArray) { 1109 return nullptr; 1110 } 1111 1112 for (cnt = 0; cnt < channelCount; cnt++) { 1113 channelID = OSNumber::withNumber(*channelIDs++, 64); 1114 const char *name = *channelNames++; 1115 if (name) { 1116 channelName = OSSymbol::withCString(name); 1117 } else { 1118 // grab a reference to our shared global 1119 channelName = gIOReportNoChannelName; 1120 } 1121 if (!channelID || !channelName) { 1122 return nullptr; 1123 } 1124 if (!channelIDsArray->setObject(cnt, channelID) || 1125 !channelNamesArray->setObject(cnt, channelName)) { 1126 return nullptr; 1127 } 1128 } 1129 1130 return legendWith(channelIDsArray.get(), channelNamesArray.get(), channelType, unit); 1131 } 1132