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