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