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