| 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(inColl: 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(inColl: 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(capacity: 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(cString: "_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(lock: _configLock); | 
|---|
| 247 | } | 
|---|
| 248 | if (_reporterLock) { | 
|---|
| 249 | IOSimpleLockFree(lock: _reporterLock); | 
|---|
| 250 | } | 
|---|
| 251 |  | 
|---|
| 252 | if (_elements) { | 
|---|
| 253 | PREFL_MEMOP_PANIC(_nElements, IOReportElement); | 
|---|
| 254 | IOFreeData(address: _elements, size: (size_t)_nElements * sizeof(IOReportElement)); | 
|---|
| 255 | } | 
|---|
| 256 | if (_enableCounts) { | 
|---|
| 257 | PREFL_MEMOP_PANIC(_nChannels, int); | 
|---|
| 258 | IOFreeData(address: _enableCounts, size: (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(newCapacity: (unsigned)newNChannels) < | 
|---|
| 299 | (unsigned)newNChannels) { | 
|---|
| 300 | res = kIOReturnNoMemory; goto finish; | 
|---|
| 301 | } | 
|---|
| 302 | if (channelName) { | 
|---|
| 303 | symChannelName = OSSymbol::withCString(cString: 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(channel_id: channelID, symChannelName: 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(swapNChannels: 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(lock: _reporterLock); | 
|---|
| 399 | _reporterIsLocked = true; | 
|---|
| 400 | } | 
|---|
| 401 |  | 
|---|
| 402 |  | 
|---|
| 403 | void | 
|---|
| 404 | IOReporter::unlockReporter() | 
|---|
| 405 | { | 
|---|
| 406 | _reporterIsLocked = false; | 
|---|
| 407 | IOSimpleLockUnlockEnableInterrupt(lock: _reporterLock, state: _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(address: _swapElements, size: newElementsSize); | 
|---|
| 480 | _swapElements = NULL; | 
|---|
| 481 | } | 
|---|
| 482 | if (_swapEnableCounts) { | 
|---|
| 483 | IOFreeData(address: _swapEnableCounts, size: 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(dst: _swapElements, src: _elements, | 
|---|
| 517 | n: (size_t)_nElements * sizeof(IOReportElement)); | 
|---|
| 518 |  | 
|---|
| 519 | PREFL_MEMOP_PANIC(_nElements, int); | 
|---|
| 520 | memcpy(dst: _swapEnableCounts, src: _enableCounts, | 
|---|
| 521 | n: (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(index: (unsigned)_nChannels, anObject: 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(address: _swapElements, size: (size_t)swapNElements * sizeof(IOReportElement)); | 
|---|
| 592 | _swapElements = NULL; | 
|---|
| 593 | } | 
|---|
| 594 | if (_swapEnableCounts) { | 
|---|
| 595 | PREFL_MEMOP_PANIC(swapNChannels, int); | 
|---|
| 596 | IOFreeData(address: _swapEnableCounts, size: (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(channel_id: channelList->channels[chIdx].channel_id, | 
|---|
| 627 | channel_index: &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(usec: 1); | 
|---|
| 708 | lockReporter(); | 
|---|
| 709 | } | 
|---|
| 710 | if (getChannelIndex(channel_id: channelList->channels[chIdx].channel_id, | 
|---|
| 711 | channel_index: &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, destination: 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: channelIDs.get(), channelNames: _channelNames.get(), channelType: _channelType, unit: _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(dst: &_elements[element_index].values, src: values, n: 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(bytes: &_elements[start_element_idx], withLength: 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(dst: elementValues, src: &_elements[element_index].values, n: 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: &channel_index, element_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: &channel_index, element_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(capacity: (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(value: _elements[cnt2].channel_id, numberOfBits: 64); | 
|---|
| 1003 | if (!tmpNum) { | 
|---|
| 1004 | IORLOG( "ERROR: Could not create array of channelIDs"); | 
|---|
| 1005 | return nullptr; | 
|---|
| 1006 | } | 
|---|
| 1007 |  | 
|---|
| 1008 | channelIDs->setObject(index: (unsigned)cnt, anObject: 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(capacity: chCnt); | 
|---|
| 1039 |  | 
|---|
| 1040 | for (cnt = 0; cnt < chCnt; cnt++) { | 
|---|
| 1041 | tmpChannelArray = OSArray::withCapacity(capacity: 3); | 
|---|
| 1042 |  | 
|---|
| 1043 | // Encapsulate the Channel ID in OSNumber | 
|---|
| 1044 | tmpChannelArray->setObject(kIOReportChannelIDIdx, anObject: channelIDs->getObject(index: cnt)); | 
|---|
| 1045 |  | 
|---|
| 1046 | // Encapsulate the Channel Type in OSNumber | 
|---|
| 1047 | memcpy(dst: &type64, src: &channelType, n: sizeof(type64)); | 
|---|
| 1048 | tmpNum = OSNumber::withNumber(value: type64, numberOfBits: 64); | 
|---|
| 1049 | if (!tmpNum) { | 
|---|
| 1050 | goto finish; | 
|---|
| 1051 | } | 
|---|
| 1052 | tmpChannelArray->setObject(kIOReportChannelTypeIdx, anObject: 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, anObject: tmpSymbol); | 
|---|
| 1061 | } // Else, skip and leave name field empty | 
|---|
| 1062 | } | 
|---|
| 1063 |  | 
|---|
| 1064 | channelLegendArray->setObject(index: cnt, anObject: 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(capacity: 1); | 
|---|
| 1071 |  | 
|---|
| 1072 | if (!channelInfoDict) { | 
|---|
| 1073 | goto finish; | 
|---|
| 1074 | } | 
|---|
| 1075 |  | 
|---|
| 1076 | tmpNum = OSNumber::withNumber(value: unit, numberOfBits: 64); | 
|---|
| 1077 | if (tmpNum) { | 
|---|
| 1078 | channelInfoDict->setObject(kIOReportLegendUnitKey, anObject: tmpNum.get()); | 
|---|
| 1079 | } | 
|---|
| 1080 |  | 
|---|
| 1081 | legendEntry = OSDictionary::withCapacity(capacity: 1); | 
|---|
| 1082 |  | 
|---|
| 1083 | if (legendEntry) { | 
|---|
| 1084 | legendEntry->setObject(kIOReportLegendChannelsKey, anObject: channelLegendArray.get()); | 
|---|
| 1085 | legendEntry->setObject(kIOReportLegendInfoKey, anObject: 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(capacity: channelCount); | 
|---|
| 1107 | channelNamesArray = OSArray::withCapacity(capacity: channelCount); | 
|---|
| 1108 | if (!channelIDsArray || !channelNamesArray) { | 
|---|
| 1109 | return nullptr; | 
|---|
| 1110 | } | 
|---|
| 1111 |  | 
|---|
| 1112 | for (cnt = 0; cnt < channelCount; cnt++) { | 
|---|
| 1113 | channelID = OSNumber::withNumber(value: *channelIDs++, numberOfBits: 64); | 
|---|
| 1114 | const char *name = *channelNames++; | 
|---|
| 1115 | if (name) { | 
|---|
| 1116 | channelName = OSSymbol::withCString(cString: 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(index: cnt, anObject: channelID) || | 
|---|
| 1125 | !channelNamesArray->setObject(index: cnt, anObject: channelName)) { | 
|---|
| 1126 | return nullptr; | 
|---|
| 1127 | } | 
|---|
| 1128 | } | 
|---|
| 1129 |  | 
|---|
| 1130 | return legendWith(channelIDs: channelIDsArray.get(), channelNames: channelNamesArray.get(), channelType, unit); | 
|---|
| 1131 | } | 
|---|
| 1132 |  | 
|---|