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