]> git.saurik.com Git - apple/xnu.git/blob - tools/lldbmacros/ioreg.py
xnu-7195.81.3.tar.gz
[apple/xnu.git] / tools / lldbmacros / ioreg.py
1 from xnu import *
2 from utils import *
3 from kdp import *
4 from core import caching
5 import sys
6 import lldb
7 from collections import deque
8
9 ######################################
10 # Globals
11 ######################################
12 plane = None
13
14 #####################################
15 # Utility functions.
16 #####################################
17 def 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 #####################################
31 class 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("")
49 def 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
107 def 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("")
128 def 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')
184 def 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')
197 def 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')
209 def 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')
247 def 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')
265 def 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')
279 def 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')
287 def 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')
295 def 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')
309 def 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')
323 def 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')
340 def 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')
360 def 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')
380 def 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')
400 def 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')
422 def 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')
444 def 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')
466 def 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("")
535 def 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')
557 def 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("")
566 def 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')
591 def 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")
604 def 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")
684 def 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 ######################################
714 def 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
747 def 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
793 def 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
836 def 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
856 class 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
924 def 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
952 def 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
989 def 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
1014 def 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
1077 def 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
1113 def 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
1150 def 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
1183 def 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
1241 class 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
1307 def 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
1327 def 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
1350 def 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
1366 def 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
1381 def 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
1387 def GetNumber(num):
1388 out_string = "{0:d}".format(CastIOKitClass(num, 'OSNumber *').value)
1389 return out_string
1390
1391 def 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
1401 def 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
1407 def 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
1422 def 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
1439 def GetSet(se):
1440 """ Returns a string containing info about a given OSSet
1441 """
1442 out_string = "[" + GetArray(se.members) + "]"
1443 return out_string
1444
1445 def 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
1490 def 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')
1544 def 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')
1587 def 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
1649 def 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
1655 def 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
1664 def 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")
1741 def 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