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