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