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