1/*
2 * Copyright (c) 2000-2013 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <CoreFoundation/CoreFoundation.h>            // (CFDictionary, ...)
25#include <IOKit/IOCFSerialize.h>                      // (IOCFSerialize, ...)
26#include <IOKit/IOKitLib.h>                           // (IOMasterPort, ...)
27#include <IOKit/IOKitLibPrivate.h>                    // (IOServiceGetState, ...)
28#include <sys/ioctl.h>                                // (TIOCGWINSZ, ...)
29#include <term.h>                                     // (tputs, ...)
30#include <unistd.h>                                   // (getopt, ...)
31
32// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
33
34#define assertion(e, message) ((void) (__builtin_expect(!(e), 0) ? fprintf(stderr, "ioreg: error: %s.\n", message), exit(1) : 0))
35
36// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
37
38struct options
39{
40    UInt32 archive:1;                                 // (-a option)
41    UInt32 bold:1;                                    // (-b option)
42    UInt32 format:1;                                  // (-f option)
43    UInt32 hex:1;                                     // (-x option)
44    UInt32 inheritance:1;                             // (-i option)
45    UInt32 list:1;                                    // (-l option)
46    UInt32 root:1;                                    // (-r option)
47    UInt32 tree:1;                                    // (-t option)
48
49    char * class;                                     // (-c option)
50    UInt32 depth;                                     // (-d option)
51    char * key;                                       // (-k option)
52    char * name;                                      // (-n option)
53    char * plane;                                     // (-p option)
54    UInt32 width;                                     // (-w option)
55};
56
57struct context
58{
59    io_registry_entry_t service;
60    UInt32              serviceDepth;
61    UInt64              stackOfBits;
62    struct options      options;
63};
64
65// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
66
67static void boldinit();
68static void boldon();
69static void boldoff();
70static void printinit(int width);
71static void print(const char * format, ...);
72static void println(const char * format, ...);
73
74static void cfshowinit(Boolean hex);
75static void cfshow(CFTypeRef object);
76static void cfarrayshow(CFArrayRef object);
77static void cfbooleanshow(CFBooleanRef object);
78static void cfdatashow(CFDataRef object);
79static void cfdictionaryshow(CFDictionaryRef object);
80static void cfnumbershow(CFNumberRef object);
81static void cfsetshow(CFSetRef object);
82static void cfstringshow(CFStringRef object);
83
84static CFStringRef createInheritanceStringForIORegistryClassName(CFStringRef name);
85
86static void printProp(CFStringRef key, CFTypeRef value, struct context * context);
87static void printPhysAddr(CFTypeRef value, struct context * context);
88static void printSlotNames(CFTypeRef value, struct context * context);
89static void printPCIRanges(CFTypeRef value, struct context * context);
90static void printInterruptMap(CFTypeRef value, struct context * context);
91static void printInterrupts(CFTypeRef value, struct context * context);
92static void printInterruptParent( CFTypeRef value, struct context * context );
93static void printData(CFTypeRef value, struct context * context);
94
95// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
96
97static CFMutableDictionaryRef archive( io_registry_entry_t service,
98                                       struct options      options ) CF_RETURNS_RETAINED;
99
100static CFMutableDictionaryRef archive_scan( io_registry_entry_t service,
101                                            UInt32              serviceDepth,
102                                            struct options      options ) CF_RETURNS_RETAINED;
103
104static CFMutableArrayRef archive_search( io_registry_entry_t service,
105                                         UInt32              serviceHasMatchedDepth,
106                                         UInt32              serviceDepth,
107                                         io_registry_entry_t stackOfObjects[],
108                                         struct options      options ) CF_RETURNS_RETAINED;
109
110static Boolean compare( io_registry_entry_t service,
111                        struct options      options );
112
113static void indent( Boolean isNode,
114                    UInt32  serviceDepth,
115                    UInt64  stackOfBits );
116
117static void scan( io_registry_entry_t service,
118                  Boolean             serviceHasMoreSiblings,
119                  UInt32              serviceDepth,
120                  UInt64              stackOfBits,
121                  struct options      options );
122
123static void search( io_registry_entry_t service,
124                    UInt32              serviceHasMatchedDepth,
125                    UInt32              serviceDepth,
126                    io_registry_entry_t stackOfObjects[],
127                    struct options      options );
128
129static void show( io_registry_entry_t service,
130                  UInt32              serviceDepth,
131                  UInt64              stackOfBits,
132                  struct options      options );
133
134static void showitem( const void * key,
135                      const void * value,
136                      void *       parameter );
137
138static void usage();
139
140// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
141
142int main(int argc, char ** argv)
143{
144    int                 argument = 0;
145    CFWriteStreamRef    file     = 0; // (needs release)
146    CFTypeRef           object   = 0; // (needs release)
147    struct options      options;
148    CFURLRef            path     = 0; // (needs release)
149    io_registry_entry_t service  = 0; // (needs release)
150    io_registry_entry_t stackOfObjects[64];
151    Boolean             success  = FALSE;
152    struct winsize      winsize;
153
154    // Initialize our minimal state.
155
156    options.archive     = FALSE;
157    options.bold        = FALSE;
158    options.format      = FALSE;
159    options.hex         = FALSE;
160    options.inheritance = FALSE;
161    options.list        = FALSE;
162    options.root        = FALSE;
163    options.tree        = FALSE;
164
165    options.class = 0;
166    options.depth = 0;
167    options.key   = 0;
168    options.name  = 0;
169    options.plane = kIOServicePlane;
170    options.root  = 0;
171    options.width = 0;
172
173    // Obtain the screen width.
174
175    if (isatty(fileno(stdout)))
176    {
177        if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) == 0)
178        {
179            options.width = winsize.ws_col;
180        }
181    }
182
183    // Obtain the command-line arguments.
184
185    while ( (argument = getopt(argc, argv, ":abc:d:fik:ln:p:rsStw:x")) != -1 )
186    {
187        switch (argument)
188        {
189            case 'a':
190                options.archive = TRUE;
191                break;
192            case 'b':
193                options.bold = TRUE;
194                break;
195            case 'c':
196                options.class = optarg;
197                break;
198            case 'd':
199                options.depth = atoi(optarg);
200                break;
201            case 'f':
202                options.format = TRUE;
203                break;
204            case 'i':
205                options.inheritance = TRUE;
206                break;
207            case 'k':
208                options.key = optarg;
209                break;
210            case 'l':
211                options.list = TRUE;
212                break;
213            case 'n':
214                options.name = optarg;
215                break;
216            case 'p':
217                options.plane = optarg;
218                break;
219            case 'r':
220                options.root = TRUE;
221                break;
222            case 's':
223                break;
224            case 'S':
225                break;
226            case 't':
227                options.tree = TRUE;
228                break;
229            case 'w':
230                options.width = atoi(optarg);
231                break;
232            case 'x':
233                options.hex = TRUE;
234                break;
235            default:
236                usage();
237                break;
238        }
239    }
240
241    // Initialize text output functions.
242
243    cfshowinit(options.hex);
244
245    printinit(options.width);
246
247    if (options.bold)  boldinit();
248
249    // Obtain the I/O Kit root service.
250
251    service = IORegistryGetRootEntry(kIOMasterPortDefault);
252    assertion(service, "can't obtain I/O Kit's root service");
253
254    // Traverse over all the I/O Kit services.
255
256    if (options.archive)
257    {
258        if (options.root)
259        {
260            object = archive_search( /* service                */ service,
261                                     /* serviceHasMatchedDepth */ 0,
262                                     /* serviceDepth           */ 0,
263                                     /* stackOfObjects         */ stackOfObjects,
264                                     /* options                */ options );
265        }
266        else
267        {
268            object = archive_scan( /* service      */ service,
269                                   /* serviceDepth */ 0,
270                                   /* options      */ options );
271        }
272
273        if (object)
274        {
275            path = CFURLCreateWithFileSystemPath( /* allocator   */ kCFAllocatorDefault,
276                                                  /* filePath    */ CFSTR("/dev/stdout"),
277                                                  /* pathStyle   */ kCFURLPOSIXPathStyle,
278                                                  /* isDirectory */ FALSE );
279            assertion(path != NULL, "can't create path");
280
281            file = CFWriteStreamCreateWithFile(kCFAllocatorDefault, path);
282            assertion(file != NULL, "can't create file");
283
284            success = CFWriteStreamOpen(file);
285            assertion(success, "can't open file");
286
287            CFPropertyListWrite( /* propertyList */ object,
288                                 /* stream       */ file,
289                                 /* format       */ kCFPropertyListXMLFormat_v1_0,
290                                 /* options      */ 0,
291                                 /* error        */ NULL );
292
293            CFWriteStreamClose(file);
294
295            CFRelease(file);
296            CFRelease(path);
297            CFRelease(object);
298        }
299    }
300    else
301    {
302        if (options.root)
303        {
304            search( /* service                */ service,
305                    /* serviceHasMatchedDepth */ 0,
306                    /* serviceDepth           */ 0,
307                    /* stackOfObjects         */ stackOfObjects,
308                    /* options                */ options );
309        }
310        else
311        {
312            scan( /* service                */ service,
313                  /* serviceHasMoreSiblings */ FALSE,
314                  /* serviceDepth           */ 0,
315                  /* stackOfBits            */ 0,
316                  /* options                */ options );
317        }
318    }
319
320    // Release resources.
321
322    IOObjectRelease(service);
323
324    // Quit.
325
326    exit(0);
327}
328
329// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
330
331static CFMutableDictionaryRef archive( io_registry_entry_t service,
332                                       struct options      options )
333{
334    io_name_t              class;          // (don't release)
335    uint32_t               count      = 0;
336    CFMutableDictionaryRef dictionary = 0; // (needs release)
337    uint64_t               identifier = 0;
338    io_name_t              location;       // (don't release)
339    io_name_t              name;           // (don't release)
340    CFTypeRef              object     = 0; // (needs release)
341    uint64_t               state      = 0;
342    kern_return_t          status     = KERN_SUCCESS;
343    uint64_t               time       = 0;
344
345    // Determine whether the service is a match.
346
347    if (options.list || compare(service, options))
348    {
349        // Obtain the service's properties.
350
351        status = IORegistryEntryCreateCFProperties( service,
352                                                    &dictionary,
353                                                    kCFAllocatorDefault,
354                                                    kNilOptions );
355        assertion(status == KERN_SUCCESS, "can't obtain properties");
356    }
357    else
358    {
359        dictionary = CFDictionaryCreateMutable( kCFAllocatorDefault,
360                                                0,
361                                                &kCFTypeDictionaryKeyCallBacks,
362                                                &kCFTypeDictionaryValueCallBacks );
363        assertion(dictionary != NULL, "can't create dictionary");
364    }
365
366    // Obtain the name of the service.
367
368    status = IORegistryEntryGetNameInPlane(service, options.plane, name);
369    assertion(status == KERN_SUCCESS, "can't obtain name");
370
371    object = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingUTF8);
372    assertion(object != NULL, "can't create name");
373
374    CFDictionarySetValue(dictionary, CFSTR("IORegistryEntryName"), object);
375    CFRelease(object);
376
377    // Obtain the location of the service.
378
379    status = IORegistryEntryGetLocationInPlane(service, options.plane, location);
380    if (status == KERN_SUCCESS)
381    {
382        object = CFStringCreateWithCString(kCFAllocatorDefault, location, kCFStringEncodingUTF8);
383        assertion(object != NULL, "can't create location");
384
385        CFDictionarySetValue(dictionary, CFSTR("IORegistryEntryLocation"), object);
386        CFRelease(object);
387    }
388
389    // Obtain the ID of the service.
390
391    status = IORegistryEntryGetRegistryEntryID(service, &identifier);
392    assertion(status == KERN_SUCCESS, "can't obtain identifier");
393
394    object = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &identifier);
395    assertion(object != NULL, "can't create identifier");
396
397    CFDictionarySetValue(dictionary, CFSTR("IORegistryEntryID"), object);
398    CFRelease(object);
399
400    // Obtain the class of the service.
401
402    status = IOObjectGetClass(service, class);
403    assertion(status == KERN_SUCCESS, "can't obtain class");
404
405    object = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingUTF8);
406    assertion(object != NULL, "can't create class");
407
408    CFDictionarySetValue(dictionary, CFSTR("IOObjectClass"), object);
409    CFRelease(object);
410
411    // Obtain the retain count of the service.
412
413    count = IOObjectGetKernelRetainCount(service);
414
415    object = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &count);
416    assertion(object != NULL, "can't create retain count");
417
418    CFDictionarySetValue(dictionary, CFSTR("IOObjectRetainCount"), object);
419    CFRelease(object);
420
421    // Obtain the busy state of the service (for IOService objects).
422
423    if (IOObjectConformsTo(service, "IOService"))
424    {
425        status = IOServiceGetBusyStateAndTime(service, &state, &count, &time);
426        assertion(status == KERN_SUCCESS, "can't obtain state");
427
428        object = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &state);
429        assertion(object != NULL, "can't create state");
430
431        CFDictionarySetValue(dictionary, CFSTR("IOServiceState"), object);
432        CFRelease(object);
433
434        object = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &count);
435        assertion(object != NULL, "can't create busy state");
436
437        CFDictionarySetValue(dictionary, CFSTR("IOServiceBusyState"), object);
438        CFRelease(object);
439
440        object = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &time);
441        assertion(object != NULL, "can't create busy time");
442
443        CFDictionarySetValue(dictionary, CFSTR("IOServiceBusyTime"), object);
444        CFRelease(object);
445    }
446
447    return dictionary;
448}
449
450// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
451
452static CFMutableDictionaryRef archive_scan( io_registry_entry_t service,
453                                            UInt32              serviceDepth,
454                                            struct options      options )
455{
456    CFMutableArrayRef      array       = 0; // (needs release)
457    io_registry_entry_t    child       = 0; // (needs release)
458    io_registry_entry_t    childUpNext = 0; // (don't release)
459    io_iterator_t          children    = 0; // (needs release)
460    CFMutableDictionaryRef dictionary  = 0; // (needs release)
461    CFTypeRef              object      = 0; // (needs release)
462    kern_return_t          status      = KERN_SUCCESS;
463
464    // Obtain the service's children.
465
466    status = IORegistryEntryGetChildIterator(service, options.plane, &children);
467    if (status == KERN_SUCCESS)
468    {
469        childUpNext = IOIteratorNext(children);
470
471        // Obtain the relevant service information.
472
473        dictionary = archive(service, options);
474
475        // Traverse over the children of this service.
476
477        if (options.depth == 0 || options.depth > serviceDepth + 1)
478        {
479            if (childUpNext)
480            {
481                array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
482                assertion(array != NULL, "can't create array");
483
484                while (childUpNext)
485                {
486                    child       = childUpNext;
487                    childUpNext = IOIteratorNext(children);
488
489                    object = archive_scan( /* service      */ child,
490                                           /* serviceDepth */ serviceDepth + 1,
491                                           /* options      */ options );
492                    assertion(object != NULL, "can't obtain child");
493
494                    CFArrayAppendValue(array, object);
495                    CFRelease(object);
496
497                    IOObjectRelease(child);
498                }
499
500                CFDictionarySetValue(dictionary, CFSTR("IORegistryEntryChildren"), array);
501                CFRelease(array);
502            }
503        }
504
505        IOObjectRelease(children);
506    }
507
508    return dictionary;
509}
510
511// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
512
513static CFMutableArrayRef archive_search( io_registry_entry_t service,
514                                         UInt32              serviceHasMatchedDepth,
515                                         UInt32              serviceDepth,
516                                         io_registry_entry_t stackOfObjects[],
517                                         struct options      options )
518{
519    CFMutableArrayRef      array       = 0; // (needs release)
520    CFMutableArrayRef      array2      = 0; // (needs release)
521    io_registry_entry_t    child       = 0; // (needs release)
522    io_registry_entry_t    childUpNext = 0; // (don't release)
523    io_iterator_t          children    = 0; // (needs release)
524    CFMutableDictionaryRef dictionary  = 0; // (needs release)
525    CFMutableDictionaryRef dictionary2 = 0; // (needs release)
526    UInt32                 index       = 0;
527    kern_return_t          status      = KERN_SUCCESS;
528
529    // Determine whether the service is a match.
530
531    if (serviceHasMatchedDepth < serviceDepth + 1 && compare(service, options))
532    {
533        if (options.depth)
534        {
535            serviceHasMatchedDepth = serviceDepth + options.depth;
536        }
537        else
538        {
539            serviceHasMatchedDepth = UINT32_MAX;
540        }
541
542        if (options.tree)
543        {
544            if (options.depth)  options.depth += serviceDepth;
545
546            dictionary = archive_scan( /* service      */ service,
547                                       /* serviceDepth */ serviceDepth,
548                                       /* options      */ options );
549
550            if (options.depth)  options.depth -= serviceDepth;
551
552            for (index = serviceDepth; index > 0; index--)
553            {
554                dictionary2 = archive(stackOfObjects[index - 1], options);
555                assertion(dictionary2 != NULL, "can't obtain parent");
556
557                CFDictionarySetValue(dictionary2, CFSTR("IORegistryEntryChildren"), dictionary);
558                CFRelease(dictionary);
559
560                dictionary = dictionary2;
561            }
562        }
563        else
564        {
565            dictionary = archive_scan( /* service      */ service,
566                                       /* serviceDepth */ 0,
567                                       /* options      */ options );
568        }
569
570        array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
571        assertion(array != NULL, "can't create array");
572
573        CFArrayAppendValue(array, dictionary);
574        CFRelease(dictionary);
575    }
576
577    // Save service into stackOfObjects for this depth.
578
579    stackOfObjects[serviceDepth] = service;
580
581    // Obtain the service's children.
582
583    status = IORegistryEntryGetChildIterator(service, options.plane, &children);
584    if (status == KERN_SUCCESS)
585    {
586        childUpNext = IOIteratorNext(children);
587
588        // Traverse over the children of this service.
589
590        while (childUpNext)
591        {
592            child       = childUpNext;
593            childUpNext = IOIteratorNext(children);
594
595            array2 = archive_search( /* service                */ child,
596                                     /* serviceHasMatchedDepth */ serviceHasMatchedDepth,
597                                     /* serviceDepth           */ serviceDepth + 1,
598                                     /* stackOfObjects         */ stackOfObjects,
599                                     /* options                */ options );
600            if (array2)
601            {
602                if (array)
603                {
604                    CFArrayAppendArray(array, array2, CFRangeMake(0, CFArrayGetCount(array2)));
605                    CFRelease(array2);
606                }
607                else
608                {
609                    array = array2;
610                }
611            }
612
613            IOObjectRelease(child);
614        }
615
616        IOObjectRelease(children);
617    }
618
619    return array;
620}
621
622// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
623
624static Boolean compare( io_registry_entry_t service,
625                        struct options      options )
626{
627    CFStringRef   key      = 0; // (needs release)
628    io_name_t     location;     // (don't release)
629    Boolean       match    = FALSE;
630    io_name_t     name;         // (don't release)
631    kern_return_t status   = KERN_SUCCESS;
632    CFTypeRef     value    = 0; // (needs release)
633
634    // Determine whether the class of the service is a match.
635
636    if (options.class)
637    {
638        if (IOObjectConformsTo(service, options.class) == FALSE)
639        {
640            return FALSE;
641        }
642
643        match = TRUE;
644    }
645
646    // Determine whether the key of the service is a match.
647
648    if (options.key)
649    {
650        key = CFStringCreateWithCString( kCFAllocatorDefault,
651                                         options.key,
652                                         kCFStringEncodingUTF8 );
653        assertion(key != NULL, "can't create key");
654
655        value = IORegistryEntryCreateCFProperty( service,
656                                                 key,
657                                                 kCFAllocatorDefault,
658                                                 kNilOptions );
659
660        CFRelease(key);
661
662        if (value == NULL)
663        {
664            return FALSE;
665        }
666
667        CFRelease(value);
668
669        match = TRUE;
670    }
671
672    // Determine whether the name of the service is a match.
673
674    if (options.name)
675    {
676        // Obtain the name of the service.
677
678        status = IORegistryEntryGetNameInPlane(service, options.plane, name);
679        assertion(status == KERN_SUCCESS, "can't obtain name");
680
681        if (strchr(options.name, '@'))
682        {
683            strlcat(name, "@", sizeof(name));
684
685            // Obtain the location of the service.
686
687            status = IORegistryEntryGetLocationInPlane(service, options.plane, location);
688            if (status == KERN_SUCCESS)  strlcat(name, location, sizeof(name));
689        }
690
691        if (strcmp(options.name, name))
692        {
693            return FALSE;
694        }
695
696        match = TRUE;
697    }
698
699    return match;
700}
701
702// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
703
704static void scan( io_registry_entry_t service,
705                  Boolean             serviceHasMoreSiblings,
706                  UInt32              serviceDepth,
707                  UInt64              stackOfBits,
708                  struct options      options )
709{
710    io_registry_entry_t child       = 0; // (needs release)
711    io_registry_entry_t childUpNext = 0; // (don't release)
712    io_iterator_t       children    = 0; // (needs release)
713    kern_return_t       status      = KERN_SUCCESS;
714
715    // Obtain the service's children.
716
717    status = IORegistryEntryGetChildIterator(service, options.plane, &children);
718    if (status == KERN_SUCCESS)
719    {
720        childUpNext = IOIteratorNext(children);
721
722        // Save has-more-siblings state into stackOfBits for this depth.
723
724        if (serviceHasMoreSiblings)
725            stackOfBits |=  (1 << serviceDepth);
726        else
727            stackOfBits &= ~(1 << serviceDepth);
728
729        // Save has-children state into stackOfBits for this depth.
730
731        if (options.depth == 0 || options.depth > serviceDepth + 1)
732        {
733            if (childUpNext)
734                stackOfBits |=  (2 << serviceDepth);
735            else
736                stackOfBits &= ~(2 << serviceDepth);
737        }
738
739        // Print out the relevant service information.
740
741        show(service, serviceDepth, stackOfBits, options);
742
743        // Traverse over the children of this service.
744
745        if (options.depth == 0 || options.depth > serviceDepth + 1)
746        {
747            while (childUpNext)
748            {
749                child       = childUpNext;
750                childUpNext = IOIteratorNext(children);
751
752                scan( /* service                */ child,
753                      /* serviceHasMoreSiblings */ (childUpNext) ? TRUE : FALSE,
754                      /* serviceDepth           */ serviceDepth + 1,
755                      /* stackOfBits            */ stackOfBits,
756                      /* options                */ options );
757
758                IOObjectRelease(child);
759            }
760        }
761
762        IOObjectRelease(children);
763    }
764}
765
766// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
767
768static void search( io_registry_entry_t service,
769                    UInt32              serviceHasMatchedDepth,
770                    UInt32              serviceDepth,
771                    io_registry_entry_t stackOfObjects[],
772                    struct options      options )
773{
774    io_registry_entry_t child       = 0; // (needs release)
775    io_registry_entry_t childUpNext = 0; // (don't release)
776    io_iterator_t       children    = 0; // (needs release)
777    UInt32              index       = 0;
778    kern_return_t       status      = KERN_SUCCESS;
779
780    // Determine whether the service is a match.
781
782    if (serviceHasMatchedDepth < serviceDepth + 1 && compare(service, options))
783    {
784        if (options.depth)
785        {
786            serviceHasMatchedDepth = serviceDepth + options.depth;
787        }
788        else
789        {
790            serviceHasMatchedDepth = UINT32_MAX;
791        }
792
793        if (options.tree)
794        {
795            for (index = 0; index < serviceDepth; index++)
796            {
797                show(stackOfObjects[index], index, (2 << index), options);
798            }
799
800            if (options.depth)  options.depth += serviceDepth;
801
802            scan( /* service                */ service,
803                  /* serviceHasMoreSiblings */ FALSE,
804                  /* serviceDepth           */ serviceDepth,
805                  /* stackOfBits            */ 0,
806                  /* options                */ options );
807
808            if (options.depth)  options.depth -= serviceDepth;
809        }
810        else
811        {
812            scan( /* service                */ service,
813                  /* serviceHasMoreSiblings */ FALSE,
814                  /* serviceDepth           */ 0,
815                  /* stackOfBits            */ 0,
816                  /* options                */ options );
817        }
818
819        println("");
820    }
821
822    // Save service into stackOfObjects for this depth.
823
824    stackOfObjects[serviceDepth] = service;
825
826    // Obtain the service's children.
827
828    status = IORegistryEntryGetChildIterator(service, options.plane, &children);
829    if (status == KERN_SUCCESS)
830    {
831        childUpNext = IOIteratorNext(children);
832
833        // Traverse over the children of this service.
834
835        while (childUpNext)
836        {
837            child       = childUpNext;
838            childUpNext = IOIteratorNext(children);
839
840            search( /* service                */ child,
841                    /* serviceHasMatchedDepth */ serviceHasMatchedDepth,
842                    /* serviceDepth           */ serviceDepth + 1,
843                    /* stackOfObjects         */ stackOfObjects,
844                    /* options                */ options );
845
846            IOObjectRelease(child);
847        }
848
849        IOObjectRelease(children);
850    }
851}
852
853// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
854
855static void show( io_registry_entry_t service,
856                  UInt32              serviceDepth,
857                  UInt64              stackOfBits,
858                  struct options      options )
859{
860    io_name_t              class;          // (don't release)
861    struct context         context    = { service, serviceDepth, stackOfBits, options };
862    uint32_t               integer    = 0;
863    uint64_t               state      = 0;
864    uint64_t               accumulated_busy_time;
865    io_name_t              location;       // (don't release)
866    io_name_t              name;           // (don't release)
867    CFMutableDictionaryRef properties = 0; // (needs release)
868    kern_return_t          status     = KERN_SUCCESS;
869
870    // Print out the name of the service.
871
872    status = IORegistryEntryGetNameInPlane(service, options.plane, name);
873    assertion(status == KERN_SUCCESS, "can't obtain name");
874
875    indent(TRUE, serviceDepth, stackOfBits);
876
877    if (options.bold)  boldon();
878
879    print("%s", name);
880
881    if (options.bold)  boldoff();
882
883    // Print out the location of the service.
884
885    status = IORegistryEntryGetLocationInPlane(service, options.plane, location);
886    if (status == KERN_SUCCESS)  print("@%s", location);
887
888    // Print out the class of the service.
889
890    print("  <class ");
891
892    if (options.inheritance)
893    {
894        CFStringRef classCFStr;
895        CFStringRef ancestryCFStr;
896        char *      aCStr;
897
898        classCFStr = IOObjectCopyClass (service);
899        ancestryCFStr = createInheritanceStringForIORegistryClassName (classCFStr);
900
901        aCStr = (char *) CFStringGetCStringPtr (ancestryCFStr, kCFStringEncodingMacRoman);
902        if (NULL != aCStr)
903        {
904            print(aCStr);
905        }
906
907        CFRelease (classCFStr);
908        CFRelease (ancestryCFStr);
909    }
910    else
911    {
912        status = IOObjectGetClass(service, class);
913        assertion(status == KERN_SUCCESS, "can't obtain class");
914
915        print("%s", class);
916    }
917
918    // Prepare to print out the service's useful debug information.
919
920    uint64_t entryID;
921
922    status = IORegistryEntryGetRegistryEntryID(service, &entryID);
923    if (status == KERN_SUCCESS)
924    {
925        print(", id 0x%llx", entryID);
926    }
927
928    // Print out the busy state of the service (for IOService objects).
929
930    if (IOObjectConformsTo(service, "IOService"))
931    {
932        status = IOServiceGetBusyStateAndTime(service, &state, &integer, &accumulated_busy_time);
933        assertion(status == KERN_SUCCESS, "can't obtain state");
934
935        print( ", %sregistered, %smatched, %sactive",
936               state & kIOServiceRegisteredState ? "" : "!",
937               state & kIOServiceMatchedState    ? "" : "!",
938               state & kIOServiceInactiveState   ? "in" : "" );
939
940        print(", busy %ld",
941        (unsigned long)integer);
942
943        if (accumulated_busy_time)
944        {
945            print(" (%lld ms)",
946            accumulated_busy_time / kMillisecondScale);
947        }
948    }
949
950    // Print out the retain count of the service.
951
952    integer = IOObjectGetKernelRetainCount(service);
953
954    print(", retain %ld", (unsigned long)integer);
955
956    println(">");
957
958    // Determine whether the service is a match.
959
960    if (options.list || compare(service, options))
961    {
962        indent(FALSE, serviceDepth, stackOfBits);
963        println("{");
964
965        // Obtain the service's properties.
966
967        status = IORegistryEntryCreateCFProperties( service,
968                                                    &properties,
969                                                    kCFAllocatorDefault,
970                                                    kNilOptions );
971        assertion(status == KERN_SUCCESS, "can't obtain properties");
972
973        // Print out the service's properties.
974
975        CFDictionaryApplyFunction(properties, showitem, &context);
976
977        indent(FALSE, serviceDepth, stackOfBits);
978        println("}");
979        indent(FALSE, serviceDepth, stackOfBits);
980        println("");
981
982        // Release resources.
983
984        CFRelease(properties);
985    }
986}
987
988// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
989
990static void showitem(const void * key, const void * value, void * parameter)
991{
992    struct context * context = parameter; // (don't release)
993
994    // Print out one of the service's properties.
995
996    indent(FALSE, context->serviceDepth, context->stackOfBits);
997    print("  ");
998    cfshow(key);
999    print(" = ");
1000
1001    if (context->options.format)
1002    {
1003        printProp(key, value, context);
1004    }
1005    else
1006    {
1007        cfshow(value);
1008        println("");
1009    }
1010}
1011
1012// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1013
1014static void indent(Boolean isNode, UInt32 serviceDepth, UInt64 stackOfBits)
1015{
1016    // stackOfBits representation, given current zero-based depth is n:
1017    //   bit n+1             = does depth n have children?       1=yes, 0=no
1018    //   bit [n, .. i .., 0] = does depth i have more siblings?  1=yes, 0=no
1019
1020    UInt32 index;
1021
1022    if (isNode)
1023    {
1024        for (index = 0; index < serviceDepth; index++)
1025            print( (stackOfBits & (1 << index)) ? "| " : "  " );
1026
1027        print("+-o ");
1028    }
1029    else // if (!isNode)
1030    {
1031        for (index = 0; index <= serviceDepth + 1; index++)
1032            print( (stackOfBits & (1 << index)) ? "| " : "  " );
1033    }
1034}
1035
1036// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1037
1038void usage()
1039{
1040    fprintf( stderr,
1041     "usage: ioreg [-abfilrtx] [-c class] [-d depth] [-k key] [-n name] [-p plane] [-w width]\n"
1042     "where options are:\n"
1043     "\t-a archive output\n"
1044     "\t-b show object name in bold\n"
1045     "\t-c list properties of objects with the given class\n"
1046     "\t-d limit tree to the given depth\n"
1047     "\t-f enable smart formatting\n"
1048     "\t-i show object inheritance\n"
1049     "\t-k list properties of objects with the given key\n"
1050     "\t-l list properties of all objects\n"
1051     "\t-n list properties of objects with the given name\n"
1052     "\t-p traverse registry over the given plane (IOService is default)\n"
1053     "\t-r show subtrees rooted by the given criteria\n"
1054     "\t-t show location of each subtree\n"
1055     "\t-w clip output to the given line width (0 is unlimited)\n"
1056     "\t-x show data and numbers as hexadecimal\n"
1057     );
1058    exit(1);
1059}
1060
1061// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1062
1063static char * termcapstr_boldon  = 0;
1064static char * termcapstr_boldoff = 0;
1065
1066static int termcapstr_outc(int c)
1067{
1068    return putchar(c);
1069}
1070
1071static void boldinit()
1072{
1073    char *      term;
1074    static char termcapbuf[64];
1075    char *      termcapbufptr = termcapbuf;
1076
1077    term = getenv("TERM");
1078
1079    if (term)
1080    {
1081        if (tgetent(NULL, term) > 0)
1082        {
1083            termcapstr_boldon  = tgetstr("md", &termcapbufptr);
1084            termcapstr_boldoff = tgetstr("me", &termcapbufptr);
1085
1086            assertion(termcapbufptr - termcapbuf <= sizeof(termcapbuf), "can't obtain terminfo");
1087        }
1088    }
1089
1090    if (termcapstr_boldon  == 0)  termcapstr_boldon  = "";
1091    if (termcapstr_boldoff == 0)  termcapstr_boldoff = "";
1092}
1093
1094static void boldon()
1095{
1096    tputs(termcapstr_boldon, 1, termcapstr_outc);
1097}
1098
1099static void boldoff()
1100{
1101    tputs(termcapstr_boldoff, 1, termcapstr_outc);
1102}
1103
1104// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1105
1106static char * printbuf     = 0;
1107static int    printbufclip = FALSE;
1108static int    printbufleft = 0;
1109static int    printbufsize = 0;
1110
1111static void printinit(int width)
1112{
1113    if (width)
1114    {
1115        printbuf     = malloc(width);
1116        printbufleft = width;
1117        printbufsize = width;
1118
1119        assertion(printbuf != NULL, "can't allocate buffer");
1120    }
1121}
1122
1123static void printva(const char * format, va_list arguments)
1124{
1125    if (printbufsize)
1126    {
1127        char * c;
1128        int    count = vsnprintf(printbuf, printbufleft, format, arguments);
1129
1130        while ( (c = strchr(printbuf, '\n')) )  *c = ' ';    // (strip newlines)
1131
1132        printf("%s", printbuf);
1133
1134        if (count >= printbufleft)
1135        {
1136            count = printbufleft - 1;
1137            printbufclip = TRUE;
1138        }
1139
1140        printbufleft -= count;   // (printbufleft never hits zero, stops at one)
1141    }
1142    else
1143    {
1144        vprintf(format, arguments);
1145    }
1146}
1147
1148static void print(const char * format, ...)
1149{
1150    va_list arguments;
1151    va_start(arguments, format);
1152    printva(format, arguments);
1153    va_end(arguments);
1154}
1155
1156static void println(const char * format, ...)
1157{
1158    va_list arguments;
1159    va_start(arguments, format);
1160    printva(format, arguments);
1161    va_end(arguments);
1162
1163    if (printbufclip)  printf("$");
1164
1165    printf("\n");
1166
1167    printbufclip = FALSE;
1168    printbufleft = printbufsize;
1169}
1170
1171// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1172
1173static Boolean cfshowhex;
1174
1175static void cfshowinit(Boolean hex)
1176{
1177    cfshowhex = hex;
1178}
1179
1180static void cfshow(CFTypeRef object)
1181{
1182    CFTypeID type = CFGetTypeID(object);
1183
1184    if      ( type == CFArrayGetTypeID()      )  cfarrayshow(object);
1185    else if ( type == CFBooleanGetTypeID()    )  cfbooleanshow(object);
1186    else if ( type == CFDataGetTypeID()       )  cfdatashow(object);
1187    else if ( type == CFDictionaryGetTypeID() )  cfdictionaryshow(object);
1188    else if ( type == CFNumberGetTypeID()     )  cfnumbershow(object);
1189    else if ( type == CFSetGetTypeID()        )  cfsetshow(object);
1190    else if ( type == CFStringGetTypeID()     )  cfstringshow(object);
1191    else print("<unknown object>");
1192}
1193
1194static void cfarrayshowapplier(const void * value, void * parameter)
1195{
1196    Boolean * first = (Boolean *) parameter;
1197
1198    if (*first)
1199        *first = FALSE;
1200    else
1201        print(",");
1202
1203    cfshow(value);
1204}
1205
1206static void cfarrayshow(CFArrayRef object)
1207{
1208    Boolean first = TRUE;
1209    CFRange range = { 0, CFArrayGetCount(object) };
1210
1211    print("(");
1212    CFArrayApplyFunction(object, range, cfarrayshowapplier, &first);
1213    print(")");
1214}
1215
1216static void cfbooleanshow(CFBooleanRef object)
1217{
1218    print(CFBooleanGetValue(object) ? "Yes" : "No");
1219}
1220
1221static void cfdatashow(CFDataRef object)
1222{
1223    UInt32        asciiNormalCount = 0;
1224    UInt32        asciiSymbolCount = 0;
1225    const UInt8 * bytes;
1226    CFIndex       index;
1227    CFIndex       length;
1228
1229    print("<");
1230    length = CFDataGetLength(object);
1231    bytes  = CFDataGetBytePtr(object);
1232
1233    //
1234    // This algorithm detects ascii strings, or a set of ascii strings, inside a
1235    // stream of bytes.  The string, or last string if in a set, needn't be null
1236    // terminated.  High-order symbol characters are accepted, unless they occur
1237    // too often (80% of characters must be normal).  Zero padding at the end of
1238    // the string(s) is valid.  If the data stream is only one byte, it is never
1239    // considered to be a string.
1240    //
1241
1242    for (index = 0; index < length; index++)  // (scan for ascii string/strings)
1243    {
1244        if (bytes[index] == 0)       // (detected null in place of a new string,
1245        {                            //  ensure remainder of the string is null)
1246            break;          // (either end of data or a non-null byte in stream)
1247        }
1248        else                         // (scan along this potential ascii string)
1249        {
1250            for (; index < length; index++)
1251            {
1252                if (isprint(bytes[index]))
1253                    asciiNormalCount++;
1254                else if (bytes[index] >= 128 && bytes[index] <= 254)
1255                    asciiSymbolCount++;
1256                else
1257                    break;
1258            }
1259
1260            if (index < length && bytes[index] == 0)          // (end of string)
1261                continue;
1262            else             // (either end of data or an unprintable character)
1263                break;
1264        }
1265    }
1266
1267    if ((asciiNormalCount >> 2) < asciiSymbolCount)    // (is 80% normal ascii?)
1268        index = 0;
1269    else if (length == 1)                                 // (is just one byte?)
1270        index = 0;
1271    else if (cfshowhex)
1272        index = 0;
1273
1274    if (index >= length && asciiNormalCount) // (is a string or set of strings?)
1275    {
1276        Boolean quoted = FALSE;
1277
1278        for (index = 0; index < length; index++)
1279        {
1280            if (bytes[index])
1281            {
1282                if (quoted == FALSE)
1283                {
1284                    quoted = TRUE;
1285                    if (index)
1286                        print(",\"");
1287                    else
1288                        print("\"");
1289                }
1290                print("%c", bytes[index]);
1291            }
1292            else
1293            {
1294                if (quoted == TRUE)
1295                {
1296                    quoted = FALSE;
1297                    print("\"");
1298                }
1299                else
1300                    break;
1301            }
1302        }
1303        if (quoted == TRUE)
1304            print("\"");
1305    }
1306    else                                  // (is not a string or set of strings)
1307    {
1308        for (index = 0; index < length; index++)  print("%02x", bytes[index]);
1309    }
1310
1311    print(">");
1312}
1313
1314static void cfdictionaryshowapplier( const void * key,
1315                                     const void * value,
1316                                     void *       parameter )
1317{
1318    Boolean * first = (Boolean *) parameter;
1319
1320    if (*first)
1321        *first = FALSE;
1322    else
1323        print(",");
1324
1325    cfshow(key);
1326    print("=");
1327    cfshow(value);
1328}
1329
1330static void cfdictionaryshow(CFDictionaryRef object)
1331{
1332    Boolean first = TRUE;
1333
1334    print("{");
1335    CFDictionaryApplyFunction(object, cfdictionaryshowapplier, &first);
1336    print("}");
1337}
1338
1339static void cfnumbershow(CFNumberRef object)
1340{
1341    long long number;
1342
1343    if (CFNumberGetValue(object, kCFNumberLongLongType, &number))
1344    {
1345        if (cfshowhex)
1346            print("0x%qx", (unsigned long long)number);
1347        else
1348            print("%qu", (unsigned long long)number);
1349    }
1350}
1351
1352static void cfsetshowapplier(const void * value, void * parameter)
1353{
1354    Boolean * first = (Boolean *) parameter;
1355
1356    if (*first)
1357        *first = FALSE;
1358    else
1359        print(",");
1360
1361    cfshow(value);
1362}
1363
1364static void cfsetshow(CFSetRef object)
1365{
1366    Boolean first = TRUE;
1367    print("[");
1368    CFSetApplyFunction(object, cfsetshowapplier, &first);
1369    print("]");
1370}
1371
1372static void cfstringshow(CFStringRef object)
1373{
1374    const char * c = CFStringGetCStringPtr(object, kCFStringEncodingMacRoman);
1375
1376    if (c)
1377        print("\"%s\"", c);
1378    else
1379    {
1380        CFIndex bufferSize = CFStringGetLength(object) + 1;
1381        char *  buffer     = malloc(bufferSize);
1382
1383        if (buffer)
1384        {
1385            if ( CFStringGetCString(
1386                    /* string     */ object,
1387                    /* buffer     */ buffer,
1388                    /* bufferSize */ bufferSize,
1389                    /* encoding   */ kCFStringEncodingMacRoman ) )
1390                print("\"%s\"", buffer);
1391
1392            free(buffer);
1393        }
1394    }
1395}
1396
1397// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1398
1399static CFStringRef createInheritanceStringForIORegistryClassName(CFStringRef name)
1400{
1401	CFStringRef				curClassCFStr;
1402	CFStringRef				oldClassCFStr;
1403	CFMutableStringRef		outCFStr;
1404
1405	outCFStr = CFStringCreateMutable (NULL, 512);
1406	CFStringInsert (outCFStr, 0, name);
1407
1408	curClassCFStr = CFStringCreateCopy (NULL, name);
1409
1410	for (;;)
1411	{
1412		oldClassCFStr = curClassCFStr;
1413		curClassCFStr = IOObjectCopySuperclassForClass (curClassCFStr);
1414		CFRelease (oldClassCFStr);
1415
1416		if (FALSE == CFEqual (curClassCFStr, CFSTR ("OSObject")))
1417		{
1418			CFStringInsert (outCFStr, 0, CFSTR (":"));
1419			CFStringInsert (outCFStr, 0, curClassCFStr);
1420		}
1421		else
1422		{
1423			break;
1424		}
1425	}
1426
1427    if (curClassCFStr) CFRelease(curClassCFStr);
1428
1429	// Return the CFMutableStringRef as a CFStringRef because it is derived and compatible:
1430	return (CFStringRef) outCFStr;
1431}
1432
1433// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1434
1435static void printProp(CFStringRef key, CFTypeRef value, struct context * context)
1436{
1437    kern_return_t       status     = KERN_SUCCESS;
1438    Boolean             valueShown = FALSE;  // Flag is set when property is printed
1439    io_registry_entry_t thisObj;
1440
1441    thisObj = context->service;
1442
1443	// Match "reg" property for PCI devices.
1444	if (CFStringCompare(key, CFSTR("reg"), 0 ) == 0)
1445	{
1446		io_registry_entry_t parentObj;  // (needs release)
1447		io_name_t parentName;
1448
1449		// If the parent entry in the IODeviceTree plane is "pci",
1450		// then we've found what we're looking for.
1451
1452		status = IORegistryEntryGetParentEntry( thisObj,
1453												kIODeviceTreePlane,
1454												&parentObj );
1455		if (status == KERN_SUCCESS)
1456		{
1457            status = IORegistryEntryGetNameInPlane( parentObj,
1458                                                    kIODeviceTreePlane,
1459                                                    parentName );
1460            assertion(status == KERN_SUCCESS, "could not get name of parent");
1461
1462            IOObjectRelease(parentObj);
1463
1464            if (strncmp(parentName, "pci", 3) == 0)
1465            {
1466                printPhysAddr(value, context);
1467                valueShown = TRUE;
1468            }
1469		}
1470	}
1471
1472	// Match "assigned-addresses" property.
1473	else if (CFStringCompare(key, CFSTR("assigned-addresses"), 0) == 0)
1474	{
1475		printPhysAddr(value, context);
1476		valueShown = TRUE;
1477	}
1478
1479	// Match "slot-names" property.
1480	else if (CFStringCompare(key, CFSTR("slot-names"), 0) == 0)
1481	{
1482		printSlotNames(value, context);
1483		valueShown = TRUE;
1484	}
1485
1486	// Match "ranges" property.
1487	else if (CFStringCompare(key, CFSTR("ranges"), 0) == 0)
1488	{
1489		printPCIRanges(value, context);
1490		valueShown = TRUE;
1491	}
1492
1493	// Match "interrupt-map" property.
1494	else if (CFStringCompare(key, CFSTR("interrupt-map"), 0) == 0)
1495	{
1496		printInterruptMap(value, context);
1497		valueShown = TRUE;
1498	}
1499
1500	// Match "interrupts" property.
1501	else if ( CFStringCompare( key, CFSTR("interrupts"), 0) == 0 )
1502	{
1503		printInterrupts( value, context );
1504		valueShown = TRUE;
1505	}
1506
1507	// Match "interrupt-parent" property.
1508	else if ( CFStringCompare( key, CFSTR("interrupt-parent"), 0) == 0 )
1509	{
1510		printInterruptParent( value, context );
1511		valueShown = TRUE;
1512	}
1513
1514    // Print the value if it doesn't have a formatter.
1515    if (valueShown == FALSE)
1516    {
1517        if (CFGetTypeID(value) == CFDataGetTypeID())
1518        {
1519            printData(value, context);
1520        }
1521        else
1522        {
1523            cfshow(value);
1524            println("");
1525        }
1526    }
1527}
1528
1529/* The following data structures, masks and shift values are used to decode
1530 * physical address properties as defined by IEEE 1275-1994.  The format is
1531 * used in 'reg' and 'assigned-address' properties.
1532 *
1533 * The format of the physHi word is as follows:
1534 *
1535 * npt000ss bbbbbbbb dddddfff rrrrrrrr
1536 *
1537 * n         1 = Relocatable, 0 = Absolute               (1 bit)
1538 * p         1 = Prefetchable                            (1 bit)
1539 * t         1 = Alias                                   (1 bit)
1540 * ss        Space code (Config, I/O, Mem, 64-bit Mem)   (2 bits)
1541 * bbbbbbbb  Bus number                                  (8 bits)
1542 * ddddd     Device number                               (5 bits)
1543 * fff       Function number                             (3 bits)
1544 * rrrrrrrr  Register number                             (8 bits)
1545 */
1546
1547struct physAddrProperty {
1548    UInt32  physHi;
1549    UInt32  physMid;
1550    UInt32  physLo;
1551    UInt32  sizeHi;
1552    UInt32  sizeLo;
1553};
1554
1555#define kPhysAbsoluteMask   0x80000000
1556#define kPhysPrefetchMask   0x40000000
1557#define kPhysAliasMask      0x20000000
1558#define kPhysSpaceMask      0x03000000
1559#define kPhysSpaceShift     24
1560#define kPhysBusMask        0x00FF0000
1561#define kPhysBusShift       16
1562#define kPhysDeviceMask     0x0000F800
1563#define kPhysDeviceShift    11
1564#define kPhysFunctionMask   0x00000700
1565#define kPhysFunctionShift  8
1566#define kPhysRegisterMask   0x000000FF
1567#define kPhysRegisterShift  0
1568
1569static SInt32
1570getRecursivePropValue( io_registry_entry_t thisRegEntry, CFStringRef propertyNameToLookFor )
1571{
1572	SInt32		returnValue;
1573	CFTypeRef	ptr;
1574
1575    ptr = IORegistryEntrySearchCFProperty(thisRegEntry,
1576                                          kIODeviceTreePlane,
1577                                          propertyNameToLookFor,
1578                                          kCFAllocatorDefault,
1579                                          kIORegistryIterateParents | kIORegistryIterateRecursively);
1580    assertion( ptr != NULL, "unable to get properties" );
1581
1582	returnValue = *(SInt32 *)CFDataGetBytePtr( (CFDataRef) ptr );
1583
1584	CFRelease( ptr );
1585	return( returnValue );
1586}
1587
1588static void printPhysAddr(CFTypeRef value, struct context * context)
1589{
1590    CFIndex length;                      // stores total byte count in this prop.
1591    struct physAddrProperty *physAddr;   // points to current physAddr property
1592    UInt64 numPhysAddr,                  // how many physAddr's to decode?
1593           count;                        // loop counter variable
1594    UInt32 tmpCell;                      // temp storage for a single word
1595
1596    UInt32 busNumber,                    // temp storage for decoded values
1597           deviceNumber,
1598           functionNumber,
1599           registerNumber;
1600    const char *addressType,
1601               *isPrefetch,
1602               *isAlias,
1603               *isAbsolute;
1604
1605    // Ensure that the object passed in is in fact a CFData object.
1606
1607    assertion(CFGetTypeID(value) == CFDataGetTypeID(), "invalid phys addr");
1608
1609    // Make sure there is actually data in the object.
1610    length = CFDataGetLength((CFDataRef)value);
1611
1612    if (length == 0)
1613    {
1614        println("<>");
1615        return;
1616    }
1617
1618    numPhysAddr = length / sizeof(struct physAddrProperty);
1619    physAddr = (struct physAddrProperty *)CFDataGetBytePtr((CFDataRef)value);
1620
1621    println("");
1622
1623    for (count = 0; count < numPhysAddr; count++)
1624    {
1625        tmpCell = physAddr[count].physHi;  // copy physHi word to a temp var
1626
1627        // Decode the fields in the physHi word.
1628
1629        busNumber      = (tmpCell & kPhysBusMask) >> kPhysBusShift;
1630        deviceNumber   = (tmpCell & kPhysDeviceMask) >> kPhysDeviceShift;
1631        functionNumber = (tmpCell & kPhysFunctionMask) >> kPhysFunctionShift;
1632        registerNumber = (tmpCell & kPhysRegisterMask) >> kPhysRegisterShift;
1633        isAbsolute     = ((tmpCell & kPhysAbsoluteMask) != 0) ? "abs" : "rel";
1634        isPrefetch     = ((tmpCell & kPhysPrefetchMask) != 0) ? ", prefetch" : "";
1635        isAlias        = ((tmpCell & kPhysAliasMask) != 0) ? ", alias" : "";
1636        switch ((tmpCell & kPhysSpaceMask) >> kPhysSpaceShift)
1637        {
1638            case 0:  addressType = "Config"; break;
1639            case 1:  addressType = "I/O";    break;
1640            case 2:  addressType = "Mem";    break;
1641            case 3:  addressType = "64-bit"; break;
1642            default: addressType = "?";      break;
1643        }
1644
1645        // Format and print the information for this entry.
1646
1647        indent(FALSE, context->serviceDepth, context->stackOfBits);
1648        println("    %02lu: phys.hi: %08lx phys.mid: %08lx phys.lo: %08lx",
1649                (unsigned long)count,
1650                (unsigned long)physAddr[count].physHi,
1651                (unsigned long)physAddr[count].physMid,
1652                (unsigned long)physAddr[count].physLo );
1653
1654        indent(FALSE, context->serviceDepth, context->stackOfBits);
1655        println("        size.hi: %08lx size.lo: %08lx",
1656                (unsigned long)physAddr[count].sizeHi,
1657                (unsigned long)physAddr[count].sizeLo );
1658
1659        indent(FALSE, context->serviceDepth, context->stackOfBits);
1660        println("        bus: %lu dev: %lu func: %lu reg: %lu",
1661                (unsigned long)busNumber,
1662                (unsigned long)deviceNumber,
1663                (unsigned long)functionNumber,
1664                (unsigned long)registerNumber );
1665
1666        indent(FALSE, context->serviceDepth, context->stackOfBits);
1667        println("        type: %s flags: %s%s%s",
1668                addressType,
1669                isAbsolute,
1670                isPrefetch,
1671                isAlias );
1672    }
1673}
1674
1675static void printSlotNames(CFTypeRef value, struct context * context)
1676{
1677    CFIndex length;
1678    char * bytePtr;
1679    UInt32 count;
1680    UInt32 * avail_slots;
1681
1682    // Ensure that the object passed in is in fact a CFData object.
1683
1684    assertion(CFGetTypeID(value) == CFDataGetTypeID(), "invalid phys addr");
1685
1686    // Make sure there is actually data in the object.
1687
1688    length = CFDataGetLength((CFDataRef)value);
1689
1690    if (length == 0)
1691    {
1692        println("<>");
1693        return;
1694    }
1695
1696    avail_slots = (UInt32 *)CFDataGetBytePtr((CFDataRef)value);
1697    bytePtr = (char *)avail_slots + sizeof(UInt32);
1698
1699    // Ignore entries that have no named slots.
1700
1701    if (*avail_slots == 0)
1702    {
1703        println("<>");
1704        return;
1705    }
1706
1707    println("");
1708
1709    // Cycle through all 32 bit positions and print slot names.
1710
1711    for (count = 0; count < 32; count++)
1712    {
1713        if ((*avail_slots & (1 << count)) != 0)
1714        {
1715            indent(FALSE, context->serviceDepth, context->stackOfBits);
1716            println("    %02lu: %s", (unsigned long)count, bytePtr);
1717            bytePtr += strlen(bytePtr) + 1;  // advance to next string
1718        }
1719    }
1720}
1721
1722static void printPCIRanges(CFTypeRef value, struct context * context)
1723{
1724    kern_return_t		   status = KERN_SUCCESS;
1725    CFIndex 			   length;
1726    UInt32                 *quadletPtr;
1727    SInt32				   parentACells, childACells, childSCells, elemSize;
1728    io_registry_entry_t    parentObj;   // must be released
1729    UInt64                 i,j,nRanges;
1730    SInt32				   counts[3];
1731    const char			   *titles[] = {"-child--", "-parent-", "-size---"};
1732
1733    // Ensure that the object passed in is in fact a CFData object.
1734    assertion(CFGetTypeID(value) == CFDataGetTypeID(), "invalid ranges");
1735
1736    // Make sure there is actually data in the object.
1737    length = CFDataGetLength((CFDataRef)value);
1738
1739    if (length == 0)
1740    {
1741        println("<>");
1742        return;
1743    }
1744
1745    quadletPtr = (UInt32 *)CFDataGetBytePtr((CFDataRef)value);
1746
1747    // Get #address-cells of device-tree parent
1748    status = IORegistryEntryGetParentEntry( context->service, kIODeviceTreePlane, &parentObj );
1749    assertion(status == KERN_SUCCESS, "unable to get device tree parent");
1750
1751	parentACells = getRecursivePropValue( parentObj, CFSTR( "#address-cells" ) );
1752
1753    IOObjectRelease( parentObj );
1754
1755    // Get #address-cells and #size-cells for owner
1756	childACells = getRecursivePropValue( context->service, CFSTR( "#address-cells" ) );
1757	childSCells = getRecursivePropValue( context->service, CFSTR( "#size-cells"    ) );
1758
1759    // ranges property is a list of [[child addr][parent addr][size]]
1760    elemSize = childACells + parentACells + childSCells;
1761
1762    // print a title line
1763    println("");
1764    indent(FALSE, context->serviceDepth, context->stackOfBits);
1765    print("    ");
1766
1767    // set up array of cell counts (only used to print title)
1768    counts[0] = childACells;
1769    counts[1] = parentACells;
1770    counts[2] = childSCells;
1771
1772    for (j = 0; j < 3; j++)
1773    {
1774        print("%s", titles[j]);  // titles is init'ed at start of func.
1775        if (counts[j] > 1)
1776        {
1777            print("-");
1778            for( i = 2; i <= counts[j]; i++)
1779            {
1780                if(i == counts[j])
1781                    print("-------- ");
1782                else
1783                    print("---------");
1784            }
1785        }
1786        else
1787            print(" ");
1788    }
1789    println("");
1790
1791    nRanges = length/(elemSize * sizeof(UInt32));
1792
1793    for(j = 0; j < nRanges; j++)
1794    {
1795        indent(FALSE, context->serviceDepth, context->stackOfBits);
1796        print("    ");
1797        for(i = 0; i < elemSize; i++) print("%08lx ", (unsigned long)*quadletPtr++);
1798        println("");
1799    }
1800}
1801
1802// constructs a path string for a node in the device tree
1803static void makepath(io_registry_entry_t target, io_string_t path)
1804{
1805    kern_return_t status = KERN_SUCCESS;
1806
1807    status = IORegistryEntryGetPath(target, kIODeviceTreePlane, path);
1808    assertion(status == KERN_SUCCESS, "unable to get path");
1809
1810    strlcpy(path, strchr(path, ':') + 1, sizeof(io_string_t));
1811}
1812
1813static Boolean lookupPHandle(UInt32 phandle, io_registry_entry_t * device)
1814{
1815    CFDictionaryRef props;
1816    Boolean         ret = FALSE;  // pre-set to failure
1817    CFStringRef     key = CFSTR(kIOPropertyMatchKey);
1818    CFDictionaryRef value;
1819    CFStringRef     phandleKey = CFSTR("AAPL,phandle");
1820    CFDataRef		data;
1821
1822    data = CFDataCreate(NULL, (void *)&phandle, sizeof(UInt32));
1823
1824    props = CFDictionaryCreate( NULL,
1825                                (void *)&phandleKey,
1826                                (void *)&data,
1827                                1,
1828                                &kCFCopyStringDictionaryKeyCallBacks,
1829                                &kCFTypeDictionaryValueCallBacks );
1830
1831    value = CFDictionaryCreate( NULL,
1832                                (void *)&key,
1833                                (void *)&props,
1834                                1,
1835                                &kCFCopyStringDictionaryKeyCallBacks,
1836                                &kCFTypeDictionaryValueCallBacks );
1837
1838   /* This call consumes 'value', so do not release it.
1839    */
1840    *device = IOServiceGetMatchingService(kIOMasterPortDefault, value);
1841
1842    if (*device)
1843        ret = TRUE;
1844
1845    CFRelease(props);
1846    CFRelease(data);
1847
1848    return(ret);
1849}
1850
1851static void printInterruptMap(CFTypeRef value, struct context * context)
1852{
1853    io_registry_entry_t		intParent;
1854    io_string_t             path;
1855    SInt32					childCells, parentCells;
1856    UInt32					*position, *end;
1857    CFIndex						length, count, index;
1858
1859    // Get #address-cells and #interrupt-cells for owner
1860	childCells = getRecursivePropValue( context->service, CFSTR("#address-cells"   ) )
1861			   + getRecursivePropValue( context->service, CFSTR("#interrupt-cells" ) );
1862
1863    // Walk through each table entry.
1864    position = (UInt32 *)CFDataGetBytePtr((CFDataRef)value);
1865    length = CFDataGetLength((CFDataRef)value)/sizeof(UInt32);
1866    end = position + length;
1867    count = 0;
1868
1869    println("");
1870
1871    while (position < end)
1872    {
1873        indent(FALSE, context->serviceDepth, context->stackOfBits);
1874        print("    %02ld: ", (unsigned long)count);
1875
1876        // Display the child's unit interrupt specifier.
1877        print("  child: ");
1878        for (index = 0; index < childCells; index++) {
1879            print("%08lx ", (unsigned long)*position++);
1880        }
1881        println("");
1882
1883        // Lookup the phandle and retreive needed info.
1884        assertion( lookupPHandle(*position, &intParent), "error looking up phandle" );
1885
1886		parentCells = getRecursivePropValue( intParent, CFSTR( "#address-cells"   ) )
1887					+ getRecursivePropValue( intParent, CFSTR( "#interrupt-cells" ) );
1888
1889        *path = '\0';
1890        makepath(intParent, path);
1891
1892        IOObjectRelease(intParent);
1893
1894        // Display the phandle, corresponding device path, and
1895        // the parent interrupt specifier.
1896        indent(FALSE, context->serviceDepth, context->stackOfBits);
1897        println("        phandle: %08lx (%s)", (unsigned long)*position++, path);
1898
1899        indent(FALSE, context->serviceDepth, context->stackOfBits);
1900        print("         parent: ");
1901        for (index = 0; index < parentCells; index++) {
1902            print("%08lx ", (unsigned long)*position++);
1903        }
1904        println("");
1905
1906        count++;
1907    }
1908}
1909
1910static void printInterrupts(CFTypeRef value, struct context * context)
1911{
1912    UInt32					*position, *end;
1913    CFIndex					length, count, index;
1914
1915    // Walk through each table entry.
1916    position = (UInt32 *)CFDataGetBytePtr((CFDataRef)value);
1917    length   = CFDataGetLength((CFDataRef)value) / sizeof(UInt32);
1918    end      = position + length;
1919    count    = 0;
1920	index    = 0;
1921
1922    println("");
1923
1924    while (position < end)
1925    {
1926        indent(FALSE, context->serviceDepth, context->stackOfBits);
1927        print("    %02ld: ", (unsigned long)index);
1928
1929		if ( count < (length-1) )
1930		{
1931			print("specifier: %08lx (vector: %02lx) sense: %08lx (",
1932                (unsigned long)*position,
1933                (unsigned long)((*position) & 0x000000FF),
1934                (unsigned long)*(position+1) );
1935			position ++;
1936			count    ++;
1937			if ( (*position & 0x00000002 ) )	// HyperTransport
1938			{
1939				print( "HyperTransport vector: %04lx, ",
1940                    (unsigned long)((*position >> 16) & 0x0000FFFF));
1941			}
1942
1943			println( "%s)", (*position & 1)? "level" : "edge" );
1944		}
1945		else
1946		{
1947			println("parent interrupt-map entry: %08lx",
1948                (unsigned long)*position );
1949		}
1950
1951		position ++;
1952        count ++;
1953		index ++;
1954    }
1955}
1956
1957static void printInterruptParent( CFTypeRef value, struct context * context )
1958{
1959io_registry_entry_t		parentRegEntry;
1960io_string_t             path;
1961UInt32					* pHandleValue = (UInt32 *) CFDataGetBytePtr( (CFDataRef) value );
1962
1963	if ( lookupPHandle( *pHandleValue, &parentRegEntry ) )
1964	{
1965        *path = '\0';
1966		makepath( parentRegEntry, path );
1967
1968		print( "<%08lx>", (unsigned long)*pHandleValue );
1969		if ( *path != '\0' )
1970			print( " (%s)", path );
1971		println( "" );
1972
1973		IOObjectRelease( parentRegEntry );
1974	}
1975}
1976
1977static char ToAscii(UInt32 nibble)
1978{
1979    nibble &= 0x0F;
1980
1981    if (nibble <= 9)
1982        return((char)nibble + '0');
1983    else
1984        return((char)nibble - 10 + 'A');
1985}
1986
1987static void printData(CFTypeRef value, struct context * context)
1988{
1989    UInt32        asciiNormalCount = 0;
1990    UInt32        asciiSymbolCount = 0;
1991    const UInt8 * bytes;
1992    CFIndex       index;
1993    CFIndex       length;
1994
1995    length = CFDataGetLength(value);
1996    bytes  = CFDataGetBytePtr(value);
1997
1998    //
1999    // This algorithm detects ascii strings, or a set of ascii strings, inside a
2000    // stream of bytes.  The string, or last string if in a set, needn't be null
2001    // terminated.  High-order symbol characters are accepted, unless they occur
2002    // too often (80% of characters must be normal).  Zero padding at the end of
2003    // the string(s) is valid.  If the data stream is only one byte, it is never
2004    // considered to be a string.
2005    //
2006
2007    for (index = 0; index < length; index++)  // (scan for ascii string/strings)
2008    {
2009        if (bytes[index] == 0)       // (detected null in place of a new string,
2010        {                            //  ensure remainder of the string is null)
2011            for (; index < length && bytes[index] == 0; index++) { }
2012
2013            break;          // (either end of data or a non-null byte in stream)
2014        }
2015        else                         // (scan along this potential ascii string)
2016        {
2017            for (; index < length; index++)
2018            {
2019                if (isprint(bytes[index]))
2020                    asciiNormalCount++;
2021                else if (bytes[index] >= 128 && bytes[index] <= 254)
2022                    asciiSymbolCount++;
2023                else
2024                    break;
2025            }
2026
2027            if (index < length && bytes[index] == 0)          // (end of string)
2028                continue;
2029            else             // (either end of data or an unprintable character)
2030                break;
2031        }
2032    }
2033
2034    if ((asciiNormalCount >> 2) < asciiSymbolCount)    // (is 80% normal ascii?)
2035        index = 0;
2036    else if (length == 1)                                 // (is just one byte?)
2037        index = 0;
2038    else if (cfshowhex)
2039        index = 0;
2040
2041    if (index >= length && asciiNormalCount) // (is a string or set of strings?)
2042    {
2043        Boolean quoted = FALSE;
2044
2045        print("<");
2046
2047        for (index = 0; index < length; index++)
2048        {
2049            if (bytes[index])
2050            {
2051                if (quoted == FALSE)
2052                {
2053                    quoted = TRUE;
2054                    if (index)
2055                        print(",\"");
2056                    else
2057                        print("\"");
2058                }
2059                print("%c", bytes[index]);
2060            }
2061            else
2062            {
2063                if (quoted == TRUE)
2064                {
2065                    quoted = FALSE;
2066                    print("\"");
2067                }
2068                else
2069                    break;
2070            }
2071        }
2072        if (quoted == TRUE)
2073            print("\"");
2074
2075        print(">");
2076    }
2077
2078    else if (length > 8)                        // (is not a string or set of strings)
2079    {
2080        SInt8  work[ 256 ];
2081        SInt8* p;
2082        UInt32    i;
2083        UInt32 offset;
2084        CFIndex totalBytes;
2085        CFIndex nBytesToDraw;
2086        UInt32 bytesPerLine;
2087        UInt8  c;
2088
2089        totalBytes = length;	// assume length is greater than zero
2090
2091        // Calculate number of bytes per line to print, use as much screen
2092        // as possible.  The numbers used are derived by counting the number
2093        // of characters that are always printed (data address offset, white
2094        // space, etc ~= 20), indentation from the tree structure (2*depth)
2095        // and 4 characters printed per byte (two hex digits, one space, and
2096        // one ascii char).
2097
2098        bytesPerLine = (context->options.width - 20 - (2*context->serviceDepth))/4;
2099
2100        // Make sure we don't overflow the work buffer (256 bytes)
2101        bytesPerLine = bytesPerLine > 32 ? 32 : bytesPerLine;
2102
2103        for ( offset = 0; offset < totalBytes; offset += bytesPerLine )
2104        {
2105            UInt32 offsetCopy;
2106            UInt16 text;
2107
2108            println("");
2109
2110            if ( ( offset + bytesPerLine ) <= totalBytes )
2111                nBytesToDraw = bytesPerLine;
2112            else
2113                nBytesToDraw = totalBytes - offset;
2114
2115            offsetCopy = offset;
2116
2117            // Convert offset to ASCII.
2118            work[ 8 ] = ':';
2119            p = &work[ 7 ];
2120
2121            while ( offsetCopy != 0 )
2122            {
2123                *p-- = ToAscii( offsetCopy & 0x0F );
2124                offsetCopy >>= 4;
2125            }
2126
2127            // Insert leading zeros.
2128            while ( p >= work )
2129                *p-- = '0';
2130
2131            // Add kBytesPerLine bytes of data.
2132            p = &work[ 9 ];
2133            for ( i = 0; i < nBytesToDraw; i++ )
2134            {
2135                c = bytes[ offset + i ];
2136                *p++ = ' ';
2137                *p++ = ToAscii( ( c & 0xF0 ) >> 4 );
2138                *p++ = ToAscii( c & 0x0F );
2139            }
2140
2141            // Add padding spaces.
2142            for ( ; i < bytesPerLine; i++ )
2143            {
2144                text = ( ( ' ' << 8 ) | ' ' );
2145                *( UInt16 * ) p = text;
2146                p[ 2 ] = ' ';
2147                p += 3;
2148            }
2149
2150            *p++ = ' ';
2151
2152            // Insert ASCII representation of data.
2153            for ( i = 0; i < nBytesToDraw; i++ )
2154            {
2155                c = bytes[ offset + i ];
2156                if ( ( c < ' ' ) || ( c > '~' ) )
2157                    c = '.';
2158
2159                *p++ = c;
2160            }
2161
2162            *p = 0;
2163
2164            // Print this line.
2165            indent(FALSE, context->serviceDepth, context->stackOfBits);
2166            print("    %s", work);
2167
2168        } // for
2169
2170    } // else if (length > 32)
2171    else
2172    {
2173        print("<");
2174        for (index = 0; index < length; index++)  print("%02x", bytes[index]);
2175        print(">");
2176    }
2177
2178    println("");
2179}
2180