xref: /xnu-12377.1.9/tools/lldbmacros/ioreg.py (revision f6217f891ac0bb64f3d375211650a4c1ff8ca1ea)
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    head = kern.StripKernelPAC(addressof(wq.fWorkQueue))
554    head = kern.GetValueFromAddress(head, 'queue_head_t *')
555
556    for next in IterateQueue(head, 'IOServicePM *', 'WorkChain'):
557        out_str += ioservicepm_header.format("IOService", "ps", "ms", "wr", "name")
558        out_str += "0x{:<16x}  {:<2d}  {:<2d}  {:<2d}  {:<s}\n".format(
559            next.Owner, next.CurrentPowerState, next.MachineState, next.WaitReason, next.Name)
560        out_str += iopmrequest_header.format("IOPMRequest", "type", "next_req", "root_req", "work_wait", "free_wait", "arg0", "arg1", "arg2")
561        next_head = kern.StripKernelPAC(addressof(next.RequestHead))
562        next_head = kern.GetValueFromAddress(next_head, 'queue_head_t *')
563        for request in IterateQueue(next_head, 'IOPMRequest *', 'fCommandChain'):
564            out_str += iopmrequest_indent
565            out_str += "0x{:<16x}  0x{:<2x}  0x{:<16x}  0x{:<16x}".format(
566                request, request.fRequestType, request.fRequestNext, request.fRequestRoot)
567            out_str += "  0x{:<8x}  0x{:<8x}".format(
568                request.fWorkWaitCount, request.fFreeWaitCount)
569            out_str += "  0x{:<16x}  0x{:<16x}  0x{:<16x}\n".format(
570                request.fArg0, request.fArg1, request.fArg2)
571    return out_str
572
573@lldb_command('showiopmqueues')
574def ShowIOPMQueues(cmd_args=None):
575    """ Show IOKit power management queues and IOPMRequest objects.
576    """
577    print("IOPMWorkQueue 0x{:<16x} ({:<d} IOServicePM)\n".format(
578        kern.globals.gIOPMWorkQueue, kern.globals.gIOPMWorkQueue.fQueueLength))
579    print(GetIOPMWorkQueueSummary(kern.globals.gIOPMWorkQueue))
580
581@lldb_type_summary(['IOService *'])
582@header("")
583def GetIOPMInterest(service):
584    iopm = CastIOKitClass(service.pwrMgt, 'IOServicePM *')
585    if unsigned(iopm) == 0:
586        raise ArgumentError("error: no IOServicePM")
587        return
588
589    list = CastIOKitClass(iopm.InterestedDrivers, 'IOPMinformeeList *')
590    out_str = "IOServicePM 0x{:<16x} ({:<d} interest, {:<d} pending ack)\n".format(
591        iopm, list.length, iopm.HeadNotePendingAcks)
592    if list.length == 0:
593        return
594
595    out_str += "    {:<20s}{:<8s}{:<10s}{:<20s}{:<20s}{:<20s}{:<s}\n".format(
596        "informee", "active", "ticks", "notifyTime", "service", "regId", "name")
597    next = CastIOKitClass(list.firstItem, 'IOPMinformee *')
598    while unsigned(next) != 0:
599        driver = CastIOKitClass(next.whatObject, 'IOService *')
600        name = GetRegistryEntryName(driver)
601        reg_id = CastIOKitClass(driver, 'IORegistryEntry *').reserved.fRegistryEntryID;
602        out_str += "    0x{:<16x}  {:<6s}  {:<8d}  0x{:<16x}  0x{:<16x}  0x{:<16x}  {:<s}\n".format(
603            next, "Yes" if next.active != 0 else "No" , next.timer, next.startTime, next.whatObject, reg_id, name)
604        next = CastIOKitClass(next.nextInList, 'IOPMinformee *')
605    return out_str
606
607@lldb_command('showiopminterest')
608def ShowIOPMInterest(cmd_args=None):
609    """ Show the interested drivers for an IOService.
610        syntax: (lldb) showiopminterest <IOService>
611    """
612    if cmd_args is None or len(cmd_args) == 0:
613        raise ArgumentError("Please specify the address of the IOService")
614
615    obj = kern.GetValueFromAddress(cmd_args[0], 'IOService *')
616    print(GetIOPMInterest(obj))
617
618@lldb_command("showinterruptvectors")
619def ShowInterruptVectorInfo(cmd_args=None):
620    """
621    Shows interrupt vectors.
622    """
623
624    # Constants
625    kInterruptTriggerModeMask  = 0x01
626    kInterruptTriggerModeEdge  = 0x00
627    kInterruptTriggerModeLevel = kInterruptTriggerModeMask
628    kInterruptPolarityMask     = 0x02
629    kInterruptPolarityHigh     = 0x00
630    kInterruptPolarityLow      = kInterruptPolarityMask
631    kInterruptShareableMask    = 0x04
632    kInterruptNotShareable     = 0x00
633    kInterruptIsShareable      = kInterruptShareableMask
634    kIOInterruptTypePCIMessaged = 0x00010000
635
636    # Get all interrupt controllers
637    interrupt_controllers = list(SearchInterruptControllerDrivers())
638
639    print("Interrupt controllers: ")
640    for ic in interrupt_controllers:
641        print("  {}".format(ic))
642    print("")
643
644    # Iterate over all entries in the registry
645    for entry in GetMatchingEntries(lambda _: True):
646        # Get the name of the entry
647        entry_name = GetRegistryEntryName(entry)
648
649        # Get the location of the entry
650        entry_location = GetRegistryEntryLocationInPlane(entry, kern.globals.gIOServicePlane)
651        if entry_location is None:
652            entry_location = ""
653        else:
654            entry_location = "@" + entry_location
655
656        # Get the interrupt properties
657        (msi_mode, vectorDataList, vectorContList) = GetRegistryEntryInterruptProperties(entry)
658        should_print = False
659        out_str = ""
660        for (vector_data, vector_cont) in zip(vectorDataList, vectorContList):
661            # vector_cont is the name of the interrupt controller. Find the matching controller from
662            # the list of controllers obtained earlier
663            matching_ics = [ic for ic in interrupt_controllers if ic.name == vector_cont]
664
665            if len(matching_ics) > 0:
666                should_print = True
667                # Take the first match
668                matchingIC = matching_ics[0]
669
670                # Use the vector_data to determine the vector and any flags
671                data_ptr = vector_data.data
672                data_length = vector_data.length
673
674                # Dereference vector_data as a uint32_t * and add the base vector number
675                gsi = unsigned(dereference(Cast(data_ptr, 'uint32_t *')))
676                gsi += matchingIC.base_vector_number
677
678                # If data_length is >= 8 then vector_data contains interrupt flags
679                if data_length >= 8:
680                    # Add sizeof(uint32_t) to data_ptr to get the flags pointer
681                    flags_ptr = kern.GetValueFromAddress(unsigned(data_ptr) + sizeof("uint32_t"))
682                    flags = unsigned(dereference(Cast(flags_ptr, 'uint32_t *')))
683                    out_str += "  +----- [Interrupt Controller {ic}] vector {gsi}, {trigger_level}, {active}, {shareable}{messaged}\n" \
684                            .format(ic=matchingIC.name, gsi=hex(gsi),
685                                    trigger_level="level trigger" if flags & kInterruptTriggerModeLevel else "edge trigger",
686                                    active="active low" if flags & kInterruptPolarityLow else "active high",
687                                    shareable="shareable" if flags & kInterruptIsShareable else "exclusive",
688                                    messaged=", messaged" if flags & kIOInterruptTypePCIMessaged else "")
689                else:
690                    out_str += "  +----- [Interrupt Controller {ic}] vector {gsi}\n".format(ic=matchingIC.name, gsi=hex(gsi))
691        if should_print:
692            print("[ {entry_name}{entry_location} ]{msi_mode}\n{out_str}" \
693                .format(entry_name=entry_name,
694                        entry_location=entry_location,
695                        msi_mode=" - MSIs enabled" if msi_mode else "",
696                        out_str=out_str))
697
698@lldb_command("showiokitclasshierarchy")
699def ShowIOKitClassHierarchy(cmd_args=None):
700    """
701    Show class hierarchy for a IOKit class
702    """
703    if cmd_args is None or len(cmd_args) == 0:
704        raise ArgumentError("Usage: showiokitclasshierarchy <IOKit class name>")
705
706    class_name = cmd_args[0]
707    metaclasses = GetMetaClasses()
708    if class_name not in metaclasses:
709        print("Class {} does not exist".format(class_name))
710        return
711    metaclass = metaclasses[class_name]
712
713    # loop over superclasses
714    hierarchy = []
715    current_metaclass = metaclass
716    while current_metaclass is not None:
717        hierarchy.insert(0, current_metaclass)
718        current_metaclass = current_metaclass.superclass()
719
720    for (index, mc) in enumerate(hierarchy):
721        indent = ("    " * index) + "+---"
722        print("{}[ {} ] {}".format(indent, str(mc.className()), str(mc.data())))
723
724
725######################################
726#  Helper routines
727######################################
728def ShowRegistryEntryRecurse(entry, prefix, printProps):
729    """ prints registry entry summary and recurses through all its children.
730    """
731    # Setup
732    global plane
733    out_string = ""
734    plen = (len(prefix)//2)
735    registryTable = entry.fRegistryTable
736    propertyTable = entry.fPropertyTable
737
738    # Print entry details
739    print("{0:s}{1:s}".format(prefix, GetRegistryEntrySummary(entry)))
740    # Printing large property tables make it look like lldb is 'stuck'
741    if printProps:
742        print(GetRegDictionary(propertyTable, prefix + "  | "))
743
744    # Recurse
745    if plane is None:
746        childKey = kern.globals.gIOServicePlane.keys[1]
747    else:
748        childKey = plane.keys[1]
749    childArray = LookupKeyInOSDict(registryTable, childKey)
750    if childArray is not None:
751        idx = 0
752        ca = CastIOKitClass(childArray, 'OSArray *')
753        count = unsigned(ca.count)
754        array = ca.array
755        while idx < count:
756            if plen != 0 and plen != 1 and (plen & (plen - 1)) == 0:
757                ShowRegistryEntryRecurse(CastIOKitClass(array[idx], 'IORegistryEntry *'), prefix + "| ", printProps)
758            else:
759                ShowRegistryEntryRecurse(CastIOKitClass(array[idx], 'IORegistryEntry *'), prefix + "  ", printProps)
760            idx += 1
761
762def FindRegistryEntryRecurse(entry, search_name, stopAfterFirst):
763    """ Checks if given registry entry's name matches the search_name we're looking for
764        If yes, it prints the entry's summary and then recurses through its children
765        If no, it does nothing and recurses through its children
766    """
767    # Setup
768    global plane
769    registryTable = entry.fRegistryTable
770    propertyTable = entry.fPropertyTable
771
772    # Compare
773    name = None
774    name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey)
775    if name is None:
776        name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey)
777    if name is None:
778        name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey)
779
780    if name is not None:
781        if str(CastIOKitClass(name, 'OSString *').string) == search_name:
782            print(GetRegistryEntrySummary(entry))
783            if stopAfterFirst is True:
784                return True
785    elif (pwrMgt := CastIOKitClass(entry, 'IOService *').pwrMgt) and (name := pwrMgt.Name):
786        if str(name) == search_name:
787            print(GetRegistryEntrySummary(entry))
788            if stopAfterFirst is True:
789                return True
790
791    # Recurse
792    if plane is None:
793        childKey = kern.globals.gIOServicePlane.keys[1]
794    else:
795        childKey = plane.keys[1]
796    childArray = LookupKeyInOSDict(registryTable, childKey)
797    if childArray is not None:
798        idx = 0
799        ca = CastIOKitClass(childArray, 'OSArray *')
800        array = ca.array
801        count = unsigned(ca.count)
802        while idx < count:
803            if FindRegistryEntryRecurse(CastIOKitClass(array[idx], 'IORegistryEntry *'), search_name, stopAfterFirst) is True:
804                return True
805            idx += 1
806    return False
807
808def FindRegistryObjectRecurse(entry, search_name):
809    """ Checks if given registry entry's name matches the search_name we're looking for
810        If yes, return the entry
811        If no, it does nothing and recurses through its children
812        Implicitly stops after finding the first entry
813    """
814    # Setup
815    global plane
816    registryTable = entry.fRegistryTable
817    propertyTable = entry.fPropertyTable
818
819    # Compare
820    name = None
821    name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey)
822    if name is None:
823        name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey)
824    if name is None:
825        name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey)
826
827    if name is not None:
828        if str(CastIOKitClass(name, 'OSString *').string) == search_name:
829            return entry
830    elif (pwrMgt := CastIOKitClass(entry, 'IOService *').pwrMgt) and (name := pwrMgt.Name):
831        if str(name) == search_name:
832            return entry
833
834    # Recurse
835    if plane is None:
836        childKey = kern.globals.gIOServicePlane.keys[1]
837    else:
838        childKey = plane.keys[1]
839    childArray = LookupKeyInOSDict(registryTable, childKey)
840    if childArray is not None:
841        ca = CastIOKitClass(childArray, 'OSArray *')
842        array = ca.array
843        for idx in range(ca.count):
844            registry_object = FindRegistryObjectRecurse(CastIOKitClass(array[idx], 'IORegistryEntry *'), search_name)
845            if not registry_object or int(registry_object) == int(0):
846                continue
847            else:
848                return registry_object
849    return None
850
851def CompareStringToOSSymbol(string, os_sym):
852    """
853    Lexicographically compare python string to OSSymbol
854    Params:
855    string - python string
856    os_sym - OSSymbol
857
858    Returns:
859    0 if string == os_sym
860    1 if string > os_sym
861    -1 if string < os_sym
862    """
863    os_sym_str = GetString(os_sym)
864    if string > os_sym_str:
865        return 1
866    elif string < os_sym_str:
867        return -1
868    else:
869        return 0
870
871class IOKitMetaClass(object):
872    """
873    A class that represents a IOKit metaclass. This is used to represent the
874    IOKit inheritance hierarchy.
875    """
876
877    def __init__(self, meta):
878        """
879        Initialize a IOKitMetaClass object.
880
881        Args:
882            meta (core.cvalue.value): A LLDB value representing a
883                OSMetaClass *.
884        """
885        self._meta = meta
886        self._superclass = None
887
888    def data(self):
889        return self._meta
890
891    def setSuperclass(self, superclass):
892        """
893        Set the superclass for this metaclass.
894
895        Args:
896            superclass (core.cvalue.value): A LLDB value representing a
897                OSMetaClass *.
898        """
899        self._superclass = superclass
900
901    def superclass(self):
902        """
903        Get the superclass for this metaclass (set by the setSuperclass method).
904
905        Returns:
906            core.cvalue.value: A LLDB value representing a OSMetaClass *.
907        """
908        return self._superclass
909
910    def className(self):
911        """
912        Get the name of the class this metaclass represents.
913
914        Returns:
915            str: The class name
916        """
917        return self._meta.className.string
918
919    def inheritsFrom(self, other):
920        """
921        Check if the class represented by this metaclass inherits from a class
922        represented by another metaclass.
923
924        Args:
925            other (IOKitMetaClass): The other metaclass
926
927        Returns:
928            bool: Returns True if this class inherits from the other class and
929                False otherwise.
930        """
931        current = self
932        while current is not None:
933            if current == other:
934                return True
935            else:
936                current = current.superclass()
937
938
939def GetRegistryEntryClassName(entry):
940    """
941    Get the class name of a registry entry.
942
943    Args:
944        entry (core.cvalue.value): A LLDB value representing a
945            IORegistryEntry *.
946
947    Returns:
948        str: The class name of the entry or None if a class name could not be
949            found.
950    """
951    # Check using IOClass key
952    result = LookupKeyInOSDict(entry.fPropertyTable, kern.globals.gIOClassKey)
953    if result is not None:
954        return GetString(result).replace("\"", "")
955    else:
956        # Use the vtable of the entry to determine the concrete type
957        vt = dereference(Cast(entry, 'uintptr_t *')) - 2 * sizeof('uintptr_t')
958        vt = kern.StripKernelPAC(vt)
959        vtype = kern.SymbolicateFromAddress(vt)
960        if len(vtype) > 0:
961            vtableName = vtype[0].GetName()
962            return vtableName[11:] # strip off "vtable for "
963        else:
964            return None
965
966
967def GetRegistryEntryName(entry):
968    """
969    Get the name of a registry entry.
970
971    Args:
972        entry (core.cvalue.value): A LLDB value representing a
973            IORegistryEntry *.
974
975    Returns:
976        str: The name of the entry or None if a name could not be found.
977    """
978    name = None
979
980    # First check the IOService plane nameKey
981    result = LookupKeyInOSDict(entry.fRegistryTable, kern.globals.gIOServicePlane.nameKey)
982    if result is not None:
983        name = GetString(result)
984
985    # Check the global IOName key
986    if name is None:
987        result = LookupKeyInOSDict(entry.fRegistryTable, kern.globals.gIONameKey)
988        if result is not None:
989            name = GetString(result)
990
991    # Check the IOClass key
992    if name is None:
993        result = LookupKeyInOSDict(entry.fPropertyTable, kern.globals.gIOClassKey)
994        if result is not None:
995            name = GetString(result)
996
997    # Remove extra quotes
998    if name is not None:
999        return name.replace("\"", "")
1000    else:
1001        return GetRegistryEntryClassName(entry)
1002
1003
1004def GetRegistryEntryLocationInPlane(entry, plane):
1005    """
1006    Get the registry entry location in a IOKit plane.
1007
1008    Args:
1009        entry (core.cvalue.value): A LLDB value representing a
1010            IORegistryEntry *.
1011        plane: An IOKit plane such as kern.globals.gIOServicePlane.
1012
1013    Returns:
1014        str: The location of the entry or None if a location could not be
1015            found.
1016    """
1017    # Check the plane's pathLocationKey
1018    sym = LookupKeyInOSDict(entry.fRegistryTable, plane.pathLocationKey)
1019
1020    # Check the global IOLocation key
1021    if sym is None:
1022        sym = LookupKeyInOSDict(entry.fRegistryTable, kern.globals.gIOLocationKey)
1023    if sym is not None:
1024        return GetString(sym).replace("\"", "")
1025    else:
1026        return None
1027
1028
1029@caching.cache_dynamically
1030def GetMetaClasses(target=None):
1031    """
1032    Enumerate all IOKit metaclasses. Uses dynamic caching.
1033
1034    Returns:
1035        Dict[str, IOKitMetaClass]: A dictionary mapping each metaclass name to
1036            a IOKitMetaClass object representing the metaclass.
1037    """
1038
1039    # This method takes a while, so it prints a progress indicator
1040    print("Enumerating IOKit metaclasses: ")
1041
1042    do_progress = os.isatty(sys.__stderr__.fileno())
1043
1044    # Iterate over all classes present in sAllClassesDict
1045    count = unsigned(kern.globals.sAllClassesDict.count)
1046    metaclasses_by_address = {}
1047    for idx in range(count):
1048        if do_progress and idx % 10 == 0:
1049            sys.stderr.write("\033[K  {} metaclass found...\r".format(idx))
1050
1051        # Address of metaclass
1052        address = kern.globals.sAllClassesDict.dictionary[idx].value
1053
1054        # Create IOKitMetaClass and store in dict
1055        metaclasses_by_address[int(address)] = IOKitMetaClass(CastIOKitClass(kern.globals.sAllClassesDict.dictionary[idx].value, 'OSMetaClass *'))
1056
1057    # At this point, each metaclass is independent of each other. We don't have superclass links set up yet.
1058
1059    for address, metaclass in metaclasses_by_address.items():
1060        # Get the address of the superclass using the superClassLink in IOMetaClass
1061        superclass_address = int(metaclass.data().superClassLink)
1062
1063        # Skip null superclass
1064        if superclass_address == 0:
1065            continue
1066
1067        # Find the superclass object in the dict
1068        if superclass_address in metaclasses_by_address:
1069            metaclass.setSuperclass(metaclasses_by_address[superclass_address])
1070        else:
1071            print("warning: could not find superclass for {}".format(str(metaclass.data())))
1072
1073    # This method returns a dictionary mapping each class name to the associated metaclass object
1074    metaclasses_by_name = {}
1075    for idx, (_, metaclass) in enumerate(metaclasses_by_address.items()):
1076        if do_progress and idx % 10 == 0:
1077            sys.stderr.write("\033[K  {} metaclass indexed...\r".format(idx))
1078
1079        metaclasses_by_name[str(metaclass.className())] = metaclass
1080
1081    print("  Indexed {} IOKit metaclasses.".format(count))
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    dictionary = osdict.dictionary
1346    key_value = unsigned(key) if type(key) is value else key
1347    while idx < count and result is None:
1348        elem = dictionary[idx]
1349        if comparer is not None:
1350            if comparer(key, elem.key) == 0:
1351                result = elem.value
1352        elif key_value == unsigned(elem.key):
1353            result = elem.value
1354        idx += 1
1355    return result
1356
1357def LookupKeyInPropTable(propertyTable, key_str):
1358    """ Returns the value corresponding to a given key from a registry entry's property table
1359        Returns None if the key was not found
1360        The property that is being searched for is specified as a string in key_str
1361    """
1362    if not propertyTable:
1363        return
1364    count = unsigned(propertyTable.count)
1365    result = None
1366    idx = 0
1367    while idx < count and result is None:
1368        if key_str == str(propertyTable.dictionary[idx].key.string):
1369            result = propertyTable.dictionary[idx].value
1370        idx += 1
1371    return result
1372
1373def GetRegDictionary(osdict, prefix):
1374    """ Returns a specially formatted string summary of the given OSDictionary
1375        This is done in order to pretty-print registry property tables in showregistry
1376        and other macros
1377    """
1378    out_string = prefix + "{\n"
1379    idx = 0
1380    count = unsigned(osdict.count)
1381
1382    dictionary = osdict.dictionary
1383    while idx < count:
1384        entry = dictionary[idx]
1385        out_string += prefix + "  " + GetObjectSummary(entry.key) + " = " + GetObjectSummary(entry.value) + "\n"
1386        idx += 1
1387    out_string += prefix + "}\n"
1388    return out_string
1389
1390def GetString(string):
1391    """ Returns the python string representation of a given OSString
1392    """
1393    out_string = "{0:s}".format(CastIOKitClass(string, 'OSString *').string)
1394    return out_string
1395
1396def GetNumber(num):
1397    out_string = "{0:d}".format(CastIOKitClass(num, 'OSNumber *').value)
1398    return out_string
1399
1400def GetBoolean(b):
1401    """ Shows info about a given OSBoolean
1402    """
1403    out_string = ""
1404    if b == kern.globals.gOSBooleanFalse:
1405        out_string += "No"
1406    else:
1407        out_string += "Yes"
1408    return out_string
1409
1410def GetMetaClass(mc):
1411    """ Shows info about a given OSSymbol
1412    """
1413    out_string = "{0: <5d}x {1: >5d} bytes {2:s}\n".format(mc.instanceCount, mc.classSize, mc.className.string)
1414    return out_string
1415
1416def GetArray(arr):
1417    """ Returns a string containing info about a given OSArray
1418    """
1419    out_string = ""
1420    idx = 0
1421    count = unsigned(arr.count)
1422
1423    array = arr.array
1424    while idx < count:
1425        obj = array[idx]
1426        idx += 1
1427        out_string += GetObjectSummary(obj)
1428        if idx < count:
1429            out_string += ","
1430    return out_string
1431
1432def GetDictionary(d):
1433    """ Returns a string containing info about a given OSDictionary
1434    """
1435    if d is None:
1436        return ""
1437    out_string = "{\n"
1438    idx = 0
1439    count = unsigned(d.count)
1440    dictionary = d.dictionary
1441    while idx < count:
1442        entry = dictionary[idx]
1443        key = entry.key
1444        value = entry.value
1445        out_string += "    \"{}\" = {}\n".format(GetString(key), GetObjectSummary(value))
1446        idx += 1
1447    out_string += "}"
1448    return out_string
1449
1450def GetSet(se):
1451    """ Returns a string containing info about a given OSSet
1452    """
1453    out_string = "[" + GetArray(se.members) + "]"
1454    return out_string
1455
1456def ReadIOPortInt(addr, numbytes, lcpu):
1457    """ Prints results after reading a given ioport
1458    """
1459    result = 0xBAD10AD
1460
1461    if "kdp" != GetConnectionProtocol():
1462        print("Target is not connected over kdp. Nothing to do here.")
1463        return
1464
1465    # Set up the manual KDP packet
1466    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
1467    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
1468    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
1469    if not WriteInt32ToMemoryAddress(0, input_address):
1470        print("0x{0: <4x}: 0x{1: <1x}".format(addr, result))
1471        return
1472
1473    kdp_pkt_size = GetType('kdp_readioport_req_t').GetByteSize()
1474    if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
1475        print("0x{0: <4x}: 0x{1: <1x}".format(addr, result))
1476        return
1477
1478    kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_readioport_req_t *')
1479
1480    header_value = GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_READIOPORT'), length = kdp_pkt_size)
1481
1482    if( WriteInt64ToMemoryAddress((header_value), int(addressof(kgm_pkt.hdr))) and
1483        WriteInt16ToMemoryAddress(addr, int(addressof(kgm_pkt.address))) and
1484        WriteInt32ToMemoryAddress(numbytes, int(addressof(kgm_pkt.nbytes))) and
1485        WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu))) and
1486        WriteInt32ToMemoryAddress(1, input_address)
1487        ):
1488
1489        result_pkt = Cast(addressof(kern.globals.manual_pkt.data), 'kdp_readioport_reply_t *')
1490
1491        if(result_pkt.error == 0):
1492            if numbytes == 1:
1493                result = dereference(Cast(addressof(result_pkt.data), 'uint8_t *'))
1494            elif numbytes == 2:
1495                result = dereference(Cast(addressof(result_pkt.data), 'uint16_t *'))
1496            elif numbytes == 4:
1497                result = dereference(Cast(addressof(result_pkt.data), 'uint32_t *'))
1498
1499    print("{0: <#6x}: {1:#0{2}x}".format(addr, result, (numbytes*2)+2))
1500
1501def WriteIOPortInt(addr, numbytes, value, lcpu):
1502    """ Writes 'value' into ioport specified by 'addr'. Prints errors if it encounters any
1503    """
1504    if "kdp" != GetConnectionProtocol():
1505        print("Target is not connected over kdp. Nothing to do here.")
1506        return
1507
1508    # Set up the manual KDP packet
1509    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
1510    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
1511    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
1512    if not WriteInt32ToMemoryAddress(0, input_address):
1513        print("error writing {0: #x} to port {1: <#6x}: failed to write 0 to input_address".format(value, addr))
1514        return
1515
1516    kdp_pkt_size = GetType('kdp_writeioport_req_t').GetByteSize()
1517    if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
1518        print("error writing {0: #x} to port {1: <#6x}: failed to write kdp_pkt_size".format(value, addr))
1519        return
1520
1521    kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_writeioport_req_t *')
1522
1523    header_value = GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_WRITEIOPORT'), length = kdp_pkt_size)
1524
1525    if( WriteInt64ToMemoryAddress((header_value), int(addressof(kgm_pkt.hdr))) and
1526        WriteInt16ToMemoryAddress(addr, int(addressof(kgm_pkt.address))) and
1527        WriteInt32ToMemoryAddress(numbytes, int(addressof(kgm_pkt.nbytes))) and
1528        WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu)))
1529        ):
1530        if numbytes == 1:
1531            if not WriteInt8ToMemoryAddress(value, int(addressof(kgm_pkt.data))):
1532                print("error writing {0: #x} to port {1: <#6x}: failed to write 8 bit data".format(value, addr))
1533                return
1534        elif numbytes == 2:
1535            if not WriteInt16ToMemoryAddress(value, int(addressof(kgm_pkt.data))):
1536                print("error writing {0: #x} to port {1: <#6x}: failed to write 16 bit data".format(value, addr))
1537                return
1538        elif numbytes == 4:
1539            if not WriteInt32ToMemoryAddress(value, int(addressof(kgm_pkt.data))):
1540                print("error writing {0: #x} to port {1: <#6x}: failed to write 32 bit data".format(value, addr))
1541                return
1542        if not WriteInt32ToMemoryAddress(1, input_address):
1543            print("error writing {0: #x} to port {1: <#6x}: failed to write to input_address".format(value, addr))
1544            return
1545
1546        result_pkt = Cast(addressof(kern.globals.manual_pkt.data), 'kdp_writeioport_reply_t *')
1547
1548        # Done with the write
1549        if(result_pkt.error == 0):
1550            print("Writing {0: #x} to port {1: <#6x} was successful".format(value, addr))
1551    else:
1552        print("error writing {0: #x} to port {1: <#6x}".format(value, addr))
1553
1554@lldb_command('showinterruptcounts')
1555def showinterruptcounts(cmd_args=None):
1556    """ Shows event source based interrupt counts by nub name and interrupt index.
1557        Does not cover interrupts that are not event source based.  Will report 0
1558        if interrupt accounting is disabled.
1559    """
1560
1561    header_format = "{0: <20s} {1: >5s} {2: >20s}"
1562    content_format = "{0: <20s} {1: >5d} {2: >20d}"
1563
1564    print(header_format.format("Name", "Index", "Count"))
1565
1566    for i in kern.interrupt_stats:
1567        owner = CastIOKitClass(i.owner, 'IOInterruptEventSource *')
1568        nub = CastIOKitClass(owner.provider, 'IORegistryEntry *')
1569        name = None
1570
1571        # To uniquely identify an interrupt, we need the nub name and the index.  The index
1572        # is stored with the stats object, but we need to retrieve the name.
1573
1574        registryTable = nub.fRegistryTable
1575        propertyTable = nub.fPropertyTable
1576
1577        name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey)
1578        if name is None:
1579            name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey)
1580        if name is None:
1581            name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey)
1582
1583        if name is None:
1584            nub_name = "Unknown"
1585        else:
1586            nub_name = GetString(CastIOKitClass(name, 'OSString *'))
1587
1588        # We now have everything we need; spew the requested data.
1589
1590        interrupt_index = i.interruptIndex
1591        first_level_count = i.interruptStatistics[0]
1592
1593        print(content_format.format(nub_name, interrupt_index, first_level_count))
1594
1595    return True
1596
1597@lldb_command('showinterruptstats')
1598def showinterruptstats(cmd_args=None):
1599    """ Shows event source based interrupt statistics by nub name and interrupt index.
1600        Does not cover interrupts that are not event source based.  Will report 0
1601        if interrupt accounting is disabled, or if specific statistics are disabled.
1602        Time is reported in ticks of mach_absolute_time.  Statistics are:
1603
1604        Interrupt Count: Number of times the interrupt context handler was run
1605        Interrupt Time: Total time spent in the interrupt context handler (if any)
1606        Workloop Count: Number of times the kernel context handler was run
1607        Workloop CPU Time: Total CPU time spent running the kernel context handler
1608        Workloop Time: Total time spent running the kernel context handler
1609    """
1610
1611    header_format = "{0: <20s} {1: >5s} {2: >20s} {3: >20s} {4: >20s} {5: >20s} {6: >20s} {7: >20s} {8: >20s} {9: >20s}"
1612    content_format = "{0: <20s} {1: >5d} {2: >20d} {3: >20d} {4: >20d} {5: >20d} {6: >20d} {7: >20d} {8: >20d} {9: >#20x}"
1613
1614    print(header_format.format("Name", "Index", "Interrupt Count", "Interrupt Time", "Avg Interrupt Time", "Workloop Count", "Workloop CPU Time", "Workloop Time", "Avg Workloop Time", "Owner"))
1615
1616    for i in kern.interrupt_stats:
1617        owner = CastIOKitClass(i.owner, 'IOInterruptEventSource *')
1618        nub = CastIOKitClass(owner.provider, 'IORegistryEntry *')
1619        name = None
1620
1621        # To uniquely identify an interrupt, we need the nub name and the index.  The index
1622        # is stored with the stats object, but we need to retrieve the name.
1623
1624        registryTable = nub.fRegistryTable
1625        propertyTable = nub.fPropertyTable
1626
1627        name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey)
1628        if name is None:
1629            name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey)
1630        if name is None:
1631            name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey)
1632
1633        if name is None:
1634            nub_name = "Unknown"
1635        else:
1636            nub_name = GetString(CastIOKitClass(name, 'OSString *'))
1637
1638        # We now have everything we need; spew the requested data.
1639
1640        interrupt_index = i.interruptIndex
1641        first_level_count = i.interruptStatistics[0]
1642        second_level_count = i.interruptStatistics[1]
1643        first_level_time = i.interruptStatistics[2]
1644        second_level_cpu_time = i.interruptStatistics[3]
1645        second_level_system_time = i.interruptStatistics[4]
1646
1647        avg_first_level_time = 0
1648        if first_level_count != 0:
1649            avg_first_level_time = first_level_time // first_level_count
1650
1651        avg_second_level_time = 0
1652        if second_level_count != 0:
1653            avg_second_level_time = second_level_system_time // second_level_count
1654
1655        print(content_format.format(nub_name, interrupt_index, first_level_count, first_level_time, avg_first_level_time,
1656            second_level_count, second_level_cpu_time, second_level_system_time, avg_second_level_time, owner))
1657
1658    return True
1659
1660def GetRegistryPlane(plane_name):
1661    """
1662    Given plane_name, returns IORegistryPlane * object or None if there's no such registry plane
1663    """
1664    return LookupKeyInOSDict(kern.globals.gIORegistryPlanes, plane_name, CompareStringToOSSymbol)
1665
1666def DecodePreoslogSource(source):
1667    """
1668    Given preoslog source, return a matching string representation
1669    """
1670    source_to_str = {0 : "iboot"}
1671    if source in source_to_str:
1672        return source_to_str[source]
1673    return "UNKNOWN"
1674
1675def GetPreoslogHeader():
1676    """
1677    Scan IODeviceTree for preoslog and return a python representation of it
1678    """
1679    edt_plane = GetRegistryPlane("IODeviceTree")
1680    if edt_plane is None:
1681        print("Couldn't obtain a pointer to IODeviceTree")
1682        return None
1683
1684    # Registry API functions operate on "plane" global variable
1685    global plane
1686    prev_plane = plane
1687    plane = edt_plane
1688    chosen = FindRegistryObjectRecurse(kern.globals.gRegistryRoot, "chosen")
1689    if chosen is None:
1690        print("Couldn't obtain /chosen IORegistryEntry")
1691        return None
1692
1693    memory_map = FindRegistryObjectRecurse(chosen, "memory-map")
1694    if memory_map is None:
1695        print("Couldn't obtain memory-map from /chosen")
1696        return None
1697
1698    plane = prev_plane
1699
1700    mm_preoslog = LookupKeyInOSDict(memory_map.fPropertyTable, "preoslog", CompareStringToOSSymbol)
1701    if mm_preoslog is None:
1702        print("Couldn't find preoslog entry in memory-map")
1703        return None
1704
1705    if mm_preoslog.length != 16:
1706        print("preoslog entry in memory-map is malformed, expected len is 16, given len is {:d}".format(mm_preoslog.length))
1707        return None
1708
1709    data = cast(mm_preoslog.data, "dtptr_t *")
1710    preoslog_paddr = unsigned(data[0])
1711    preoslog_vaddr = kern.PhysToKernelVirt(preoslog_paddr)
1712    preoslog_size = unsigned(data[1])
1713
1714    preoslog_header = PreoslogHeader()
1715
1716    # This structure defnition doesn't exist in xnu
1717    """
1718    typedef struct  __attribute__((packed)) {
1719        char magic[4];
1720        uint32_t size;
1721        uint32_t offset;
1722        uint8_t source;
1723        uint8_t wrapped;
1724        char data[];
1725    } preoslog_header_t;
1726    """
1727    preoslog_header_ptr = kern.GetValueFromAddress(preoslog_vaddr, "uint8_t *")
1728    preoslog_header.magic = preoslog_header_ptr[0:4]
1729    preoslog_header.source = DecodePreoslogSource(unsigned(preoslog_header_ptr[12]))
1730    preoslog_header.wrapped = unsigned(preoslog_header_ptr[13])
1731    preoslog_header_ptr = kern.GetValueFromAddress(preoslog_vaddr, "uint32_t *")
1732    preoslog_header.size = unsigned(preoslog_header_ptr[1])
1733    preoslog_header.offset = unsigned(preoslog_header_ptr[2])
1734
1735    for i in range(len(preoslog_header.valid_magic)):
1736        c = chr(unsigned(preoslog_header.magic[i]))
1737        if c != preoslog_header.valid_magic[i]:
1738            string = "Error: magic doesn't match, expected {:.4s}, given {:.4s}"
1739            print(string.format(preoslog_header.valid_magic, preoslog_header.magic))
1740            return None
1741
1742    if preoslog_header.size != preoslog_size:
1743        string = "Error: size mismatch preoslog_header.size ({}) != preoslog_size ({})"
1744        print(string.format(preoslog_header.size, preoslog_size))
1745        return None
1746
1747    preoslog_data_ptr = kern.GetValueFromAddress(preoslog_vaddr + 14, "char *")
1748    preoslog_header.data = preoslog_data_ptr.GetSBValue().GetPointeeData(0, preoslog_size)
1749    return preoslog_header
1750
1751@lldb_command("showpreoslog")
1752def showpreoslog(cmd_args=None):
1753    """ Display preoslog buffer """
1754
1755    preoslog = GetPreoslogHeader()
1756    if preoslog is None:
1757        print("Error: couldn't obtain preoslog header")
1758        return False
1759
1760    header = "".join([
1761        "----preoslog log header-----\n",
1762        "size - {} bytes\n",
1763        "write offset - {:#x}\n",
1764        "wrapped - {}\n",
1765        "source - {}\n",
1766        "----preoslog log start------"
1767        ])
1768
1769    print(header.format(preoslog.size, preoslog.offset, preoslog.wrapped, preoslog.source))
1770
1771    err = lldb.SBError()
1772    if preoslog.wrapped > 0:
1773        print(preoslog.data.GetString(err, preoslog.offset + 1))
1774
1775    print(preoslog.data.GetString(err, 0).encode(errors='backslashreplace').decode())
1776    print("-----preoslog log end-------")
1777
1778    if not err.success:
1779        raise RuntimeError(f"SBError when retreiving preoslog data: {err.GetDescription()}")
1780
1781    return True
1782
1783@lldb_command('showeventsources')
1784def ShowEventSources(cmd_args=None):
1785    """ Show all event sources for a IOWorkLoop
1786        syntax: (lldb) showeventsources <IOWorkLoop *>
1787    """
1788    if cmd_args is None or len(cmd_args) == 0:
1789        raise ArgumentError("Please specify the address of the IOWorkLoop")
1790
1791    obj = kern.GetValueFromAddress(cmd_args[0], 'IOWorkLoop *')
1792    idx = 0
1793    event = obj.eventChain
1794    while event != 0:
1795        enabled = event.enabled
1796        print("{}: {} [{}]".format(idx, GetObjectSummary(event), "enabled" if enabled else "disabled"))
1797        event = event.eventChainNext
1798        idx += 1
1799
1800def GetRegionProp(propertyTable, pattern):
1801    """ Returns the list corresponding to a given pattern from a registry entry's property table
1802        Returns empty list if the key is not found
1803        The property that is being searched for is specified as a string in pattern
1804    """
1805    if not propertyTable:
1806        return None
1807
1808    count = unsigned(propertyTable.count)
1809    result = []
1810    res = None
1811    idx = 0
1812    while idx < count:
1813        res = re.search(pattern, str(propertyTable.dictionary[idx].key.string))
1814        if res:
1815            result.append(res.group())
1816        idx += 1
1817
1818    return result
1819
1820@lldb_command("showcarveouts")
1821def ShowCarveouts(cmd_args=None):
1822    """
1823    Scan IODeviceTree for every object in carveout-memory-map and print the memory carveouts.
1824    syntax: (lldb) showcarveouts
1825    """
1826    edt_plane = GetRegistryPlane("IODeviceTree")
1827    if edt_plane is None:
1828        print("Couldn't obtain a pointer to IODeviceTree")
1829        return None
1830
1831    # Registry API functions operate on "plane" global variable
1832    global plane
1833    prev_plane = plane
1834    plane = edt_plane
1835
1836    chosen = FindRegistryObjectRecurse(kern.globals.gRegistryRoot, "chosen")
1837    if chosen is None:
1838        print("Couldn't obtain /chosen IORegistryEntry")
1839        return None
1840
1841    memory_map = FindRegistryObjectRecurse(chosen, "carveout-memory-map")
1842    if memory_map is None:
1843        print("Couldn't obtain memory-map from /chosen/carveout-memory-map")
1844        return None
1845
1846    plane = prev_plane
1847
1848    """
1849    Dynamically populated by iBoot to store memory region description
1850    region-id-<n>: <region n base> <region n size>
1851    region-name-id-<n>: <region n name>
1852    """
1853    name_prop_list = []
1854    range_prop_list = []
1855    region_id_list = []
1856    region_name_id_list = []
1857
1858    region_id = re.compile(r"region-id-\d+")
1859    region_id_list = GetRegionProp(memory_map.fPropertyTable, region_id);
1860    region_name_id = re.compile(r"region-name-id-\d+")
1861    region_name_id_list = GetRegionProp(memory_map.fPropertyTable, region_name_id);
1862
1863    for names in region_name_id_list:
1864        mm_entry = LookupKeyInOSDict(memory_map.fPropertyTable, names, CompareStringToOSSymbol)
1865        if mm_entry is None:
1866            print("Couldn't find " + names + " entry in carveout-memory-map", file=sys.stderr)
1867            continue
1868        data = cast(mm_entry.data, "char *")
1869        string = "{:<32s}: "
1870        name_prop_list.append( string.format(data) );
1871
1872    for ids in region_id_list:
1873        mm_entry = LookupKeyInOSDict(memory_map.fPropertyTable, ids, CompareStringToOSSymbol)
1874        if mm_entry is None:
1875            print("Couldn't find " + ids + " entry in carveout-memory-map")
1876            continue
1877
1878        data = cast(mm_entry.data, "dtptr_t *")
1879        paddr = unsigned(data[0])
1880        size = unsigned(data[1])
1881
1882        string = "0x{:x}-0x{:x} (size: 0x{:x})"
1883        range_prop_list.append(string.format(paddr, paddr+size, size));
1884
1885    for namep, rangep in zip(name_prop_list, range_prop_list):
1886        print(namep, rangep)
1887
1888    return True
1889