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