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 <libkern/c++/OSSharedPtr.h>
32 #include <IOKit/IOKernelReportStructs.h>
33 #include <IOKit/IOKernelReporters.h>
34 #include "IOReporterDefs.h"
35 
36 
37 #define super IOReporter
38 OSDefineMetaClassAndStructors(IOStateReporter, IOReporter);
39 
40 
41 /* static */
42 OSSharedPtr<IOStateReporter>
with(IOService * reportingService,IOReportCategories categories,int nstates,IOReportUnit unit)43 IOStateReporter::with(IOService *reportingService,
44     IOReportCategories categories,
45     int nstates,
46     IOReportUnit unit /* = kIOReportUnitHWTicks*/)
47 {
48 	OSSharedPtr<IOStateReporter> reporter;
49 
50 	if (nstates > INT16_MAX) {
51 		return nullptr;
52 	}
53 
54 	reporter = OSMakeShared<IOStateReporter>();
55 	if (!reporter) {
56 		return nullptr;
57 	}
58 
59 	if (!reporter->initWith(reportingService, categories, (int16_t) nstates, unit)) {
60 		return nullptr;
61 	}
62 
63 	return reporter;
64 }
65 
66 bool
initWith(IOService * reportingService,IOReportCategories categories,int16_t nstates,IOReportUnit unit)67 IOStateReporter::initWith(IOService *reportingService,
68     IOReportCategories categories,
69     int16_t nstates,
70     IOReportUnit unit)
71 {
72 	bool success = false;
73 
74 	IOReportChannelType channelType = {
75 		.categories = categories,
76 		.report_format = kIOReportFormatState,
77 		.nelements = static_cast<uint16_t>(nstates),
78 		.element_idx = 0
79 	};
80 
81 	if (super::init(reportingService, channelType, unit) != true) {
82 		IORLOG("ERROR super::initWith failed");
83 		success = false;
84 		goto finish;
85 	}
86 
87 	_currentStates = NULL;
88 	_lastUpdateTimes = NULL;
89 
90 	success = true;
91 
92 finish:
93 	return success;
94 }
95 
96 
97 void
free(void)98 IOStateReporter::free(void)
99 {
100 	if (_currentStates) {
101 		PREFL_MEMOP_PANIC(_nChannels, int);
102 		IOFreeData(_currentStates, (size_t)_nChannels * sizeof(int));
103 	}
104 	if (_lastUpdateTimes) {
105 		PREFL_MEMOP_PANIC(_nChannels, uint64_t);
106 		IOFreeData(_lastUpdateTimes, (size_t)_nChannels * sizeof(uint64_t));
107 	}
108 
109 	super::free();
110 }
111 
112 
113 IOReturn
handleSwapPrepare(int newNChannels)114 IOStateReporter::handleSwapPrepare(int newNChannels)
115 {
116 	IOReturn res = kIOReturnError;
117 	size_t newCurStatesSize, newTSSize;
118 
119 	//IORLOG("handleSwapPrepare (state) _nChannels before = %u", _nChannels);
120 
121 	IOREPORTER_CHECK_CONFIG_LOCK();
122 
123 	if (_swapCurrentStates || _swapLastUpdateTimes) {
124 		panic("IOStateReporter::_swap* already in use");
125 	}
126 
127 	// new currentStates buffer
128 	PREFL_MEMOP_FAIL(newNChannels, int);
129 	newCurStatesSize = (size_t)newNChannels * sizeof(int);
130 	_swapCurrentStates = (int*)IOMallocData(newCurStatesSize);
131 	if (_swapCurrentStates == NULL) {
132 		res = kIOReturnNoMemory; goto finish;
133 	}
134 	memset(_swapCurrentStates, -1, newCurStatesSize); // init w/"no state"
135 
136 	// new timestamps buffer
137 	PREFL_MEMOP_FAIL(newNChannels, uint64_t);
138 	newTSSize = (size_t)newNChannels * sizeof(uint64_t);
139 	_swapLastUpdateTimes = (uint64_t *)IOMallocZeroData(newTSSize);
140 	if (_swapLastUpdateTimes == NULL) {
141 		res = kIOReturnNoMemory; goto finish;
142 	}
143 
144 	res = super::handleSwapPrepare(newNChannels);
145 
146 finish:
147 	if (res) {
148 		if (_swapCurrentStates) {
149 			IOFreeData(_swapCurrentStates, newCurStatesSize);
150 			_swapCurrentStates = NULL;
151 		}
152 		if (_swapLastUpdateTimes) {
153 			IOFreeData(_swapLastUpdateTimes, newTSSize);
154 			_swapLastUpdateTimes = NULL;
155 		}
156 	}
157 
158 	return res;
159 }
160 
161 IOReturn
handleAddChannelSwap(uint64_t channelID,const OSSymbol * symChannelName)162 IOStateReporter::handleAddChannelSwap(uint64_t channelID,
163     const OSSymbol *symChannelName)
164 {
165 	IOReturn res = kIOReturnError;
166 	int cnt;
167 	int *tmpCurStates;
168 	uint64_t *tmpTimestamps;
169 	bool swapComplete = false;
170 
171 	//IORLOG("IOStateReporter::handleSwap");
172 
173 	if (!_swapCurrentStates || !_swapLastUpdateTimes) {
174 		IORLOG("IOReporter::handleSwap ERROR swap variables uninitialized!");
175 		goto finish;
176 	}
177 
178 	IOREPORTER_CHECK_CONFIG_LOCK();
179 	IOREPORTER_CHECK_LOCK();
180 
181 	// Copy any existing buffers
182 	if (_currentStates) {
183 		PREFL_MEMOP_FAIL(_nChannels, int);
184 		memcpy(_swapCurrentStates, _currentStates,
185 		    (size_t)_nChannels * sizeof(int));
186 
187 		if (!_lastUpdateTimes) {
188 			panic("IOStateReporter::handleAddChannelSwap _lastUpdateTimes unset despite non-NULL _currentStates");
189 		}
190 		PREFL_MEMOP_FAIL(_nChannels, uint64_t);
191 		memcpy(_swapLastUpdateTimes, _lastUpdateTimes,
192 		    (size_t)_nChannels * sizeof(uint64_t));
193 	}
194 
195 	// Update principal instance variables, keep old values in _swap* for cleanup
196 	tmpCurStates = _currentStates;
197 	_currentStates = _swapCurrentStates;
198 	_swapCurrentStates = tmpCurStates;
199 
200 	tmpTimestamps = _lastUpdateTimes;
201 	_lastUpdateTimes = _swapLastUpdateTimes;
202 	_swapLastUpdateTimes = tmpTimestamps;
203 
204 	swapComplete = true;
205 
206 	// subclass success
207 
208 	// invoke superclass(es): base class updates _nChannels & _nElements
209 	res = super::handleAddChannelSwap(channelID, symChannelName);
210 	if (res) {
211 		IORLOG("handleSwap(state) ERROR super::handleSwap failed!");
212 		goto finish;
213 	}
214 
215 	// Channel added successfully, initialize the new channel's state_ids to 0..nStates-1
216 	for (cnt = 0; cnt < _channelDimension; cnt++) {
217 		handleSetStateID(channelID, cnt, (uint64_t)cnt);
218 	}
219 
220 finish:
221 	if (res && swapComplete) {
222 		// unswap so the unused buffers get cleaned up
223 		tmpCurStates = _currentStates;
224 		_currentStates = _swapCurrentStates;
225 		_swapCurrentStates = tmpCurStates;
226 
227 		tmpTimestamps = _lastUpdateTimes;
228 		_lastUpdateTimes = _swapLastUpdateTimes;
229 		_swapLastUpdateTimes = tmpTimestamps;
230 	}
231 
232 	return res;
233 }
234 
235 
236 void
handleSwapCleanup(int swapNChannels)237 IOStateReporter::handleSwapCleanup(int swapNChannels)
238 {
239 	IOREPORTER_CHECK_CONFIG_LOCK();
240 
241 	super::handleSwapCleanup(swapNChannels);
242 
243 	if (_swapCurrentStates) {
244 		PREFL_MEMOP_PANIC(swapNChannels, int);
245 		IOFreeData(_swapCurrentStates, (size_t)swapNChannels * sizeof(int));
246 		_swapCurrentStates = NULL;
247 	}
248 	if (_swapLastUpdateTimes) {
249 		PREFL_MEMOP_PANIC(swapNChannels, uint64_t);
250 		IOFreeData(_swapLastUpdateTimes, (size_t)swapNChannels * sizeof(uint64_t));
251 		_swapLastUpdateTimes = NULL;
252 	}
253 }
254 
255 
256 IOReturn
_getStateIndices(uint64_t channel_id,uint64_t state_id,int * channel_index,int * state_index)257 IOStateReporter::_getStateIndices(uint64_t channel_id,
258     uint64_t state_id,
259     int *channel_index,
260     int *state_index)
261 {
262 	IOReturn res = kIOReturnError;
263 	int cnt;
264 	IOStateReportValues *values;
265 	int element_index = 0;
266 
267 	IOREPORTER_CHECK_LOCK();
268 
269 	if (getChannelIndices(channel_id,
270 	    channel_index,
271 	    &element_index) != kIOReturnSuccess) {
272 		res = kIOReturnBadArgument;
273 
274 		goto finish;
275 	}
276 
277 	for (cnt = 0; cnt < _channelDimension; cnt++) {
278 		values = (IOStateReportValues *)getElementValues(element_index + cnt);
279 
280 		if (values == NULL) {
281 			res = kIOReturnError;
282 			goto finish;
283 		}
284 
285 		if (values->state_id == state_id) {
286 			*state_index = cnt;
287 			res = kIOReturnSuccess;
288 			goto finish;
289 		}
290 	}
291 
292 	res = kIOReturnBadArgument;
293 
294 finish:
295 	return res;
296 }
297 
298 
299 IOReturn
setChannelState(uint64_t channel_id,uint64_t new_state_id)300 IOStateReporter::setChannelState(uint64_t channel_id,
301     uint64_t new_state_id)
302 {
303 	IOReturn res = kIOReturnError;
304 	int channel_index, new_state_index;
305 	uint64_t last_intransition = 0;
306 	uint64_t prev_state_residency = 0;
307 
308 	lockReporter();
309 
310 	if (_getStateIndices(channel_id, new_state_id, &channel_index, &new_state_index) == kIOReturnSuccess) {
311 		res = handleSetStateByIndices(channel_index, new_state_index,
312 		    last_intransition,
313 		    prev_state_residency);
314 		goto finish;
315 	}
316 
317 	res = kIOReturnBadArgument;
318 
319 finish:
320 	unlockReporter();
321 	return res;
322 }
323 
324 IOReturn
setChannelState(uint64_t channel_id,uint64_t new_state_id,uint64_t last_intransition,uint64_t prev_state_residency)325 IOStateReporter::setChannelState(uint64_t channel_id,
326     uint64_t new_state_id,
327     uint64_t last_intransition,
328     uint64_t prev_state_residency)
329 {
330 	return setChannelState(channel_id, new_state_id);
331 }
332 
333 IOReturn
overrideChannelState(uint64_t channel_id,uint64_t state_id,uint64_t time_in_state,uint64_t intransitions,uint64_t last_intransition)334 IOStateReporter::overrideChannelState(uint64_t channel_id,
335     uint64_t state_id,
336     uint64_t time_in_state,
337     uint64_t intransitions,
338     uint64_t last_intransition /*=0*/)
339 {
340 	IOReturn res = kIOReturnError;
341 	int channel_index, state_index;
342 
343 	lockReporter();
344 
345 	if (_getStateIndices(channel_id, state_id, &channel_index, &state_index) == kIOReturnSuccess) {
346 		if (_lastUpdateTimes[channel_index]) {
347 			panic("overrideChannelState() cannot be used after setChannelState()!");
348 		}
349 
350 		res = handleOverrideChannelStateByIndices(channel_index, state_index,
351 		    time_in_state, intransitions,
352 		    last_intransition);
353 		goto finish;
354 	}
355 
356 	res = kIOReturnBadArgument;
357 
358 finish:
359 	unlockReporter();
360 	return res;
361 }
362 
363 
364 IOReturn
handleOverrideChannelStateByIndices(int channel_index,int state_index,uint64_t time_in_state,uint64_t intransitions,uint64_t last_intransition)365 IOStateReporter::handleOverrideChannelStateByIndices(int channel_index,
366     int state_index,
367     uint64_t time_in_state,
368     uint64_t intransitions,
369     uint64_t last_intransition /*=0*/)
370 {
371 	IOReturn kerr, result = kIOReturnError;
372 	IOStateReportValues state_values;
373 	int element_index;
374 
375 	if (channel_index < 0 || channel_index >= _nChannels) {
376 		result = kIOReturnBadArgument; goto finish;
377 	}
378 
379 	if (channel_index < 0 || channel_index > (_nElements - state_index)
380 	    / _channelDimension) {
381 		result = kIOReturnOverrun; goto finish;
382 	}
383 	element_index = channel_index * _channelDimension + state_index;
384 
385 	kerr = copyElementValues(element_index, (IOReportElementValues*)&state_values);
386 	if (kerr) {
387 		result = kerr; goto finish;
388 	}
389 
390 	// last_intransition = 0 -> no current state ("residency summary only")
391 	state_values.last_intransition = last_intransition;
392 	state_values.intransitions = intransitions;
393 	state_values.upticks = time_in_state;
394 
395 	// determines current time for metadata
396 	kerr = setElementValues(element_index, (IOReportElementValues *)&state_values);
397 	if (kerr) {
398 		result = kerr; goto finish;
399 	}
400 
401 	// success
402 	result = kIOReturnSuccess;
403 
404 finish:
405 	return result;
406 }
407 
408 
409 IOReturn
incrementChannelState(uint64_t channel_id,uint64_t state_id,uint64_t time_in_state,uint64_t intransitions,uint64_t last_intransition)410 IOStateReporter::incrementChannelState(uint64_t channel_id,
411     uint64_t state_id,
412     uint64_t time_in_state,
413     uint64_t intransitions,
414     uint64_t last_intransition /*=0*/)
415 {
416 	IOReturn res = kIOReturnError;
417 	int channel_index, state_index;
418 
419 	lockReporter();
420 
421 	if (_getStateIndices(channel_id, state_id, &channel_index, &state_index) == kIOReturnSuccess) {
422 		if (_lastUpdateTimes[channel_index]) {
423 			panic("incrementChannelState() cannot be used after setChannelState()!");
424 		}
425 
426 		res = handleIncrementChannelStateByIndices(channel_index, state_index,
427 		    time_in_state, intransitions,
428 		    last_intransition);
429 		goto finish;
430 	}
431 
432 	res = kIOReturnBadArgument;
433 
434 finish:
435 	unlockReporter();
436 	return res;
437 }
438 
439 
440 IOReturn
handleIncrementChannelStateByIndices(int channel_index,int state_index,uint64_t time_in_state,uint64_t intransitions,uint64_t last_intransition)441 IOStateReporter::handleIncrementChannelStateByIndices(int channel_index,
442     int state_index,
443     uint64_t time_in_state,
444     uint64_t intransitions,
445     uint64_t last_intransition /*=0*/)
446 {
447 	IOReturn kerr, result = kIOReturnError;
448 	IOStateReportValues state_values;
449 	int element_index;
450 
451 	if (channel_index < 0 || channel_index >= _nChannels) {
452 		result = kIOReturnBadArgument; goto finish;
453 	}
454 
455 	if (channel_index < 0 || channel_index > (_nElements - state_index)
456 	    / _channelDimension) {
457 		result = kIOReturnOverrun; goto finish;
458 	}
459 	element_index = channel_index * _channelDimension + state_index;
460 
461 	kerr = copyElementValues(element_index, (IOReportElementValues*)&state_values);
462 	if (kerr) {
463 		result = kerr;
464 		goto finish;
465 	}
466 
467 	state_values.last_intransition = last_intransition;
468 	state_values.intransitions += intransitions;
469 	state_values.upticks += time_in_state;
470 
471 	// determines current time for metadata
472 	kerr = setElementValues(element_index, (IOReportElementValues *)&state_values);
473 	if (kerr) {
474 		result = kerr;
475 		goto finish;
476 	}
477 
478 	// success
479 	result = kIOReturnSuccess;
480 
481 finish:
482 	return result;
483 }
484 
485 
486 IOReturn
setState(uint64_t new_state_id)487 IOStateReporter::setState(uint64_t new_state_id)
488 {
489 	uint64_t last_intransition = 0;
490 	uint64_t prev_state_residency = 0;
491 	IOReturn res = kIOReturnError;
492 	IOStateReportValues *values;
493 	int channel_index = 0, element_index = 0, new_state_index = 0;
494 	int cnt;
495 
496 	lockReporter();
497 
498 	if (_nChannels == 1) {
499 		for (cnt = 0; cnt < _channelDimension; cnt++) {
500 			new_state_index = element_index + cnt;
501 
502 			values = (IOStateReportValues *)getElementValues(new_state_index);
503 
504 			if (values == NULL) {
505 				res = kIOReturnError;
506 				goto finish;
507 			}
508 
509 			if (values->state_id == new_state_id) {
510 				res = handleSetStateByIndices(channel_index, new_state_index,
511 				    last_intransition,
512 				    prev_state_residency);
513 				goto finish;
514 			}
515 		}
516 	}
517 
518 	res = kIOReturnBadArgument;
519 
520 finish:
521 	unlockReporter();
522 	return res;
523 }
524 
525 IOReturn
setState(uint64_t new_state_id,uint64_t last_intransition,uint64_t prev_state_residency)526 IOStateReporter::setState(uint64_t new_state_id,
527     uint64_t last_intransition,
528     uint64_t prev_state_residency)
529 {
530 	return setState(new_state_id);
531 }
532 
533 IOReturn
setStateID(uint64_t channel_id,int state_index,uint64_t state_id)534 IOStateReporter::setStateID(uint64_t channel_id,
535     int state_index,
536     uint64_t state_id)
537 {
538 	IOReturn res = kIOReturnError;
539 
540 	lockReporter();
541 
542 	res = handleSetStateID(channel_id, state_index, state_id);
543 
544 	unlockReporter();
545 
546 	return res;
547 }
548 
549 
550 IOReturn
handleSetStateID(uint64_t channel_id,int state_index,uint64_t state_id)551 IOStateReporter::handleSetStateID(uint64_t channel_id,
552     int state_index,
553     uint64_t state_id)
554 {
555 	IOReturn res = kIOReturnError;
556 	IOStateReportValues state_values;
557 	int element_index = 0;
558 
559 	IOREPORTER_CHECK_LOCK();
560 
561 	if (getFirstElementIndex(channel_id, &element_index) == kIOReturnSuccess) {
562 		if (state_index >= _channelDimension) {
563 			res = kIOReturnBadArgument; goto finish;
564 		}
565 		if (_nElements - state_index <= element_index) {
566 			res = kIOReturnOverrun; goto finish;
567 		}
568 		element_index += state_index;
569 
570 		if (copyElementValues(element_index, (IOReportElementValues *)&state_values) != kIOReturnSuccess) {
571 			res = kIOReturnBadArgument;
572 			goto finish;
573 		}
574 
575 		state_values.state_id = state_id;
576 
577 		res = setElementValues(element_index, (IOReportElementValues *)&state_values);
578 	}
579 
580 	// FIXME: set a bit somewhere (reporter-wide?) that state_ids can no longer be
581 	// assumed to be contiguous
582 finish:
583 	return res;
584 }
585 
586 IOReturn
setStateByIndices(int channel_index,int new_state_index)587 IOStateReporter::setStateByIndices(int channel_index,
588     int new_state_index)
589 {
590 	IOReturn res = kIOReturnError;
591 	uint64_t last_intransition = 0;
592 	uint64_t prev_state_residency = 0;
593 
594 	lockReporter();
595 
596 	res = handleSetStateByIndices(channel_index, new_state_index,
597 	    last_intransition, prev_state_residency);
598 
599 	unlockReporter();
600 
601 	return res;
602 }
603 
604 IOReturn
setStateByIndices(int channel_index,int new_state_index,uint64_t last_intransition,uint64_t prev_state_residency)605 IOStateReporter::setStateByIndices(int channel_index,
606     int new_state_index,
607     uint64_t last_intransition,
608     uint64_t prev_state_residency)
609 {
610 	return setStateByIndices(channel_index, new_state_index);
611 }
612 
613 IOReturn
handleSetStateByIndices(int channel_index,int new_state_index,uint64_t last_intransition,uint64_t prev_state_residency)614 IOStateReporter::handleSetStateByIndices(int channel_index,
615     int new_state_index,
616     uint64_t last_intransition,
617     uint64_t prev_state_residency)
618 {
619 	IOReturn res = kIOReturnError;
620 
621 	IOStateReportValues curr_state_values, new_state_values;
622 	int curr_state_index = 0;
623 	int curr_element_index, new_element_index;
624 	uint64_t last_ch_update_time = 0;
625 	uint64_t recordTime = mach_absolute_time();
626 
627 	IOREPORTER_CHECK_LOCK();
628 
629 	if (channel_index < 0 || channel_index >= _nChannels) {
630 		res = kIOReturnBadArgument; goto finish;
631 	}
632 
633 	// if no timestamp provided, last_intransition = time of recording (now)
634 	if (last_intransition == 0) {
635 		last_intransition = recordTime;
636 	}
637 
638 	// First update target state if different than the current state
639 	// _currentStates[] initialized to -1 to detect first state transition
640 	curr_state_index = _currentStates[channel_index];
641 	if (new_state_index != curr_state_index) {
642 		// fetch element data
643 		if (channel_index < 0 || channel_index > (_nElements - new_state_index)
644 		    / _channelDimension) {
645 			res = kIOReturnOverrun; goto finish;
646 		}
647 		new_element_index = channel_index * _channelDimension + new_state_index;
648 		if (copyElementValues(new_element_index,
649 		    (IOReportElementValues *)&new_state_values)) {
650 			res = kIOReturnBadArgument;
651 			goto finish;
652 		}
653 
654 		// Update new state's transition info
655 		new_state_values.intransitions += 1;
656 		new_state_values.last_intransition = last_intransition;
657 
658 		// and store the values
659 		res = setElementValues(new_element_index,
660 		    (IOReportElementValues *)&new_state_values,
661 		    recordTime);
662 
663 		if (res != kIOReturnSuccess) {
664 			goto finish;
665 		}
666 
667 		_currentStates[channel_index] = new_state_index;
668 	}
669 
670 	/* Now update time spent in any previous state
671 	 *  If new_state_index = curr_state_index, this updates time in the
672 	 *  current state.  If this is the channel's first state transition,
673 	 *  the last update time will be zero.
674 	 *
675 	 *  Note: While setState() should never be called on a channel being
676 	 *  updated with increment/overrideChannelState(), that's another way
677 	 *  that the last update time might not exist.  Regardless, if there
678 	 *  is no basis for determining time spent in previous state, there's
679 	 *  nothing to update!
680 	 */
681 	last_ch_update_time = _lastUpdateTimes[channel_index];
682 	if (last_ch_update_time != 0) {
683 		if (channel_index < 0 || channel_index > (_nElements - curr_state_index)
684 		    / _channelDimension) {
685 			res = kIOReturnOverrun; goto finish;
686 		}
687 		curr_element_index = channel_index * _channelDimension + curr_state_index;
688 		if (copyElementValues(curr_element_index,
689 		    (IOReportElementValues *)&curr_state_values)) {
690 			res = kIOReturnBadArgument;
691 			goto finish;
692 		}
693 		// compute the time spent in previous state, unless provided
694 		if (prev_state_residency == 0) {
695 			prev_state_residency = last_intransition - last_ch_update_time;
696 		}
697 
698 		curr_state_values.upticks += prev_state_residency;
699 
700 		res = setElementValues(curr_element_index,
701 		    (IOReportElementValues*)&curr_state_values,
702 		    recordTime);
703 
704 		if (res != kIOReturnSuccess) {
705 			goto finish;
706 		}
707 	}
708 
709 	// record basis for next "time in prior state" calculation
710 	// (also arms a panic in override/incrementChannelState())
711 	_lastUpdateTimes[channel_index] = last_intransition;
712 
713 finish:
714 	return res;
715 }
716 
717 
718 // blocks might make this slightly easier?
719 uint64_t
getStateInTransitions(uint64_t channel_id,uint64_t state_id)720 IOStateReporter::getStateInTransitions(uint64_t channel_id,
721     uint64_t state_id)
722 {
723 	return _getStateValue(channel_id, state_id, kInTransitions);
724 }
725 
726 uint64_t
getStateResidencyTime(uint64_t channel_id,uint64_t state_id)727 IOStateReporter::getStateResidencyTime(uint64_t channel_id,
728     uint64_t state_id)
729 {
730 	return _getStateValue(channel_id, state_id, kResidencyTime);
731 }
732 
733 uint64_t
getStateLastTransitionTime(uint64_t channel_id,uint64_t state_id)734 IOStateReporter::getStateLastTransitionTime(uint64_t channel_id,
735     uint64_t state_id)
736 {
737 	return _getStateValue(channel_id, state_id, kLastTransitionTime);
738 }
739 
740 uint64_t
_getStateValue(uint64_t channel_id,uint64_t state_id,enum valueSelector value)741 IOStateReporter::_getStateValue(uint64_t channel_id,
742     uint64_t state_id,
743     enum valueSelector value)
744 {
745 	int channel_index = 0, element_index = 0, cnt;
746 	IOStateReportValues *values = NULL;
747 	uint64_t result = kIOReportInvalidValue;
748 
749 	lockReporter();
750 
751 	if (getChannelIndices(channel_id, &channel_index, &element_index) == kIOReturnSuccess) {
752 		if (updateChannelValues(channel_index) == kIOReturnSuccess) {
753 			for (cnt = 0; cnt < _channelDimension; cnt++) {
754 				values = (IOStateReportValues *)getElementValues(element_index);
755 
756 				if (state_id == values->state_id) {
757 					switch (value) {
758 					case kInTransitions:
759 						result = values->intransitions;
760 						break;
761 					case kResidencyTime:
762 						result = values->upticks;
763 						break;
764 					case kLastTransitionTime:
765 						result = values->last_intransition;
766 						break;
767 					default:
768 						break;
769 					}
770 
771 					break;
772 				}
773 
774 				element_index++;
775 			}
776 		}
777 	}
778 
779 	unlockReporter();
780 	return result;
781 }
782 
783 
784 uint64_t
getStateLastChannelUpdateTime(uint64_t channel_id)785 IOStateReporter::getStateLastChannelUpdateTime(uint64_t channel_id)
786 {
787 	int channel_index;
788 	uint64_t result = kIOReportInvalidValue;
789 
790 	lockReporter();
791 
792 	if (getChannelIndex(channel_id, &channel_index) == kIOReturnSuccess) {
793 		result = _lastUpdateTimes[channel_index];
794 	}
795 
796 	unlockReporter();
797 
798 	return result;
799 }
800 
801 
802 /* updateChannelValues() is called to refresh state before being
803  *  reported outside the reporter.  In the case of IOStateReporter,
804  *  this is primarily an update to the "time in state" data.
805  */
806 IOReturn
updateChannelValues(int channel_index)807 IOStateReporter::updateChannelValues(int channel_index)
808 {
809 	IOReturn kerr, result = kIOReturnError;
810 
811 	int state_index, element_idx;
812 	uint64_t currentTime;
813 	uint64_t last_ch_update_time;
814 	uint64_t time_in_state;
815 	IOStateReportValues state_values;
816 
817 	IOREPORTER_CHECK_LOCK();
818 
819 	if (channel_index < 0 || channel_index >= _nChannels) {
820 		result = kIOReturnBadArgument; goto finish;
821 	}
822 
823 	/* First check to see whether this channel has begun self-
824 	 *  calculation of time in state.  It's possible this channel
825 	 *  has yet to be initialized or that the driver is updating
826 	 *  the channel with override/incrementChannelState() which
827 	 *  never enable automatic time-in-state updates.  In that case,
828 	 *  there is nothing to update and we return success.
829 	 */
830 	last_ch_update_time = _lastUpdateTimes[channel_index];
831 	if (last_ch_update_time == 0) {
832 		result = kIOReturnSuccess; goto finish;
833 	}
834 
835 	// figure out the current state (if any)
836 	state_index = _currentStates[channel_index];
837 
838 	// e.g. given 4 4-state channels, the boundary is ch[3].st[3] <- _elems[15]
839 	if (channel_index < 0 || channel_index > (_nElements - state_index)
840 	    / _channelDimension) {
841 		result = kIOReturnOverrun; goto finish;
842 	}
843 	element_idx = channel_index * _channelDimension + state_index;
844 
845 	// get the current values
846 	kerr = copyElementValues(element_idx, (IOReportElementValues*)&state_values);
847 	if (kerr) {
848 		result = kerr; goto finish;
849 	}
850 
851 	// calculate time in state
852 	currentTime = mach_absolute_time();
853 	time_in_state = currentTime - last_ch_update_time;
854 	state_values.upticks += time_in_state;
855 
856 	// and store the values
857 	kerr = setElementValues(element_idx,
858 	    (IOReportElementValues *)&state_values,
859 	    currentTime);
860 	if (kerr) {
861 		result = kerr; goto finish;
862 	}
863 
864 	// Record basis for next "prior time" calculation
865 	_lastUpdateTimes[channel_index] = currentTime;
866 
867 
868 	// success
869 	result = kIOReturnSuccess;
870 
871 finish:
872 	return result;
873 }
874 
875 /* static */ OSPtr<IOReportLegendEntry>
createLegend(const uint64_t * channelIDs,const char ** channelNames,int channelCount,int nstates,IOReportCategories categories,IOReportUnit unit)876 IOStateReporter::createLegend(const uint64_t *channelIDs,
877     const char **channelNames,
878     int channelCount,
879     int nstates,
880     IOReportCategories categories,
881     IOReportUnit unit)
882 {
883 	IOReportChannelType channelType = {
884 		.categories = categories,
885 		.report_format = kIOReportFormatState,
886 		.nelements = static_cast<uint16_t>(nstates),
887 		.element_idx = 0
888 	};
889 
890 	return IOReporter::legendWith(channelIDs, channelNames, channelCount, channelType, unit);
891 }
892