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
37extern "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) \
83do { \
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) \
113do { \
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) \
128do { \
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) \
153do { \
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) \
168do { \
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
209typedef 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) \
238do { \
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) \
278do { \
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) \
296do { \
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) \
321do { \
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) \
346do { \
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) \
426do { \
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) \
480do { \
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) \
494do { \
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) \
513do { \
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) \
531do { \
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
576typedef 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) \
604do { \
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) \
638do { \
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) \
677do { \
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) \
694do { \
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