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