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
40 OSDefineMetaClassAndStructors(IOReporter, OSObject);
41
42 static OSSharedPtr<const OSSymbol> gIOReportNoChannelName;
43
44 // * We might someday want an IOReportManager (vs. these static funcs)
45
46 /**************************************/
47 /*** STATIC METHODS ***/
48 /**************************************/
49 IOReturn
configureAllReports(OSSet * reporters,IOReportChannelList * channelList,IOReportConfigureAction action,void * result,void * destination)50 IOReporter::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(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
95 finish:
96 return rval;
97 }
98
99 // the duplication in these functions almost makes one want Objective-C SEL* ;)
100 IOReturn
updateAllReports(OSSet * reporters,IOReportChannelList * channelList,IOReportConfigureAction action,void * result,void * destination)101 IOReporter::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(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
146 finish:
147 return rval;
148 }
149
150
151 /**************************************/
152 /*** COMMON INIT METHODS ***/
153 /**************************************/
154
155 bool
init(IOService * reportingService,IOReportChannelType channelType,IOReportUnit unit)156 IOReporter::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(1);
218 if (!_channelNames) {
219 goto finish;
220 }
221
222 // success
223 success = true;
224
225 finish:
226 return success;
227 }
228
229
230 /*******************************/
231 /*** PUBLIC METHODS ***/
232 /*******************************/
233
234 void
initialize(void)235 IOReporter::initialize(void)
236 {
237 gIOReportNoChannelName = OSSymbol::withCString("_NO_NAME_4");
238 }
239
240 // init() [possibly via init*()] must be called before free()
241 // to ensure that _<var> = NULL
242 void
free(void)243 IOReporter::free(void)
244 {
245 if (_configLock) {
246 IOLockFree(_configLock);
247 }
248 if (_reporterLock) {
249 IOSimpleLockFree(_reporterLock);
250 }
251
252 if (_elements) {
253 PREFL_MEMOP_PANIC(_nElements, IOReportElement);
254 IOFreeData(_elements, (size_t)_nElements * sizeof(IOReportElement));
255 }
256 if (_enableCounts) {
257 PREFL_MEMOP_PANIC(_nChannels, int);
258 IOFreeData(_enableCounts, (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 */
273 IOReturn
addChannel(uint64_t channelID,const char * channelName)274 IOReporter::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((unsigned)newNChannels) <
299 (unsigned)newNChannels) {
300 res = kIOReturnNoMemory; goto finish;
301 }
302 if (channelName) {
303 symChannelName = OSSymbol::withCString(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(channelID, 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
329 finish:
330 // free up not-in-use buffers (tracked by _swap*)
331 handleSwapCleanup(freeNChannels);
332
333 unlockReporterConfig();
334
335 return res;
336 }
337
338
339 OSSharedPtr<IOReportLegendEntry>
createLegend(void)340 IOReporter::createLegend(void)
341 {
342 OSSharedPtr<IOReportLegendEntry> legendEntry;
343
344 lockReporterConfig();
345
346 legendEntry = handleCreateLegend();
347
348 unlockReporterConfig();
349
350 return legendEntry;
351 }
352
353
354 IOReturn
configureReport(IOReportChannelList * channelList,IOReportConfigureAction action,void * result,void * destination)355 IOReporter::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
372 IOReturn
updateReport(IOReportChannelList * channelList,IOReportConfigureAction action,void * result,void * destination)373 IOReporter::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
395 void
lockReporter()396 IOReporter::lockReporter()
397 {
398 _interruptState = IOSimpleLockLockDisableInterrupt(_reporterLock);
399 _reporterIsLocked = true;
400 }
401
402
403 void
unlockReporter()404 IOReporter::unlockReporter()
405 {
406 _reporterIsLocked = false;
407 IOSimpleLockUnlockEnableInterrupt(_reporterLock, _interruptState);
408 }
409
410 void
lockReporterConfig()411 IOReporter::lockReporterConfig()
412 {
413 IOLockLock(_configLock);
414 _reporterConfigIsLocked = true;
415 }
416
417 void
unlockReporterConfig()418 IOReporter::unlockReporterConfig()
419 {
420 _reporterConfigIsLocked = false;
421 IOLockUnlock(_configLock);
422 }
423
424
425 IOReturn
handleSwapPrepare(int newNChannels)426 IOReporter::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
476 finish:
477 if (res) {
478 if (_swapElements) {
479 IOFreeData(_swapElements, newElementsSize);
480 _swapElements = NULL;
481 }
482 if (_swapEnableCounts) {
483 IOFreeData(_swapEnableCounts, newECSize);
484 _swapEnableCounts = NULL;
485 }
486 }
487
488 return res;
489 }
490
491
492 IOReturn
handleAddChannelSwap(uint64_t channel_id,const OSSymbol * symChannelName)493 IOReporter::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(_swapElements, _elements,
517 (size_t)_nElements * sizeof(IOReportElement));
518
519 PREFL_MEMOP_PANIC(_nElements, int);
520 memcpy(_swapEnableCounts, _enableCounts,
521 (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((unsigned)_nChannels, 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
561 finish:
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
575 void
handleSwapCleanup(int swapNChannels)576 IOReporter::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(_swapElements, (size_t)swapNElements * sizeof(IOReportElement));
592 _swapElements = NULL;
593 }
594 if (_swapEnableCounts) {
595 PREFL_MEMOP_PANIC(swapNChannels, int);
596 IOFreeData(_swapEnableCounts, (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).
605 IOReturn
handleConfigureReport(IOReportChannelList * channelList,IOReportConfigureAction action,void * result,void * destination)606 IOReporter::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(channelList->channels[chIdx].channel_id,
627 &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
660 finish:
661 return res;
662 }
663
664 static const uint32_t UNLOCK_PERIOD = 1 << 5;
665 static_assert(powerof2(UNLOCK_PERIOD),
666 "unlock period not a power of 2: period check must be efficient");
667 static const uint32_t UNLOCK_PERIOD_MASK = UNLOCK_PERIOD - 1;
668
669 IOReturn
handleUpdateReport(IOReportChannelList * channelList,IOReportConfigureAction action,void * result,void * destination)670 IOReporter::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(1);
708 lockReporter();
709 }
710 if (getChannelIndex(channelList->channels[chIdx].channel_id,
711 &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, 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
742 finish:
743 return res;
744 }
745
746
747 OSSharedPtr<IOReportLegendEntry>
handleCreateLegend(void)748 IOReporter::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.get(), _channelNames.get(), _channelType, _unit);
757 }
758
759 return legendEntry;
760 }
761
762
763 IOReturn
setElementValues(int element_index,IOReportElementValues * values,uint64_t record_time)764 IOReporter::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(&_elements[element_index].values, values, sizeof(IOReportElementValues));
782
783 _elements[element_index].timestamp = record_time;
784
785 //IOREPORTER_DEBUG_ELEMENT(index);
786
787 res = kIOReturnSuccess;
788
789 finish:
790 return res;
791 }
792
793
794 const IOReportElementValues*
getElementValues(int element_index)795 IOReporter::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
808 finish:
809 return elementValues;
810 }
811
812
813 IOReturn
updateChannelValues(int channel_index)814 IOReporter::updateChannelValues(int channel_index)
815 {
816 return kIOReturnSuccess;
817 }
818
819
820 IOReturn
updateReportChannel(int channel_index,int * nElements,IOBufferMemoryDescriptor * destination)821 IOReporter::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(&_elements[start_element_idx], size2cpy);
866 *nElements += chElems;
867
868 res = kIOReturnSuccess;
869
870 finish:
871 return res;
872 }
873
874
875 IOReturn
copyElementValues(int element_index,IOReportElementValues * elementValues)876 IOReporter::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(elementValues, &_elements[element_index].values, sizeof(IOReportElementValues));
894 res = kIOReturnSuccess;
895
896 finish:
897 return res;
898 }
899
900
901 IOReturn
getFirstElementIndex(uint64_t channel_id,int * index)902 IOReporter::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, &element_index);
913
914 if (res == kIOReturnSuccess) {
915 *index = element_index;
916 }
917
918 finish:
919 return res;
920 }
921
922
923 IOReturn
getChannelIndex(uint64_t channel_id,int * index)924 IOReporter::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, &element_index);
935
936 if (res == kIOReturnSuccess) {
937 *index = channel_index;
938 }
939
940 finish:
941 return res;
942 }
943
944
945 IOReturn
getChannelIndices(uint64_t channel_id,int * channel_index,int * element_index)946 IOReporter::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
975 finish:
976 return res;
977 }
978
979 /********************************/
980 /*** PRIVATE METHODS ***/
981 /********************************/
982
983
984 // copyChannelIDs relies on the caller to take lock
985 OSSharedPtr<OSArray>
copyChannelIDs()986 IOReporter::copyChannelIDs()
987 {
988 int cnt, cnt2;
989 OSSharedPtr<OSArray> channelIDs;
990 OSSharedPtr<OSNumber> tmpNum;
991
992 channelIDs = OSArray::withCapacity((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(_elements[cnt2].channel_id, 64);
1003 if (!tmpNum) {
1004 IORLOG("ERROR: Could not create array of channelIDs");
1005 return nullptr;
1006 }
1007
1008 channelIDs->setObject((unsigned)cnt, 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>
legendWith(OSArray * channelIDs,OSArray * channelNames,IOReportChannelType channelType,IOReportUnit unit)1018 IOReporter::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(chCnt);
1039
1040 for (cnt = 0; cnt < chCnt; cnt++) {
1041 tmpChannelArray = OSArray::withCapacity(3);
1042
1043 // Encapsulate the Channel ID in OSNumber
1044 tmpChannelArray->setObject(kIOReportChannelIDIdx, channelIDs->getObject(cnt));
1045
1046 // Encapsulate the Channel Type in OSNumber
1047 memcpy(&type64, &channelType, sizeof(type64));
1048 tmpNum = OSNumber::withNumber(type64, 64);
1049 if (!tmpNum) {
1050 goto finish;
1051 }
1052 tmpChannelArray->setObject(kIOReportChannelTypeIdx, 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, tmpSymbol);
1061 } // Else, skip and leave name field empty
1062 }
1063
1064 channelLegendArray->setObject(cnt, 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(1);
1071
1072 if (!channelInfoDict) {
1073 goto finish;
1074 }
1075
1076 tmpNum = OSNumber::withNumber(unit, 64);
1077 if (tmpNum) {
1078 channelInfoDict->setObject(kIOReportLegendUnitKey, tmpNum.get());
1079 }
1080
1081 legendEntry = OSDictionary::withCapacity(1);
1082
1083 if (legendEntry) {
1084 legendEntry->setObject(kIOReportLegendChannelsKey, channelLegendArray.get());
1085 legendEntry->setObject(kIOReportLegendInfoKey, channelInfoDict.get());
1086 }
1087 }
1088
1089 finish:
1090 return legendEntry;
1091 }
1092
1093 /*static */ OSPtr<IOReportLegendEntry>
legendWith(const uint64_t * channelIDs,const char ** channelNames,int channelCount,IOReportChannelType channelType,IOReportUnit unit)1094 IOReporter::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(channelCount);
1107 channelNamesArray = OSArray::withCapacity(channelCount);
1108 if (!channelIDsArray || !channelNamesArray) {
1109 return nullptr;
1110 }
1111
1112 for (cnt = 0; cnt < channelCount; cnt++) {
1113 channelID = OSNumber::withNumber(*channelIDs++, 64);
1114 const char *name = *channelNames++;
1115 if (name) {
1116 channelName = OSSymbol::withCString(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(cnt, channelID) ||
1125 !channelNamesArray->setObject(cnt, channelName)) {
1126 return nullptr;
1127 }
1128 }
1129
1130 return legendWith(channelIDsArray.get(), channelNamesArray.get(), channelType, unit);
1131 }
1132