xref: /xnu-12377.41.6/iokit/IOKit/IOReportMacros.h (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
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