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