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