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