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