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