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