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