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