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