xref: /xnu-8796.101.5/tools/lldbmacros/ioreg.py (revision aca3beaa3dfbd42498b42c5e5ce20a938e6554e5)
1from __future__ import absolute_import, division, print_function
2
3from builtins import chr
4from builtins import zip
5from builtins import hex
6from builtins import range
7from builtins import object
8
9from xnu import *
10from utils import *
11from kdp import *
12from core import caching
13from core.pointer import NativePointer
14import sys
15import lldb
16import os
17import sys
18from collections import deque
19
20######################################
21# Globals
22######################################
23plane = None
24
25#####################################
26# Utility functions.
27#####################################
28def CastIOKitClass(obj, target_type):
29    """ Type cast an object to another IOKIT CPP class.
30        params:
31            obj - core.value  object representing some C construct in lldb
32            target_type - str : ex 'OSString *'
33                        - lldb.SBType :
34    """
35    v = obj.GetSBValue()
36    # We need to do that so that LLDB doesn't try to "helpfully"
37    # Guess which instance type it is...
38    v.SetPreferDynamicValue(lldb.eNoDynamicValues)
39    if isinstance(target_type, six.string_types):
40        target_type = gettype(target_type)
41    return value(v.Cast(target_type))
42
43#####################################
44# Classes.
45#####################################
46class PreoslogHeader(object):
47    """
48    Represents preoslog buffer header. There's no symbol in the kernel for it.
49    """
50    valid_magic = "POSL"
51    def __init__(self):
52        self.magic = ""
53        self.offset = 0
54        self.size = 0
55        self.source = 0
56        self.wrapped = 0
57        self.data = None
58
59
60class IOKitSmartPointer(NativePointer):
61    """ IOKit's smart pointer
62
63        Every smart pointer inherits from libkern::intrusive_shared_ptr.
64        The real pointer is wrapped behind ptr_ member.
65    """
66
67    @classmethod
68    def match(cls, sbvalue):
69
70        # Smart pointers in IOKit are OSSharedPtr and OSTaggedSharedPtr
71        name = sbvalue.GetType().GetCanonicalType().GetName()
72        if name.startswith(("OSSharedPtr", "OSTaggedSharedPtr")):
73            return cls()
74
75        return None
76
77    def GetPointerSBValue(self, sbvalue):
78        sbv = sbvalue.GetChildMemberWithName('ptr_')
79        return super(IOKitSmartPointer, self).GetPointerSBValue(sbv)
80
81
82######################################
83# Type Summaries
84######################################
85@lldb_type_summary(['OSObject *'])
86@header("")
87def GetObjectSummary(obj):
88    """ Show info about an OSObject - its vtable ptr and retain count, & more info for simple container classes.
89    """
90    if obj is None:
91        return
92
93    vt = dereference(Cast(obj, 'uintptr_t *')) - 2 * sizeof('uintptr_t')
94    vt = kern.StripKernelPAC(vt)
95    vtype = kern.SymbolicateFromAddress(vt)
96    if len(vtype):
97        vtype_str = " <" + vtype[0].GetName() + ">"
98    else:
99        vtype_str = ""
100    if hasattr(obj, 'retainCount'):
101        retCount = (obj.retainCount & 0xffff)
102        cntnrRetCount = (obj.retainCount >> 16)
103        out_string = "`object 0x{0: <16x}, vt 0x{1: <16x}{2:s}, retain count {3:d}, container retain {4:d}` ".format(obj, vt, vtype_str, retCount, cntnrRetCount)
104    else:
105        out_string = "`object 0x{0: <16x}, vt 0x{1: <16x}{2:s}` ".format(obj, vt, vtype_str)
106
107    ztvAddr = kern.GetLoadAddressForSymbol('_ZTV8OSString')
108    if vt == ztvAddr:
109        out_string += GetString(obj)
110        return out_string
111
112    ztvAddr = kern.GetLoadAddressForSymbol('_ZTV8OSSymbol')
113    if vt == ztvAddr:
114        out_string += GetString(obj)
115        return out_string
116
117    ztvAddr = kern.GetLoadAddressForSymbol('_ZTV8OSNumber')
118    if vt == ztvAddr:
119        out_string += GetNumber(obj)
120        return out_string
121
122    ztvAddr = kern.GetLoadAddressForSymbol('_ZTV9OSBoolean')
123    if vt == ztvAddr:
124        out_string += GetBoolean(obj)
125        return out_string
126
127    ztvAddr = kern.GetLoadAddressForSymbol('_ZTV7OSArray')
128    if vt == ztvAddr:
129        out_string += "(" + GetArray(CastIOKitClass(obj, 'OSArray *')) + ")"
130        return out_string
131
132    ztvAddr = kern.GetLoadAddressForSymbol('_ZTV5OSSet')
133    if vt == ztvAddr:
134        out_string += GetSet(CastIOKitClass(obj, 'OSSet *'))
135        return out_string
136
137    ztvAddr = kern.GetLoadAddressForSymbol('_ZTV12OSDictionary')
138    if vt == ztvAddr:
139        out_string += GetDictionary(CastIOKitClass(obj, 'OSDictionary *'))
140        return out_string
141
142    return out_string
143
144
145def GetObjectTypeStr(obj):
146    """ Return the type of an OSObject's container class
147    """
148    if obj is None:
149        return None
150
151    vt = dereference(Cast(obj, 'uintptr_t *')) - 2 * sizeof('uintptr_t')
152    vt = kern.StripKernelPAC(vt)
153    vtype = kern.SymbolicateFromAddress(vt)
154    if len(vtype):
155        return vtype[0].GetName()
156
157    # See if the value is in a kext with no symbols
158    for kval in IterateLinkedList(kern.globals.kmod, 'next'):
159        if vt >= unsigned(kval.address) and vt <= (unsigned(kval.address) + unsigned(kval.size)):
160            return "kmod:{:s}+{:#0x}".format(kval.name, vt - unsigned(kval.address))
161    return None
162
163
164@lldb_type_summary(['IORegistryEntry *'])
165@header("")
166def GetRegistryEntrySummary(entry):
167    """ returns a string containing summary information about an IORegistry
168        object including it's registry id , vtable ptr and retain count
169    """
170    name = None
171    out_string = ""
172    registryTable = entry.fRegistryTable
173    propertyTable = entry.fPropertyTable
174
175    name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey)
176    if name is None:
177        name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey)
178    if name is None:
179        name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey)
180
181    if name is not None:
182        out_string += "+-o {0:s}  ".format(GetString(CastIOKitClass(name, 'OSString *')))
183    elif CastIOKitClass(entry, 'IOService *').pwrMgt and CastIOKitClass(entry, 'IOService *').pwrMgt.Name:
184        out_string += "+-o {0:s}  ".format(CastIOKitClass(entry, 'IOService *').pwrMgt.Name)
185    else:
186        out_string += "+-o ??  "
187
188    # I'm using uintptr_t for now to work around <rdar://problem/12749733> FindFirstType & Co. should allow you to make pointer types directly
189    vtableAddr = dereference(Cast(entry, 'uintptr_t *')) - 2 * sizeof('uintptr_t *')
190    vtableAddr = kern.StripKernelPAC(vtableAddr)
191    vtype = kern.SymbolicateFromAddress(vtableAddr)
192    if vtype is None or len(vtype) < 1:
193        out_string += "<object 0x{0: <16x}, id 0x{1:x}, vtable 0x{2: <16x}".format(entry, CastIOKitClass(entry, 'IORegistryEntry *').reserved.fRegistryEntryID, vtableAddr)
194    else:
195        out_string += "<object 0x{0: <16x}, id 0x{1:x}, vtable 0x{2: <16x} <{3:s}>".format(entry, CastIOKitClass(entry, 'IORegistryEntry *').reserved.fRegistryEntryID,
196                                                                                           vtableAddr, vtype[0].GetName())
197
198    ztvAddr = kern.GetLoadAddressForSymbol('_ZTV15IORegistryEntry')
199    if vtableAddr != ztvAddr:
200        out_string += ", "
201        state = CastIOKitClass(entry, 'IOService *').__state[0]
202        # kIOServiceRegisteredState
203        if 0 == state & 2:
204            out_string += "!"
205        out_string += "registered, "
206        # kIOServiceMatchedState
207        if 0 == state & 4:
208            out_string += "!"
209        out_string += "matched, "
210        #kIOServiceInactiveState
211        if 0 != state & 1:
212            out_string += "in"
213        busyCount = (CastIOKitClass(entry, 'IOService *').__state[1] & 0xff)
214        retCount = (CastIOKitClass(entry, 'IOService *').retainCount & 0xffff)
215        out_string += "active, busy {0}, retain count {1}>".format(busyCount, retCount)
216    return out_string
217
218######################################
219# Commands
220######################################
221@lldb_command('showallclasses')
222def ShowAllClasses(cmd_args=None):
223    """ Show the instance counts and ivar size of all OSObject subclasses.
224        See ioclasscount man page for details
225    """
226    idx = 0
227    count = unsigned(kern.globals.sAllClassesDict.count)
228
229    while idx < count:
230        meta = CastIOKitClass(kern.globals.sAllClassesDict.dictionary[idx].value, 'OSMetaClass *')
231        idx += 1
232        print(GetMetaClass(meta))
233
234@lldb_command('showobject')
235def ShowObject(cmd_args=None):
236    """ Show info about an OSObject - its vtable ptr and retain count, & more info for simple container classes.
237    """
238    if not cmd_args:
239        print("Please specify the address of the OSObject whose info you want to view. Type help showobject for help")
240        return
241
242    obj = kern.GetValueFromAddress(cmd_args[0], 'OSObject *')
243    print(GetObjectSummary(obj))
244
245#Macro: dumpobject
246@lldb_command('dumpobject')
247def DumpObject(cmd_args=None):
248    """ Dumps object information if it is a valid object confirmed by showobject
249        Usage: dumpobject <address of object to be dumped> [class/struct type of object]
250    """
251    if not cmd_args:
252        print("No arguments passed")
253        print(DumpObject.__doc__)
254        return False
255
256    if len(cmd_args) == 1:
257        try:
258            object_info = lldb_run_command("showobject {:s}".format(cmd_args[0]))
259        except:
260            print("Error!! showobject failed due to invalid value")
261            print(DumpObject.__doc__)
262            return False
263
264        srch = re.search(r'<vtable for ([A-Za-z].*)>', object_info)
265        if not srch:
266            print("Error!! Couldn't find object in registry, input type manually as 2nd argument")
267            print(DumpObject.__doc__)
268            return False
269
270        object_type = srch.group(1)
271    else:
272        type_lookup = lldb_run_command("image lookup -t {:s}".format(cmd_args[1]))
273        if type_lookup.find(cmd_args[1])!= -1:
274            object_type = cmd_args[1]
275        else:
276            print("Error!! Input type {:s} isn't available in image lookup".format(cmd_args[1]))
277            return False
278
279    print("******** Object Dump for value \'{:s}\' with type \"{:s}\" ********".format(cmd_args[0], object_type))
280    print(lldb_run_command("p/x *({:s}*){:s}".format(object_type, cmd_args[0])))
281
282#EndMacro: dumpobject
283
284@lldb_command('setregistryplane')
285def SetRegistryPlane(cmd_args=None):
286    """ Set the plane to be used for the IOKit registry macros
287        syntax: (lldb) setregistryplane 0  - will display all known planes
288        syntax: (lldb) setregistryplane 0xaddr      - will set the registry plane to 0xaddr
289        syntax: (lldb) setregistryplane gIODTPlane  - will set the registry plane to gIODTPlane
290    """
291    if not cmd_args:
292        print("Please specify the name of the plane you want to use with the IOKit registry macros.")
293        print(SetRegistryPlane.__doc__)
294
295    if cmd_args[0] == "0":
296        print(GetObjectSummary(kern.globals.gIORegistryPlanes))
297    else:
298        global plane
299        plane = kern.GetValueFromAddress(cmd_args[0], 'IORegistryPlane *')
300    return
301
302@lldb_command('showregistryentry')
303def ShowRegistryEntry(cmd_args=None):
304    """ Show info about a registry entry; its properties and descendants in the current plane
305        syntax: (lldb) showregistryentry 0xaddr
306        syntax: (lldb) showregistryentry gIOPMRootDomain
307    """
308    if not cmd_args:
309        print("Please specify the address of the registry entry whose info you want to view.")
310        print(ShowRegistryEntry.__doc__)
311        return
312
313    entry = kern.GetValueFromAddress(cmd_args[0], 'IORegistryEntry *')
314    ShowRegistryEntryRecurse(entry, "", True)
315
316@lldb_command('showregistry')
317def ShowRegistry(cmd_args=None):
318    """ Show info about all registry entries in the current plane
319        If prior to invoking this command no registry plane is specified
320        using 'setregistryplane', the command defaults to the IOService plane
321    """
322    ShowRegistryEntryRecurse(kern.globals.gRegistryRoot, "", False)
323
324@lldb_command('showregistryprops')
325def ShowRegistryProps(cmd_args=None):
326    """ Show info about all registry entries in the current plane, and their properties
327        If prior to invoking this command no registry plane is specified
328        using 'setregistryplane', the command defaults to the IOService plane
329    """
330    ShowRegistryEntryRecurse(kern.globals.gRegistryRoot, "", True)
331
332@lldb_command('findregistryentry')
333def FindRegistryEntry(cmd_args=None):
334    """ Search for registry entry that matches the given string
335        If prior to invoking this command no registry plane is specified
336        using 'setregistryplane', the command defaults to searching entries from the IOService plane
337        syntax: (lldb) findregistryentries AppleACPICPU - will find the first registry entry that matches AppleACPICPU
338    """
339    if not cmd_args:
340        print("Please specify the name of the registry entry you want to find")
341        print(FindRegistryEntry.__doc__)
342        return
343
344    FindRegistryEntryRecurse(kern.globals.gRegistryRoot, cmd_args[0], True)
345
346@lldb_command('findregistryentries')
347def FindRegistryEntries(cmd_args=None):
348    """ Search for all registry entries that match the given string
349        If prior to invoking this command no registry plane is specified
350        using 'setregistryplane', the command defaults to searching entries from the IOService plane
351        syntax: (lldb) findregistryentries AppleACPICPU - will find all registry entries that match AppleACPICPU
352    """
353    if not cmd_args:
354        print("Please specify the name of the registry entry/entries you want to find")
355        print(FindRegistryEntries.__doc__)
356        return
357
358    FindRegistryEntryRecurse(kern.globals.gRegistryRoot, cmd_args[0], False)
359
360@lldb_command('findregistryprop')
361def FindRegistryProp(cmd_args=None):
362    """ Given a registry entry, print out the contents for the property that matches
363        a specific string
364        syntax: (lldb) findregistryprop 0xaddr IOSleepSupported
365        syntax: (lldb) findregistryprop gIOPMRootDomain IOSleepSupported
366        syntax: (lldb) findregistryprop gIOPMRootDomain "Supported Features"
367    """
368    if not cmd_args or len(cmd_args) < 2:
369        print("Please specify the address of a IORegistry entry and the property you're looking for")
370        print(FindRegistryProp.__doc__)
371        return
372
373    entry = kern.GetValueFromAddress(cmd_args[0], 'IOService *')
374    propertyTable = entry.fPropertyTable
375    print(GetObjectSummary(LookupKeyInPropTable(propertyTable, cmd_args[1])))
376
377@lldb_command('readioport8')
378def ReadIOPort8(cmd_args=None):
379    """ Read value stored in the specified IO port. The CPU can be optionally
380        specified as well.
381        Prints 0xBAD10AD in case of a bad read
382        Syntax: (lldb) readioport8 <port> [lcpu (kernel's numbering convention)]
383    """
384    if not cmd_args:
385        print("Please specify a port to read out of")
386        print(ReadIOPort8.__doc__)
387        return
388
389    portAddr = ArgumentStringToInt(cmd_args[0])
390    if len(cmd_args) >= 2:
391        lcpu = ArgumentStringToInt(cmd_args[1])
392    else:
393        lcpu = xnudefines.lcpu_self
394
395    ReadIOPortInt(portAddr, 1, lcpu)
396
397@lldb_command('readioport16')
398def ReadIOPort16(cmd_args=None):
399    """ Read value stored in the specified IO port. The CPU can be optionally
400        specified as well.
401        Prints 0xBAD10AD in case of a bad read
402        Syntax: (lldb) readioport16 <port> [lcpu (kernel's numbering convention)]
403    """
404    if not cmd_args:
405        print("Please specify a port to read out of")
406        print(ReadIOPort16.__doc__)
407        return
408
409    portAddr = ArgumentStringToInt(cmd_args[0])
410    if len(cmd_args) >= 2:
411        lcpu = ArgumentStringToInt(cmd_args[1])
412    else:
413        lcpu = xnudefines.lcpu_self
414
415    ReadIOPortInt(portAddr, 2, lcpu)
416
417@lldb_command('readioport32')
418def ReadIOPort32(cmd_args=None):
419    """ Read value stored in the specified IO port. The CPU can be optionally
420        specified as well.
421        Prints 0xBAD10AD in case of a bad read
422        Syntax: (lldb) readioport32 <port> [lcpu (kernel's numbering convention)]
423    """
424    if not cmd_args:
425        print("Please specify a port to read out of")
426        print(ReadIOPort32.__doc__)
427        return
428
429    portAddr = ArgumentStringToInt(cmd_args[0])
430    if len(cmd_args) >= 2:
431        lcpu = ArgumentStringToInt(cmd_args[1])
432    else:
433        lcpu = xnudefines.lcpu_self
434
435    ReadIOPortInt(portAddr, 4, lcpu)
436
437@lldb_command('writeioport8')
438def WriteIOPort8(cmd_args=None):
439    """ Write the value to the specified IO port. The size of the value is
440        determined by the name of the command. The CPU used can be optionally
441        specified as well.
442        Syntax: (lldb) writeioport8 <port> <value> [lcpu (kernel's numbering convention)]
443    """
444    if not cmd_args or len(cmd_args) < 2:
445        print("Please specify a port to write to, followed by the value you want to write")
446        print(WriteIOPort8.__doc__)
447        return
448
449    portAddr = ArgumentStringToInt(cmd_args[0])
450    value = ArgumentStringToInt(cmd_args[1])
451
452    if len(cmd_args) >= 3:
453        lcpu = ArgumentStringToInt(cmd_args[2])
454    else:
455        lcpu = xnudefines.lcpu_self
456
457    WriteIOPortInt(portAddr, 1, value, lcpu)
458
459@lldb_command('writeioport16')
460def WriteIOPort16(cmd_args=None):
461    """ Write the value to the specified IO port. The size of the value is
462        determined by the name of the command. The CPU used can be optionally
463        specified as well.
464        Syntax: (lldb) writeioport16 <port> <value> [lcpu (kernel's numbering convention)]
465    """
466    if not cmd_args or len(cmd_args) < 2:
467        print("Please specify a port to write to, followed by the value you want to write")
468        print(WriteIOPort16.__doc__)
469        return
470
471    portAddr = ArgumentStringToInt(cmd_args[0])
472    value = ArgumentStringToInt(cmd_args[1])
473
474    if len(cmd_args) >= 3:
475        lcpu = ArgumentStringToInt(cmd_args[2])
476    else:
477        lcpu = xnudefines.lcpu_self
478
479    WriteIOPortInt(portAddr, 2, value, lcpu)
480
481@lldb_command('writeioport32')
482def WriteIOPort32(cmd_args=None):
483    """ Write the value to the specified IO port. The size of the value is
484        determined by the name of the command. The CPU used can be optionally
485        specified as well.
486        Syntax: (lldb) writeioport32 <port> <value> [lcpu (kernel's numbering convention)]
487    """
488    if not cmd_args or len(cmd_args) < 2:
489        print("Please specify a port to write to, followed by the value you want to write")
490        print(WriteIOPort32.__doc__)
491        return
492
493    portAddr = ArgumentStringToInt(cmd_args[0])
494    value = ArgumentStringToInt(cmd_args[1])
495
496    if len(cmd_args) >= 3:
497        lcpu = ArgumentStringToInt(cmd_args[2])
498    else:
499        lcpu = xnudefines.lcpu_self
500
501    WriteIOPortInt(portAddr, 4, value, lcpu)
502
503@lldb_command('showioservicepm')
504def ShowIOServicePM(cmd_args=None):
505    """ Routine to dump the IOServicePM object
506        Syntax: (lldb) showioservicepm <IOServicePM pointer>
507    """
508    if not cmd_args:
509        print("Please enter the pointer to the IOServicePM object you'd like to introspect")
510        print(ShowIOServicePM.__doc__)
511        return
512
513    iopmpriv = kern.GetValueFromAddress(cmd_args[0], 'IOServicePM *')
514    out_string = "MachineState {0: <6d} (".format(iopmpriv.MachineState)
515
516    # Power state map
517    pstate_map = {
518            0:  'kIOPM_Finished',
519            1:  'kIOPM_OurChangeTellClientsPowerDown',
520            2:  'kIOPM_OurChangeTellClientsPowerDown',
521            3:  'kIOPM_OurChangeNotifyInterestedDriversWillChange',
522            4:  'kIOPM_OurChangeSetPowerState',
523            5:  'kIOPM_OurChangeWaitForPowerSettle',
524            6:  'kIOPM_OurChangeNotifyInterestedDriversDidChange',
525            7:  'kIOPM_OurChangeTellCapabilityDidChange',
526            8:  'kIOPM_OurChangeFinish',
527            9:  'Unused_MachineState_9',
528            10: 'kIOPM_ParentChangeTellPriorityClientsPowerDown',
529            11: 'kIOPM_ParentChangeNotifyInterestedDriversWillChange',
530            12: 'kIOPM_ParentChangeSetPowerState',
531            13: 'kIOPM_ParentChangeWaitForPowerSettle',
532            14: 'kIOPM_ParentChangeNotifyInterestedDriversDidChange',
533            15: 'kIOPM_ParentChangeTellCapabilityDidChange',
534            16: 'kIOPM_ParentChangeAcknowledgePowerChange',
535            17: 'kIOPM_NotifyChildrenStart',
536            18: 'kIOPM_NotifyChildrenOrdered',
537            19: 'kIOPM_NotifyChildrenDelayed',
538            20: 'kIOPM_SyncTellClientsPowerDown',
539            21: 'kIOPM_SyncTellPriorityClientsPowerDown',
540            22: 'kIOPM_SyncNotifyWillChange',
541            23: 'kIOPM_SyncNotifyDidChange',
542            24: 'kIOPM_SyncTellCapabilityDidChange',
543            25: 'kIOPM_SyncFinish',
544            26: 'kIOPM_TellCapabilityChangeDone',
545            27: 'kIOPM_DriverThreadCallDone'
546        }
547    powerstate = unsigned(iopmpriv.MachineState)
548    if powerstate in pstate_map:
549        out_string += "{0:s}".format(pstate_map[powerstate])
550    else:
551        out_string += "Unknown_MachineState"
552    out_string += "), "
553
554    if iopmpriv.MachineState != 20:
555        out_string += "DriverTimer = {0: <6d}, SettleTime = {1: < 6d}, HeadNoteFlags = {2: #12x}, HeadNotePendingAcks = {3: #012x}, ".format(
556                unsigned(iopmpriv.DriverTimer),
557                unsigned(iopmpriv.SettleTimeUS),
558                unsigned(iopmpriv.HeadNoteChangeFlags),
559                unsigned(iopmpriv.HeadNotePendingAcks))
560
561    if iopmpriv.DeviceOverrideEnabled != 0:
562        out_string += "DeviceOverrides, "
563
564    out_string += "DeviceDesire = {0: <6d}, DesiredPowerState = {1: <6d}, PreviousRequest = {2: <6d}\n".format(
565            unsigned(iopmpriv.DeviceDesire),
566            unsigned(iopmpriv.DesiredPowerState),
567            unsigned(iopmpriv.PreviousRequestPowerFlags))
568
569    print(out_string)
570
571@lldb_type_summary(['IOPMWorkQueue *'])
572@header("")
573def GetIOPMWorkQueueSummary(wq):
574    out_str = ""
575    ioservicepm_header = "{:<20s}{:<4s}{:<4s}{:<4s}{:<4s}\n"
576    iopmrequest_indent = "    "
577    iopmrequest_header = iopmrequest_indent + "{:<20s}{:<6s}{:<20s}{:<20s}{:<12s}{:<12s}{:<20s}{:<20s}{:<20s}\n"
578
579    for next in IterateQueue(wq.fWorkQueue, 'IOServicePM *', 'WorkChain'):
580        out_str += ioservicepm_header.format("IOService", "ps", "ms", "wr", "name")
581        out_str += "0x{:<16x}  {:<2d}  {:<2d}  {:<2d}  {:<s}\n".format(
582            next.Owner, next.CurrentPowerState, next.MachineState, next.WaitReason, next.Name)
583        out_str += iopmrequest_header.format("IOPMRequest", "type", "next_req", "root_req", "work_wait", "free_wait", "arg0", "arg1", "arg2")
584        for request in IterateQueue(next.RequestHead, 'IOPMRequest *', 'fCommandChain'):
585            out_str += iopmrequest_indent
586            out_str += "0x{:<16x}  0x{:<2x}  0x{:<16x}  0x{:<16x}".format(
587                request, request.fRequestType, request.fRequestNext, request.fRequestRoot)
588            out_str += "  0x{:<8x}  0x{:<8x}".format(
589                request.fWorkWaitCount, request.fFreeWaitCount)
590            out_str += "  0x{:<16x}  0x{:<16x}  0x{:<16x}\n".format(
591                request.fArg0, request.fArg1, request.fArg2)
592    return out_str
593
594@lldb_command('showiopmqueues')
595def ShowIOPMQueues(cmd_args=None):
596    """ Show IOKit power management queues and IOPMRequest objects.
597    """
598    print("IOPMWorkQueue 0x{:<16x} ({:<d} IOServicePM)\n".format(
599        kern.globals.gIOPMWorkQueue, kern.globals.gIOPMWorkQueue.fQueueLength))
600    print(GetIOPMWorkQueueSummary(kern.globals.gIOPMWorkQueue))
601
602@lldb_type_summary(['IOService *'])
603@header("")
604def GetIOPMInterest(service):
605    iopm = CastIOKitClass(service.pwrMgt, 'IOServicePM *')
606    if unsigned(iopm) == 0:
607        print("error: no IOServicePM")
608        return
609
610    list = CastIOKitClass(iopm.InterestedDrivers, 'IOPMinformeeList *')
611    out_str = "IOServicePM 0x{:<16x} ({:<d} interest, {:<d} pending ack)\n".format(
612        iopm, list.length, iopm.HeadNotePendingAcks)
613    if list.length == 0:
614        return
615
616    out_str += "    {:<20s}{:<8s}{:<10s}{:<20s}{:<20s}{:<20s}{:<s}\n".format(
617        "informee", "active", "ticks", "notifyTime", "service", "regId", "name")
618    next = CastIOKitClass(list.firstItem, 'IOPMinformee *')
619    while unsigned(next) != 0:
620        driver = CastIOKitClass(next.whatObject, 'IOService *')
621        name = GetRegistryEntryName(driver)
622        reg_id = CastIOKitClass(driver, 'IORegistryEntry *').reserved.fRegistryEntryID;
623        out_str += "    0x{:<16x}  {:<6s}  {:<8d}  0x{:<16x}  0x{:<16x}  0x{:<16x}  {:<s}\n".format(
624            next, "Yes" if next.active != 0 else "No" , next.timer, next.startTime, next.whatObject, reg_id, name)
625        next = CastIOKitClass(next.nextInList, 'IOPMinformee *')
626    return out_str
627
628@lldb_command('showiopminterest')
629def ShowIOPMInterest(cmd_args=None):
630    """ Show the interested drivers for an IOService.
631        syntax: (lldb) showiopminterest <IOService>
632    """
633    if not cmd_args:
634        print("Please specify the address of the IOService")
635        print(ShowIOPMInterest.__doc__)
636        return
637
638    obj = kern.GetValueFromAddress(cmd_args[0], 'IOService *')
639    print(GetIOPMInterest(obj))
640
641@lldb_command("showinterruptvectors")
642def ShowInterruptVectorInfo(cmd_args=None):
643    """
644    Shows interrupt vectors.
645    """
646
647    # Constants
648    kInterruptTriggerModeMask  = 0x01
649    kInterruptTriggerModeEdge  = 0x00
650    kInterruptTriggerModeLevel = kInterruptTriggerModeMask
651    kInterruptPolarityMask     = 0x02
652    kInterruptPolarityHigh     = 0x00
653    kInterruptPolarityLow      = kInterruptPolarityMask
654    kInterruptShareableMask    = 0x04
655    kInterruptNotShareable     = 0x00
656    kInterruptIsShareable      = kInterruptShareableMask
657    kIOInterruptTypePCIMessaged = 0x00010000
658
659    # Get all interrupt controllers
660    interrupt_controllers = list(SearchInterruptControllerDrivers())
661
662    print("Interrupt controllers: ")
663    for ic in interrupt_controllers:
664        print("  {}".format(ic))
665    print("")
666
667    # Iterate over all entries in the registry
668    for entry in GetMatchingEntries(lambda _: True):
669        # Get the name of the entry
670        entry_name = GetRegistryEntryName(entry)
671
672        # Get the location of the entry
673        entry_location = GetRegistryEntryLocationInPlane(entry, kern.globals.gIOServicePlane)
674        if entry_location is None:
675            entry_location = ""
676        else:
677            entry_location = "@" + entry_location
678
679        # Get the interrupt properties
680        (msi_mode, vectorDataList, vectorContList) = GetRegistryEntryInterruptProperties(entry)
681        should_print = False
682        out_str = ""
683        for (vector_data, vector_cont) in zip(vectorDataList, vectorContList):
684            # vector_cont is the name of the interrupt controller. Find the matching controller from
685            # the list of controllers obtained earlier
686            matching_ics = [ic for ic in interrupt_controllers if ic.name == vector_cont]
687
688            if len(matching_ics) > 0:
689                should_print = True
690                # Take the first match
691                matchingIC = matching_ics[0]
692
693                # Use the vector_data to determine the vector and any flags
694                data_ptr = vector_data.data
695                data_length = vector_data.length
696
697                # Dereference vector_data as a uint32_t * and add the base vector number
698                gsi = unsigned(dereference(Cast(data_ptr, 'uint32_t *')))
699                gsi += matchingIC.base_vector_number
700
701                # If data_length is >= 8 then vector_data contains interrupt flags
702                if data_length >= 8:
703                    # Add sizeof(uint32_t) to data_ptr to get the flags pointer
704                    flags_ptr = kern.GetValueFromAddress(unsigned(data_ptr) + sizeof("uint32_t"))
705                    flags = unsigned(dereference(Cast(flags_ptr, 'uint32_t *')))
706                    out_str += "  +----- [Interrupt Controller {ic}] vector {gsi}, {trigger_level}, {active}, {shareable}{messaged}\n" \
707                            .format(ic=matchingIC.name, gsi=hex(gsi),
708                                    trigger_level="level trigger" if flags & kInterruptTriggerModeLevel else "edge trigger",
709                                    active="active low" if flags & kInterruptPolarityLow else "active high",
710                                    shareable="shareable" if flags & kInterruptIsShareable else "exclusive",
711                                    messaged=", messaged" if flags & kIOInterruptTypePCIMessaged else "")
712                else:
713                    out_str += "  +----- [Interrupt Controller {ic}] vector {gsi}\n".format(ic=matchingIC.name, gsi=hex(gsi))
714        if should_print:
715            print("[ {entry_name}{entry_location} ]{msi_mode}\n{out_str}" \
716                .format(entry_name=entry_name,
717                        entry_location=entry_location,
718                        msi_mode=" - MSIs enabled" if msi_mode else "",
719                        out_str=out_str))
720
721@lldb_command("showiokitclasshierarchy")
722def ShowIOKitClassHierarchy(cmd_args=None):
723    """
724    Show class hierarchy for a IOKit class
725    """
726    if not cmd_args:
727        print("Usage: showiokitclasshierarchy <IOKit class name>")
728        return
729
730    class_name = cmd_args[0]
731    metaclasses = GetMetaClasses()
732    if class_name not in metaclasses:
733        print("Class {} does not exist".format(class_name))
734        return
735    metaclass = metaclasses[class_name]
736
737    # loop over superclasses
738    hierarchy = []
739    current_metaclass = metaclass
740    while current_metaclass is not None:
741        hierarchy.insert(0, current_metaclass)
742        current_metaclass = current_metaclass.superclass()
743
744    for (index, mc) in enumerate(hierarchy):
745        indent = ("    " * index) + "+---"
746        print("{}[ {} ] {}".format(indent, str(mc.className()), str(mc.data())))
747
748
749######################################
750#  Helper routines
751######################################
752def ShowRegistryEntryRecurse(entry, prefix, printProps):
753    """ prints registry entry summary and recurses through all its children.
754    """
755    # Setup
756    global plane
757    out_string = ""
758    plen = (len(prefix)//2)
759    registryTable = entry.fRegistryTable
760    propertyTable = entry.fPropertyTable
761
762    # Print entry details
763    print("{0:s}{1:s}".format(prefix, GetRegistryEntrySummary(entry)))
764    # Printing large property tables make it look like lldb is 'stuck'
765    if printProps:
766        print(GetRegDictionary(propertyTable, prefix + "  | "))
767
768    # Recurse
769    if plane is None:
770        childKey = kern.globals.gIOServicePlane.keys[1]
771    else:
772        childKey = plane.keys[1]
773    childArray = LookupKeyInOSDict(registryTable, childKey)
774    if childArray is not None:
775        idx = 0
776        ca = CastIOKitClass(childArray, 'OSArray *')
777        count = unsigned(ca.count)
778        while idx < count:
779            if plen != 0 and plen != 1 and (plen & (plen - 1)) == 0:
780                ShowRegistryEntryRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), prefix + "| ", printProps)
781            else:
782                ShowRegistryEntryRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), prefix + "  ", printProps)
783            idx += 1
784
785def FindRegistryEntryRecurse(entry, search_name, stopAfterFirst):
786    """ Checks if given registry entry's name matches the search_name we're looking for
787        If yes, it prints the entry's summary and then recurses through its children
788        If no, it does nothing and recurses through its children
789    """
790    # Setup
791    global plane
792    registryTable = entry.fRegistryTable
793    propertyTable = entry.fPropertyTable
794
795    # Compare
796    name = None
797    name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey)
798    if name is None:
799        name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey)
800    if name is None:
801        name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey)
802
803    if name is not None:
804        if str(CastIOKitClass(name, 'OSString *').string) == search_name:
805            print(GetRegistryEntrySummary(entry))
806            if stopAfterFirst is True:
807                return True
808    elif CastIOKitClass(entry, 'IOService *').pwrMgt and CastIOKitClass(entry, 'IOService *').pwrMgt.Name:
809        name = CastIOKitClass(entry, 'IOService *').pwrMgt.Name
810        if str(name) == search_name:
811            print(GetRegistryEntrySummary(entry))
812            if stopAfterFirst is True:
813                return True
814
815    # Recurse
816    if plane is None:
817        childKey = kern.globals.gIOServicePlane.keys[1]
818    else:
819        childKey = plane.keys[1]
820    childArray = LookupKeyInOSDict(registryTable, childKey)
821    if childArray is not None:
822        idx = 0
823        ca = CastIOKitClass(childArray, 'OSArray *')
824        count = unsigned(ca.count)
825        while idx < count:
826            if FindRegistryEntryRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), search_name, stopAfterFirst) is True:
827                return True
828            idx += 1
829    return False
830
831def FindRegistryObjectRecurse(entry, search_name):
832    """ Checks if given registry entry's name matches the search_name we're looking for
833        If yes, return the entry
834        If no, it does nothing and recurses through its children
835        Implicitly stops after finding the first entry
836    """
837    # Setup
838    global plane
839    registryTable = entry.fRegistryTable
840    propertyTable = entry.fPropertyTable
841
842    # Compare
843    name = None
844    name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey)
845    if name is None:
846        name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey)
847    if name is None:
848        name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey)
849
850    if name is not None:
851        if str(CastIOKitClass(name, 'OSString *').string) == search_name:
852            return entry
853    elif CastIOKitClass(entry, 'IOService *').pwrMgt and CastIOKitClass(entry, 'IOService *').pwrMgt.Name:
854        name = CastIOKitClass(entry, 'IOService *').pwrMgt.Name
855        if str(name) == search_name:
856            return entry
857
858    # Recurse
859    if plane is None:
860        childKey = kern.globals.gIOServicePlane.keys[1]
861    else:
862        childKey = plane.keys[1]
863    childArray = LookupKeyInOSDict(registryTable, childKey)
864    if childArray is not None:
865        ca = CastIOKitClass(childArray, 'OSArray *')
866        for idx in range(ca.count):
867            registry_object = FindRegistryObjectRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), search_name)
868            if not registry_object or int(registry_object) == int(0):
869                continue
870            else:
871                return registry_object
872    return None
873
874def CompareStringToOSSymbol(string, os_sym):
875    """
876    Lexicographically compare python string to OSSymbol
877    Params:
878    string - python string
879    os_sym - OSSymbol
880
881    Returns:
882    0 if string == os_sym
883    1 if string > os_sym
884    -1 if string < os_sym
885    """
886    os_sym_str = GetString(os_sym)
887    if string > os_sym_str:
888        return 1
889    elif string < os_sym_str:
890        return -1
891    else:
892        return 0
893
894class IOKitMetaClass(object):
895    """
896    A class that represents a IOKit metaclass. This is used to represent the
897    IOKit inheritance hierarchy.
898    """
899
900    def __init__(self, meta):
901        """
902        Initialize a IOKitMetaClass object.
903
904        Args:
905            meta (core.cvalue.value): A LLDB value representing a
906                OSMetaClass *.
907        """
908        self._meta = meta
909        self._superclass = None
910
911    def data(self):
912        return self._meta
913
914    def setSuperclass(self, superclass):
915        """
916        Set the superclass for this metaclass.
917
918        Args:
919            superclass (core.cvalue.value): A LLDB value representing a
920                OSMetaClass *.
921        """
922        self._superclass = superclass
923
924    def superclass(self):
925        """
926        Get the superclass for this metaclass (set by the setSuperclass method).
927
928        Returns:
929            core.cvalue.value: A LLDB value representing a OSMetaClass *.
930        """
931        return self._superclass
932
933    def className(self):
934        """
935        Get the name of the class this metaclass represents.
936
937        Returns:
938            str: The class name
939        """
940        return self._meta.className.string
941
942    def inheritsFrom(self, other):
943        """
944        Check if the class represented by this metaclass inherits from a class
945        represented by another metaclass.
946
947        Args:
948            other (IOKitMetaClass): The other metaclass
949
950        Returns:
951            bool: Returns True if this class inherits from the other class and
952                False otherwise.
953        """
954        current = self
955        while current is not None:
956            if current == other:
957                return True
958            else:
959                current = current.superclass()
960
961
962def GetRegistryEntryClassName(entry):
963    """
964    Get the class name of a registry entry.
965
966    Args:
967        entry (core.cvalue.value): A LLDB value representing a
968            IORegistryEntry *.
969
970    Returns:
971        str: The class name of the entry or None if a class name could not be
972            found.
973    """
974    # Check using IOClass key
975    result = LookupKeyInOSDict(entry.fPropertyTable, kern.globals.gIOClassKey)
976    if result is not None:
977        return GetString(result).replace("\"", "")
978    else:
979        # Use the vtable of the entry to determine the concrete type
980        vt = dereference(Cast(entry, 'uintptr_t *')) - 2 * sizeof('uintptr_t')
981        vt = kern.StripKernelPAC(vt)
982        vtype = kern.SymbolicateFromAddress(vt)
983        if len(vtype) > 0:
984            vtableName = vtype[0].GetName()
985            return vtableName[11:] # strip off "vtable for "
986        else:
987            return None
988
989
990def GetRegistryEntryName(entry):
991    """
992    Get the name of a registry entry.
993
994    Args:
995        entry (core.cvalue.value): A LLDB value representing a
996            IORegistryEntry *.
997
998    Returns:
999        str: The name of the entry or None if a name could not be found.
1000    """
1001    name = None
1002
1003    # First check the IOService plane nameKey
1004    result = LookupKeyInOSDict(entry.fRegistryTable, kern.globals.gIOServicePlane.nameKey)
1005    if result is not None:
1006        name = GetString(result)
1007
1008    # Check the global IOName key
1009    if name is None:
1010        result = LookupKeyInOSDict(entry.fRegistryTable, kern.globals.gIONameKey)
1011        if result is not None:
1012            name = GetString(result)
1013
1014    # Check the IOClass key
1015    if name is None:
1016        result = LookupKeyInOSDict(entry.fPropertyTable, kern.globals.gIOClassKey)
1017        if result is not None:
1018            name = GetString(result)
1019
1020    # Remove extra quotes
1021    if name is not None:
1022        return name.replace("\"", "")
1023    else:
1024        return GetRegistryEntryClassName(entry)
1025
1026
1027def GetRegistryEntryLocationInPlane(entry, plane):
1028    """
1029    Get the registry entry location in a IOKit plane.
1030
1031    Args:
1032        entry (core.cvalue.value): A LLDB value representing a
1033            IORegistryEntry *.
1034        plane: An IOKit plane such as kern.globals.gIOServicePlane.
1035
1036    Returns:
1037        str: The location of the entry or None if a location could not be
1038            found.
1039    """
1040    # Check the plane's pathLocationKey
1041    sym = LookupKeyInOSDict(entry.fRegistryTable, plane.pathLocationKey)
1042
1043    # Check the global IOLocation key
1044    if sym is None:
1045        sym = LookupKeyInOSDict(entry.fRegistryTable, kern.globals.gIOLocationKey)
1046    if sym is not None:
1047        return GetString(sym).replace("\"", "")
1048    else:
1049        return None
1050
1051
1052@caching.cache_dynamically
1053def GetMetaClasses(target=None):
1054    """
1055    Enumerate all IOKit metaclasses. Uses dynamic caching.
1056
1057    Returns:
1058        Dict[str, IOKitMetaClass]: A dictionary mapping each metaclass name to
1059            a IOKitMetaClass object representing the metaclass.
1060    """
1061
1062    # This method takes a while, so it prints a progress indicator
1063    print("Enumerating IOKit metaclasses: ")
1064
1065    do_progress = os.isatty(sys.__stderr__.fileno())
1066
1067    # Iterate over all classes present in sAllClassesDict
1068    count = unsigned(kern.globals.sAllClassesDict.count)
1069    metaclasses_by_address = {}
1070    for idx in range(count):
1071        if do_progress and idx % 10 == 0:
1072            sys.stderr.write("\033[K  {} metaclass found...\r".format(idx))
1073
1074        # Address of metaclass
1075        address = kern.globals.sAllClassesDict.dictionary[idx].value
1076
1077        # Create IOKitMetaClass and store in dict
1078        metaclasses_by_address[int(address)] = IOKitMetaClass(CastIOKitClass(kern.globals.sAllClassesDict.dictionary[idx].value, 'OSMetaClass *'))
1079
1080    # At this point, each metaclass is independent of each other. We don't have superclass links set up yet.
1081
1082    for address, metaclass in metaclasses_by_address.items():
1083        # Get the address of the superclass using the superClassLink in IOMetaClass
1084        superclass_address = int(metaclass.data().superClassLink)
1085
1086        # Skip null superclass
1087        if superclass_address == 0:
1088            continue
1089
1090        # Find the superclass object in the dict
1091        if superclass_address in metaclasses_by_address:
1092            metaclass.setSuperclass(metaclasses_by_address[superclass_address])
1093        else:
1094            print("warning: could not find superclass for {}".format(str(metaclass.data())))
1095
1096    # This method returns a dictionary mapping each class name to the associated metaclass object
1097    metaclasses_by_name = {}
1098    for idx, (_, metaclass) in enumerate(metaclasses_by_address.items()):
1099        if do_progress and idx % 10 == 0:
1100            sys.stderr.write("\033[K  {} metaclass indexed...\r".format(idx))
1101
1102        metaclasses_by_name[str(metaclass.className())] = metaclass
1103
1104    print("  Indexed {} IOKit metaclasses.".format(count))
1105    return metaclasses_by_name
1106
1107
1108def GetMatchingEntries(matcher):
1109    """
1110    Iterate over the IOKit registry and find entries that match specific
1111        criteria.
1112
1113    Args:
1114        matcher (function): A matching function that returns True for a match
1115            and False otherwise.
1116
1117    Yields:
1118        core.cvalue.value: LLDB values that represent IORegistryEntry * for
1119            each registry entry found.
1120    """
1121
1122    # Perform a BFS over the IOKit registry tree
1123    bfs_queue = deque()
1124    bfs_queue.append(kern.globals.gRegistryRoot)
1125    while len(bfs_queue) > 0:
1126        # Dequeue an entry
1127        entry = bfs_queue.popleft()
1128
1129        # Check if entry matches
1130        if matcher(entry):
1131            yield entry
1132
1133        # Find children of this entry and enqueue them
1134        child_array = LookupKeyInOSDict(entry.fRegistryTable, kern.globals.gIOServicePlane.keys[1])
1135        if child_array is not None:
1136            idx = 0
1137            ca = CastIOKitClass(child_array, 'OSArray *')
1138            count = unsigned(ca.count)
1139            while idx < count:
1140                bfs_queue.append(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'))
1141                idx += 1
1142
1143
1144def FindMatchingServices(matching_name):
1145    """
1146    Finds registry entries that match the given string. Works similarly to:
1147
1148    io_iterator_t iter;
1149    IOServiceGetMatchingServices(..., IOServiceMatching(matching_name), &iter);
1150    while (( io_object_t next = IOIteratorNext(iter))) { ... }
1151
1152    Args:
1153        matching_name (str): The class name to search for.
1154
1155    Yields:
1156        core.cvalue.value: LLDB values that represent IORegistryEntry * for
1157            each registry entry found.
1158    """
1159
1160    # Check if the argument is valid
1161    metaclasses = GetMetaClasses()
1162    if matching_name not in metaclasses:
1163        return
1164    matching_metaclass = metaclasses[matching_name]
1165
1166    # An entry matches if it inherits from matching_metaclass
1167    def matcher(entry):
1168        # Get the class name of the entry and the associated metaclass
1169        entry_name = GetRegistryEntryClassName(entry)
1170        if entry_name in metaclasses:
1171            entry_metaclass = metaclasses[entry_name]
1172            return entry_metaclass.inheritsFrom(matching_metaclass)
1173        else:
1174            return False
1175
1176    # Search for entries
1177    for entry in GetMatchingEntries(matcher):
1178        yield entry
1179
1180
1181def GetRegistryEntryParent(entry, iokit_plane=None):
1182    """
1183    Gets the parent entry of a registry entry.
1184
1185    Args:
1186        entry (core.cvalue.value): A LLDB value representing a
1187            IORegistryEntry *.
1188        iokit_plane (core.cvalue.value, optional): A LLDB value representing a
1189            IORegistryPlane *. By default, this method uses the IOService
1190            plane.
1191
1192    Returns:
1193        core.cvalue.value: A LLDB value representing a IORegistryEntry* that
1194            is the parent entry of the entry argument in the specified plane.
1195            Returns None if no entry could be found.
1196    """
1197    kParentSetIndex = 0
1198    parent_key = None
1199    if iokit_plane is None:
1200        parent_key = kern.globals.gIOServicePlane.keys[kParentSetIndex]
1201    else:
1202        parent_key = plane.keys[kParentSetIndex]
1203    parent_array = LookupKeyInOSDict(entry.fRegistryTable, parent_key)
1204    parent_entry = None
1205    if parent_array is not None:
1206        idx = 0
1207        ca = CastIOKitClass(parent_array, 'OSArray *')
1208        count = unsigned(ca.count)
1209        if count > 0:
1210            parent_entry = CastIOKitClass(ca.array[0], 'IORegistryEntry *')
1211    return parent_entry
1212
1213
1214def GetRegistryEntryInterruptProperties(entry):
1215    """
1216    Get the interrupt properties of a registry entry.
1217
1218    Args:
1219        entry (core.cvalue.value): A LLDB value representing a IORegistryEntry *.
1220
1221    Returns:
1222        (bool, List[core.cvalue.value], List[str]): A tuple with the following
1223            fields:
1224                - First field (bool): Whether this entry has a non-null
1225                    IOPCIMSIMode.
1226                - Second field (List[core.cvalue.value]): A list of LLDB values
1227                    representing OSData *. The OSData* pointer points to
1228                    interrupt vector data.
1229                - Third field (List[str]): A list of strings representing the
1230                    interrupt controller names from the
1231                    IOInterruptControllers property.
1232    """
1233    INTERRUPT_SPECIFIERS_PROPERTY = "IOInterruptSpecifiers"
1234    INTERRUPT_CONTROLLERS_PROPERTY = "IOInterruptControllers"
1235    MSI_MODE_PROPERTY = "IOPCIMSIMode"
1236
1237    # Check IOInterruptSpecifiers
1238    interrupt_specifiers = LookupKeyInPropTable(entry.fPropertyTable, INTERRUPT_SPECIFIERS_PROPERTY)
1239    if interrupt_specifiers is not None:
1240        interrupt_specifiers = CastIOKitClass(interrupt_specifiers, 'OSArray *')
1241
1242    # Check IOInterruptControllers
1243    interrupt_controllers = LookupKeyInPropTable(entry.fPropertyTable, INTERRUPT_CONTROLLERS_PROPERTY)
1244    if interrupt_controllers is not None:
1245        interrupt_controllers = CastIOKitClass(interrupt_controllers, 'OSArray *')
1246
1247    # Check MSI mode
1248    msi_mode = LookupKeyInPropTable(entry.fPropertyTable, MSI_MODE_PROPERTY)
1249
1250    result_vector_data = []
1251    result_vector_cont = []
1252    if interrupt_specifiers is not None and interrupt_controllers is not None:
1253        interrupt_specifiers_array_count = unsigned(interrupt_specifiers.count)
1254        interrupt_controllers_array_count = unsigned(interrupt_controllers.count)
1255        # The array lengths should be the same
1256        if interrupt_specifiers_array_count == interrupt_controllers_array_count and interrupt_specifiers_array_count > 0:
1257            idx = 0
1258            while idx < interrupt_specifiers_array_count:
1259                # IOInterruptSpecifiers is an array of OSData *
1260                vector_data = CastIOKitClass(interrupt_specifiers.array[idx], "OSData *")
1261
1262                # IOInterruptControllers is an array of OSString *
1263                vector_cont = GetString(interrupt_controllers.array[idx])
1264
1265                result_vector_data.append(vector_data)
1266                result_vector_cont.append(vector_cont)
1267                idx += 1
1268
1269    return (msi_mode is not None, result_vector_data, result_vector_cont)
1270
1271
1272class InterruptControllerDevice(object):
1273    """Represents a IOInterruptController"""
1274
1275    def __init__(self, device, driver, base_vector_number, name):
1276        """
1277        Initialize a InterruptControllerDevice.
1278
1279        Args:
1280            device (core.cvalue.value): The device object.
1281            driver (core.cvalue.value): The driver object.
1282            base_vector_number (int): The base interrupt vector.
1283            name (str): The name of this interrupt controller.
1284
1285        Note:
1286            Use the factory method makeInterruptControllerDevice to validate
1287            properties.
1288        """
1289        self.device = device
1290        self.driver = driver
1291        self.name = name
1292        self.base_vector_number = base_vector_number
1293
1294
1295    def __str__(self):
1296        """
1297        String representation of this InterruptControllerDevice.
1298        """
1299        return " Name {}, base vector = {}, device = {}, driver = {}".format(
1300            self.name, hex(self.base_vector_number), str(self.device), str(self.driver))
1301
1302    @staticmethod
1303    def makeInterruptControllerDevice(device, driver):
1304        """
1305        Factory method to create a InterruptControllerDevice.
1306
1307        Args:
1308            device (core.cvalue.value): The device object.
1309            driver (core.cvalue.value): The driver object.
1310
1311        Returns:
1312            InterruptControllerDevice: Returns an instance of
1313                InterruptControllerDevice or None if the arguments do not have
1314                the required properties.
1315        """
1316        BASE_VECTOR_PROPERTY = "Base Vector Number"
1317        INTERRUPT_CONTROLLER_NAME_PROPERTY = "InterruptControllerName"
1318        base_vector = LookupKeyInPropTable(device.fPropertyTable, BASE_VECTOR_PROPERTY)
1319        if base_vector is None:
1320            base_vector = LookupKeyInPropTable(driver.fPropertyTable, BASE_VECTOR_PROPERTY)
1321        device_name = LookupKeyInPropTable(device.fPropertyTable, INTERRUPT_CONTROLLER_NAME_PROPERTY)
1322        if device_name is None:
1323            device_name = LookupKeyInPropTable(driver.fPropertyTable, INTERRUPT_CONTROLLER_NAME_PROPERTY)
1324
1325        if device_name is not None:
1326            # Some interrupt controllers do not have a base vector number. Assume it is 0.
1327            base_vector_number = 0
1328            if base_vector is not None:
1329                base_vector_number = unsigned(GetNumber(base_vector))
1330            device_name = GetString(device_name)
1331            # Construct object and return
1332            return InterruptControllerDevice(device, driver, base_vector_number, device_name)
1333        else:
1334            # error case
1335            return None
1336
1337
1338def SearchInterruptControllerDrivers():
1339    """
1340    Search the IOKit registry for entries that match IOInterruptController.
1341
1342    Yields:
1343        core.cvalue.value: A LLDB value representing a IORegistryEntry * that
1344        inherits from IOInterruptController.
1345    """
1346    for entry in FindMatchingServices("IOInterruptController"):
1347        # Get parent
1348        parent = GetRegistryEntryParent(entry)
1349
1350        # Make the interrupt controller object
1351        ic = InterruptControllerDevice.makeInterruptControllerDevice(parent, entry)
1352
1353        # Yield object
1354        if ic is not None:
1355            yield ic
1356
1357
1358def LookupKeyInOSDict(osdict, key, comparer = None):
1359    """ Returns the value corresponding to a given key in a OSDictionary
1360        Returns None if the key was not found
1361    """
1362    if not osdict:
1363        return
1364    count = unsigned(osdict.count)
1365    result = None
1366    idx = 0
1367
1368    while idx < count and result is None:
1369        if comparer is not None:
1370            if comparer(key, osdict.dictionary[idx].key) == 0:
1371                result = osdict.dictionary[idx].value
1372        elif key == osdict.dictionary[idx].key:
1373            result = osdict.dictionary[idx].value
1374        idx += 1
1375    return result
1376
1377def LookupKeyInPropTable(propertyTable, key_str):
1378    """ Returns the value corresponding to a given key from a registry entry's property table
1379        Returns None if the key was not found
1380        The property that is being searched for is specified as a string in key_str
1381    """
1382    if not propertyTable:
1383        return
1384    count = unsigned(propertyTable.count)
1385    result = None
1386    idx = 0
1387    while idx < count and result is None:
1388        if key_str == str(propertyTable.dictionary[idx].key.string):
1389            result = propertyTable.dictionary[idx].value
1390        idx += 1
1391    return result
1392
1393def GetRegDictionary(osdict, prefix):
1394    """ Returns a specially formatted string summary of the given OSDictionary
1395        This is done in order to pretty-print registry property tables in showregistry
1396        and other macros
1397    """
1398    out_string = prefix + "{\n"
1399    idx = 0
1400    count = unsigned(osdict.count)
1401
1402    while idx < count:
1403        out_string += prefix + "  " + GetObjectSummary(osdict.dictionary[idx].key) + " = " + GetObjectSummary(osdict.dictionary[idx].value) + "\n"
1404        idx += 1
1405    out_string += prefix + "}\n"
1406    return out_string
1407
1408def GetString(string):
1409    """ Returns the python string representation of a given OSString
1410    """
1411    out_string = "{0:s}".format(CastIOKitClass(string, 'OSString *').string)
1412    return out_string
1413
1414def GetNumber(num):
1415    out_string = "{0:d}".format(CastIOKitClass(num, 'OSNumber *').value)
1416    return out_string
1417
1418def GetBoolean(b):
1419    """ Shows info about a given OSBoolean
1420    """
1421    out_string = ""
1422    if b == kern.globals.gOSBooleanFalse:
1423        out_string += "No"
1424    else:
1425        out_string += "Yes"
1426    return out_string
1427
1428def GetMetaClass(mc):
1429    """ Shows info about a given OSSymbol
1430    """
1431    out_string = "{0: <5d}x {1: >5d} bytes {2:s}\n".format(mc.instanceCount, mc.classSize, mc.className.string)
1432    return out_string
1433
1434def GetArray(arr):
1435    """ Returns a string containing info about a given OSArray
1436    """
1437    out_string = ""
1438    idx = 0
1439    count = unsigned(arr.count)
1440
1441    while idx < count:
1442        obj = arr.array[idx]
1443        idx += 1
1444        out_string += GetObjectSummary(obj)
1445        if idx < unsigned(arr.count):
1446            out_string += ","
1447    return out_string
1448
1449def GetDictionary(d):
1450    """ Returns a string containing info about a given OSDictionary
1451    """
1452    if d is None:
1453        return ""
1454    out_string = "{\n"
1455    idx = 0
1456    count = unsigned(d.count)
1457
1458    while idx < count:
1459        key = d.dictionary[idx].key
1460        value = d.dictionary[idx].value
1461        out_string += "    \"{}\" = {}\n".format(GetString(key), GetObjectSummary(value))
1462        idx += 1
1463    out_string += "}"
1464    return out_string
1465
1466def GetSet(se):
1467    """ Returns a string containing info about a given OSSet
1468    """
1469    out_string = "[" + GetArray(se.members) + "]"
1470    return out_string
1471
1472def ReadIOPortInt(addr, numbytes, lcpu):
1473    """ Prints results after reading a given ioport
1474    """
1475    result = 0xBAD10AD
1476
1477    if "kdp" != GetConnectionProtocol():
1478        print("Target is not connected over kdp. Nothing to do here.")
1479        return
1480
1481    # Set up the manual KDP packet
1482    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
1483    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
1484    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
1485    if not WriteInt32ToMemoryAddress(0, input_address):
1486        print("0x{0: <4x}: 0x{1: <1x}".format(addr, result))
1487        return
1488
1489    kdp_pkt_size = GetType('kdp_readioport_req_t').GetByteSize()
1490    if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
1491        print("0x{0: <4x}: 0x{1: <1x}".format(addr, result))
1492        return
1493
1494    kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_readioport_req_t *')
1495
1496    header_value = GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_READIOPORT'), length = kdp_pkt_size)
1497
1498    if( WriteInt64ToMemoryAddress((header_value), int(addressof(kgm_pkt.hdr))) and
1499        WriteInt16ToMemoryAddress(addr, int(addressof(kgm_pkt.address))) and
1500        WriteInt32ToMemoryAddress(numbytes, int(addressof(kgm_pkt.nbytes))) and
1501        WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu))) and
1502        WriteInt32ToMemoryAddress(1, input_address)
1503        ):
1504
1505        result_pkt = Cast(addressof(kern.globals.manual_pkt.data), 'kdp_readioport_reply_t *')
1506
1507        if(result_pkt.error == 0):
1508            if numbytes == 1:
1509                result = dereference(Cast(addressof(result_pkt.data), 'uint8_t *'))
1510            elif numbytes == 2:
1511                result = dereference(Cast(addressof(result_pkt.data), 'uint16_t *'))
1512            elif numbytes == 4:
1513                result = dereference(Cast(addressof(result_pkt.data), 'uint32_t *'))
1514
1515    print("{0: <#6x}: {1:#0{2}x}".format(addr, result, (numbytes*2)+2))
1516
1517def WriteIOPortInt(addr, numbytes, value, lcpu):
1518    """ Writes 'value' into ioport specified by 'addr'. Prints errors if it encounters any
1519    """
1520    if "kdp" != GetConnectionProtocol():
1521        print("Target is not connected over kdp. Nothing to do here.")
1522        return
1523
1524    # Set up the manual KDP packet
1525    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
1526    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
1527    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
1528    if not WriteInt32ToMemoryAddress(0, input_address):
1529        print("error writing {0: #x} to port {1: <#6x}: failed to write 0 to input_address".format(value, addr))
1530        return
1531
1532    kdp_pkt_size = GetType('kdp_writeioport_req_t').GetByteSize()
1533    if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
1534        print("error writing {0: #x} to port {1: <#6x}: failed to write kdp_pkt_size".format(value, addr))
1535        return
1536
1537    kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_writeioport_req_t *')
1538
1539    header_value = GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_WRITEIOPORT'), length = kdp_pkt_size)
1540
1541    if( WriteInt64ToMemoryAddress((header_value), int(addressof(kgm_pkt.hdr))) and
1542        WriteInt16ToMemoryAddress(addr, int(addressof(kgm_pkt.address))) and
1543        WriteInt32ToMemoryAddress(numbytes, int(addressof(kgm_pkt.nbytes))) and
1544        WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu)))
1545        ):
1546        if numbytes == 1:
1547            if not WriteInt8ToMemoryAddress(value, int(addressof(kgm_pkt.data))):
1548                print("error writing {0: #x} to port {1: <#6x}: failed to write 8 bit data".format(value, addr))
1549                return
1550        elif numbytes == 2:
1551            if not WriteInt16ToMemoryAddress(value, int(addressof(kgm_pkt.data))):
1552                print("error writing {0: #x} to port {1: <#6x}: failed to write 16 bit data".format(value, addr))
1553                return
1554        elif numbytes == 4:
1555            if not WriteInt32ToMemoryAddress(value, int(addressof(kgm_pkt.data))):
1556                print("error writing {0: #x} to port {1: <#6x}: failed to write 32 bit data".format(value, addr))
1557                return
1558        if not WriteInt32ToMemoryAddress(1, input_address):
1559            print("error writing {0: #x} to port {1: <#6x}: failed to write to input_address".format(value, addr))
1560            return
1561
1562        result_pkt = Cast(addressof(kern.globals.manual_pkt.data), 'kdp_writeioport_reply_t *')
1563
1564        # Done with the write
1565        if(result_pkt.error == 0):
1566            print("Writing {0: #x} to port {1: <#6x} was successful".format(value, addr))
1567    else:
1568        print("error writing {0: #x} to port {1: <#6x}".format(value, addr))
1569
1570@lldb_command('showinterruptcounts')
1571def showinterruptcounts(cmd_args=None):
1572    """ Shows event source based interrupt counts by nub name and interrupt index.
1573        Does not cover interrupts that are not event source based.  Will report 0
1574        if interrupt accounting is disabled.
1575    """
1576
1577    header_format = "{0: <20s} {1: >5s} {2: >20s}"
1578    content_format = "{0: <20s} {1: >5d} {2: >20d}"
1579
1580    print(header_format.format("Name", "Index", "Count"))
1581
1582    for i in kern.interrupt_stats:
1583        owner = CastIOKitClass(i.owner, 'IOInterruptEventSource *')
1584        nub = CastIOKitClass(owner.provider, 'IORegistryEntry *')
1585        name = None
1586
1587        # To uniquely identify an interrupt, we need the nub name and the index.  The index
1588        # is stored with the stats object, but we need to retrieve the name.
1589
1590        registryTable = nub.fRegistryTable
1591        propertyTable = nub.fPropertyTable
1592
1593        name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey)
1594        if name is None:
1595            name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey)
1596        if name is None:
1597            name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey)
1598
1599        if name is None:
1600            nub_name = "Unknown"
1601        else:
1602            nub_name = GetString(CastIOKitClass(name, 'OSString *'))
1603
1604        # We now have everything we need; spew the requested data.
1605
1606        interrupt_index = i.interruptIndex
1607        first_level_count = i.interruptStatistics[0]
1608
1609        print(content_format.format(nub_name, interrupt_index, first_level_count))
1610
1611    return True
1612
1613@lldb_command('showinterruptstats')
1614def showinterruptstats(cmd_args=None):
1615    """ Shows event source based interrupt statistics by nub name and interrupt index.
1616        Does not cover interrupts that are not event source based.  Will report 0
1617        if interrupt accounting is disabled, or if specific statistics are disabled.
1618        Time is reported in ticks of mach_absolute_time.  Statistics are:
1619
1620        Interrupt Count: Number of times the interrupt context handler was run
1621        Interrupt Time: Total time spent in the interrupt context handler (if any)
1622        Workloop Count: Number of times the kernel context handler was run
1623        Workloop CPU Time: Total CPU time spent running the kernel context handler
1624        Workloop Time: Total time spent running the kernel context handler
1625    """
1626
1627    header_format = "{0: <20s} {1: >5s} {2: >20s} {3: >20s} {4: >20s} {5: >20s} {6: >20s} {7: >20s} {8: >20s} {9: >20s}"
1628    content_format = "{0: <20s} {1: >5d} {2: >20d} {3: >20d} {4: >20d} {5: >20d} {6: >20d} {7: >20d} {8: >20d} {9: >#20x}"
1629
1630    print(header_format.format("Name", "Index", "Interrupt Count", "Interrupt Time", "Avg Interrupt Time", "Workloop Count", "Workloop CPU Time", "Workloop Time", "Avg Workloop Time", "Owner"))
1631
1632    for i in kern.interrupt_stats:
1633        owner = CastIOKitClass(i.owner, 'IOInterruptEventSource *')
1634        nub = CastIOKitClass(owner.provider, 'IORegistryEntry *')
1635        name = None
1636
1637        # To uniquely identify an interrupt, we need the nub name and the index.  The index
1638        # is stored with the stats object, but we need to retrieve the name.
1639
1640        registryTable = nub.fRegistryTable
1641        propertyTable = nub.fPropertyTable
1642
1643        name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey)
1644        if name is None:
1645            name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey)
1646        if name is None:
1647            name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey)
1648
1649        if name is None:
1650            nub_name = "Unknown"
1651        else:
1652            nub_name = GetString(CastIOKitClass(name, 'OSString *'))
1653
1654        # We now have everything we need; spew the requested data.
1655
1656        interrupt_index = i.interruptIndex
1657        first_level_count = i.interruptStatistics[0]
1658        second_level_count = i.interruptStatistics[1]
1659        first_level_time = i.interruptStatistics[2]
1660        second_level_cpu_time = i.interruptStatistics[3]
1661        second_level_system_time = i.interruptStatistics[4]
1662
1663        avg_first_level_time = 0
1664        if first_level_count != 0:
1665            avg_first_level_time = first_level_time // first_level_count
1666
1667        avg_second_level_time = 0
1668        if second_level_count != 0:
1669            avg_second_level_time = second_level_system_time // second_level_count
1670
1671        print(content_format.format(nub_name, interrupt_index, first_level_count, first_level_time, avg_first_level_time,
1672            second_level_count, second_level_cpu_time, second_level_system_time, avg_second_level_time, owner))
1673
1674    return True
1675
1676def GetRegistryPlane(plane_name):
1677    """
1678    Given plane_name, returns IORegistryPlane * object or None if there's no such registry plane
1679    """
1680    return LookupKeyInOSDict(kern.globals.gIORegistryPlanes, plane_name, CompareStringToOSSymbol)
1681
1682def DecodePreoslogSource(source):
1683    """
1684    Given preoslog source, return a matching string representation
1685    """
1686    source_to_str = {0 : "iboot"}
1687    if source in source_to_str:
1688        return source_to_str[source]
1689    return "UNKNOWN"
1690
1691def GetPreoslogHeader():
1692    """
1693    Scan IODeviceTree for preoslog and return a python representation of it
1694    """
1695    edt_plane = GetRegistryPlane("IODeviceTree")
1696    if edt_plane is None:
1697        print("Couldn't obtain a pointer to IODeviceTree")
1698        return None
1699
1700    # Registry API functions operate on "plane" global variable
1701    global plane
1702    prev_plane = plane
1703    plane = edt_plane
1704    chosen = FindRegistryObjectRecurse(kern.globals.gRegistryRoot, "chosen")
1705    if chosen is None:
1706        print("Couldn't obtain /chosen IORegistryEntry")
1707        return None
1708
1709    memory_map = FindRegistryObjectRecurse(chosen, "memory-map")
1710    if memory_map is None:
1711        print("Couldn't obtain memory-map from /chosen")
1712        return None
1713
1714    plane = prev_plane
1715
1716    mm_preoslog = LookupKeyInOSDict(memory_map.fPropertyTable, "preoslog", CompareStringToOSSymbol)
1717    if mm_preoslog is None:
1718        print("Couldn't find preoslog entry in memory-map")
1719        return None
1720
1721    if mm_preoslog.length != 16:
1722        print("preoslog entry in memory-map is malformed, expected len is 16, given len is {:d}".format(mm_preoslog.length))
1723        return None
1724
1725    data = cast(mm_preoslog.data, "dtptr_t *")
1726    preoslog_paddr = unsigned(data[0])
1727    preoslog_vaddr = kern.PhysToKernelVirt(preoslog_paddr)
1728    preoslog_size = unsigned(data[1])
1729
1730    preoslog_header = PreoslogHeader()
1731
1732    # This structure defnition doesn't exist in xnu
1733    """
1734    typedef struct  __attribute__((packed)) {
1735        char magic[4];
1736        uint32_t size;
1737        uint32_t offset;
1738        uint8_t source;
1739        uint8_t wrapped;
1740        char data[];
1741    } preoslog_header_t;
1742    """
1743    preoslog_header_ptr = kern.GetValueFromAddress(preoslog_vaddr, "uint8_t *")
1744    preoslog_header.magic = preoslog_header_ptr[0:4]
1745    preoslog_header.source = DecodePreoslogSource(unsigned(preoslog_header_ptr[12]))
1746    preoslog_header.wrapped = unsigned(preoslog_header_ptr[13])
1747    preoslog_header_ptr = kern.GetValueFromAddress(preoslog_vaddr, "uint32_t *")
1748    preoslog_header.size = unsigned(preoslog_header_ptr[1])
1749    preoslog_header.offset = unsigned(preoslog_header_ptr[2])
1750
1751    for i in range(len(preoslog_header.valid_magic)):
1752        c = chr(unsigned(preoslog_header.magic[i]))
1753        if c != preoslog_header.valid_magic[i]:
1754            string = "Error: magic doesn't match, expected {:.4s}, given {:.4s}"
1755            print(string.format(preoslog_header.valid_magic, preoslog_header.magic))
1756            return None
1757
1758    if preoslog_header.size != preoslog_size:
1759        string = "Error: size mismatch preoslog_header.size ({}) != preoslog_size ({})"
1760        print(string.format(preoslog_header.size, preoslog_size))
1761        return None
1762
1763    preoslog_data_ptr = kern.GetValueFromAddress(preoslog_vaddr + 14, "char *")
1764    preoslog_header.data = preoslog_data_ptr.GetSBValue().GetPointeeData(0, preoslog_size)
1765    return preoslog_header
1766
1767@lldb_command("showpreoslog")
1768def showpreoslog(cmd_args=None):
1769    """ Display preoslog buffer """
1770
1771    preoslog = GetPreoslogHeader()
1772    if preoslog is None:
1773        print("Error: couldn't obtain preoslog header")
1774        return False
1775
1776    header = "".join([
1777        "----preoslog log header-----\n",
1778        "size - {} bytes\n",
1779        "write offset - {:#x}\n",
1780        "wrapped - {}\n",
1781        "source - {}\n",
1782        "----preoslog log start------"
1783        ])
1784
1785    print(header.format(preoslog.size, preoslog.offset, preoslog.wrapped, preoslog.source))
1786
1787    err = lldb.SBError()
1788    if preoslog.wrapped > 0:
1789        print(six.ensure_str(preoslog.data.GetString(err, preoslog.offset + 1)))
1790    print(six.ensure_str(preoslog.data.GetString(err, 0)))
1791    print("-----preoslog log end-------")
1792    return True
1793
1794@lldb_command('showeventsources')
1795def ShowEventSources(cmd_args=None):
1796    """ Show all event sources for a IOWorkLoop
1797        syntax: (lldb) showeventsources <IOWorkLoop *>
1798    """
1799    if not cmd_args:
1800        print("Please specify the address of the IOWorkLoop")
1801        print(ShowEventSources.__doc__)
1802        return
1803
1804    obj = kern.GetValueFromAddress(cmd_args[0], 'IOWorkLoop *')
1805    idx = 0
1806    event = obj.eventChain
1807    while event != 0:
1808        enabled = event.enabled
1809        print("{}: {} [{}]".format(idx, GetObjectSummary(event), "enabled" if enabled else "disabled"))
1810        event = event.eventChainNext
1811        idx += 1
1812