]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOReporter.cpp
xnu-7195.101.1.tar.gz
[apple/xnu.git] / iokit / Kernel / IOReporter.cpp
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
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
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
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
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
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 IOFree(_elements, (size_t)_nElements * sizeof(IOReportElement));
254 }
255 if (_enableCounts) {
256 PREFL_MEMOP_PANIC(_nChannels, int);
257 IOFree(_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
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>
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
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
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
395 IOReporter::lockReporter()
396 {
397 _interruptState = IOSimpleLockLockDisableInterrupt(_reporterLock);
398 _reporterIsLocked = true;
399 }
400
401
402 void
403 IOReporter::unlockReporter()
404 {
405 _reporterIsLocked = false;
406 IOSimpleLockUnlockEnableInterrupt(_reporterLock, _interruptState);
407 }
408
409 void
410 IOReporter::lockReporterConfig()
411 {
412 IOLockLock(_configLock);
413 _reporterConfigIsLocked = true;
414 }
415
416 void
417 IOReporter::unlockReporterConfig()
418 {
419 _reporterConfigIsLocked = false;
420 IOLockUnlock(_configLock);
421 }
422
423
424 IOReturn
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 *)IOMalloc(newElementsSize);
460 if (_swapElements == NULL) {
461 res = kIOReturnNoMemory; goto finish;
462 }
463 memset(_swapElements, 0, newElementsSize);
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 *)IOMalloc(newECSize);
469 if (_swapEnableCounts == NULL) {
470 res = kIOReturnNoMemory; goto finish;
471 }
472 memset(_swapEnableCounts, 0, newECSize);
473
474 // success
475 res = kIOReturnSuccess;
476
477 finish:
478 if (res) {
479 if (_swapElements) {
480 IOFree(_swapElements, newElementsSize);
481 _swapElements = NULL;
482 }
483 if (_swapEnableCounts) {
484 IOFree(_swapEnableCounts, newECSize);
485 _swapEnableCounts = NULL;
486 }
487 }
488
489 return res;
490 }
491
492
493 IOReturn
494 IOReporter::handleAddChannelSwap(uint64_t channel_id,
495 const OSSymbol *symChannelName)
496 {
497 IOReturn res = kIOReturnError;
498 int cnt;
499 int *tmpWatchCounts = NULL;
500 IOReportElement *tmpElements = NULL;
501 bool swapComplete = false;
502
503 //IORLOG("IOReporter::handleSwap");
504
505 IOREPORTER_CHECK_CONFIG_LOCK();
506 IOREPORTER_CHECK_LOCK();
507
508 if (!_swapElements || !_swapEnableCounts) {
509 IORLOG("IOReporter::handleSwap ERROR swap variables uninitialized!");
510 goto finish;
511 }
512
513 // Copy any existing elements to the new location
514 //IORLOG("handleSwap (base) -> copying %u elements over...", _nChannels);
515 if (_elements) {
516 PREFL_MEMOP_PANIC(_nElements, IOReportElement);
517 memcpy(_swapElements, _elements,
518 (size_t)_nElements * sizeof(IOReportElement));
519
520 PREFL_MEMOP_PANIC(_nElements, int);
521 memcpy(_swapEnableCounts, _enableCounts,
522 (size_t)_nChannels * sizeof(int));
523 }
524
525 // Update principal instance variables, keep old buffers for cleanup
526 tmpElements = _elements;
527 _elements = _swapElements;
528 _swapElements = tmpElements;
529
530 tmpWatchCounts = _enableCounts;
531 _enableCounts = _swapEnableCounts;
532 _swapEnableCounts = tmpWatchCounts;
533
534 swapComplete = true;
535
536 // but _nChannels & _nElements is still the old (one smaller) size
537
538 // Initialize new element metadata (existing elements copied above)
539 for (cnt = 0; cnt < _channelDimension; cnt++) {
540 _elements[_nElements + cnt].channel_id = channel_id;
541 _elements[_nElements + cnt].provider_id = _driver_id;
542 _elements[_nElements + cnt].channel_type = _channelType;
543 _elements[_nElements + cnt].channel_type.element_idx = ((int16_t) cnt);
544
545 //IOREPORTER_DEBUG_ELEMENT(_swapNElements + cnt);
546 }
547
548 // Store a channel name at the end
549 if (!_channelNames->setObject((unsigned)_nChannels, symChannelName)) {
550 // Should never happen because we ensured capacity in addChannel()
551 res = kIOReturnNoMemory;
552 goto finish;
553 }
554
555 // And update the metadata: addChannel() always adds just one channel
556 _nChannels += 1;
557 _nElements += _channelDimension;
558
559 // success
560 res = kIOReturnSuccess;
561
562 finish:
563 if (res && swapComplete) {
564 // unswap so new buffers get cleaned up instead of old
565 tmpElements = _elements;
566 _elements = _swapElements;
567 _swapElements = tmpElements;
568
569 tmpWatchCounts = _enableCounts;
570 _enableCounts = _swapEnableCounts;
571 _swapEnableCounts = tmpWatchCounts;
572 }
573 return res;
574 }
575
576 void
577 IOReporter::handleSwapCleanup(int swapNChannels)
578 {
579 int swapNElements;
580
581 if (!_channelDimension || swapNChannels > INT_MAX / _channelDimension) {
582 panic("%s - can't free %d channels of dimension %d", __func__,
583 swapNChannels, _channelDimension);
584 }
585 swapNElements = swapNChannels * _channelDimension;
586
587 IOREPORTER_CHECK_CONFIG_LOCK();
588
589 // release buffers no longer used after swapping
590 if (_swapElements) {
591 PREFL_MEMOP_PANIC(swapNElements, IOReportElement);
592 IOFree(_swapElements, (size_t)swapNElements * sizeof(IOReportElement));
593 _swapElements = NULL;
594 }
595 if (_swapEnableCounts) {
596 PREFL_MEMOP_PANIC(swapNChannels, int);
597 IOFree(_swapEnableCounts, (size_t)swapNChannels * sizeof(int));
598 _swapEnableCounts = NULL;
599 }
600 }
601
602
603 // The reporter wants to know if its channels have observers.
604 // Eventually we'll add some sort of bool ::anyChannelsInUse() which
605 // clients can use to cull unused reporters after configureReport(disable).
606 IOReturn
607 IOReporter::handleConfigureReport(IOReportChannelList *channelList,
608 IOReportConfigureAction action,
609 void *result,
610 void *destination)
611 {
612 IOReturn res = kIOReturnError;
613 int channel_index = 0;
614 uint32_t chIdx;
615 int *nElements, *nChannels;
616
617 // Check on channelList and result because used below
618 if (!channelList || !result) {
619 goto finish;
620 }
621
622 //IORLOG("IOReporter::configureReport action %u for %u channels",
623 // action, channelList->nchannels);
624
625 // Make sure channel is present, increase matching watch count, 'result'
626 for (chIdx = 0; chIdx < channelList->nchannels; chIdx++) {
627 if (getChannelIndex(channelList->channels[chIdx].channel_id,
628 &channel_index) == kIOReturnSuccess) {
629 // IORLOG("reporter %p recognizes channel %lld", this, channelList->channels[chIdx].channel_id);
630
631 switch (action) {
632 case kIOReportEnable:
633 nChannels = (int*)result;
634 _enabled++;
635 _enableCounts[channel_index]++;
636 (*nChannels)++;
637 break;
638
639 case kIOReportDisable:
640 nChannels = (int*)result;
641 _enabled--;
642 _enableCounts[channel_index]--;
643 (*nChannels)++;
644 break;
645
646 case kIOReportGetDimensions:
647 nElements = (int *)result;
648 *nElements += _channelDimension;
649 break;
650
651 default:
652 IORLOG("ERROR configureReport unknown action!");
653 break;
654 }
655 }
656 }
657
658 // success
659 res = kIOReturnSuccess;
660
661 finish:
662 return res;
663 }
664
665
666 IOReturn
667 IOReporter::handleUpdateReport(IOReportChannelList *channelList,
668 IOReportConfigureAction action,
669 void *result,
670 void *destination)
671 {
672 IOReturn res = kIOReturnError;
673 int *nElements = (int *)result;
674 int channel_index = 0;
675 uint32_t chIdx;
676 IOBufferMemoryDescriptor *dest;
677
678 if (!channelList || !result || !destination) {
679 goto finish;
680 }
681
682 dest = OSDynamicCast(IOBufferMemoryDescriptor, (OSObject *)destination);
683 if (dest == NULL) {
684 // Invalid destination
685 res = kIOReturnBadArgument;
686 goto finish;
687 }
688
689 if (!_enabled) {
690 goto finish;
691 }
692
693 for (chIdx = 0; chIdx < channelList->nchannels; chIdx++) {
694 if (getChannelIndex(channelList->channels[chIdx].channel_id,
695 &channel_index) == kIOReturnSuccess) {
696 //IORLOG("%s - found channel_id %llx @ index %d", __func__,
697 // channelList->channels[chIdx].channel_id,
698 // channel_index);
699
700 switch (action) {
701 case kIOReportCopyChannelData:
702 res = updateChannelValues(channel_index);
703 if (res) {
704 IORLOG("ERROR: updateChannelValues() failed: %x", res);
705 goto finish;
706 }
707
708 res = updateReportChannel(channel_index, nElements, dest);
709 if (res) {
710 IORLOG("ERROR: updateReportChannel() failed: %x", res);
711 goto finish;
712 }
713 break;
714
715 default:
716 IORLOG("ERROR updateReport unknown action!");
717 res = kIOReturnError;
718 goto finish;
719 }
720 }
721 }
722
723 // success
724 res = kIOReturnSuccess;
725
726 finish:
727 return res;
728 }
729
730
731 OSSharedPtr<IOReportLegendEntry>
732 IOReporter::handleCreateLegend(void)
733 {
734 OSSharedPtr<IOReportLegendEntry> legendEntry = nullptr;
735 OSSharedPtr<OSArray> channelIDs;
736
737 channelIDs = copyChannelIDs();
738
739 if (channelIDs) {
740 legendEntry = IOReporter::legendWith(channelIDs.get(), _channelNames.get(), _channelType, _unit);
741 }
742
743 return legendEntry;
744 }
745
746
747 IOReturn
748 IOReporter::setElementValues(int element_index,
749 IOReportElementValues *values,
750 uint64_t record_time /* = 0 */)
751 {
752 IOReturn res = kIOReturnError;
753
754 IOREPORTER_CHECK_LOCK();
755
756 if (record_time == 0) {
757 record_time = mach_absolute_time();
758 }
759
760 if (element_index >= _nElements || values == NULL) {
761 res = kIOReturnBadArgument;
762 goto finish;
763 }
764
765 memcpy(&_elements[element_index].values, values, sizeof(IOReportElementValues));
766
767 _elements[element_index].timestamp = record_time;
768
769 //IOREPORTER_DEBUG_ELEMENT(index);
770
771 res = kIOReturnSuccess;
772
773 finish:
774 return res;
775 }
776
777
778 const IOReportElementValues*
779 IOReporter::getElementValues(int element_index)
780 {
781 IOReportElementValues *elementValues = NULL;
782
783 IOREPORTER_CHECK_LOCK();
784
785 if (element_index < 0 || element_index >= _nElements) {
786 IORLOG("ERROR getElementValues out of bounds!");
787 goto finish;
788 }
789
790 elementValues = &_elements[element_index].values;
791
792 finish:
793 return elementValues;
794 }
795
796
797 IOReturn
798 IOReporter::updateChannelValues(int channel_index)
799 {
800 return kIOReturnSuccess;
801 }
802
803
804 IOReturn
805 IOReporter::updateReportChannel(int channel_index,
806 int *nElements,
807 IOBufferMemoryDescriptor *destination)
808 {
809 IOReturn res = kIOReturnError;
810 int start_element_idx, chElems;
811 size_t size2cpy;
812
813 res = kIOReturnBadArgument;
814 if (!nElements || !destination) {
815 goto finish;
816 }
817 if (channel_index > _nChannels) {
818 goto finish;
819 }
820
821 IOREPORTER_CHECK_LOCK();
822
823 res = kIOReturnOverrun;
824
825 start_element_idx = channel_index * _channelDimension;
826 if (start_element_idx >= _nElements) {
827 goto finish;
828 }
829
830 chElems = _elements[start_element_idx].channel_type.nelements;
831
832 // make sure we don't go beyond the end of _elements[_nElements-1]
833 if (start_element_idx + chElems > _nElements) {
834 goto finish;
835 }
836
837 PREFL_MEMOP_FAIL(chElems, IOReportElement);
838 size2cpy = (size_t)chElems * sizeof(IOReportElement);
839
840 // make sure there's space in the destination
841 if (size2cpy > (destination->getCapacity() - destination->getLength())) {
842 IORLOG("CRITICAL ERROR: Report Buffer Overflow (buffer cap %luB, length %luB, size2cpy %luB",
843 (unsigned long)destination->getCapacity(),
844 (unsigned long)destination->getLength(),
845 (unsigned long)size2cpy);
846 goto finish;
847 }
848
849 destination->appendBytes(&_elements[start_element_idx], size2cpy);
850 *nElements += chElems;
851
852 res = kIOReturnSuccess;
853
854 finish:
855 return res;
856 }
857
858
859 IOReturn
860 IOReporter::copyElementValues(int element_index,
861 IOReportElementValues *elementValues)
862 {
863 IOReturn res = kIOReturnError;
864
865 if (!elementValues) {
866 goto finish;
867 }
868
869 IOREPORTER_CHECK_LOCK();
870
871 if (element_index >= _nElements) {
872 IORLOG("ERROR getElementValues out of bounds!");
873 res = kIOReturnBadArgument;
874 goto finish;
875 }
876
877 memcpy(elementValues, &_elements[element_index].values, sizeof(IOReportElementValues));
878 res = kIOReturnSuccess;
879
880 finish:
881 return res;
882 }
883
884
885 IOReturn
886 IOReporter::getFirstElementIndex(uint64_t channel_id,
887 int *index)
888 {
889 IOReturn res = kIOReturnError;
890 int channel_index = 0, element_index = 0;
891
892 if (!index) {
893 goto finish;
894 }
895
896 res = getChannelIndices(channel_id, &channel_index, &element_index);
897
898 if (res == kIOReturnSuccess) {
899 *index = element_index;
900 }
901
902 finish:
903 return res;
904 }
905
906
907 IOReturn
908 IOReporter::getChannelIndex(uint64_t channel_id,
909 int *index)
910 {
911 IOReturn res = kIOReturnError;
912 int channel_index = 0, element_index = 0;
913
914 if (!index) {
915 goto finish;
916 }
917
918 res = getChannelIndices(channel_id, &channel_index, &element_index);
919
920 if (res == kIOReturnSuccess) {
921 *index = channel_index;
922 }
923
924 finish:
925 return res;
926 }
927
928
929 IOReturn
930 IOReporter::getChannelIndices(uint64_t channel_id,
931 int *channel_index,
932 int *element_index)
933 {
934 IOReturn res = kIOReturnNotFound;
935 int chIdx, elemIdx;
936
937 if (!channel_index || !element_index) {
938 goto finish;
939 }
940
941 for (chIdx = 0; chIdx < _nChannels; chIdx++) {
942 elemIdx = chIdx * _channelDimension;
943 if (elemIdx >= _nElements) {
944 IORLOG("ERROR getChannelIndices out of bounds!");
945 res = kIOReturnOverrun;
946 goto finish;
947 }
948
949 if (channel_id == _elements[elemIdx].channel_id) {
950 // The channel index does not care about the depth of elements...
951 *channel_index = chIdx;
952 *element_index = elemIdx;
953
954 res = kIOReturnSuccess;
955 goto finish;
956 }
957 }
958
959 finish:
960 return res;
961 }
962
963 /********************************/
964 /*** PRIVATE METHODS ***/
965 /********************************/
966
967
968 // copyChannelIDs relies on the caller to take lock
969 OSSharedPtr<OSArray>
970 IOReporter::copyChannelIDs()
971 {
972 int cnt, cnt2;
973 OSSharedPtr<OSArray> channelIDs;
974 OSSharedPtr<OSNumber> tmpNum;
975
976 channelIDs = OSArray::withCapacity((unsigned)_nChannels);
977
978 if (!channelIDs) {
979 return nullptr;
980 }
981
982 for (cnt = 0; cnt < _nChannels; cnt++) {
983 cnt2 = cnt * _channelDimension;
984
985 // Encapsulate the Channel ID in OSNumber
986 tmpNum = OSNumber::withNumber(_elements[cnt2].channel_id, 64);
987 if (!tmpNum) {
988 IORLOG("ERROR: Could not create array of channelIDs");
989 return nullptr;
990 }
991
992 channelIDs->setObject((unsigned)cnt, tmpNum.get());
993 tmpNum.reset();
994 }
995
996 return channelIDs;
997 }
998
999
1000 // DO NOT REMOVE THIS METHOD WHICH IS THE MAIN LEGEND CREATION FUNCTION
1001 /*static */ OSSharedPtr<IOReportLegendEntry>
1002 IOReporter::legendWith(OSArray *channelIDs,
1003 OSArray *channelNames,
1004 IOReportChannelType channelType,
1005 IOReportUnit unit)
1006 {
1007 unsigned int cnt, chCnt;
1008 uint64_t type64;
1009 OSSharedPtr<OSNumber> tmpNum;
1010 const OSSymbol *tmpSymbol;
1011 OSSharedPtr<OSArray> channelLegendArray;
1012 OSSharedPtr<OSArray> tmpChannelArray;
1013 OSSharedPtr<OSDictionary> channelInfoDict;
1014 OSSharedPtr<IOReportLegendEntry> legendEntry = nullptr;
1015
1016 // No need to check validity of channelNames because param is optional
1017 if (!channelIDs) {
1018 goto finish;
1019 }
1020 chCnt = channelIDs->getCount();
1021
1022 channelLegendArray = OSArray::withCapacity(chCnt);
1023
1024 for (cnt = 0; cnt < chCnt; cnt++) {
1025 tmpChannelArray = OSArray::withCapacity(3);
1026
1027 // Encapsulate the Channel ID in OSNumber
1028 tmpChannelArray->setObject(kIOReportChannelIDIdx, channelIDs->getObject(cnt));
1029
1030 // Encapsulate the Channel Type in OSNumber
1031 memcpy(&type64, &channelType, sizeof(type64));
1032 tmpNum = OSNumber::withNumber(type64, 64);
1033 if (!tmpNum) {
1034 goto finish;
1035 }
1036 tmpChannelArray->setObject(kIOReportChannelTypeIdx, tmpNum.get());
1037 tmpNum.reset();
1038
1039 // Encapsulate the Channel Name in OSSymbol
1040 // Use channelNames if provided
1041 if (channelNames != NULL) {
1042 tmpSymbol = OSDynamicCast(OSSymbol, channelNames->getObject(cnt));
1043 if (tmpSymbol && tmpSymbol != gIOReportNoChannelName) {
1044 tmpChannelArray->setObject(kIOReportChannelNameIdx, tmpSymbol);
1045 } // Else, skip and leave name field empty
1046 }
1047
1048 channelLegendArray->setObject(cnt, tmpChannelArray.get());
1049 tmpChannelArray.reset();
1050 }
1051
1052 // Stuff the legend entry only if we have channels...
1053 if (channelLegendArray->getCount() != 0) {
1054 channelInfoDict = OSDictionary::withCapacity(1);
1055
1056 if (!channelInfoDict) {
1057 goto finish;
1058 }
1059
1060 tmpNum = OSNumber::withNumber(unit, 64);
1061 if (tmpNum) {
1062 channelInfoDict->setObject(kIOReportLegendUnitKey, tmpNum.get());
1063 }
1064
1065 legendEntry = OSDictionary::withCapacity(1);
1066
1067 if (legendEntry) {
1068 legendEntry->setObject(kIOReportLegendChannelsKey, channelLegendArray.get());
1069 legendEntry->setObject(kIOReportLegendInfoKey, channelInfoDict.get());
1070 }
1071 }
1072
1073 finish:
1074 return legendEntry;
1075 }