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