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