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