xref: /xnu-11215/iokit/Kernel/IOReporter.cpp (revision 5c2921b0)
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