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