]>
Commit | Line | Data |
---|---|---|
1 | from xnu import * | |
2 | from utils import * | |
3 | import sys | |
4 | ||
5 | ###################################### | |
6 | # Globals | |
7 | ###################################### | |
8 | plane = None | |
9 | ||
10 | ##################################### | |
11 | # Utility functions. | |
12 | ##################################### | |
13 | def CastIOKitClass(obj, target_type): | |
14 | """ Type cast an object to another IOKIT CPP class. | |
15 | params: | |
16 | obj - core.value object representing some C construct in lldb | |
17 | target_type - str : ex 'OSString *' | |
18 | - lldb.SBType : | |
19 | """ | |
20 | v = Cast(obj, target_type) | |
21 | v.GetSBValue().SetPreferDynamicValue(lldb.eNoDynamicValues) | |
22 | return v | |
23 | ||
24 | ###################################### | |
25 | # Type Summaries | |
26 | ###################################### | |
27 | @lldb_type_summary(['OSObject *']) | |
28 | @header("") | |
29 | def GetObjectSummary(obj): | |
30 | """ Show info about an OSObject - its vtable ptr and retain count, & more info for simple container classes. | |
31 | """ | |
32 | if obj is None: | |
33 | return | |
34 | ||
35 | vt = dereference(Cast(obj, 'uintptr_t *')) - 2 * sizeof('uintptr_t') | |
36 | vtype = kern.SymbolicateFromAddress(vt) | |
37 | if len(vtype): | |
38 | vtype_str = " <" + vtype[0].GetName() + ">" | |
39 | else: | |
40 | vtype_str = "" | |
41 | if hasattr(obj, 'retainCount'): | |
42 | retCount = (obj.retainCount & 0xffff) | |
43 | cntnrRetCount = (obj.retainCount >> 16) | |
44 | 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) | |
45 | else: | |
46 | out_string = "`object 0x{0: <16x}, vt 0x{1: <16x}{2:s}` ".format(obj, vt, vtype_str) | |
47 | ||
48 | ztvAddr = kern.GetLoadAddressForSymbol('_ZTV8OSString') | |
49 | if vt == ztvAddr: | |
50 | out_string += GetString(obj) | |
51 | return out_string | |
52 | ||
53 | ztvAddr = kern.GetLoadAddressForSymbol('_ZTV8OSSymbol') | |
54 | if vt == ztvAddr: | |
55 | out_string += GetString(obj) | |
56 | return out_string | |
57 | ||
58 | ztvAddr = kern.GetLoadAddressForSymbol('_ZTV8OSNumber') | |
59 | if vt == ztvAddr: | |
60 | out_string += GetNumber(obj) | |
61 | return out_string | |
62 | ||
63 | ztvAddr = kern.GetLoadAddressForSymbol('_ZTV9OSBoolean') | |
64 | if vt == ztvAddr: | |
65 | out_string += GetBoolean(obj) | |
66 | return out_string | |
67 | ||
68 | ztvAddr = kern.GetLoadAddressForSymbol('_ZTV7OSArray') | |
69 | if vt == ztvAddr: | |
70 | out_string += "(" + GetArray(CastIOKitClass(obj, 'OSArray *')) + ")" | |
71 | return out_string | |
72 | ||
73 | ztvAddr = kern.GetLoadAddressForSymbol('_ZTV5OSSet') | |
74 | if vt == ztvAddr: | |
75 | out_string += GetSet(CastIOKitClass(obj, 'OSSet *')) | |
76 | return out_string | |
77 | ||
78 | ztvAddr = kern.GetLoadAddressForSymbol('_ZTV12OSDictionary') | |
79 | if vt == ztvAddr: | |
80 | out_string += GetDictionary(CastIOKitClass(obj, 'OSDictionary *')) | |
81 | return out_string | |
82 | ||
83 | return out_string | |
84 | ||
85 | ||
86 | def GetObjectTypeStr(obj): | |
87 | """ Return the type of an OSObject's container class | |
88 | """ | |
89 | if obj is None: | |
90 | return None | |
91 | ||
92 | vt = dereference(Cast(obj, 'uintptr_t *')) - 2 * sizeof('uintptr_t') | |
93 | vtype = kern.SymbolicateFromAddress(vt) | |
94 | if len(vtype): | |
95 | return vtype[0].GetName() | |
96 | ||
97 | # See if the value is in a kext with no symbols | |
98 | for kval in IterateLinkedList(kern.globals.kmod, 'next'): | |
99 | if vt >= unsigned(kval.address) and vt <= (unsigned(kval.address) + unsigned(kval.size)): | |
100 | return "kmod:{:s}+{:#0x}".format(kval.name, vt - unsigned(kval.address)) | |
101 | return None | |
102 | ||
103 | ||
104 | @lldb_type_summary(['IORegistryEntry *']) | |
105 | @header("") | |
106 | def GetRegistryEntrySummary(entry): | |
107 | """ returns a string containing summary information about an IORegistry | |
108 | object including it's registry id , vtable ptr and retain count | |
109 | """ | |
110 | name = None | |
111 | out_string = "" | |
112 | registryTable = entry.fRegistryTable | |
113 | propertyTable = entry.fPropertyTable | |
114 | ||
115 | name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) | |
116 | if name is None: | |
117 | name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) | |
118 | if name is None: | |
119 | name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) | |
120 | ||
121 | if name is not None: | |
122 | out_string += "+-o {0:s} ".format(GetString(CastIOKitClass(name, 'OSString *'))) | |
123 | elif CastIOKitClass(entry, 'IOService *').pwrMgt and CastIOKitClass(entry, 'IOService *').pwrMgt.Name: | |
124 | out_string += "+-o {0:s} ".format(CastIOKitClass(entry, 'IOService *').pwrMgt.Name) | |
125 | else: | |
126 | out_string += "+-o ?? " | |
127 | ||
128 | # I'm using uintptr_t for now to work around <rdar://problem/12749733> FindFirstType & Co. should allow you to make pointer types directly | |
129 | vtableAddr = dereference(Cast(entry, 'uintptr_t *')) - 2 * sizeof('uintptr_t *') | |
130 | vtype = kern.SymbolicateFromAddress(vtableAddr) | |
131 | if vtype is None or len(vtype) < 1: | |
132 | out_string += "<object 0x{0: <16x}, id 0x{1:x}, vtable 0x{2: <16x}".format(entry, CastIOKitClass(entry, 'IORegistryEntry *').reserved.fRegistryEntryID, vtableAddr) | |
133 | else: | |
134 | out_string += "<object 0x{0: <16x}, id 0x{1:x}, vtable 0x{2: <16x} <{3:s}>".format(entry, CastIOKitClass(entry, 'IORegistryEntry *').reserved.fRegistryEntryID, | |
135 | vtableAddr, vtype[0].GetName()) | |
136 | ||
137 | ztvAddr = kern.GetLoadAddressForSymbol('_ZTV15IORegistryEntry') | |
138 | if vtableAddr != ztvAddr: | |
139 | out_string += ", " | |
140 | state = CastIOKitClass(entry, 'IOService *').__state[0] | |
141 | # kIOServiceRegisteredState | |
142 | if 0 == state & 2: | |
143 | out_string += "!" | |
144 | out_string += "registered, " | |
145 | # kIOServiceMatchedState | |
146 | if 0 == state & 4: | |
147 | out_string += "!" | |
148 | out_string += "matched, " | |
149 | #kIOServiceInactiveState | |
150 | if 0 != state & 1: | |
151 | out_string += "in" | |
152 | busyCount = (CastIOKitClass(entry, 'IOService *').__state[1] & 0xff) | |
153 | retCount = (CastIOKitClass(entry, 'IOService *').retainCount & 0xffff) | |
154 | out_string += "active, busy {0}, retain count {1}>".format(busyCount, retCount) | |
155 | return out_string | |
156 | ||
157 | ###################################### | |
158 | # Commands | |
159 | ###################################### | |
160 | @lldb_command('showallclasses') | |
161 | def ShowAllClasses(cmd_args=None): | |
162 | """ Show the instance counts and ivar size of all OSObject subclasses. | |
163 | See ioclasscount man page for details | |
164 | """ | |
165 | idx = 0 | |
166 | count = unsigned(kern.globals.sAllClassesDict.count) | |
167 | ||
168 | while idx < count: | |
169 | meta = CastIOKitClass(kern.globals.sAllClassesDict.dictionary[idx].value, 'OSMetaClass *') | |
170 | idx += 1 | |
171 | print GetMetaClass(meta) | |
172 | ||
173 | @lldb_command('showobject') | |
174 | def ShowObject(cmd_args=None): | |
175 | """ Show info about an OSObject - its vtable ptr and retain count, & more info for simple container classes. | |
176 | """ | |
177 | if not cmd_args: | |
178 | print "Please specify the address of the OSObject whose info you want to view. Type help showobject for help" | |
179 | return | |
180 | ||
181 | obj = kern.GetValueFromAddress(cmd_args[0], 'OSObject *') | |
182 | print GetObjectSummary(obj) | |
183 | ||
184 | #Macro: dumpobject | |
185 | @lldb_command('dumpobject') | |
186 | def DumpObject(cmd_args=None): | |
187 | """ Dumps object information if it is a valid object confirmed by showobject | |
188 | Usage: dumpobject <address of object to be dumped> [class/struct type of object] | |
189 | """ | |
190 | if not cmd_args: | |
191 | print "No arguments passed" | |
192 | print DumpObject.__doc__ | |
193 | return False | |
194 | ||
195 | if len(cmd_args) == 1: | |
196 | try: | |
197 | object_info = lldb_run_command("showobject {:s}".format(cmd_args[0])) | |
198 | except: | |
199 | print "Error!! showobject failed due to invalid value" | |
200 | print DumpObject.__doc__ | |
201 | return False | |
202 | ||
203 | srch = re.search(r'<vtable for ([A-Za-z].*)>', object_info) | |
204 | if not srch: | |
205 | print "Error!! Couldn't find object in registry, input type manually as 2nd argument" | |
206 | print DumpObject.__doc__ | |
207 | return False | |
208 | ||
209 | object_type = srch.group(1) | |
210 | else: | |
211 | type_lookup = lldb_run_command("image lookup -t {:s}".format(cmd_args[1])) | |
212 | if type_lookup.find(cmd_args[1])!= -1: | |
213 | object_type = cmd_args[1] | |
214 | else: | |
215 | print "Error!! Input type {:s} isn't available in image lookup".format(cmd_args[1]) | |
216 | return False | |
217 | ||
218 | print "******** Object Dump for value \'{:s}\' with type \"{:s}\" ********".format(cmd_args[0], object_type) | |
219 | print lldb_run_command("p/x *({:s}*){:s}".format(object_type, cmd_args[0])) | |
220 | ||
221 | #EndMacro: dumpobject | |
222 | ||
223 | @lldb_command('setregistryplane') | |
224 | def SetRegistryPlane(cmd_args=None): | |
225 | """ Set the plane to be used for the IOKit registry macros | |
226 | syntax: (lldb) setregistryplane 0 - will display all known planes | |
227 | syntax: (lldb) setregistryplane 0xaddr - will set the registry plane to 0xaddr | |
228 | syntax: (lldb) setregistryplane gIODTPlane - will set the registry plane to gIODTPlane | |
229 | """ | |
230 | if not cmd_args: | |
231 | print "Please specify the name of the plane you want to use with the IOKit registry macros." | |
232 | print SetRegistryPlane.__doc__ | |
233 | ||
234 | if cmd_args[0] == "0": | |
235 | print GetObjectSummary(kern.globals.gIORegistryPlanes) | |
236 | else: | |
237 | global plane | |
238 | plane = kern.GetValueFromAddress(cmd_args[0], 'IORegistryPlane *') | |
239 | return | |
240 | ||
241 | @lldb_command('showregistryentry') | |
242 | def ShowRegistryEntry(cmd_args=None): | |
243 | """ Show info about a registry entry; its properties and descendants in the current plane | |
244 | syntax: (lldb) showregistryentry 0xaddr | |
245 | syntax: (lldb) showregistryentry gIOPMRootDomain | |
246 | """ | |
247 | if not cmd_args: | |
248 | print "Please specify the address of the registry entry whose info you want to view." | |
249 | print ShowRegistryEntry.__doc__ | |
250 | return | |
251 | ||
252 | entry = kern.GetValueFromAddress(cmd_args[0], 'IORegistryEntry *') | |
253 | ShowRegistryEntryRecurse(entry, "", True) | |
254 | ||
255 | @lldb_command('showregistry') | |
256 | def ShowRegistry(cmd_args=None): | |
257 | """ Show info about all registry entries in the current plane | |
258 | If prior to invoking this command no registry plane is specified | |
259 | using 'setregistryplane', the command defaults to the IOService plane | |
260 | """ | |
261 | ShowRegistryEntryRecurse(kern.globals.gRegistryRoot, "", False) | |
262 | ||
263 | @lldb_command('showregistryprops') | |
264 | def ShowRegistryProps(cmd_args=None): | |
265 | """ Show info about all registry entries in the current plane, and their properties | |
266 | If prior to invoking this command no registry plane is specified | |
267 | using 'setregistryplane', the command defaults to the IOService plane | |
268 | """ | |
269 | ShowRegistryEntryRecurse(kern.globals.gRegistryRoot, "", True) | |
270 | ||
271 | @lldb_command('findregistryentry') | |
272 | def FindRegistryEntry(cmd_args=None): | |
273 | """ Search for registry entry that matches the given string | |
274 | If prior to invoking this command no registry plane is specified | |
275 | using 'setregistryplane', the command defaults to searching entries from the IOService plane | |
276 | syntax: (lldb) findregistryentries AppleACPICPU - will find the first registry entry that matches AppleACPICPU | |
277 | """ | |
278 | if not cmd_args: | |
279 | print "Please specify the name of the registry entry you want to find" | |
280 | print FindRegistryEntry.__doc__ | |
281 | return | |
282 | ||
283 | FindRegistryEntryRecurse(kern.globals.gRegistryRoot, cmd_args[0], True) | |
284 | ||
285 | @lldb_command('findregistryentries') | |
286 | def FindRegistryEntries(cmd_args=None): | |
287 | """ Search for all registry entries that match the given string | |
288 | If prior to invoking this command no registry plane is specified | |
289 | using 'setregistryplane', the command defaults to searching entries from the IOService plane | |
290 | syntax: (lldb) findregistryentries AppleACPICPU - will find all registry entries that match AppleACPICPU | |
291 | """ | |
292 | if not cmd_args: | |
293 | print "Please specify the name of the registry entry/entries you want to find" | |
294 | print FindRegistryEntries.__doc__ | |
295 | return | |
296 | ||
297 | FindRegistryEntryRecurse(kern.globals.gRegistryRoot, cmd_args[0], False) | |
298 | ||
299 | @lldb_command('findregistryprop') | |
300 | def FindRegistryProp(cmd_args=None): | |
301 | """ Given a registry entry, print out the contents for the property that matches | |
302 | a specific string | |
303 | syntax: (lldb) findregistryprop 0xaddr IOSleepSupported | |
304 | syntax: (lldb) findregistryprop gIOPMRootDomain IOSleepSupported | |
305 | syntax: (lldb) findregistryprop gIOPMRootDomain "Supported Features" | |
306 | """ | |
307 | if not cmd_args or len(cmd_args) < 2: | |
308 | print "Please specify the address of a IORegistry entry and the property you're looking for" | |
309 | print FindRegistryProp.__doc__ | |
310 | return | |
311 | ||
312 | entry = kern.GetValueFromAddress(cmd_args[0], 'IOService *') | |
313 | propertyTable = entry.fPropertyTable | |
314 | print GetObjectSummary(LookupKeyInPropTable(propertyTable, cmd_args[1])) | |
315 | ||
316 | @lldb_command('readioport8') | |
317 | def ReadIOPort8(cmd_args=None): | |
318 | """ Read value stored in the specified IO port. The CPU can be optionally | |
319 | specified as well. | |
320 | Prints 0xBAD10AD in case of a bad read | |
321 | Syntax: (lldb) readioport8 <port> [lcpu (kernel's numbering convention)] | |
322 | """ | |
323 | if not cmd_args: | |
324 | print "Please specify a port to read out of" | |
325 | print ReadIOPort8.__doc__ | |
326 | return | |
327 | ||
328 | portAddr = ArgumentStringToInt(cmd_args[0]) | |
329 | if len(cmd_args) >= 2: | |
330 | lcpu = ArgumentStringToInt(cmd_args[1]) | |
331 | else: | |
332 | lcpu = xnudefines.lcpu_self | |
333 | ||
334 | ReadIOPortInt(portAddr, 1, lcpu) | |
335 | ||
336 | @lldb_command('readioport16') | |
337 | def ReadIOPort16(cmd_args=None): | |
338 | """ Read value stored in the specified IO port. The CPU can be optionally | |
339 | specified as well. | |
340 | Prints 0xBAD10AD in case of a bad read | |
341 | Syntax: (lldb) readioport16 <port> [lcpu (kernel's numbering convention)] | |
342 | """ | |
343 | if not cmd_args: | |
344 | print "Please specify a port to read out of" | |
345 | print ReadIOPort16.__doc__ | |
346 | return | |
347 | ||
348 | portAddr = ArgumentStringToInt(cmd_args[0]) | |
349 | if len(cmd_args) >= 2: | |
350 | lcpu = ArgumentStringToInt(cmd_args[1]) | |
351 | else: | |
352 | lcpu = xnudefines.lcpu_self | |
353 | ||
354 | ReadIOPortInt(portAddr, 2, lcpu) | |
355 | ||
356 | @lldb_command('readioport32') | |
357 | def ReadIOPort32(cmd_args=None): | |
358 | """ Read value stored in the specified IO port. The CPU can be optionally | |
359 | specified as well. | |
360 | Prints 0xBAD10AD in case of a bad read | |
361 | Syntax: (lldb) readioport32 <port> [lcpu (kernel's numbering convention)] | |
362 | """ | |
363 | if not cmd_args: | |
364 | print "Please specify a port to read out of" | |
365 | print ReadIOPort32.__doc__ | |
366 | return | |
367 | ||
368 | portAddr = ArgumentStringToInt(cmd_args[0]) | |
369 | if len(cmd_args) >= 2: | |
370 | lcpu = ArgumentStringToInt(cmd_args[1]) | |
371 | else: | |
372 | lcpu = xnudefines.lcpu_self | |
373 | ||
374 | ReadIOPortInt(portAddr, 4, lcpu) | |
375 | ||
376 | @lldb_command('writeioport8') | |
377 | def WriteIOPort8(cmd_args=None): | |
378 | """ Write the value to the specified IO port. The size of the value is | |
379 | determined by the name of the command. The CPU used can be optionally | |
380 | specified as well. | |
381 | Syntax: (lldb) writeioport8 <port> <value> [lcpu (kernel's numbering convention)] | |
382 | """ | |
383 | if not cmd_args or len(cmd_args) < 2: | |
384 | print "Please specify a port to write to, followed by the value you want to write" | |
385 | print WriteIOPort8.__doc__ | |
386 | return | |
387 | ||
388 | portAddr = ArgumentStringToInt(cmd_args[0]) | |
389 | value = ArgumentStringToInt(cmd_args[1]) | |
390 | ||
391 | if len(cmd_args) >= 3: | |
392 | lcpu = ArgumentStringToInt(cmd_args[2]) | |
393 | else: | |
394 | lcpu = xnudefines.lcpu_self | |
395 | ||
396 | WriteIOPortInt(portAddr, 1, value, lcpu) | |
397 | ||
398 | @lldb_command('writeioport16') | |
399 | def WriteIOPort16(cmd_args=None): | |
400 | """ Write the value to the specified IO port. The size of the value is | |
401 | determined by the name of the command. The CPU used can be optionally | |
402 | specified as well. | |
403 | Syntax: (lldb) writeioport16 <port> <value> [lcpu (kernel's numbering convention)] | |
404 | """ | |
405 | if not cmd_args or len(cmd_args) < 2: | |
406 | print "Please specify a port to write to, followed by the value you want to write" | |
407 | print WriteIOPort16.__doc__ | |
408 | return | |
409 | ||
410 | portAddr = ArgumentStringToInt(cmd_args[0]) | |
411 | value = ArgumentStringToInt(cmd_args[1]) | |
412 | ||
413 | if len(cmd_args) >= 3: | |
414 | lcpu = ArgumentStringToInt(cmd_args[2]) | |
415 | else: | |
416 | lcpu = xnudefines.lcpu_self | |
417 | ||
418 | WriteIOPortInt(portAddr, 2, value, lcpu) | |
419 | ||
420 | @lldb_command('writeioport32') | |
421 | def WriteIOPort32(cmd_args=None): | |
422 | """ Write the value to the specified IO port. The size of the value is | |
423 | determined by the name of the command. The CPU used can be optionally | |
424 | specified as well. | |
425 | Syntax: (lldb) writeioport32 <port> <value> [lcpu (kernel's numbering convention)] | |
426 | """ | |
427 | if not cmd_args or len(cmd_args) < 2: | |
428 | print "Please specify a port to write to, followed by the value you want to write" | |
429 | print WriteIOPort32.__doc__ | |
430 | return | |
431 | ||
432 | portAddr = ArgumentStringToInt(cmd_args[0]) | |
433 | value = ArgumentStringToInt(cmd_args[1]) | |
434 | ||
435 | if len(cmd_args) >= 3: | |
436 | lcpu = ArgumentStringToInt(cmd_args[2]) | |
437 | else: | |
438 | lcpu = xnudefines.lcpu_self | |
439 | ||
440 | WriteIOPortInt(portAddr, 4, value, lcpu) | |
441 | ||
442 | @lldb_command('showioservicepm') | |
443 | def ShowIOServicePM(cmd_args=None): | |
444 | """ Routine to dump the IOServicePM object | |
445 | Syntax: (lldb) showioservicepm <IOServicePM pointer> | |
446 | """ | |
447 | if not cmd_args: | |
448 | print "Please enter the pointer to the IOServicePM object you'd like to introspect" | |
449 | print ShowIOServicePM.__doc__ | |
450 | return | |
451 | ||
452 | iopmpriv = kern.GetValueFromAddress(cmd_args[0], 'IOServicePM *') | |
453 | out_string = "MachineState {0: <6d} (".format(iopmpriv.MachineState) | |
454 | ||
455 | # Power state map | |
456 | pstate_map = { | |
457 | 0: 'kIOPM_Finished', | |
458 | 1: 'kIOPM_OurChangeTellClientsPowerDown', | |
459 | 2: 'kIOPM_OurChangeTellClientsPowerDown', | |
460 | 3: 'kIOPM_OurChangeNotifyInterestedDriversWillChange', | |
461 | 4: 'kIOPM_OurChangeSetPowerState', | |
462 | 5: 'kIOPM_OurChangeWaitForPowerSettle', | |
463 | 6: 'kIOPM_OurChangeNotifyInterestedDriversDidChange', | |
464 | 7: 'kIOPM_OurChangeTellCapabilityDidChange', | |
465 | 8: 'kIOPM_OurChangeFinish', | |
466 | 9: 'Unused_MachineState_9', | |
467 | 10: 'kIOPM_ParentChangeTellPriorityClientsPowerDown', | |
468 | 11: 'kIOPM_ParentChangeNotifyInterestedDriversWillChange', | |
469 | 12: 'kIOPM_ParentChangeSetPowerState', | |
470 | 13: 'kIOPM_ParentChangeWaitForPowerSettle', | |
471 | 14: 'kIOPM_ParentChangeNotifyInterestedDriversDidChange', | |
472 | 15: 'kIOPM_ParentChangeTellCapabilityDidChange', | |
473 | 16: 'kIOPM_ParentChangeAcknowledgePowerChange', | |
474 | 17: 'kIOPM_NotifyChildrenStart', | |
475 | 18: 'kIOPM_NotifyChildrenOrdered', | |
476 | 19: 'kIOPM_NotifyChildrenDelayed', | |
477 | 20: 'kIOPM_SyncTellClientsPowerDown', | |
478 | 21: 'kIOPM_SyncTellPriorityClientsPowerDown', | |
479 | 22: 'kIOPM_SyncNotifyWillChange', | |
480 | 23: 'kIOPM_SyncNotifyDidChange', | |
481 | 24: 'kIOPM_SyncTellCapabilityDidChange', | |
482 | 25: 'kIOPM_SyncFinish', | |
483 | 26: 'kIOPM_TellCapabilityChangeDone', | |
484 | 27: 'kIOPM_DriverThreadCallDone' | |
485 | } | |
486 | powerstate = unsigned(iopmpriv.MachineState) | |
487 | if powerstate in pstate_map: | |
488 | out_string += "{0:s}".format(pstate_map[powerstate]) | |
489 | else: | |
490 | out_string += "Unknown_MachineState" | |
491 | out_string += "), " | |
492 | ||
493 | if iopmpriv.MachineState != 20: | |
494 | out_string += "DriverTimer = {0: <6d}, SettleTime = {1: < 6d}, HeadNoteFlags = {2: #12x}, HeadNotePendingAcks = {3: #012x}, ".format( | |
495 | unsigned(iopmpriv.DriverTimer), | |
496 | unsigned(iopmpriv.SettleTimeUS), | |
497 | unsigned(iopmpriv.HeadNoteChangeFlags), | |
498 | unsigned(iopmpriv.HeadNotePendingAcks)) | |
499 | ||
500 | if iopmpriv.DeviceOverrideEnabled != 0: | |
501 | out_string += "DeviceOverrides, " | |
502 | ||
503 | out_string += "DeviceDesire = {0: <6d}, DesiredPowerState = {1: <6d}, PreviousRequest = {2: <6d}\n".format( | |
504 | unsigned(iopmpriv.DeviceDesire), | |
505 | unsigned(iopmpriv.DesiredPowerState), | |
506 | unsigned(iopmpriv.PreviousRequestPowerFlags)) | |
507 | ||
508 | print out_string | |
509 | ||
510 | ###################################### | |
511 | # Helper routines | |
512 | ###################################### | |
513 | def ShowRegistryEntryRecurse(entry, prefix, printProps): | |
514 | """ prints registry entry summary and recurses through all its children. | |
515 | """ | |
516 | # Setup | |
517 | global plane | |
518 | out_string = "" | |
519 | plen = (len(prefix)//2) | |
520 | registryTable = entry.fRegistryTable | |
521 | propertyTable = entry.fPropertyTable | |
522 | ||
523 | # Print entry details | |
524 | print "{0:s}{1:s}".format(prefix, GetRegistryEntrySummary(entry)) | |
525 | # Printing large property tables make it look like lldb is 'stuck' | |
526 | if printProps: | |
527 | print GetRegDictionary(propertyTable, prefix + " | ") | |
528 | ||
529 | # Recurse | |
530 | if plane is None: | |
531 | childKey = kern.globals.gIOServicePlane.keys[1] | |
532 | else: | |
533 | childKey = plane.keys[1] | |
534 | childArray = LookupKeyInOSDict(registryTable, childKey) | |
535 | if childArray is not None: | |
536 | idx = 0 | |
537 | ca = CastIOKitClass(childArray, 'OSArray *') | |
538 | count = unsigned(ca.count) | |
539 | while idx < count: | |
540 | if plen != 0 and plen != 1 and (plen & (plen - 1)) == 0: | |
541 | ShowRegistryEntryRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), prefix + "| ", printProps) | |
542 | else: | |
543 | ShowRegistryEntryRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), prefix + " ", printProps) | |
544 | idx += 1 | |
545 | ||
546 | def FindRegistryEntryRecurse(entry, search_name, stopAfterFirst): | |
547 | """ Checks if given registry entry's name matches the search_name we're looking for | |
548 | If yes, it prints the entry's summary and then recurses through its children | |
549 | If no, it does nothing and recurses through its children | |
550 | """ | |
551 | # Setup | |
552 | global plane | |
553 | registryTable = entry.fRegistryTable | |
554 | propertyTable = entry.fPropertyTable | |
555 | ||
556 | # Compare | |
557 | name = None | |
558 | name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) | |
559 | if name is None: | |
560 | name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) | |
561 | if name is None: | |
562 | name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) | |
563 | ||
564 | if name is not None: | |
565 | if str(CastIOKitClass(name, 'OSString *').string) == search_name: | |
566 | print GetRegistryEntrySummary(entry) | |
567 | if stopAfterFirst is True: | |
568 | return True | |
569 | elif CastIOKitClass(entry, 'IOService *').pwrMgt and CastIOKitClass(entry, 'IOService *').pwrMgt.Name: | |
570 | name = CastIOKitClass(entry, 'IOService *').pwrMgt.Name | |
571 | if str(name) == search_name: | |
572 | print GetRegistryEntrySummary(entry) | |
573 | if stopAfterFirst is True: | |
574 | return True | |
575 | ||
576 | # Recurse | |
577 | if plane is None: | |
578 | childKey = kern.globals.gIOServicePlane.keys[1] | |
579 | else: | |
580 | childKey = plane.keys[1] | |
581 | childArray = LookupKeyInOSDict(registryTable, childKey) | |
582 | if childArray is not None: | |
583 | idx = 0 | |
584 | ca = CastIOKitClass(childArray, 'OSArray *') | |
585 | count = unsigned(ca.count) | |
586 | while idx < count: | |
587 | if FindRegistryEntryRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), search_name, stopAfterFirst) is True: | |
588 | return True | |
589 | idx += 1 | |
590 | return False | |
591 | ||
592 | def FindRegistryObjectRecurse(entry, search_name): | |
593 | """ Checks if given registry entry's name matches the search_name we're looking for | |
594 | If yes, return the entry | |
595 | If no, it does nothing and recurses through its children | |
596 | Implicitly stops after finding the first entry | |
597 | """ | |
598 | # Setup | |
599 | global plane | |
600 | registryTable = entry.fRegistryTable | |
601 | propertyTable = entry.fPropertyTable | |
602 | ||
603 | # Compare | |
604 | name = None | |
605 | name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) | |
606 | if name is None: | |
607 | name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) | |
608 | if name is None: | |
609 | name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) | |
610 | ||
611 | if name is not None: | |
612 | if str(CastIOKitClass(name, 'OSString *').string) == search_name: | |
613 | return entry | |
614 | elif CastIOKitClass(entry, 'IOService *').pwrMgt and CastIOKitClass(entry, 'IOService *').pwrMgt.Name: | |
615 | name = CastIOKitClass(entry, 'IOService *').pwrMgt.Name | |
616 | if str(name) == search_name: | |
617 | return entry | |
618 | ||
619 | # Recurse | |
620 | if plane is None: | |
621 | childKey = kern.globals.gIOServicePlane.keys[1] | |
622 | else: | |
623 | childKey = plane.keys[1] | |
624 | childArray = LookupKeyInOSDict(registryTable, childKey) | |
625 | if childArray is not None: | |
626 | ca = CastIOKitClass(childArray, 'OSArray *') | |
627 | for idx in range(ca.count): | |
628 | registry_object = FindRegistryObjectRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), search_name) | |
629 | if not registry_object or int(registry_object) == int(0): | |
630 | continue | |
631 | else: | |
632 | return registry_object | |
633 | return None | |
634 | ||
635 | def LookupKeyInOSDict(osdict, key): | |
636 | """ Returns the value corresponding to a given key in a OSDictionary | |
637 | Returns None if the key was not found | |
638 | """ | |
639 | if not osdict: | |
640 | return | |
641 | count = unsigned(osdict.count) | |
642 | result = None | |
643 | idx = 0 | |
644 | while idx < count and result is None: | |
645 | if key == osdict.dictionary[idx].key: | |
646 | result = osdict.dictionary[idx].value | |
647 | idx += 1 | |
648 | return result | |
649 | ||
650 | def LookupKeyInPropTable(propertyTable, key_str): | |
651 | """ Returns the value corresponding to a given key from a registry entry's property table | |
652 | Returns None if the key was not found | |
653 | The property that is being searched for is specified as a string in key_str | |
654 | """ | |
655 | if not propertyTable: | |
656 | return | |
657 | count = unsigned(propertyTable.count) | |
658 | result = None | |
659 | idx = 0 | |
660 | while idx < count and result is None: | |
661 | if key_str == str(propertyTable.dictionary[idx].key.string): | |
662 | result = propertyTable.dictionary[idx].value | |
663 | idx += 1 | |
664 | return result | |
665 | ||
666 | def GetRegDictionary(osdict, prefix): | |
667 | """ Returns a specially formatted string summary of the given OSDictionary | |
668 | This is done in order to pretty-print registry property tables in showregistry | |
669 | and other macros | |
670 | """ | |
671 | out_string = prefix + "{\n" | |
672 | idx = 0 | |
673 | count = unsigned(osdict.count) | |
674 | ||
675 | while idx < count: | |
676 | out_string += prefix + " " + GetObjectSummary(osdict.dictionary[idx].key) + " = " + GetObjectSummary(osdict.dictionary[idx].value) + "\n" | |
677 | idx += 1 | |
678 | out_string += prefix + "}\n" | |
679 | return out_string | |
680 | ||
681 | def GetString(string): | |
682 | """ Returns the python string representation of a given OSString | |
683 | """ | |
684 | out_string = "\"{0:s}\"".format(CastIOKitClass(string, 'OSString *').string) | |
685 | return out_string | |
686 | ||
687 | def GetNumber(num): | |
688 | out_string = "{0:d}".format(CastIOKitClass(num, 'OSNumber *').value) | |
689 | return out_string | |
690 | ||
691 | def GetBoolean(b): | |
692 | """ Shows info about a given OSBoolean | |
693 | """ | |
694 | out_string = "" | |
695 | if b == kern.globals.gOSBooleanFalse: | |
696 | out_string += "No" | |
697 | else: | |
698 | out_string += "Yes" | |
699 | return out_string | |
700 | ||
701 | def GetMetaClass(mc): | |
702 | """ Shows info about a given OSSymbol | |
703 | """ | |
704 | out_string = "{0: <5d}x {1: >5d} bytes {2:s}\n".format(mc.instanceCount, mc.classSize, mc.className.string) | |
705 | return out_string | |
706 | ||
707 | def GetArray(arr): | |
708 | """ Returns a string containing info about a given OSArray | |
709 | """ | |
710 | out_string = "" | |
711 | idx = 0 | |
712 | count = unsigned(arr.count) | |
713 | ||
714 | while idx < count: | |
715 | obj = arr.array[idx] | |
716 | idx += 1 | |
717 | out_string += GetObjectSummary(obj) | |
718 | if idx < unsigned(arr.count): | |
719 | out_string += "," | |
720 | return out_string | |
721 | ||
722 | def GetDictionary(d): | |
723 | """ Returns a string containing info about a given OSDictionary | |
724 | """ | |
725 | out_string = "{" | |
726 | idx = 0 | |
727 | count = unsigned(d.count) | |
728 | ||
729 | while idx < count: | |
730 | obj = d.dictionary[idx].key | |
731 | out_string += GetObjectSummary(obj) + "=" | |
732 | obj = d.dictionary[idx].value | |
733 | idx += 1 | |
734 | out_string += GetObjectSummary(obj) | |
735 | if idx < count: | |
736 | out_string += "," | |
737 | out_string += "}" | |
738 | return out_string | |
739 | ||
740 | def GetSet(se): | |
741 | """ Returns a string containing info about a given OSSet | |
742 | """ | |
743 | out_string += "[" + GetArray(se.members) + "]" | |
744 | return out_string | |
745 | ||
746 | def ReadIOPortInt(addr, numbytes, lcpu): | |
747 | """ Prints results after reading a given ioport | |
748 | """ | |
749 | result = 0xBAD10AD | |
750 | ||
751 | if "kdp" != GetConnectionProtocol(): | |
752 | print "Target is not connected over kdp. Nothing to do here." | |
753 | return | |
754 | ||
755 | # Set up the manual KDP packet | |
756 | input_address = unsigned(addressof(kern.globals.manual_pkt.input)) | |
757 | len_address = unsigned(addressof(kern.globals.manual_pkt.len)) | |
758 | data_address = unsigned(addressof(kern.globals.manual_pkt.data)) | |
759 | if not WriteInt32ToMemoryAddress(0, input_address): | |
760 | print "0x{0: <4x}: 0x{1: <1x}".format(addr, result) | |
761 | return | |
762 | ||
763 | kdp_pkt_size = GetType('kdp_readioport_req_t').GetByteSize() | |
764 | if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address): | |
765 | print "0x{0: <4x}: 0x{1: <1x}".format(addr, result) | |
766 | return | |
767 | ||
768 | kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_readioport_req_t *') | |
769 | ||
770 | header_value = GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_READIOPORT'), length = kdp_pkt_size) | |
771 | ||
772 | if( WriteInt64ToMemoryAddress((header_value), int(addressof(kgm_pkt.hdr))) and | |
773 | WriteInt16ToMemoryAddress(addr, int(addressof(kgm_pkt.address))) and | |
774 | WriteInt32ToMemoryAddress(numbytes, int(addressof(kgm_pkt.nbytes))) and | |
775 | WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu))) and | |
776 | WriteInt32ToMemoryAddress(1, input_address) | |
777 | ): | |
778 | ||
779 | result_pkt = Cast(addressof(kern.globals.manual_pkt.data), 'kdp_readioport_reply_t *') | |
780 | ||
781 | if(result_pkt.error == 0): | |
782 | if numbytes == 1: | |
783 | result = dereference(Cast(addressof(result_pkt.data), 'uint8_t *')) | |
784 | elif numbytes == 2: | |
785 | result = dereference(Cast(addressof(result_pkt.data), 'uint16_t *')) | |
786 | elif numbytes == 4: | |
787 | result = dereference(Cast(addressof(result_pkt.data), 'uint32_t *')) | |
788 | ||
789 | print "{0: <#6x}: {1:#0{2}x}".format(addr, result, (numbytes*2)+2) | |
790 | ||
791 | def WriteIOPortInt(addr, numbytes, value, lcpu): | |
792 | """ Writes 'value' into ioport specified by 'addr'. Prints errors if it encounters any | |
793 | """ | |
794 | if "kdp" != GetConnectionProtocol(): | |
795 | print "Target is not connected over kdp. Nothing to do here." | |
796 | return | |
797 | ||
798 | # Set up the manual KDP packet | |
799 | input_address = unsigned(addressof(kern.globals.manual_pkt.input)) | |
800 | len_address = unsigned(addressof(kern.globals.manual_pkt.len)) | |
801 | data_address = unsigned(addressof(kern.globals.manual_pkt.data)) | |
802 | if not WriteInt32ToMemoryAddress(0, input_address): | |
803 | print "error writing {0: #x} to port {1: <#6x}: failed to write 0 to input_address".format(value, addr) | |
804 | return | |
805 | ||
806 | kdp_pkt_size = GetType('kdp_writeioport_req_t').GetByteSize() | |
807 | if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address): | |
808 | print "error writing {0: #x} to port {1: <#6x}: failed to write kdp_pkt_size".format(value, addr) | |
809 | return | |
810 | ||
811 | kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_writeioport_req_t *') | |
812 | ||
813 | header_value = GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_WRITEIOPORT'), length = kdp_pkt_size) | |
814 | ||
815 | if( WriteInt64ToMemoryAddress((header_value), int(addressof(kgm_pkt.hdr))) and | |
816 | WriteInt16ToMemoryAddress(addr, int(addressof(kgm_pkt.address))) and | |
817 | WriteInt32ToMemoryAddress(numbytes, int(addressof(kgm_pkt.nbytes))) and | |
818 | WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu))) | |
819 | ): | |
820 | if numbytes == 1: | |
821 | if not WriteInt8ToMemoryAddress(value, int(addressof(kgm_pkt.data))): | |
822 | print "error writing {0: #x} to port {1: <#6x}: failed to write 8 bit data".format(value, addr) | |
823 | return | |
824 | elif numbytes == 2: | |
825 | if not WriteInt16ToMemoryAddress(value, int(addressof(kgm_pkt.data))): | |
826 | print "error writing {0: #x} to port {1: <#6x}: failed to write 16 bit data".format(value, addr) | |
827 | return | |
828 | elif numbytes == 4: | |
829 | if not WriteInt32ToMemoryAddress(value, int(addressof(kgm_pkt.data))): | |
830 | print "error writing {0: #x} to port {1: <#6x}: failed to write 32 bit data".format(value, addr) | |
831 | return | |
832 | if not WriteInt32ToMemoryAddress(1, input_address): | |
833 | print "error writing {0: #x} to port {1: <#6x}: failed to write to input_address".format(value, addr) | |
834 | return | |
835 | ||
836 | result_pkt = Cast(addressof(kern.globals.manual_pkt.data), 'kdp_writeioport_reply_t *') | |
837 | ||
838 | # Done with the write | |
839 | if(result_pkt.error == 0): | |
840 | print "Writing {0: #x} to port {1: <#6x} was successful".format(value, addr) | |
841 | else: | |
842 | print "error writing {0: #x} to port {1: <#6x}".format(value, addr) | |
843 | ||
844 | @lldb_command('showinterruptcounts') | |
845 | def showinterruptcounts(cmd_args=None): | |
846 | """ Shows event source based interrupt counts by nub name and interrupt index. | |
847 | Does not cover interrupts that are not event source based. Will report 0 | |
848 | if interrupt accounting is disabled. | |
849 | """ | |
850 | ||
851 | header_format = "{0: <20s} {1: >5s} {2: >20s}" | |
852 | content_format = "{0: <20s} {1: >5d} {2: >20d}" | |
853 | ||
854 | print header_format.format("Name", "Index", "Count") | |
855 | ||
856 | for i in kern.interrupt_stats: | |
857 | owner = CastIOKitClass(i.owner, 'IOInterruptEventSource *') | |
858 | nub = CastIOKitClass(owner.provider, 'IORegistryEntry *') | |
859 | name = None | |
860 | ||
861 | # To uniquely identify an interrupt, we need the nub name and the index. The index | |
862 | # is stored with the stats object, but we need to retrieve the name. | |
863 | ||
864 | registryTable = nub.fRegistryTable | |
865 | propertyTable = nub.fPropertyTable | |
866 | ||
867 | name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) | |
868 | if name is None: | |
869 | name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) | |
870 | if name is None: | |
871 | name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) | |
872 | ||
873 | if name is None: | |
874 | nub_name = "Unknown" | |
875 | else: | |
876 | nub_name = GetString(CastIOKitClass(name, 'OSString *')) | |
877 | ||
878 | # We now have everything we need; spew the requested data. | |
879 | ||
880 | interrupt_index = i.interruptIndex | |
881 | first_level_count = i.interruptStatistics[0] | |
882 | ||
883 | print content_format.format(nub_name, interrupt_index, first_level_count) | |
884 | ||
885 | return True | |
886 | ||
887 | @lldb_command('showinterruptstats') | |
888 | def showinterruptstats(cmd_args=None): | |
889 | """ Shows event source based interrupt statistics by nub name and interrupt index. | |
890 | Does not cover interrupts that are not event source based. Will report 0 | |
891 | if interrupt accounting is disabled, or if specific statistics are disabled. | |
892 | Time is reported in ticks of mach_absolute_time. Statistics are: | |
893 | ||
894 | Interrupt Count: Number of times the interrupt context handler was run | |
895 | Interrupt Time: Total time spent in the interrupt context handler (if any) | |
896 | Workloop Count: Number of times the kernel context handler was run | |
897 | Workloop CPU Time: Total CPU time spent running the kernel context handler | |
898 | Workloop Time: Total time spent running the kernel context handler | |
899 | """ | |
900 | ||
901 | header_format = "{0: <20s} {1: >5s} {2: >20s} {3: >20s} {4: >20s} {5: >20s} {6: >20s} {7: >20s} {8: >20s} {9: >20s}" | |
902 | content_format = "{0: <20s} {1: >5d} {2: >20d} {3: >20d} {4: >20d} {5: >20d} {6: >20d} {7: >20d} {8: >20d} {9: >#20x}" | |
903 | ||
904 | print header_format.format("Name", "Index", "Interrupt Count", "Interrupt Time", "Avg Interrupt Time", "Workloop Count", "Workloop CPU Time", "Workloop Time", "Avg Workloop Time", "Owner") | |
905 | ||
906 | for i in kern.interrupt_stats: | |
907 | owner = CastIOKitClass(i.owner, 'IOInterruptEventSource *') | |
908 | nub = CastIOKitClass(owner.provider, 'IORegistryEntry *') | |
909 | name = None | |
910 | ||
911 | # To uniquely identify an interrupt, we need the nub name and the index. The index | |
912 | # is stored with the stats object, but we need to retrieve the name. | |
913 | ||
914 | registryTable = nub.fRegistryTable | |
915 | propertyTable = nub.fPropertyTable | |
916 | ||
917 | name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) | |
918 | if name is None: | |
919 | name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) | |
920 | if name is None: | |
921 | name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) | |
922 | ||
923 | if name is None: | |
924 | nub_name = "Unknown" | |
925 | else: | |
926 | nub_name = GetString(CastIOKitClass(name, 'OSString *')) | |
927 | ||
928 | # We now have everything we need; spew the requested data. | |
929 | ||
930 | interrupt_index = i.interruptIndex | |
931 | first_level_count = i.interruptStatistics[0] | |
932 | second_level_count = i.interruptStatistics[1] | |
933 | first_level_time = i.interruptStatistics[2] | |
934 | second_level_cpu_time = i.interruptStatistics[3] | |
935 | second_level_system_time = i.interruptStatistics[4] | |
936 | ||
937 | avg_first_level_time = 0 | |
938 | if first_level_count != 0: | |
939 | avg_first_level_time = first_level_time / first_level_count | |
940 | ||
941 | avg_second_level_time = 0 | |
942 | if second_level_count != 0: | |
943 | avg_second_level_time = second_level_system_time / second_level_count | |
944 | ||
945 | print content_format.format(nub_name, interrupt_index, first_level_count, first_level_time, avg_first_level_time, | |
946 | second_level_count, second_level_cpu_time, second_level_system_time, avg_second_level_time, owner) | |
947 | ||
948 | return True | |
949 |