1 | /* |
2 | * Copyright (c) 2012-2020 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 | #ifndef _IOREPORT_MACROS_H_ |
30 | #define _IOREPORT_MACROS_H_ |
31 | |
32 | #include "IOReportTypes.h" |
33 | #include <string.h> |
34 | #include <os/overflow.h> |
35 | |
36 | #ifdef __cplusplus |
37 | extern "C" { |
38 | #endif |
39 | |
40 | #ifndef IOREPORT_ABORT |
41 | #define IOREPORT_ABORT panic |
42 | #endif |
43 | |
44 | /* |
45 | * Background |
46 | * |
47 | * These macros allow non-I/O Kit software to generate IOReporting |
48 | * reports. Clients must prevent concurrent access to any given |
49 | * report buffer from multiple threads. |
50 | * |
51 | * While these macros allow non-I/O Kit software to participate |
52 | * in IOReporting, an IOService instance must lend its driver ID, |
53 | * respond to the appropriate IOService overrides, and shuttle |
54 | * data back and forth. In some cases, it may be useful to have |
55 | * the I/O Kit driver initialize the report buffer with the |
56 | * appropriate macro. |
57 | */ |
58 | |
59 | |
60 | /* ----- Reporting Single Integers (SimpleReport) ----- */ |
61 | |
62 | /* |
63 | * The buffer size required for a SimpleReport. |
64 | */ |
65 | |
66 | #define SIMPLEREPORT_BUFSIZE (sizeof(IOReportElement)) |
67 | |
68 | |
69 | /* |
70 | * Initialize a buffer to hold a SimpleReport. |
71 | * |
72 | * void* buffer - ptr to SIMPLEREPORT_BUFSIZE bytes |
73 | * size_t bufSize - sanity check of buffer's size |
74 | * uint64_t providerID - registry Entry ID of the reporting service |
75 | * uint64_t channelID - the report's channel ID |
76 | * IOReportCategories categories - categories of this channel |
77 | * |
78 | * If the buffer is not of sufficient size, the macro calls IOREPORT_ABORT(). |
79 | * If that returns, the buffer is left full of '&'. |
80 | */ |
81 | |
82 | #define SIMPLEREPORT_INIT(buf, bufSize, providerID, channelID, cats) \ |
83 | do { \ |
84 | memset((buf), '&', (bufSize)); \ |
85 | IOReportElement *__elem = (IOReportElement *)(buf); \ |
86 | IOSimpleReportValues *__vals; \ |
87 | if ((bufSize) >= SIMPLEREPORT_BUFSIZE) { \ |
88 | __elem->provider_id = (providerID); \ |
89 | __elem->channel_id = (channelID); \ |
90 | __elem->channel_type.report_format = kIOReportFormatSimple; \ |
91 | __elem->channel_type.reserved = 0; \ |
92 | __elem->channel_type.categories = (cats); \ |
93 | __elem->channel_type.nelements = 1; \ |
94 | __elem->channel_type.element_idx = 0; \ |
95 | __elem->timestamp = 0; \ |
96 | __vals = (IOSimpleReportValues*)&__elem->values; \ |
97 | __vals->simple_value = kIOReportInvalidIntValue; \ |
98 | } \ |
99 | else { \ |
100 | IOREPORT_ABORT("bufSize is smaller than the required size\n"); \ |
101 | } \ |
102 | } while(0) |
103 | |
104 | |
105 | /* |
106 | * Set a SimpleReport to a new value. |
107 | * |
108 | * void* simp_buf - ptr to memory initialized by SIMPLEREPORT_INIT() |
109 | * int64_t new_value - new value for the report |
110 | */ |
111 | |
112 | #define SIMPLEREPORT_SETVALUE(simp_buf, new_value) \ |
113 | do { \ |
114 | IOReportElement *__elem = (IOReportElement *)(simp_buf); \ |
115 | IOSimpleReportValues *__vals; \ |
116 | __vals = (IOSimpleReportValues*)&__elem->values; \ |
117 | __vals->simple_value = (new_value); \ |
118 | } while(0) |
119 | |
120 | |
121 | /* |
122 | * Increment the value of a SimpleReport. |
123 | * |
124 | * void* simp_buf - ptr to memory initialized by SIMPLEREPORT_INIT() |
125 | * int64_t increment - amount by which to increment the value |
126 | */ |
127 | #define SIMPLEREPORT_INCREMENTVALUE(simp_buf, increment_by) \ |
128 | do { \ |
129 | IOReportElement *__elem = (IOReportElement *)(simp_buf); \ |
130 | IOSimpleReportValues *__vals; \ |
131 | __vals = (IOSimpleReportValues*)&__elem->values; \ |
132 | int64_t __simple_value = INT64_MAX; \ |
133 | if (os_add_overflow(__vals->simple_value, (increment_by), &__simple_value)) { \ |
134 | __vals->simple_value = INT64_MAX; \ |
135 | } else { \ |
136 | __vals->simple_value = __simple_value; \ |
137 | } \ |
138 | } while(0) |
139 | |
140 | |
141 | /* |
142 | * Prepare a SimpleReport for |
143 | * IOService::updateReport(kIOReportCopyChannelData...) |
144 | * |
145 | * void* simp_buf - Ptr to memory updated by SIMPLEREPORT_SETVALUE() |
146 | * void* ptr2cpy - On return, 'ptr2cpy' points to the memory that needs to be |
147 | * copied for kIOReportCopyChannelData. |
148 | * size_t size2cpy - On return, 'size2cpy' is set to the size of the report |
149 | * data that needs to be copied for kIOReportCopyChannelData. |
150 | */ |
151 | |
152 | #define SIMPLEREPORT_UPDATEPREP(simp_buf, ptr2cpy, size2cpy) \ |
153 | do { \ |
154 | (ptr2cpy) = (simp_buf); \ |
155 | (size2cpy) = sizeof(IOReportElement); \ |
156 | } while(0) |
157 | |
158 | |
159 | /* |
160 | * Update the result field received as a parameter for |
161 | * kIOReportGetDimensions & kIOReportCopyChannelData actions. |
162 | * |
163 | * IOReportConfigureAction action - configure/updateReport() 'action' param |
164 | * void* result - configure/updateReport() 'result' param |
165 | */ |
166 | |
167 | #define SIMPLEREPORT_UPDATERES(action, result) \ |
168 | do { \ |
169 | if (((action) == kIOReportGetDimensions) || ((action) == kIOReportCopyChannelData)) { \ |
170 | int *__nElements = (int *)(result); \ |
171 | *__nElements += 1; \ |
172 | } \ |
173 | } while (0) |
174 | |
175 | |
176 | /* |
177 | * Get the 64-bit channel ID of a SimpleReport. |
178 | * |
179 | * void* simp_buf - ptr to memory initialized by SIMPLEREPORT_INIT() |
180 | */ |
181 | |
182 | #define SIMPLEREPORT_GETCHID(simp_buf) \ |
183 | (((IOReportElement *)(simp_buf))->channel_id) |
184 | |
185 | /* |
186 | * Get the IOReportChannelType of a SimpleReport. |
187 | * |
188 | * void* simp_buf - ptr to memory initialized by SIMPLEREPORT_INIT() |
189 | */ |
190 | |
191 | #define SIMPLEREPORT_GETCHTYPE(simp_buf) \ |
192 | (*(uint64_t*)&(((IOReportElement *)(simp_buf))->channel_type)) |
193 | |
194 | |
195 | /* |
196 | * Get the integer value of a SimpleReport. |
197 | * |
198 | * void* simp_buf - memory initialized by SIMPLEREPORT_INIT() |
199 | */ |
200 | |
201 | #define SIMPLEREPORT_GETVALUE(simp_buf) \ |
202 | (((IOSimpleReportValues*)&(((IOReportElement*)(simp_buf))->values)) \ |
203 | ->simple_value) |
204 | |
205 | |
206 | /* ----- State Machine Reporting (StateReport) ----- */ |
207 | |
208 | // Internal struct for StateReport |
209 | typedef struct { |
210 | uint16_t curr_state; |
211 | uint64_t update_ts; |
212 | IOReportElement elem[]; // Array of elements |
213 | } IOStateReportInfo; |
214 | |
215 | /* |
216 | * Determine the size required for a StateReport buffer. |
217 | * |
218 | * int nstates - number of states to be reported |
219 | */ |
220 | #define STATEREPORT_BUFSIZE(nstates) \ |
221 | (sizeof(IOStateReportInfo) + (nstates) * sizeof(IOReportElement)) |
222 | |
223 | |
224 | /* |
225 | * Initialize a StateReport buffer. |
226 | * |
227 | * uint16_t nstates - number of states to be reported |
228 | * void* buffer - ptr to STATEREPORT_BUFSIZE(nstates) bytes |
229 | * size_t bufSize - sanity check of buffer's size |
230 | * uint64_t providerID - registry Entry ID of the reporting service |
231 | * uint64_t channelID - ID of this channel, see IOREPORT_MAKEID() |
232 | * IOReportCategories categories - categories of this channel |
233 | * |
234 | * If the buffer is not of sufficient size, the macro invokes IOREPORT_ABORT. |
235 | * If that returns, the buffer is left full of '&'. |
236 | */ |
237 | #define STATEREPORT_INIT(nstates, buf, bufSize, providerID, channelID, cats) \ |
238 | do { \ |
239 | memset((buf), '&', (bufSize)); \ |
240 | IOStateReportInfo *__info = (IOStateReportInfo *)(buf); \ |
241 | IOStateReportValues *__rep; \ |
242 | IOReportElement *__elem; \ |
243 | if ((bufSize) >= STATEREPORT_BUFSIZE(nstates)) { \ |
244 | for (uint16_t __no = 0; __no < (nstates); __no++) { \ |
245 | __elem = &(__info->elem[__no]); \ |
246 | __rep = (IOStateReportValues *) &(__elem->values); \ |
247 | __elem->provider_id = (providerID); \ |
248 | __elem->channel_id = (channelID); \ |
249 | __elem->channel_type.report_format = kIOReportFormatState; \ |
250 | __elem->channel_type.reserved = 0; \ |
251 | __elem->channel_type.categories = (cats); \ |
252 | __elem->channel_type.nelements = (nstates); \ |
253 | __elem->channel_type.element_idx = __no; \ |
254 | __elem->timestamp = 0; \ |
255 | __rep->state_id = __no; \ |
256 | __rep->intransitions = 0; \ |
257 | __rep->upticks = 0; \ |
258 | __rep->last_intransition = 0; \ |
259 | } \ |
260 | __info->curr_state = 0; \ |
261 | __info->update_ts = 0; \ |
262 | } \ |
263 | else { \ |
264 | IOREPORT_ABORT("bufSize is smaller than the required size\n"); \ |
265 | } \ |
266 | } while(0) |
267 | |
268 | /* |
269 | * Initialize the state id field of a state with the specified value. By |
270 | * default, STATEREPORT_INIT() initializes the state IDs with the index of |
271 | * that state. This macro can be used to provide a more descriptive state id. |
272 | * |
273 | * void* state_buf - ptr to memory initialized by STATEREPORT_INIT() |
274 | * unsigned stateIdx - index of the state, out of bounds -> no-op |
275 | * uint64_t stateID - new state id, see IOREPORT_MAKEID() |
276 | */ |
277 | #define STATEREPORT_SETSTATEID(state_buf, stateIdx, stateID) \ |
278 | do { \ |
279 | IOStateReportInfo *__info = (IOStateReportInfo *)(state_buf); \ |
280 | IOStateReportValues *__rep; \ |
281 | if ((stateIdx) < __info->elem[0].channel_type.nelements) { \ |
282 | __rep = (IOStateReportValues*) &(__info->elem[(stateIdx)].values); \ |
283 | __rep->state_id = (stateID); \ |
284 | } \ |
285 | } while (0) |
286 | |
287 | |
288 | /* |
289 | * Set the state of a StateReport. |
290 | * |
291 | * void* state_buf - pointer to memory initialized by STATEREPORT_INIT() |
292 | * unsigned newStateIdx - index of new state, out of bounds -> no-op |
293 | * uint64_t changeTime - time at which the transition occurred |
294 | */ |
295 | #define STATEREPORT_SETSTATE(state_buf, newStateIdx, changeTime) \ |
296 | do { \ |
297 | IOStateReportInfo *__info = (IOStateReportInfo *)(state_buf); \ |
298 | IOStateReportValues *__rep; \ |
299 | if ((newStateIdx) < __info->elem[0].channel_type.nelements ) { \ |
300 | __rep = (IOStateReportValues*) &(__info->elem[__info->curr_state].values); \ |
301 | if (__info->update_ts) \ |
302 | __rep->upticks += (changeTime) - __info->update_ts; \ |
303 | __info->elem[(newStateIdx)].timestamp = (changeTime); \ |
304 | __rep = (IOStateReportValues*) &(__info->elem[(newStateIdx)].values); \ |
305 | __rep->intransitions++; \ |
306 | __info->curr_state = (newStateIdx); \ |
307 | __info->update_ts = (changeTime); \ |
308 | } \ |
309 | } while(0) |
310 | |
311 | /* |
312 | * Prepare a StateReport for |
313 | * IOService::updateReport(kIOReportCopyChannelData...) |
314 | * |
315 | * void* state_buf - ptr to memory initialized by STATEREPORT_INIT() |
316 | * uint64_t currentTime - current timestamp |
317 | * void* ptr2cpy - filled in with pointer to buffer to be copied out |
318 | * size_t size2cpy - filled in with the size of the buffer to copy out |
319 | */ |
320 | #define STATEREPORT_UPDATEPREP(state_buf, currentTime, ptr2cpy, size2cpy) \ |
321 | do { \ |
322 | IOStateReportInfo *__info = (IOStateReportInfo *)(state_buf); \ |
323 | IOReportElement *__elem; \ |
324 | IOStateReportValues *__state; \ |
325 | (size2cpy) = __info->elem[0].channel_type.nelements * sizeof(IOReportElement); \ |
326 | (ptr2cpy) = (void *) &__info->elem[0]; \ |
327 | if (__info->update_ts) { \ |
328 | __elem = &__info->elem[__info->curr_state]; \ |
329 | __state = (IOStateReportValues *)&__elem->values; \ |
330 | __elem->timestamp = (currentTime); \ |
331 | __state->upticks += (currentTime) - __info->update_ts; \ |
332 | __info->update_ts = (currentTime); \ |
333 | } \ |
334 | } while(0) |
335 | |
336 | /* |
337 | * Update the result field received as a parameter for kIOReportGetDimensions & |
338 | * kIOReportCopyChannelData actions. |
339 | * |
340 | * void* state_buf - memory initialized by STATEREPORT_INIT() |
341 | * IOReportConfigureAction action - configure/updateReport() 'action' |
342 | * void* result - configure/updateReport() 'result' |
343 | */ |
344 | |
345 | #define STATEREPORT_UPDATERES(state_buf, action, result) \ |
346 | do { \ |
347 | IOStateReportInfo *__info = (IOStateReportInfo *)(state_buf); \ |
348 | IOReportElement *__elem; \ |
349 | int *__nElements = (int *)(result); \ |
350 | if (((action) == kIOReportGetDimensions) || ((action) == kIOReportCopyChannelData)) { \ |
351 | __elem = &(__info->elem[0]); \ |
352 | if (os_add_overflow(*__nElements, __elem->channel_type.nelements, __nElements)) { \ |
353 | *__nElements = INT_MAX; \ |
354 | } \ |
355 | } \ |
356 | } while (0) |
357 | |
358 | |
359 | /* |
360 | * Get the 64-bit channel ID of a StateReport. |
361 | * |
362 | * void* state_buf - ptr to memory initialized by STATEREPORT_INIT() |
363 | */ |
364 | #define STATEREPORT_GETCHID(state_buf) \ |
365 | (((IOStateReportInfo *)(state_buf))->elem[0].channel_id) |
366 | |
367 | /* |
368 | * Get the IOReportChannelType of a StateReport. |
369 | * |
370 | * void* state_buf - ptr to memory initialized by STATEREPORT_INIT() |
371 | */ |
372 | #define STATEREPORT_GETCHTYPE(state_buf) \ |
373 | (*(uint64_t*)&(((IOStateReportInfo *)(state_buf))->elem[0].channel_type)) |
374 | |
375 | /* |
376 | * Get the number of transitions into a given state. |
377 | * |
378 | * void* state_buf - ptr to memory initialized by STATEREPORT_INIT() |
379 | * unsigned stateIdx - index of state, out of bounds -> kIOReportInvalidValue |
380 | * |
381 | */ |
382 | #define STATEREPORT_GETTRANSITIONS(state_buf, stateIdx) \ |
383 | (((stateIdx) < ((IOStateReportInfo *)(state_buf))->elem[0].channel_type.nelements) \ |
384 | ? ((IOStateReportValues*)&(((IOStateReportInfo*)(state_buf))->elem[(stateIdx)].values))->intransitions \ |
385 | : kIOReportInvalidValue) |
386 | |
387 | /* |
388 | * Get the total number of ticks spent in a given state. |
389 | * |
390 | * void* state_buf - ptr to memory initialized by STATEREPORT_INIT() |
391 | * unsigned stateIdx - index of state, out of bounds -> kIOReportInvalidValue |
392 | */ |
393 | #define STATEREPORT_GETTICKS(state_buf, stateIdx) \ |
394 | (((stateIdx) < ((IOStateReportInfo*)(state_buf))->elem[0].channel_type.nelements) \ |
395 | ? ((IOStateReportValues*)&(((IOStateReportInfo*)(state_buf))->elem[(stateIdx)].values))->upticks \ |
396 | : kIOReportInvalidValue) |
397 | |
398 | |
399 | /* ----- Reporting an Array of Integers (SimpleArrayReport) ----- */ |
400 | |
401 | /* |
402 | * Determine the buffer size for a SimpleArrayReport. |
403 | * |
404 | * int nValues - number of values to be reported |
405 | */ |
406 | |
407 | #define SIMPLEARRAY_BUFSIZE(nValues) \ |
408 | ((((nValues)/IOR_VALUES_PER_ELEMENT) + (((nValues) % IOR_VALUES_PER_ELEMENT) ? 1:0)) \ |
409 | * sizeof(IOReportElement)) |
410 | |
411 | /* |
412 | * Initialize a buffer for use as a SimpleArrayReport. |
413 | * |
414 | * int nValues - number of elements to be reported |
415 | * void* buf - ptr to SIMPLEARRAY_BUFSIZE(nValues) bytes |
416 | * size_t bufSize - sanity check of buffer's size |
417 | * uint64_t providerID - registry Entry ID of the reporting service |
418 | * uint64_t channelID - ID of this channel, see IOREPORT_MAKEID() |
419 | * IOReportCategories categories - categories of this channel |
420 | * |
421 | * If the buffer is not of sufficient size, the macro invokes IOREPORT_ABORT(). |
422 | * If that returns, the buffer is left full of '&'. |
423 | */ |
424 | |
425 | #define SIMPLEARRAY_INIT(nValues, buf, bufSize, providerID, channelID, cats) \ |
426 | do { \ |
427 | memset((buf), '&', (bufSize)); \ |
428 | IOSimpleArrayReportValues *__rep; \ |
429 | IOReportElement *__elem; \ |
430 | uint32_t __nElems = (((nValues) / IOR_VALUES_PER_ELEMENT) + \ |
431 | (((nValues) % IOR_VALUES_PER_ELEMENT) ? 1 : 0)); \ |
432 | if ((bufSize) >= SIMPLEARRAY_BUFSIZE(nValues)) { \ |
433 | for (unsigned __no = 0; __no < __nElems; __no++) { \ |
434 | __elem = &(((IOReportElement *)(buf))[__no]); \ |
435 | __rep = (IOSimpleArrayReportValues *) &(__elem->values); \ |
436 | __elem->provider_id = (providerID); \ |
437 | __elem->channel_id = (channelID); \ |
438 | __elem->channel_type.report_format = kIOReportFormatSimpleArray; \ |
439 | __elem->channel_type.reserved = 0; \ |
440 | __elem->channel_type.categories = (cats); \ |
441 | __elem->channel_type.nelements = (__nElems); \ |
442 | __elem->channel_type.element_idx = __no; \ |
443 | __elem->timestamp = 0; \ |
444 | __rep->simple_values[0] = kIOReportInvalidIntValue; \ |
445 | __rep->simple_values[1] = kIOReportInvalidIntValue; \ |
446 | __rep->simple_values[2] = kIOReportInvalidIntValue; \ |
447 | __rep->simple_values[3] = kIOReportInvalidIntValue; \ |
448 | } \ |
449 | } \ |
450 | else { \ |
451 | IOREPORT_ABORT("bufSize is smaller than the required size\n"); \ |
452 | } \ |
453 | } while(0) |
454 | |
455 | |
456 | /* SimpleArrayReport helpers */ |
457 | |
458 | #define __SA_FINDREP(array_buf, idx) \ |
459 | IOSimpleArrayReportValues *__rep; \ |
460 | IOReportElement *__elem; \ |
461 | unsigned __elemIdx = (idx) / IOR_VALUES_PER_ELEMENT; \ |
462 | unsigned __valueIdx = (idx) % IOR_VALUES_PER_ELEMENT; \ |
463 | __elem = &(((IOReportElement *)(array_buf))[0]); \ |
464 | if (__elemIdx < __elem->channel_type.nelements) { \ |
465 | __elem = &(((IOReportElement *)(array_buf))[__elemIdx]); \ |
466 | __rep = (IOSimpleArrayReportValues *) &(__elem->values); \ |
467 | |
468 | #define __SA_MAXINDEX(array_buf) \ |
469 | ((((IOReportElement*)(array_buf))->channel_type.nelements) \ |
470 | * IOR_VALUES_PER_ELEMENT) - 1 |
471 | |
472 | /* |
473 | * Set a value at a specified index in a SimpleArrayReport. |
474 | * |
475 | * void* array_bufbuf - ptr to memory initialized by SIMPLEARRAY_INIT() |
476 | * unsigned idx - array index, out of bounds -> no-op |
477 | * uint64_t newValue - new value to be stored at array[idx] |
478 | */ |
479 | #define SIMPLEARRAY_SETVALUE(array_buf, idx, newValue) \ |
480 | do { \ |
481 | __SA_FINDREP((array_buf), (idx)) \ |
482 | __rep->simple_values[__valueIdx] = (newValue); \ |
483 | } \ |
484 | } while(0) |
485 | |
486 | /* |
487 | * Increment an array value within a SimpleArrayReport. |
488 | * |
489 | * void* array_buf - ptr to memory initialized by SIMPLEARRAY_INIT() |
490 | * unsigned idx - array index to increment, out of bounds -> no-op |
491 | * int64_t value - amount by which to increment array[idx] |
492 | */ |
493 | #define SIMPLEARRAY_INCREMENTVALUE(array_buf, idx, value) \ |
494 | do { \ |
495 | __SA_FINDREP((array_buf), (idx)) \ |
496 | if (os_add_overflow(__rep->simple_values[__valueIdx], (value), &__rep->simple_values[__valueIdx])) { \ |
497 | __rep->simple_values[__valueIdx] = INT64_MAX; \ |
498 | } \ |
499 | } \ |
500 | } while(0) |
501 | |
502 | |
503 | /* |
504 | * Prepare a SimpleArrayReport for |
505 | * IOService::updateReport(kIOReportCopyChannelData...) |
506 | * |
507 | * void* array_buf - ptr to memory initialized by SIMPLEARRAY_INIT() |
508 | * void* ptr2cpy - filled in with pointer to buffer to be copied out |
509 | * size_t size2cpy - filled in with the size of the buffer to copy out |
510 | */ |
511 | |
512 | #define SIMPLEARRAY_UPDATEPREP(array_buf, ptr2cpy, size2cpy) \ |
513 | do { \ |
514 | IOReportElement *__elem; \ |
515 | __elem = &(((IOReportElement *)(array_buf))[0]); \ |
516 | (ptr2cpy) = (void *) (array_buf); \ |
517 | (size2cpy) = __elem->channel_type.nelements * sizeof(IOReportElement); \ |
518 | } while(0) |
519 | |
520 | |
521 | /* |
522 | * Update the result field received as a parameter for kIOReportGetDimensions & |
523 | * kIOReportCopyChannelData actions. |
524 | * |
525 | * void* array_buf - memory initialized by SIMPLEARRAY_INIT() |
526 | * IOReportConfigureAction action - configure/updateReport() 'action' |
527 | * void* result - configure/updateReport() 'result' |
528 | */ |
529 | |
530 | #define SIMPLEARRAY_UPDATERES(array_buf, action, result) \ |
531 | do { \ |
532 | IOReportElement *__elem; \ |
533 | int *__nElements = (int *)(result); \ |
534 | __elem = &(((IOReportElement *)(array_buf))[0]); \ |
535 | if (((action) == kIOReportGetDimensions) || ((action) == kIOReportCopyChannelData)) { \ |
536 | if (os_add_overflow(*__nElements, __elem->channel_type.nelements, __nElements)) { \ |
537 | *__nElements = INT_MAX; \ |
538 | } \ |
539 | } \ |
540 | } while (0) |
541 | |
542 | |
543 | /* |
544 | * Get the 64-bit channel ID of a SimpleArrayReport. |
545 | * |
546 | * void* array_buf - ptr to memory initialized by SIMPLEARRAY_INIT() |
547 | */ |
548 | #define SIMPLEARRAY_GETCHID(array_buf) \ |
549 | (((IOReportElement *)(array_buf))->channel_id) |
550 | |
551 | |
552 | /* |
553 | * Get the IOReportChannelType of a SimpleArrayReport. |
554 | * |
555 | * void* simp_buf - ptr to memory initialized by SIMPLEREPORT_INIT() |
556 | */ |
557 | #define SIMPLEARRAY_GETCHTYPE(array_buf) \ |
558 | (*(uint64_t*)&(((IOReportElement *)(array_buf))->channel_type)) |
559 | |
560 | /* |
561 | * Get a value from a SimpleArrayReport. |
562 | * |
563 | * void* array_buf - ptr to memory initialized by SIMPLEARRAY_INIT() |
564 | * unsigned idx - index of the value, out of bounds -> kIOReportInvalidValue |
565 | */ |
566 | #define SIMPLEARRAY_GETVALUE(array_buf, idx) \ |
567 | (((idx) > __SA_MAXINDEX(array_buf) || (idx) < 0) ? kIOReportInvalidIntValue : \ |
568 | ((IOSimpleArrayReportValues*)&( \ |
569 | ((IOReportElement*)(array_buf))[(idx) / IOR_VALUES_PER_ELEMENT].values)) \ |
570 | ->simple_values[(idx) % IOR_VALUES_PER_ELEMENT]) |
571 | |
572 | |
573 | /* ----- Histogram Reporting (HistogramReport) ----- */ |
574 | |
575 | // Internal struct for HistogramReport |
576 | typedef struct { |
577 | int bucketWidth; |
578 | IOReportElement elem[]; // Array of elements |
579 | } IOHistReportInfo; |
580 | |
581 | /* |
582 | * Determine the size required for a HistogramReport buffer. |
583 | * |
584 | * int nbuckets - number of buckets in the histogram |
585 | */ |
586 | #define HISTREPORT_BUFSIZE(nbuckets) \ |
587 | (sizeof(IOHistReportInfo) + ((nbuckets) * sizeof(IOReportElement))) |
588 | |
589 | /* |
590 | * Initialize a HistogramReport buffer. Supports only linear scale histogram. |
591 | * |
592 | * uint16_t nbuckets - number of buckets data is combined into |
593 | * uint32_t bucketWidth - size of each bucket |
594 | * void* buffer - ptr to HISTREPORT_BUFSIZE(nbuckets) bytes |
595 | * size_t bufSize - sanity check of buffer's size |
596 | * uint64_t providerID - registry Entry ID of the reporting service |
597 | * uint64_t channelID - ID of this channel, see IOREPORT_MAKEID() |
598 | * IOReportCategories categories - categories of this channel |
599 | * |
600 | * If the buffer is not of sufficient size, the macro invokes IOREPORT_ABORT. |
601 | * If that returns, the buffer is left full of '&'. |
602 | */ |
603 | #define HISTREPORT_INIT(nbuckets, bktSize, buf, bufSize, providerID, channelID, cats) \ |
604 | do { \ |
605 | memset((buf), '&', (bufSize)); \ |
606 | IOHistReportInfo *__info = (IOHistReportInfo *)(buf); \ |
607 | IOReportElement *__elem; \ |
608 | IOHistogramReportValues *__rep; \ |
609 | if ((bufSize) >= HISTREPORT_BUFSIZE(nbuckets)) { \ |
610 | __info->bucketWidth = (bktSize); \ |
611 | for (uint16_t __no = 0; __no < (nbuckets); __no++) { \ |
612 | __elem = &(__info->elem[__no]); \ |
613 | __rep = (IOHistogramReportValues *) &(__elem->values); \ |
614 | __elem->provider_id = (providerID); \ |
615 | __elem->channel_id = (channelID); \ |
616 | __elem->channel_type.report_format = kIOReportFormatHistogram; \ |
617 | __elem->channel_type.reserved = 0; \ |
618 | __elem->channel_type.categories = (cats); \ |
619 | __elem->channel_type.nelements = (nbuckets); \ |
620 | __elem->channel_type.element_idx = __no; \ |
621 | __elem->timestamp = 0; \ |
622 | memset(__rep, '\0', sizeof(IOHistogramReportValues)); \ |
623 | } \ |
624 | } \ |
625 | else { \ |
626 | IOREPORT_ABORT("bufSize is smaller than the required size\n"); \ |
627 | } \ |
628 | } while (0) |
629 | |
630 | /* |
631 | * Update histogram with a new value. |
632 | * |
633 | * |
634 | * void* hist_buf - pointer to memory initialized by HISTREPORT_INIT() |
635 | * int64_t value - new value to add to the histogram |
636 | */ |
637 | #define HISTREPORT_TALLYVALUE(hist_buf, value) \ |
638 | do { \ |
639 | IOHistReportInfo *__info = (IOHistReportInfo *)(hist_buf); \ |
640 | IOReportElement *__elem; \ |
641 | IOHistogramReportValues *__rep; \ |
642 | for (unsigned __no = 0; __no < __info->elem[0].channel_type.nelements; __no++) { \ |
643 | if ((value) <= __info->bucketWidth * (__no+1)) { \ |
644 | __elem = &(__info->elem[__no]); \ |
645 | __rep = (IOHistogramReportValues *) &(__elem->values); \ |
646 | if (__rep->bucket_hits == 0) { \ |
647 | __rep->bucket_min = __rep->bucket_max = (value); \ |
648 | } \ |
649 | else if ((value) < __rep->bucket_min) { \ |
650 | __rep->bucket_min = (value); \ |
651 | } \ |
652 | else if ((value) > __rep->bucket_max) { \ |
653 | __rep->bucket_max = (value); \ |
654 | } \ |
655 | int64_t __sum = 0; \ |
656 | if (os_add_overflow(__rep->bucket_sum, (value), &__sum)) { \ |
657 | __rep->bucket_sum = INT64_MAX; \ |
658 | } else { \ |
659 | __rep->bucket_sum = __sum; \ |
660 | } \ |
661 | __rep->bucket_hits++; \ |
662 | break; \ |
663 | } \ |
664 | } \ |
665 | } while (0) |
666 | |
667 | /* |
668 | * Prepare a HistogramReport for |
669 | * IOService::updateReport(kIOReportCopyChannelData...) |
670 | * |
671 | * void* array_buf - ptr to memory initialized by HISTREPORT_INIT() |
672 | * void* ptr2cpy - filled in with pointer to buffer to be copied out |
673 | * size_t size2cpy - filled in with the size of the buffer to copy out |
674 | */ |
675 | |
676 | #define HISTREPORT_UPDATEPREP(hist_buf, ptr2cpy, size2cpy) \ |
677 | do { \ |
678 | IOHistReportInfo *__info = (IOHistReportInfo *)(hist_buf); \ |
679 | (size2cpy) = __info->elem[0].channel_type.nelements * sizeof(IOReportElement); \ |
680 | (ptr2cpy) = (void *) &__info->elem[0]; \ |
681 | } while(0) |
682 | |
683 | |
684 | /* |
685 | * Update the result field received as a parameter for kIOReportGetDimensions & |
686 | * kIOReportCopyChannelData actions. |
687 | * |
688 | * void* array_buf - memory initialized by HISTREPORT_INIT() |
689 | * IOReportConfigureAction action - configure/updateReport() 'action' |
690 | * void* result - configure/updateReport() 'result' |
691 | */ |
692 | |
693 | #define HISTREPORT_UPDATERES(hist_buf, action, result) \ |
694 | do { \ |
695 | IOHistReportInfo *__info = (IOHistReportInfo *)(hist_buf); \ |
696 | int *__nElements = (int *)(result); \ |
697 | if (((action) == kIOReportGetDimensions) || ((action) == kIOReportCopyChannelData)) { \ |
698 | if (os_add_overflow(*__nElements, __info->elem[0].channel_type.nelements, __nElements)) { \ |
699 | *__nElements = INT_MAX; \ |
700 | } \ |
701 | } \ |
702 | } while (0) |
703 | |
704 | /* |
705 | * Get the 64-bit channel ID of a HistogramReport. |
706 | * |
707 | * void* hist_buf - ptr to memory initialized by HISTREPORT_INIT() |
708 | */ |
709 | #define HISTREPORT_GETCHID(hist_buf) \ |
710 | (((IOHistReportInfo *)(hist_buf))->elem[0].channel_id) |
711 | |
712 | /* |
713 | * Get the IOReportChannelType of a HistogramReport. |
714 | * |
715 | * void* hist_buf - ptr to memory initialized by HISTREPORT_INIT() |
716 | */ |
717 | #define HISTREPORT_GETCHTYPE(hist_buf) \ |
718 | (*(uint64_t*)&(((IOHistReportInfo *)(hist_buf))->elem[0].channel_type)) |
719 | |
720 | #ifdef __cplusplus |
721 | } |
722 | #endif |
723 | |
724 | #endif // _IOREPORT_MACROS_H_ |
725 | |