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