]>
Commit | Line | Data |
---|---|---|
39236c6e A |
1 | from xnu import * |
2 | import xnudefines | |
3 | from kdp import * | |
4 | from utils import * | |
bca245ac | 5 | import struct |
39236c6e A |
6 | |
7 | def ReadPhysInt(phys_addr, bitsize = 64, cpuval = None): | |
fe8ab488 | 8 | """ Read a physical memory data based on address. |
39236c6e A |
9 | params: |
10 | phys_addr : int - Physical address to read | |
11 | bitsize : int - defines how many bytes to read. defaults to 64 bit | |
12 | cpuval : None (optional) | |
13 | returns: | |
14 | int - int value read from memory. in case of failure 0xBAD10AD is returned. | |
15 | """ | |
16 | if "kdp" == GetConnectionProtocol(): | |
17 | return KDPReadPhysMEM(phys_addr, bitsize) | |
18 | ||
19 | #NO KDP. Attempt to use physical memory | |
20 | paddr_in_kva = kern.PhysToKernelVirt(long(phys_addr)) | |
21 | if paddr_in_kva : | |
22 | if bitsize == 64 : | |
23 | return kern.GetValueFromAddress(paddr_in_kva, 'uint64_t *').GetSBValue().Dereference().GetValueAsUnsigned() | |
24 | if bitsize == 32 : | |
25 | return kern.GetValueFromAddress(paddr_in_kva, 'uint32_t *').GetSBValue().Dereference().GetValueAsUnsigned() | |
26 | if bitsize == 16 : | |
27 | return kern.GetValueFromAddress(paddr_in_kva, 'uint16_t *').GetSBValue().Dereference().GetValueAsUnsigned() | |
28 | if bitsize == 8 : | |
29 | return kern.GetValueFromAddress(paddr_in_kva, 'uint8_t *').GetSBValue().Dereference().GetValueAsUnsigned() | |
30 | return 0xBAD10AD | |
31 | ||
32 | @lldb_command('readphys') | |
33 | def ReadPhys(cmd_args = None): | |
fe8ab488 | 34 | """ Reads the specified untranslated address |
39236c6e A |
35 | The argument is interpreted as a physical address, and the 64-bit word |
36 | addressed is displayed. | |
37 | usage: readphys <nbits> <address> | |
38 | nbits: 8,16,32,64 | |
fe8ab488 | 39 | address: 1234 or 0x1234 |
39236c6e A |
40 | """ |
41 | if cmd_args == None or len(cmd_args) < 2: | |
42 | print "Insufficient arguments.", ReadPhys.__doc__ | |
43 | return False | |
44 | else: | |
45 | nbits = ArgumentStringToInt(cmd_args[0]) | |
46 | phys_addr = ArgumentStringToInt(cmd_args[1]) | |
47 | print "{0: <#x}".format(ReadPhysInt(phys_addr, nbits)) | |
48 | return True | |
49 | ||
50 | lldb_alias('readphys8', 'readphys 8 ') | |
51 | lldb_alias('readphys16', 'readphys 16 ') | |
52 | lldb_alias('readphys32', 'readphys 32 ') | |
53 | lldb_alias('readphys64', 'readphys 64 ') | |
54 | ||
55 | def KDPReadPhysMEM(address, bits): | |
56 | """ Setup the state for READPHYSMEM64 commands for reading data via kdp | |
57 | params: | |
58 | address : int - address where to read the data from | |
59 | bits : int - number of bits in the intval (8/16/32/64) | |
60 | returns: | |
fe8ab488 | 61 | int: read value from memory. |
39236c6e A |
62 | 0xBAD10AD: if failed to read data. |
63 | """ | |
64 | retval = 0xBAD10AD | |
65 | if "kdp" != GetConnectionProtocol(): | |
66 | print "Target is not connected over kdp. Nothing to do here." | |
67 | return retval | |
68 | ||
bca245ac A |
69 | if "hwprobe" == KDPMode(): |
70 | # Send the proper KDP command and payload to the bare metal debug tool via a KDP server | |
71 | addr_for_kdp = struct.unpack("<Q", struct.pack(">Q", address))[0] | |
72 | byte_count = struct.unpack("<I", struct.pack(">I", bits/8))[0] | |
73 | packet = "{0:016x}{1:08x}{2:04x}".format(addr_for_kdp, byte_count, 0x0) | |
39236c6e | 74 | |
bca245ac A |
75 | ret_obj = lldb.SBCommandReturnObject() |
76 | ci = lldb.debugger.GetCommandInterpreter() | |
77 | ci.HandleCommand('process plugin packet send -c 25 -p {0}'.format(packet), ret_obj) | |
fe8ab488 | 78 | |
bca245ac A |
79 | if ret_obj.Succeeded(): |
80 | value = ret_obj.GetOutput() | |
fe8ab488 | 81 | |
39236c6e | 82 | if bits == 64 : |
bca245ac A |
83 | pack_fmt = "<Q" |
84 | unpack_fmt = ">Q" | |
39236c6e | 85 | if bits == 32 : |
bca245ac A |
86 | pack_fmt = "<I" |
87 | unpack_fmt = ">I" | |
39236c6e | 88 | if bits == 16 : |
bca245ac A |
89 | pack_fmt = "<H" |
90 | unpack_fmt = ">H" | |
39236c6e | 91 | if bits == 8 : |
bca245ac A |
92 | pack_fmt = "<B" |
93 | unpack_fmt = ">B" | |
94 | ||
95 | retval = struct.unpack(unpack_fmt, struct.pack(pack_fmt, int(value[-((bits/4)+1):], 16)))[0] | |
96 | ||
97 | else: | |
98 | input_address = unsigned(addressof(kern.globals.manual_pkt.input)) | |
99 | len_address = unsigned(addressof(kern.globals.manual_pkt.len)) | |
100 | data_address = unsigned(addressof(kern.globals.manual_pkt.data)) | |
101 | ||
102 | if not WriteInt32ToMemoryAddress(0, input_address): | |
103 | return retval | |
104 | ||
105 | kdp_pkt_size = GetType('kdp_readphysmem64_req_t').GetByteSize() | |
106 | if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address): | |
107 | return retval | |
108 | ||
109 | data_addr = int(addressof(kern.globals.manual_pkt)) | |
110 | pkt = kern.GetValueFromAddress(data_addr, 'kdp_readphysmem64_req_t *') | |
111 | ||
112 | header_value =GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_READPHYSMEM64'), length=kdp_pkt_size) | |
113 | ||
114 | if ( WriteInt64ToMemoryAddress((header_value), int(addressof(pkt.hdr))) and | |
115 | WriteInt64ToMemoryAddress(address, int(addressof(pkt.address))) and | |
116 | WriteInt32ToMemoryAddress((bits/8), int(addressof(pkt.nbytes))) and | |
117 | WriteInt16ToMemoryAddress(xnudefines.lcpu_self, int(addressof(pkt.lcpu))) | |
118 | ): | |
119 | ||
120 | if WriteInt32ToMemoryAddress(1, input_address): | |
121 | # now read data from the kdp packet | |
122 | data_address = unsigned(addressof(kern.GetValueFromAddress(int(addressof(kern.globals.manual_pkt.data)), 'kdp_readphysmem64_reply_t *').data)) | |
123 | if bits == 64 : | |
124 | retval = kern.GetValueFromAddress(data_address, 'uint64_t *').GetSBValue().Dereference().GetValueAsUnsigned() | |
125 | if bits == 32 : | |
126 | retval = kern.GetValueFromAddress(data_address, 'uint32_t *').GetSBValue().Dereference().GetValueAsUnsigned() | |
127 | if bits == 16 : | |
128 | retval = kern.GetValueFromAddress(data_address, 'uint16_t *').GetSBValue().Dereference().GetValueAsUnsigned() | |
129 | if bits == 8 : | |
130 | retval = kern.GetValueFromAddress(data_address, 'uint8_t *').GetSBValue().Dereference().GetValueAsUnsigned() | |
131 | ||
39236c6e A |
132 | return retval |
133 | ||
134 | ||
135 | def KDPWritePhysMEM(address, intval, bits): | |
136 | """ Setup the state for WRITEPHYSMEM64 commands for saving data in kdp | |
137 | params: | |
138 | address : int - address where to save the data | |
139 | intval : int - integer value to be stored in memory | |
140 | bits : int - number of bits in the intval (8/16/32/64) | |
141 | returns: | |
142 | boolean: True if the write succeeded. | |
143 | """ | |
144 | if "kdp" != GetConnectionProtocol(): | |
145 | print "Target is not connected over kdp. Nothing to do here." | |
146 | return False | |
bca245ac A |
147 | |
148 | if "hwprobe" == KDPMode(): | |
149 | # Send the proper KDP command and payload to the bare metal debug tool via a KDP server | |
150 | addr_for_kdp = struct.unpack("<Q", struct.pack(">Q", address))[0] | |
151 | byte_count = struct.unpack("<I", struct.pack(">I", bits/8))[0] | |
152 | ||
153 | if bits == 64 : | |
154 | pack_fmt = ">Q" | |
155 | unpack_fmt = "<Q" | |
156 | if bits == 32 : | |
157 | pack_fmt = ">I" | |
158 | unpack_fmt = "<I" | |
159 | if bits == 16 : | |
160 | pack_fmt = ">H" | |
161 | unpack_fmt = "<H" | |
162 | if bits == 8 : | |
163 | pack_fmt = ">B" | |
164 | unpack_fmt = "<B" | |
165 | ||
166 | data_val = struct.unpack(unpack_fmt, struct.pack(pack_fmt, intval))[0] | |
167 | ||
168 | packet = "{0:016x}{1:08x}{2:04x}{3:016x}".format(addr_for_kdp, byte_count, 0x0, data_val) | |
169 | ||
170 | ret_obj = lldb.SBCommandReturnObject() | |
171 | ci = lldb.debugger.GetCommandInterpreter() | |
172 | ci.HandleCommand('process plugin packet send -c 26 -p {0}'.format(packet), ret_obj) | |
173 | ||
174 | if ret_obj.Succeeded(): | |
175 | return True | |
176 | else: | |
177 | return False | |
39236c6e | 178 | |
bca245ac A |
179 | else: |
180 | input_address = unsigned(addressof(kern.globals.manual_pkt.input)) | |
181 | len_address = unsigned(addressof(kern.globals.manual_pkt.len)) | |
182 | data_address = unsigned(addressof(kern.globals.manual_pkt.data)) | |
183 | if not WriteInt32ToMemoryAddress(0, input_address): | |
184 | return False | |
39236c6e | 185 | |
bca245ac A |
186 | kdp_pkt_size = GetType('kdp_writephysmem64_req_t').GetByteSize() + (bits / 8) |
187 | if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address): | |
188 | return False | |
189 | ||
190 | data_addr = int(addressof(kern.globals.manual_pkt)) | |
191 | pkt = kern.GetValueFromAddress(data_addr, 'kdp_writephysmem64_req_t *') | |
192 | ||
193 | header_value =GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_WRITEPHYSMEM64'), length=kdp_pkt_size) | |
194 | ||
195 | if ( WriteInt64ToMemoryAddress((header_value), int(addressof(pkt.hdr))) and | |
196 | WriteInt64ToMemoryAddress(address, int(addressof(pkt.address))) and | |
197 | WriteInt32ToMemoryAddress((bits/8), int(addressof(pkt.nbytes))) and | |
198 | WriteInt16ToMemoryAddress(xnudefines.lcpu_self, int(addressof(pkt.lcpu))) | |
199 | ): | |
200 | ||
201 | if bits == 8: | |
202 | if not WriteInt8ToMemoryAddress(intval, int(addressof(pkt.data))): | |
203 | return False | |
204 | if bits == 16: | |
205 | if not WriteInt16ToMemoryAddress(intval, int(addressof(pkt.data))): | |
206 | return False | |
207 | if bits == 32: | |
208 | if not WriteInt32ToMemoryAddress(intval, int(addressof(pkt.data))): | |
209 | return False | |
210 | if bits == 64: | |
211 | if not WriteInt64ToMemoryAddress(intval, int(addressof(pkt.data))): | |
212 | return False | |
213 | if WriteInt32ToMemoryAddress(1, input_address): | |
214 | return True | |
215 | return False | |
39236c6e A |
216 | |
217 | ||
218 | def WritePhysInt(phys_addr, int_val, bitsize = 64): | |
fe8ab488 | 219 | """ Write and integer value in a physical memory data based on address. |
39236c6e A |
220 | params: |
221 | phys_addr : int - Physical address to read | |
222 | int_val : int - int value to write in memory | |
223 | bitsize : int - defines how many bytes to read. defaults to 64 bit | |
224 | returns: | |
225 | bool - True if write was successful. | |
226 | """ | |
227 | if "kdp" == GetConnectionProtocol(): | |
228 | if not KDPWritePhysMEM(phys_addr, int_val, bitsize): | |
229 | print "Failed to write via KDP." | |
230 | return False | |
231 | return True | |
232 | #We are not connected via KDP. So do manual math and savings. | |
233 | print "Failed: Write to physical memory is not supported for %s connection." % GetConnectionProtocol() | |
234 | return False | |
235 | ||
236 | @lldb_command('writephys') | |
237 | def WritePhys(cmd_args=None): | |
fe8ab488 | 238 | """ writes to the specified untranslated address |
39236c6e A |
239 | The argument is interpreted as a physical address, and the 64-bit word |
240 | addressed is displayed. | |
241 | usage: writephys <nbits> <address> <value> | |
242 | nbits: 8,16,32,64 | |
243 | address: 1234 or 0x1234 | |
244 | value: int value to be written | |
fe8ab488 | 245 | ex. (lldb)writephys 16 0x12345abcd 0x25 |
39236c6e A |
246 | """ |
247 | if cmd_args == None or len(cmd_args) < 3: | |
248 | print "Invalid arguments.", WritePhys.__doc__ | |
249 | else: | |
250 | nbits = ArgumentStringToInt(cmd_args[0]) | |
251 | phys_addr = ArgumentStringToInt(cmd_args[1]) | |
252 | int_value = ArgumentStringToInt(cmd_args[2]) | |
253 | print WritePhysInt(phys_addr, int_value, nbits) | |
254 | ||
255 | ||
256 | lldb_alias('writephys8', 'writephys 8 ') | |
257 | lldb_alias('writephys16', 'writephys 16 ') | |
258 | lldb_alias('writephys32', 'writephys 32 ') | |
259 | lldb_alias('writephys64', 'writephys 64 ') | |
260 | ||
fe8ab488 | 261 | |
39236c6e A |
262 | def _PT_Step(paddr, index, verbose_level = vSCRIPT): |
263 | """ | |
264 | Step to lower-level page table and print attributes | |
265 | paddr: current page table entry physical address | |
266 | index: current page table entry index (0..511) | |
267 | verbose_level: vHUMAN: print nothing | |
268 | vSCRIPT: print basic information | |
269 | vDETAIL: print basic information and hex table dump | |
270 | returns: (pt_paddr, pt_valid, pt_large) | |
271 | pt_paddr: next level page table entry physical address | |
272 | or null if invalid | |
273 | pt_valid: 1 if $kgm_pt_paddr is valid, 0 if the walk | |
274 | should be aborted | |
275 | pt_large: 1 if kgm_pt_paddr is a page frame address | |
276 | of a large page and not another page table entry | |
277 | """ | |
278 | entry_addr = paddr + (8 * index) | |
279 | entry = ReadPhysInt(entry_addr, 64, xnudefines.lcpu_self ) | |
280 | out_string = '' | |
281 | if verbose_level >= vDETAIL: | |
282 | for pte_loop in range(0, 512): | |
283 | paddr_tmp = paddr + (8 * pte_loop) | |
284 | out_string += "{0: <#020x}:\t {1: <#020x}\n".format(paddr_tmp, ReadPhysInt(paddr_tmp, 64, xnudefines.lcpu_self)) | |
285 | paddr_mask = ~((0xfff<<52) | 0xfff) | |
286 | paddr_large_mask = ~((0xfff<<52) | 0x1fffff) | |
287 | pt_valid = False | |
288 | pt_large = False | |
289 | pt_paddr = 0 | |
290 | if verbose_level < vSCRIPT: | |
291 | if entry & 0x1 : | |
292 | pt_valid = True | |
293 | pt_large = False | |
294 | pt_paddr = entry & paddr_mask | |
295 | if entry & (0x1 <<7): | |
296 | pt_large = True | |
297 | pt_paddr = entry & paddr_large_mask | |
298 | else: | |
299 | out_string+= "{0: <#020x}:\n\t{1:#020x}\n\t".format(entry_addr, entry) | |
300 | if entry & 0x1: | |
301 | out_string += " valid" | |
302 | pt_paddr = entry & paddr_mask | |
303 | pt_valid = True | |
304 | else: | |
305 | out_string += " invalid" | |
306 | pt_paddr = 0 | |
307 | pt_valid = False | |
3e170ce0 A |
308 | if entry & (0x1 << 62): |
309 | out_string += " compressed" | |
39236c6e A |
310 | #Stop decoding other bits |
311 | entry = 0 | |
312 | if entry & (0x1 << 1): | |
313 | out_string += " writable" | |
314 | else: | |
315 | out_string += " read-only" | |
fe8ab488 | 316 | |
39236c6e A |
317 | if entry & (0x1 << 2): |
318 | out_string += " user" | |
319 | else: | |
320 | out_string += " supervisor" | |
fe8ab488 | 321 | |
39236c6e A |
322 | if entry & (0x1 << 3): |
323 | out_string += " PWT" | |
fe8ab488 | 324 | |
39236c6e A |
325 | if entry & (0x1 << 4): |
326 | out_string += " PCD" | |
fe8ab488 | 327 | |
39236c6e A |
328 | if entry & (0x1 << 5): |
329 | out_string += " accessed" | |
fe8ab488 | 330 | |
39236c6e A |
331 | if entry & (0x1 << 6): |
332 | out_string += " dirty" | |
fe8ab488 | 333 | |
39236c6e A |
334 | if entry & (0x1 << 7): |
335 | out_string += " large" | |
336 | pt_large = True | |
337 | else: | |
338 | pt_large = False | |
fe8ab488 | 339 | |
39236c6e A |
340 | if entry & (0x1 << 8): |
341 | out_string += " global" | |
fe8ab488 | 342 | |
39236c6e A |
343 | if entry & (0x3 << 9): |
344 | out_string += " avail:{0:x}".format((entry >> 9) & 0x3) | |
fe8ab488 | 345 | |
39236c6e A |
346 | if entry & (0x1 << 63): |
347 | out_string += " noexec" | |
348 | print out_string | |
349 | return (pt_paddr, pt_valid, pt_large) | |
fe8ab488 | 350 | |
3e170ce0 A |
351 | def _PT_StepEPT(paddr, index, verbose_level = vSCRIPT): |
352 | """ | |
353 | Step to lower-level page table and print attributes for EPT pmap | |
354 | paddr: current page table entry physical address | |
355 | index: current page table entry index (0..511) | |
356 | verbose_level: vHUMAN: print nothing | |
357 | vSCRIPT: print basic information | |
358 | vDETAIL: print basic information and hex table dump | |
359 | returns: (pt_paddr, pt_valid, pt_large) | |
360 | pt_paddr: next level page table entry physical address | |
361 | or null if invalid | |
362 | pt_valid: 1 if $kgm_pt_paddr is valid, 0 if the walk | |
363 | should be aborted | |
364 | pt_large: 1 if kgm_pt_paddr is a page frame address | |
365 | of a large page and not another page table entry | |
366 | """ | |
367 | entry_addr = paddr + (8 * index) | |
368 | entry = ReadPhysInt(entry_addr, 64, xnudefines.lcpu_self ) | |
369 | out_string = '' | |
370 | if verbose_level >= vDETAIL: | |
371 | for pte_loop in range(0, 512): | |
372 | paddr_tmp = paddr + (8 * pte_loop) | |
373 | out_string += "{0: <#020x}:\t {1: <#020x}\n".format(paddr_tmp, ReadPhysInt(paddr_tmp, 64, xnudefines.lcpu_self)) | |
374 | paddr_mask = ~((0xfff<<52) | 0xfff) | |
375 | paddr_large_mask = ~((0xfff<<52) | 0x1fffff) | |
376 | pt_valid = False | |
377 | pt_large = False | |
378 | pt_paddr = 0 | |
379 | if verbose_level < vSCRIPT: | |
380 | if entry & 0x7 : | |
381 | pt_valid = True | |
382 | pt_large = False | |
383 | pt_paddr = entry & paddr_mask | |
384 | if entry & (0x1 <<7): | |
385 | pt_large = True | |
386 | pt_paddr = entry & paddr_large_mask | |
387 | else: | |
388 | out_string+= "{0: <#020x}:\n\t{1:#020x}\n\t".format(entry_addr, entry) | |
389 | if entry & 0x7: | |
390 | out_string += "valid" | |
391 | pt_paddr = entry & paddr_mask | |
392 | pt_valid = True | |
393 | else: | |
394 | out_string += "invalid" | |
395 | pt_paddr = 0 | |
396 | pt_valid = False | |
397 | if entry & (0x1 << 62): | |
398 | out_string += " compressed" | |
399 | #Stop decoding other bits | |
400 | entry = 0 | |
401 | if entry & 0x1: | |
402 | out_string += " readable" | |
403 | else: | |
404 | out_string += " no read" | |
405 | if entry & (0x1 << 1): | |
406 | out_string += " writable" | |
407 | else: | |
408 | out_string += " no write" | |
409 | ||
410 | if entry & (0x1 << 2): | |
411 | out_string += " executable" | |
412 | else: | |
413 | out_string += " no exec" | |
414 | ||
415 | ctype = entry & 0x38 | |
416 | if ctype == 0x30: | |
417 | out_string += " cache-WB" | |
418 | elif ctype == 0x28: | |
419 | out_string += " cache-WP" | |
420 | elif ctype == 0x20: | |
421 | out_string += " cache-WT" | |
422 | elif ctype == 0x8: | |
423 | out_string += " cache-WC" | |
424 | else: | |
425 | out_string += " cache-NC" | |
426 | ||
427 | if (entry & 0x40) == 0x40: | |
428 | out_string += " Ignore-PTA" | |
fe8ab488 | 429 | |
3e170ce0 A |
430 | if (entry & 0x100) == 0x100: |
431 | out_string += " accessed" | |
fe8ab488 | 432 | |
3e170ce0 A |
433 | if (entry & 0x200) == 0x200: |
434 | out_string += " dirty" | |
fe8ab488 | 435 | |
3e170ce0 A |
436 | if entry & (0x1 << 7): |
437 | out_string += " large" | |
438 | pt_large = True | |
439 | else: | |
440 | pt_large = False | |
441 | print out_string | |
442 | return (pt_paddr, pt_valid, pt_large) | |
443 | ||
444 | def _PmapL4Walk(pmap_addr_val,vaddr, ept_pmap, verbose_level = vSCRIPT): | |
39236c6e A |
445 | """ Walk the l4 pmap entry. |
446 | params: pmap_addr_val - core.value representing kernel data of type pmap_addr_t | |
447 | vaddr : int - virtual address to walk | |
448 | """ | |
39236c6e A |
449 | pt_paddr = unsigned(pmap_addr_val) |
450 | pt_valid = (unsigned(pmap_addr_val) != 0) | |
451 | pt_large = 0 | |
452 | pframe_offset = 0 | |
0a7de745 | 453 | if pt_valid: |
39236c6e A |
454 | # Lookup bits 47:39 of linear address in PML4T |
455 | pt_index = (vaddr >> 39) & 0x1ff | |
456 | pframe_offset = vaddr & 0x7fffffffff | |
457 | if verbose_level > vHUMAN : | |
458 | print "pml4 (index {0:d}):".format(pt_index) | |
3e170ce0 A |
459 | if not(ept_pmap): |
460 | (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level) | |
461 | else: | |
462 | (pt_paddr, pt_valid, pt_large) = _PT_StepEPT(pt_paddr, pt_index, verbose_level) | |
39236c6e A |
463 | if pt_valid: |
464 | # Lookup bits 38:30 of the linear address in PDPT | |
465 | pt_index = (vaddr >> 30) & 0x1ff | |
466 | pframe_offset = vaddr & 0x3fffffff | |
467 | if verbose_level > vHUMAN: | |
468 | print "pdpt (index {0:d}):".format(pt_index) | |
3e170ce0 A |
469 | if not(ept_pmap): |
470 | (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level) | |
471 | else: | |
472 | (pt_paddr, pt_valid, pt_large) = _PT_StepEPT(pt_paddr, pt_index, verbose_level) | |
39236c6e A |
473 | if pt_valid and not pt_large: |
474 | #Lookup bits 29:21 of the linear address in PDPT | |
475 | pt_index = (vaddr >> 21) & 0x1ff | |
476 | pframe_offset = vaddr & 0x1fffff | |
477 | if verbose_level > vHUMAN: | |
478 | print "pdt (index {0:d}):".format(pt_index) | |
3e170ce0 A |
479 | if not(ept_pmap): |
480 | (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level) | |
481 | else: | |
482 | (pt_paddr, pt_valid, pt_large) = _PT_StepEPT(pt_paddr, pt_index, verbose_level) | |
39236c6e A |
483 | if pt_valid and not pt_large: |
484 | #Lookup bits 20:21 of linear address in PT | |
485 | pt_index = (vaddr >> 12) & 0x1ff | |
486 | pframe_offset = vaddr & 0xfff | |
487 | if verbose_level > vHUMAN: | |
488 | print "pt (index {0:d}):".format(pt_index) | |
3e170ce0 A |
489 | if not(ept_pmap): |
490 | (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level) | |
491 | else: | |
492 | (pt_paddr, pt_valid, pt_large) = _PT_StepEPT(pt_paddr, pt_index, verbose_level) | |
39236c6e A |
493 | paddr = 0 |
494 | paddr_isvalid = False | |
495 | if pt_valid: | |
496 | paddr = pt_paddr + pframe_offset | |
497 | paddr_isvalid = True | |
fe8ab488 | 498 | |
39236c6e A |
499 | if verbose_level > vHUMAN: |
500 | if paddr_isvalid: | |
501 | pvalue = ReadPhysInt(paddr, 32, xnudefines.lcpu_self) | |
502 | print "phys {0: <#020x}: {1: <#020x}".format(paddr, pvalue) | |
503 | else: | |
504 | print "no translation" | |
505 | ||
fe8ab488 A |
506 | return paddr |
507 | ||
813fb2f6 A |
508 | def PmapDecodeTTEARM(tte, level, verbose_level): |
509 | """ Display the bits of an ARM translation table or page table entry | |
510 | in human-readable form. | |
511 | tte: integer value of the TTE/PTE | |
512 | level: translation table level. Valid values are 1 or 2. | |
513 | verbose_level: verbosity. vHUMAN, vSCRIPT, vDETAIL | |
514 | """ | |
39236c6e | 515 | out_string = "" |
813fb2f6 A |
516 | if level == 1 and (tte & 0x3) == 0x2: |
517 | if verbose_level < vSCRIPT: | |
518 | return | |
fe8ab488 | 519 | |
39236c6e A |
520 | #bit [1:0] evaluated in PmapWalkARM |
521 | # B bit 2 | |
522 | b_bit = (tte & 0x4) >> 2 | |
523 | # C bit 3 | |
524 | c_bit = (tte & 0x8) >> 3 | |
525 | #XN bit 4 | |
fe8ab488 | 526 | if (tte & 0x10) : |
39236c6e A |
527 | out_string += "no-execute" |
528 | else: | |
529 | out_string += "execute" | |
530 | #Domain bit [8:5] if not supersection | |
531 | if (tte & 0x40000) == 0x0: | |
532 | out_string += " domain ({:d})".format(((tte & 0x1e0) >> 5) ) | |
533 | #IMP bit 9 | |
534 | out_string += " imp({:d})".format( ((tte & 0x200) >> 9) ) | |
535 | # AP bit 15 and [11:10] merged to a single 3 bit value | |
536 | access = ( (tte & 0xc00) >> 10 ) | ((tte & 0x8000) >> 13) | |
537 | out_string += xnudefines.arm_level2_access_strings[access] | |
fe8ab488 | 538 | |
39236c6e A |
539 | #TEX bit [14:12] |
540 | tex_bits = ((tte & 0x7000) >> 12) | |
541 | #Print TEX, C , B all together | |
542 | out_string += " TEX:C:B({:d}{:d}{:d}:{:d}:{:d})".format( | |
543 | 1 if (tex_bits & 0x4) else 0, | |
544 | 1 if (tex_bits & 0x2) else 0, | |
545 | 1 if (tex_bits & 0x1) else 0, | |
546 | c_bit, | |
547 | b_bit | |
548 | ) | |
549 | # S bit 16 | |
550 | if tte & 0x10000: | |
551 | out_string += " shareable" | |
552 | else: | |
553 | out_string += " not-shareable" | |
554 | # nG bit 17 | |
555 | if tte & 0x20000 : | |
556 | out_string += " not-global" | |
557 | else: | |
558 | out_string += " global" | |
559 | # Supersection bit 18 | |
560 | if tte & 0x40000: | |
561 | out_string += " supersection" | |
562 | else: | |
563 | out_string += " section" | |
564 | #NS bit 19 | |
565 | if tte & 0x80000 : | |
566 | out_string += " no-secure" | |
567 | else: | |
568 | out_string += " secure" | |
fe8ab488 | 569 | |
813fb2f6 A |
570 | elif level == 1 and (tte & 0x3) == 0x1: |
571 | ||
572 | if verbose_level >= vSCRIPT: | |
573 | # bit [1:0] evaluated in PmapWalkARM | |
574 | # NS bit 3 | |
575 | if tte & 0x8: | |
576 | out_string += ' no-secure' | |
577 | else: | |
578 | out_string += ' secure' | |
579 | #Domain bit [8:5] | |
580 | out_string += " domain({:d})".format(((tte & 0x1e0) >> 5)) | |
581 | # IMP bit 9 | |
582 | out_string += " imp({:d})".format( ((tte & 0x200) >> 9)) | |
583 | out_string += "\n" | |
584 | ||
585 | elif level == 2: | |
586 | pte = tte | |
587 | if verbose_level >= vSCRIPT: | |
588 | if (pte & 0x3) == 0x0: | |
589 | out_string += " invalid" | |
590 | else: | |
591 | if (pte & 0x3) == 0x1: | |
592 | out_string += " large" | |
593 | # XN bit 15 | |
594 | if pte & 0x8000 == 0x8000: | |
595 | out_string+= " no-execute" | |
596 | else: | |
597 | out_string += " execute" | |
598 | else: | |
599 | out_string += " small" | |
600 | # XN bit 0 | |
601 | if (pte & 0x1) == 0x01: | |
602 | out_string += " no-execute" | |
603 | else: | |
604 | out_string += " execute" | |
605 | # B bit 2 | |
606 | b_bit = (pte & 0x4) >> 2 | |
607 | c_bit = (pte & 0x8) >> 3 | |
608 | # AP bit 9 and [5:4], merged to a single 3-bit value | |
609 | access = (pte & 0x30) >> 4 | (pte & 0x200) >> 7 | |
610 | out_string += xnudefines.arm_level2_access_strings[access] | |
611 | ||
612 | #TEX bit [14:12] for large, [8:6] for small | |
613 | tex_bits = ((pte & 0x1c0) >> 6) | |
614 | if (pte & 0x3) == 0x1: | |
615 | tex_bits = ((pte & 0x7000) >> 12) | |
616 | ||
617 | # Print TEX, C , B alltogether | |
618 | out_string += " TEX:C:B({:d}{:d}{:d}:{:d}:{:d})".format( | |
619 | 1 if (tex_bits & 0x4) else 0, | |
620 | 1 if (tex_bits & 0x2) else 0, | |
621 | 1 if (tex_bits & 0x1) else 0, | |
622 | c_bit, | |
623 | b_bit | |
624 | ) | |
625 | # S bit 10 | |
626 | if pte & 0x400 : | |
627 | out_string += " shareable" | |
628 | else: | |
629 | out_string += " not-shareable" | |
630 | ||
631 | # nG bit 11 | |
632 | if pte & 0x800: | |
633 | out_string += " not-global" | |
634 | else: | |
635 | out_string += " global" | |
636 | ||
39236c6e | 637 | print out_string |
813fb2f6 A |
638 | |
639 | ||
640 | def _PmapWalkARMLevel1Section(tte, vaddr, verbose_level = vSCRIPT): | |
641 | paddr = 0 | |
642 | #Supersection or just section? | |
643 | if (tte & 0x40000) == 0x40000: | |
644 | paddr = ( (tte & 0xFF000000) | (vaddr & 0x00FFFFFF) ) | |
645 | else: | |
646 | paddr = ( (tte & 0xFFF00000) | (vaddr & 0x000FFFFF) ) | |
647 | ||
648 | if verbose_level >= vSCRIPT: | |
649 | print "{0: <#020x}\n\t{1: <#020x}\n\t".format(addressof(tte), tte), | |
650 | ||
651 | PmapDecodeTTEARM(tte, 1, verbose_level) | |
652 | ||
39236c6e | 653 | return paddr |
fe8ab488 A |
654 | |
655 | ||
656 | ||
39236c6e | 657 | def _PmapWalkARMLevel2(tte, vaddr, verbose_level = vSCRIPT): |
fe8ab488 A |
658 | """ Pmap walk the level 2 tte. |
659 | params: | |
39236c6e A |
660 | tte - value object |
661 | vaddr - int | |
662 | returns: str - description of the tte + additional informaiton based on verbose_level | |
663 | """ | |
664 | pte_base = kern.PhysToKernelVirt(tte & 0xFFFFFC00) | |
665 | pte_index = (vaddr >> 12) & 0xFF | |
666 | pte_base_val = kern.GetValueFromAddress(pte_base, 'pt_entry_t *') | |
667 | pte = pte_base_val[pte_index] | |
813fb2f6 A |
668 | |
669 | paddr = 0 | |
670 | if pte & 0x2: | |
671 | paddr = (unsigned(pte) & 0xFFFFF000) | (vaddr & 0xFFF) | |
672 | ||
39236c6e | 673 | if verbose_level >= vSCRIPT: |
813fb2f6 A |
674 | print "{0: <#020x}\n\t{1: <#020x}\n\t".format(addressof(tte), tte), |
675 | ||
676 | PmapDecodeTTEARM(tte, 1, verbose_level) | |
39236c6e | 677 | if verbose_level >= vSCRIPT: |
813fb2f6 | 678 | print "second-level table (index {:d}):".format(pte_index) |
39236c6e A |
679 | if verbose_level >= vDETAIL: |
680 | for i in range(256): | |
681 | tmp = pte_base_val[i] | |
813fb2f6 | 682 | print "{0: <#020x}:\t{1: <#020x}".format(addressof(tmp), unsigned(tmp)) |
39236c6e A |
683 | |
684 | if verbose_level >= vSCRIPT: | |
813fb2f6 A |
685 | print " {0: <#020x}\n\t{1: <#020x}\n\t".format(addressof(pte), unsigned(pte)), |
686 | ||
687 | PmapDecodeTTEARM(pte, 2, verbose_level) | |
fe8ab488 | 688 | |
39236c6e A |
689 | return paddr |
690 | #end of level 2 walking of arm | |
691 | ||
692 | ||
693 | def PmapWalkARM(pmap, vaddr, verbose_level = vHUMAN): | |
694 | """ Pmap walking for ARM kernel. | |
695 | params: | |
696 | pmapval: core.value - representing pmap_t in kernel | |
697 | vaddr: int - integer representing virtual address to walk | |
698 | """ | |
699 | paddr = 0 | |
700 | # shift by TTESHIFT (20) to get tte index | |
813fb2f6 A |
701 | # Assume all L1 indexing starts at VA 0...for our purposes it does, |
702 | # as that's where all user pmaps start, and the kernel pmap contains | |
703 | # 4 L1 pages (the lower 2 of which are unused after bootstrap) | |
704 | tte_index = vaddr >> 20 | |
39236c6e A |
705 | tte = pmap.tte[tte_index] |
706 | if verbose_level >= vSCRIPT: | |
707 | print "First-level table (index {:d}):".format(tte_index) | |
708 | if verbose_level >= vDETAIL: | |
813fb2f6 | 709 | for i in range(0, pmap.tte_index_max): |
39236c6e A |
710 | ptr = unsigned(addressof(pmap.tte[i])) |
711 | val = unsigned(pmap.tte[i]) | |
712 | print "{0: <#020x}:\t {1: <#020x}".format(ptr, val) | |
713 | if (tte & 0x3) == 0x1: | |
714 | paddr = _PmapWalkARMLevel2(tte, vaddr, verbose_level) | |
715 | elif (tte & 0x3) == 0x2 : | |
716 | paddr = _PmapWalkARMLevel1Section(tte, vaddr, verbose_level) | |
717 | else: | |
718 | paddr = 0 | |
719 | if verbose_level >= vSCRIPT: | |
720 | print "Invalid First-Level Translation Table Entry: {0: #020x}".format(tte) | |
721 | ||
722 | if verbose_level >= vHUMAN: | |
723 | if paddr: | |
724 | print "Translation of {:#x} is {:#x}.".format(vaddr, paddr) | |
725 | else: | |
726 | print "(no translation)" | |
727 | ||
728 | return paddr | |
729 | ||
3e170ce0 | 730 | def PmapWalkX86_64(pmapval, vaddr, verbose_level = vSCRIPT): |
39236c6e A |
731 | """ |
732 | params: pmapval - core.value representing pmap_t in kernel | |
733 | vaddr: int - int representing virtual address to walk | |
734 | """ | |
3e170ce0 A |
735 | if pmapval.pm_cr3 != 0: |
736 | if verbose_level > vHUMAN: | |
737 | print "Using normal Intel PMAP from pm_cr3\n" | |
738 | return _PmapL4Walk(pmapval.pm_cr3, vaddr, 0, config['verbosity']) | |
739 | else: | |
740 | if verbose_level > vHUMAN: | |
741 | print "Using EPT pmap from pm_eptp\n" | |
742 | return _PmapL4Walk(pmapval.pm_eptp, vaddr, 1, config['verbosity']) | |
39236c6e A |
743 | |
744 | def assert_64bit(val): | |
745 | assert(val < 2**64) | |
746 | ||
fe8ab488 | 747 | ARM64_TTE_SIZE = 8 |
39037602 | 748 | ARM64_TTE_SHIFT = 3 |
fe8ab488 A |
749 | ARM64_VMADDR_BITS = 48 |
750 | ||
f427ee49 A |
751 | def PmapBlockOffsetMaskARM64(page_size, level): |
752 | assert level >= 0 and level <= 3 | |
fe8ab488 A |
753 | ttentries = (page_size / ARM64_TTE_SIZE) |
754 | return page_size * (ttentries ** (3 - level)) - 1 | |
755 | ||
f427ee49 A |
756 | def PmapBlockBaseMaskARM64(page_size, level): |
757 | assert level >= 0 and level <= 3 | |
758 | return ((1 << ARM64_VMADDR_BITS) - 1) & ~PmapBlockOffsetMaskARM64(page_size, level) | |
fe8ab488 | 759 | |
f427ee49 | 760 | def PmapDecodeTTEARM64(tte, level, stage2 = False): |
813fb2f6 A |
761 | """ Display the bits of an ARM64 translation table or page table entry |
762 | in human-readable form. | |
763 | tte: integer value of the TTE/PTE | |
764 | level: translation table level. Valid values are 1, 2, or 3. | |
765 | """ | |
fe8ab488 A |
766 | assert(type(level) == int) |
767 | assert_64bit(tte) | |
768 | ||
f427ee49 A |
769 | if tte & 0x1 == 0x0: |
770 | print("Invalid.") | |
771 | return | |
772 | ||
773 | if (tte & 0x2 == 0x2) and (level != 0x3): | |
774 | print "Type = Table pointer." | |
775 | print "Table addr = {:#x}.".format(tte & 0xfffffffff000) | |
776 | ||
777 | if not stage2: | |
fe8ab488 A |
778 | print "PXN = {:#x}.".format((tte >> 59) & 0x1) |
779 | print "XN = {:#x}.".format((tte >> 60) & 0x1) | |
780 | print "AP = {:#x}.".format((tte >> 61) & 0x3) | |
f427ee49 A |
781 | print "NS = {:#x}.".format(tte >> 63) |
782 | else: | |
783 | print "Type = Block." | |
784 | ||
785 | if stage2: | |
786 | print "S2 MemAttr = {:#x}.".format((tte >> 2) & 0xf) | |
fe8ab488 | 787 | else: |
fe8ab488 A |
788 | print "AttrIdx = {:#x}.".format((tte >> 2) & 0x7) |
789 | print "NS = {:#x}.".format((tte >> 5) & 0x1) | |
f427ee49 A |
790 | |
791 | if stage2: | |
792 | print "S2AP = {:#x}.".format((tte >> 6) & 0x3) | |
793 | else: | |
fe8ab488 | 794 | print "AP = {:#x}.".format((tte >> 6) & 0x3) |
f427ee49 A |
795 | |
796 | print "SH = {:#x}.".format((tte >> 8) & 0x3) | |
797 | print "AF = {:#x}.".format((tte >> 10) & 0x1) | |
798 | ||
799 | if not stage2: | |
fe8ab488 | 800 | print "nG = {:#x}.".format((tte >> 11) & 0x1) |
f427ee49 A |
801 | |
802 | print "HINT = {:#x}.".format((tte >> 52) & 0x1) | |
803 | ||
804 | if stage2: | |
805 | print "S2XN = {:#x}.".format((tte >> 53) & 0x3) | |
806 | else: | |
fe8ab488 A |
807 | print "PXN = {:#x}.".format((tte >> 53) & 0x1) |
808 | print "XN = {:#x}.".format((tte >> 54) & 0x1) | |
f427ee49 A |
809 | |
810 | print "SW Use = {:#x}.".format((tte >> 55) & 0xf) | |
fe8ab488 A |
811 | |
812 | return | |
813 | ||
f427ee49 A |
814 | def PmapTTnIndexARM64(vaddr, pmap_pt_attr): |
815 | pta_max_level = unsigned(pmap_pt_attr.pta_max_level) | |
816 | ||
817 | tt_index = [] | |
818 | for i in range(pta_max_level + 1): | |
819 | tt_index.append((vaddr & unsigned(pmap_pt_attr.pta_level_info[i].index_mask)) \ | |
820 | >> unsigned(pmap_pt_attr.pta_level_info[i].shift)) | |
821 | ||
822 | return tt_index | |
823 | ||
824 | def PmapWalkARM64(pmap_pt_attr, root_tte, vaddr, verbose_level = vHUMAN): | |
fe8ab488 | 825 | assert(type(vaddr) in (long, int)) |
f427ee49 A |
826 | assert_64bit(vaddr) |
827 | assert_64bit(root_tte) | |
828 | ||
829 | # Obtain pmap attributes | |
830 | page_size = pmap_pt_attr.pta_page_size | |
fe8ab488 A |
831 | page_offset_mask = (page_size - 1) |
832 | page_base_mask = ((1 << ARM64_VMADDR_BITS) - 1) & (~page_offset_mask) | |
f427ee49 A |
833 | tt_index = PmapTTnIndexARM64(vaddr, pmap_pt_attr) |
834 | stage2 = bool(pmap_pt_attr.stage2 if hasattr(pmap_pt_attr, 'stage2') else False) | |
fe8ab488 | 835 | |
f427ee49 A |
836 | # The pmap starts at a page table level that is defined by register |
837 | # values; the root level can be obtained from the attributes structure | |
838 | level = unsigned(pmap_pt_attr.pta_root_level) | |
839 | ||
840 | root_tt_index = tt_index[level] | |
841 | root_pgtable_num_ttes = (unsigned(pmap_pt_attr.pta_level_info[level].index_mask) >> \ | |
842 | unsigned(pmap_pt_attr.pta_level_info[level].shift)) + 1 | |
843 | tte = long(unsigned(root_tte[root_tt_index])) | |
844 | ||
845 | # Walk the page tables | |
fe8ab488 | 846 | paddr = -1 |
f427ee49 | 847 | max_level = unsigned(pmap_pt_attr.pta_max_level) |
fe8ab488 | 848 | |
f427ee49 A |
849 | while (level <= max_level): |
850 | if verbose_level >= vSCRIPT: | |
851 | print "L{} entry: {:#x}".format(level, tte) | |
852 | if verbose_level >= vDETAIL: | |
853 | PmapDecodeTTEARM64(tte, level, stage2) | |
fe8ab488 | 854 | |
f427ee49 A |
855 | if tte & 0x1 == 0x0: |
856 | if verbose_level >= vHUMAN: | |
857 | print "L{} entry invalid: {:#x}\n".format(level, tte) | |
858 | break | |
39037602 | 859 | |
f427ee49 A |
860 | # Handle leaf entry |
861 | if tte & 0x2 == 0x0 or level == max_level: | |
862 | base_mask = page_base_mask if level == max_level else PmapBlockBaseMaskARM64(page_size, level) | |
863 | offset_mask = page_offset_mask if level == max_level else PmapBlockOffsetMaskARM64(page_size, level) | |
864 | paddr = tte & base_mask | |
865 | paddr = paddr | (vaddr & offset_mask) | |
39037602 | 866 | |
f427ee49 A |
867 | if level != max_level: |
868 | print "phys: {:#x}".format(paddr) | |
39037602 | 869 | |
f427ee49 A |
870 | break |
871 | else: | |
872 | # Handle page table entry | |
873 | next_phys = (tte & page_base_mask) + (ARM64_TTE_SIZE * tt_index[level + 1]) | |
874 | assert(type(next_phys) == long) | |
fe8ab488 | 875 | |
f427ee49 A |
876 | next_virt = kern.PhysToKernelVirt(next_phys) |
877 | assert(type(next_virt) == long) | |
fe8ab488 | 878 | |
fe8ab488 | 879 | if verbose_level >= vDETAIL: |
f427ee49 | 880 | print "L{} physical address: {:#x}. L{} virtual address: {:#x}".format(level + 1, next_phys, level + 1, next_virt) |
39037602 | 881 | |
f427ee49 A |
882 | ttep = kern.GetValueFromAddress(next_virt, "tt_entry_t*") |
883 | tte = long(unsigned(dereference(ttep))) | |
884 | assert(type(tte) == long) | |
39037602 A |
885 | |
886 | # We've parsed one level, so go to the next level | |
887 | assert(level <= 3) | |
888 | level = level + 1 | |
fe8ab488 A |
889 | |
890 | if verbose_level >= vHUMAN: | |
891 | if paddr: | |
892 | print "Translation of {:#x} is {:#x}.".format(vaddr, paddr) | |
893 | else: | |
894 | print "(no translation)" | |
895 | ||
896 | return paddr | |
897 | ||
39236c6e A |
898 | def PmapWalk(pmap, vaddr, verbose_level = vHUMAN): |
899 | if kern.arch == 'x86_64': | |
3e170ce0 | 900 | return PmapWalkX86_64(pmap, vaddr, verbose_level) |
39236c6e A |
901 | elif kern.arch == 'arm': |
902 | return PmapWalkARM(pmap, vaddr, verbose_level) | |
5ba3f43e | 903 | elif kern.arch.startswith('arm64'): |
f427ee49 A |
904 | # Obtain pmap attributes from pmap structure |
905 | pmap_pt_attr = pmap.pmap_pt_attr if hasattr(pmap, 'pmap_pt_attr') else kern.globals.native_pt_attr | |
906 | return PmapWalkARM64(pmap_pt_attr, pmap.tte, vaddr, verbose_level) | |
39236c6e A |
907 | else: |
908 | raise NotImplementedError("PmapWalk does not support {0}".format(kern.arch)) | |
909 | ||
910 | @lldb_command('pmap_walk') | |
911 | def PmapWalkHelper(cmd_args=None): | |
912 | """ Perform a page-table walk in <pmap> for <virtual_address>. | |
3e170ce0 | 913 | Syntax: (lldb) pmap_walk <pmap> <virtual_address> [-v] [-e] |
39236c6e A |
914 | Multiple -v's can be specified for increased verbosity |
915 | """ | |
916 | if cmd_args == None or len(cmd_args) < 2: | |
917 | raise ArgumentError("Too few arguments to pmap_walk.") | |
918 | ||
919 | pmap = kern.GetValueAsType(cmd_args[0], 'pmap_t') | |
f427ee49 | 920 | addr = ArgumentStringToInt(cmd_args[1]) |
39236c6e A |
921 | PmapWalk(pmap, addr, config['verbosity']) |
922 | return | |
813fb2f6 | 923 | |
f427ee49 A |
924 | def GetMemoryAttributesFromUser(requested_type): |
925 | pmap_attr_dict = { | |
926 | '4k' : kern.globals.pmap_pt_attr_4k, | |
927 | '16k' : kern.globals.pmap_pt_attr_16k, | |
928 | '16k_s2' : kern.globals.pmap_pt_attr_16k_stage2 if hasattr(kern.globals, 'pmap_pt_attr_16k_stage2') else None, | |
929 | } | |
930 | ||
931 | requested_type = requested_type.lower() | |
932 | if requested_type not in pmap_attr_dict: | |
933 | return None | |
934 | ||
935 | return pmap_attr_dict[requested_type] | |
936 | ||
937 | @lldb_command('ttep_walk') | |
938 | def TTEPWalkPHelper(cmd_args=None): | |
939 | """ Perform a page-table walk in <root_ttep> for <virtual_address>. | |
940 | Syntax: (lldb) ttep_walk <root_ttep> <virtual_address> [4k|16k|16k_s2] [-v] [-e] | |
941 | Multiple -v's can be specified for increased verbosity | |
942 | """ | |
943 | if cmd_args == None or len(cmd_args) < 2: | |
944 | raise ArgumentError("Too few arguments to ttep_walk.") | |
945 | ||
946 | if not kern.arch.startswith('arm64'): | |
947 | raise NotImplementedError("ttep_walk does not support {0}".format(kern.arch)) | |
948 | ||
949 | tte = kern.GetValueFromAddress(kern.PhysToKernelVirt(ArgumentStringToInt(cmd_args[0])), 'unsigned long *') | |
950 | addr = ArgumentStringToInt(cmd_args[1]) | |
951 | ||
952 | pmap_pt_attr = kern.globals.native_pt_attr if len(cmd_args) < 3 else GetMemoryAttributesFromUser(cmd_args[2]) | |
953 | if pmap_pt_attr is None: | |
954 | raise ArgumentError("Invalid translation attribute type.") | |
955 | ||
956 | return PmapWalkARM64(pmap_pt_attr, tte, addr, config['verbosity']) | |
957 | ||
813fb2f6 A |
958 | @lldb_command('decode_tte') |
959 | def DecodeTTE(cmd_args=None): | |
f427ee49 A |
960 | """ Decode the bits in the TTE/PTE value specified <tte_val> for translation level <level> and stage [s1|s2] |
961 | Syntax: (lldb) decode_tte <tte_val> <level> [s1|s2] | |
813fb2f6 A |
962 | """ |
963 | if cmd_args == None or len(cmd_args) < 2: | |
964 | raise ArgumentError("Too few arguments to decode_tte.") | |
f427ee49 A |
965 | if len(cmd_args) > 2 and cmd_args[2] not in ["s1", "s2"]: |
966 | raise ArgumentError("{} is not a valid stage of translation.".format(cmd_args[2])) | |
813fb2f6 A |
967 | if kern.arch == 'arm': |
968 | PmapDecodeTTEARM(kern.GetValueFromAddress(cmd_args[0], "unsigned long"), ArgumentStringToInt(cmd_args[1]), vSCRIPT) | |
5ba3f43e | 969 | elif kern.arch.startswith('arm64'): |
f427ee49 A |
970 | stage2 = True if len(cmd_args) > 2 and cmd_args[2] == "s2" else False |
971 | PmapDecodeTTEARM64(ArgumentStringToInt(cmd_args[0]), ArgumentStringToInt(cmd_args[1]), stage2) | |
813fb2f6 A |
972 | else: |
973 | raise NotImplementedError("decode_tte does not support {0}".format(kern.arch)) | |
974 | ||
d9a64523 A |
975 | |
976 | PVH_HIGH_FLAGS_ARM64 = (1 << 62) | (1 << 61) | (1 << 60) | (1 << 59) | |
977 | PVH_HIGH_FLAGS_ARM32 = (1 << 31) | |
978 | ||
813fb2f6 A |
979 | def PVWalkARM(pa): |
980 | """ Walk a physical-to-virtual reverse mapping list maintained by the arm pmap | |
981 | pa: physical address (NOT page number). Does not need to be page-aligned | |
982 | """ | |
983 | vm_first_phys = unsigned(kern.globals.vm_first_phys) | |
984 | vm_last_phys = unsigned(kern.globals.vm_last_phys) | |
985 | if pa < vm_first_phys or pa >= vm_last_phys: | |
986 | raise ArgumentError("PA {:#x} is outside range of managed physical addresses: [{:#x}, {:#x})".format(pa, vm_first_phys, vm_last_phys)) | |
d9a64523 | 987 | page_size = kern.globals.page_size |
813fb2f6 A |
988 | pn = (pa - unsigned(kern.globals.vm_first_phys)) / page_size |
989 | pvh = unsigned(kern.globals.pv_head_table[pn]) | |
990 | pvh_type = pvh & 0x3 | |
d9a64523 A |
991 | print "PVH raw value: ({:#x})".format(pvh) |
992 | if kern.arch.startswith('arm64'): | |
993 | iommu_flag = 0x4 | |
994 | iommu_table_flag = 1 << 63 | |
995 | pvh = pvh | PVH_HIGH_FLAGS_ARM64 | |
996 | else: | |
997 | iommu_flag = 0 | |
998 | iommu_table_flag = 0 | |
999 | pvh = pvh | PVH_HIGH_FLAGS_ARM32 | |
813fb2f6 A |
1000 | if pvh_type == 0: |
1001 | print "PVH type: NULL" | |
1002 | return | |
1003 | elif pvh_type == 3: | |
5ba3f43e | 1004 | print "PVH type: page-table descriptor ({:#x})".format(pvh & ~0x3) |
813fb2f6 A |
1005 | return |
1006 | elif pvh_type == 2: | |
1007 | ptep = pvh & ~0x3 | |
d9a64523 | 1008 | pte_str = '' |
813fb2f6 | 1009 | print "PVH type: single PTE" |
d9a64523 A |
1010 | if ptep & iommu_flag: |
1011 | ptep = ptep & ~iommu_flag | |
1012 | if ptep & iommu_table_flag: | |
1013 | pte_str = ' (IOMMU table), entry' | |
1014 | else: | |
1015 | pte_str = ' (IOMMU state), descriptor' | |
1016 | ptep = ptep | iommu_table_flag | |
1017 | print "PTE {:#x}{:s}: {:#x}".format(ptep, pte_str, dereference(kern.GetValueFromAddress(ptep, 'pt_entry_t *'))) | |
813fb2f6 A |
1018 | elif pvh_type == 1: |
1019 | pvep = pvh & ~0x3 | |
1020 | print "PVH type: PTE list" | |
1021 | while pvep != 0: | |
1022 | pve = kern.GetValueFromAddress(pvep, "pv_entry_t *") | |
1023 | if unsigned(pve.pve_next) & 0x1: | |
1024 | pve_str = ' (alt acct) ' | |
1025 | else: | |
1026 | pve_str = '' | |
5ba3f43e | 1027 | current_pvep = pvep |
813fb2f6 A |
1028 | pvep = unsigned(pve.pve_next) & ~0x1 |
1029 | ptep = unsigned(pve.pve_ptep) & ~0x3 | |
d9a64523 A |
1030 | if ptep & iommu_flag: |
1031 | ptep = ptep & ~iommu_flag | |
1032 | if ptep & iommu_table_flag: | |
1033 | pve_str = ' (IOMMU table), entry' | |
1034 | else: | |
1035 | pve_str = ' (IOMMU state), descriptor' | |
1036 | ptep = ptep | iommu_table_flag | |
f427ee49 A |
1037 | try: |
1038 | print "PVE {:#x}, PTE {:#x}{:s}: {:#x}".format(current_pvep, ptep, pve_str, dereference(kern.GetValueFromAddress(ptep, 'pt_entry_t *'))) | |
1039 | except: | |
1040 | print "PVE {:#x}, PTE {:#x}{:s}: <unavailable>".format(current_pvep, ptep, pve_str) | |
813fb2f6 A |
1041 | |
1042 | @lldb_command('pv_walk') | |
1043 | def PVWalk(cmd_args=None): | |
1044 | """ Show mappings for <physical_address> tracked in the PV list. | |
1045 | Syntax: (lldb) pv_walk <physical_address> | |
1046 | """ | |
1047 | if cmd_args == None or len(cmd_args) < 1: | |
1048 | raise ArgumentError("Too few arguments to pv_walk.") | |
5ba3f43e | 1049 | if not kern.arch.startswith('arm'): |
813fb2f6 A |
1050 | raise NotImplementedError("pv_walk does not support {0}".format(kern.arch)) |
1051 | PVWalkARM(kern.GetValueFromAddress(cmd_args[0], 'unsigned long')) | |
1052 | ||
d9a64523 A |
1053 | @lldb_command('kvtophys') |
1054 | def KVToPhys(cmd_args=None): | |
1055 | """ Translate a kernel virtual address to the corresponding physical address. | |
1056 | Assumes the virtual address falls within the kernel static region. | |
1057 | Syntax: (lldb) kvtophys <kernel virtual address> | |
1058 | """ | |
1059 | if cmd_args == None or len(cmd_args) < 1: | |
1060 | raise ArgumentError("Too few arguments to kvtophys.") | |
1061 | if kern.arch.startswith('arm'): | |
1062 | print "{:#x}".format(KVToPhysARM(long(unsigned(kern.GetValueFromAddress(cmd_args[0], 'unsigned long'))))) | |
1063 | elif kern.arch == 'x86_64': | |
1064 | print "{:#x}".format(long(unsigned(kern.GetValueFromAddress(cmd_args[0], 'unsigned long'))) - unsigned(kern.globals.physmap_base)) | |
1065 | ||
1066 | @lldb_command('phystokv') | |
1067 | def PhysToKV(cmd_args=None): | |
1068 | """ Translate a physical address to the corresponding static kernel virtual address. | |
1069 | Assumes the physical address corresponds to managed DRAM. | |
1070 | Syntax: (lldb) phystokv <physical address> | |
1071 | """ | |
1072 | if cmd_args == None or len(cmd_args) < 1: | |
1073 | raise ArgumentError("Too few arguments to phystokv.") | |
1074 | print "{:#x}".format(kern.PhysToKernelVirt(long(unsigned(kern.GetValueFromAddress(cmd_args[0], 'unsigned long'))))) | |
1075 | ||
1076 | def KVToPhysARM(addr): | |
1077 | if kern.arch.startswith('arm64'): | |
1078 | ptov_table = kern.globals.ptov_table | |
1079 | for i in range(0, kern.globals.ptov_index): | |
1080 | if (addr >= long(unsigned(ptov_table[i].va))) and (addr < (long(unsigned(ptov_table[i].va)) + long(unsigned(ptov_table[i].len)))): | |
1081 | return (addr - long(unsigned(ptov_table[i].va)) + long(unsigned(ptov_table[i].pa))) | |
1082 | return (addr - unsigned(kern.globals.gVirtBase) + unsigned(kern.globals.gPhysBase)) | |
1083 | ||
f427ee49 A |
1084 | |
1085 | def GetPtDesc(paddr): | |
1086 | pn = (paddr - unsigned(kern.globals.vm_first_phys)) / kern.globals.page_size | |
d9a64523 A |
1087 | pvh = unsigned(kern.globals.pv_head_table[pn]) |
1088 | if kern.arch.startswith('arm64'): | |
1089 | pvh = pvh | PVH_HIGH_FLAGS_ARM64 | |
1090 | else: | |
1091 | pvh = pvh | PVH_HIGH_FLAGS_ARM32 | |
813fb2f6 | 1092 | pvh_type = pvh & 0x3 |
d9a64523 | 1093 | if pvh_type != 0x3: |
813fb2f6 A |
1094 | raise ValueError("PV head {:#x} does not correspond to a page-table descriptor".format(pvh)) |
1095 | ptd = kern.GetValueFromAddress(pvh & ~0x3, 'pt_desc_t *') | |
f427ee49 A |
1096 | return ptd |
1097 | ||
1098 | def ShowPTEARM(pte, page_size, stage2 = False): | |
1099 | """ Display vital information about an ARM page table entry | |
1100 | pte: kernel virtual address of the PTE. Should be L3 PTE. May also work with L2 TTEs for certain devices. | |
1101 | """ | |
1102 | ptd = GetPtDesc(KVToPhysARM(pte)) | |
813fb2f6 A |
1103 | print "descriptor: {:#x}".format(ptd) |
1104 | print "pmap: {:#x}".format(ptd.pmap) | |
1105 | pt_index = (pte % kern.globals.page_size) / page_size | |
1106 | pte_pgoff = pte % page_size | |
5ba3f43e | 1107 | if kern.arch.startswith('arm64'): |
813fb2f6 A |
1108 | pte_pgoff = pte_pgoff / 8 |
1109 | nttes = page_size / 8 | |
1110 | else: | |
1111 | pte_pgoff = pte_pgoff / 4 | |
1112 | nttes = page_size / 4 | |
cb323159 | 1113 | if ptd.ptd_info[pt_index].refcnt == 0x4000: |
813fb2f6 A |
1114 | level = 2 |
1115 | granule = nttes * page_size | |
1116 | else: | |
1117 | level = 3 | |
1118 | granule = page_size | |
f427ee49 | 1119 | print "maps {}: {:#x}".format("IPA" if stage2 else "VA", long(unsigned(ptd.ptd_info[pt_index].va)) + (pte_pgoff * granule)) |
813fb2f6 A |
1120 | pteval = long(unsigned(dereference(kern.GetValueFromAddress(unsigned(pte), 'pt_entry_t *')))) |
1121 | print "value: {:#x}".format(pteval) | |
5ba3f43e | 1122 | if kern.arch.startswith('arm64'): |
813fb2f6 | 1123 | print "level: {:d}".format(level) |
f427ee49 | 1124 | PmapDecodeTTEARM64(pteval, level, stage2) |
813fb2f6 A |
1125 | elif kern.arch == 'arm': |
1126 | PmapDecodeTTEARM(pteval, 2, vSCRIPT) | |
1127 | ||
1128 | @lldb_command('showpte') | |
1129 | def ShowPTE(cmd_args=None): | |
1130 | """ Display vital information about the page table entry at VA <pte> | |
f427ee49 | 1131 | Syntax: (lldb) showpte <pte_va> [4k|16k|16k_s2] |
813fb2f6 A |
1132 | """ |
1133 | if cmd_args == None or len(cmd_args) < 1: | |
1134 | raise ArgumentError("Too few arguments to showpte.") | |
f427ee49 A |
1135 | |
1136 | if kern.arch == 'arm': | |
1137 | ShowPTEARM(kern.GetValueFromAddress(cmd_args[0], 'unsigned long'), kern.globals.page_size) | |
1138 | elif kern.arch.startswith('arm64'): | |
1139 | pmap_pt_attr = kern.globals.native_pt_attr if len(cmd_args) < 2 else GetMemoryAttributesFromUser(cmd_args[1]) | |
1140 | if pmap_pt_attr is None: | |
1141 | raise ArgumentError("Invalid translation attribute type.") | |
1142 | ||
1143 | stage2 = bool(pmap_pt_attr.stage2 if hasattr(pmap_pt_attr, 'stage2') else False) | |
1144 | ShowPTEARM(kern.GetValueFromAddress(cmd_args[0], 'unsigned long'), pmap_pt_attr.pta_page_size, stage2) | |
1145 | else: | |
813fb2f6 | 1146 | raise NotImplementedError("showpte does not support {0}".format(kern.arch)) |
813fb2f6 | 1147 | |
f427ee49 | 1148 | def FindMappingAtLevelARM(pmap, tt, nttes, level, va, action): |
813fb2f6 A |
1149 | """ Perform the specified action for all valid mappings in an ARM translation table |
1150 | pmap: owner of the translation table | |
1151 | tt: translation table or page table | |
1152 | nttes: number of entries in tt | |
1153 | level: translation table level, 1 or 2 | |
1154 | action: callback for each valid TTE | |
1155 | """ | |
1156 | for i in range(nttes): | |
1157 | try: | |
1158 | tte = tt[i] | |
f427ee49 | 1159 | va_size = None |
813fb2f6 A |
1160 | if level == 1: |
1161 | if tte & 0x3 == 0x1: | |
1162 | type = 'table' | |
1163 | granule = 1024 | |
f427ee49 | 1164 | va_size = kern.globals.page_size * 256 |
813fb2f6 A |
1165 | paddr = tte & 0xFFFFFC00 |
1166 | elif tte & 0x3 == 0x2: | |
1167 | type = 'block' | |
1168 | if (tte & 0x40000) == 0x40000: | |
1169 | granule = 1 << 24 | |
1170 | paddr = tte & 0xFF000000 | |
1171 | else: | |
1172 | granule = 1 << 20 | |
1173 | paddr = tte & 0xFFF00000 | |
1174 | else: | |
1175 | continue | |
1176 | elif (tte & 0x3) == 0x1: | |
1177 | type = 'entry' | |
1178 | granule = 1 << 16 | |
1179 | paddr = tte & 0xFFFF0000 | |
1180 | elif (tte & 0x3) != 0: | |
1181 | type = 'entry' | |
1182 | granule = 1 << 12 | |
1183 | paddr = tte & 0xFFFFF000 | |
1184 | else: | |
1185 | continue | |
f427ee49 A |
1186 | if va_size is None: |
1187 | va_size = granule | |
1188 | mapped_va = va + (va_size * i) | |
1189 | if action(pmap, level, type, addressof(tt[i]), paddr, mapped_va, granule): | |
1190 | if level == 1 and (tte & 0x3) == 0x1: | |
1191 | tt_next = kern.GetValueFromAddress(kern.PhysToKernelVirt(paddr), 'tt_entry_t *') | |
1192 | FindMappingAtLevelARM(pmap, tt_next, granule / 4, level + 1, mapped_va, action) | |
813fb2f6 A |
1193 | except Exception as exc: |
1194 | print "Unable to access tte {:#x}".format(unsigned(addressof(tt[i]))) | |
1195 | ||
f427ee49 | 1196 | def FindMappingAtLevelARM64(pmap, tt, nttes, level, va, action): |
813fb2f6 A |
1197 | """ Perform the specified action for all valid mappings in an ARM64 translation table |
1198 | pmap: owner of the translation table | |
1199 | tt: translation table or page table | |
1200 | nttes: number of entries in tt | |
1201 | level: translation table level, 1 2 or 3 | |
1202 | action: callback for each valid TTE | |
1203 | """ | |
f427ee49 A |
1204 | # Obtain pmap attributes |
1205 | pmap_pt_attr = pmap.pmap_pt_attr if hasattr(pmap, 'pmap_pt_attr') else kern.globals.native_pt_attr | |
1206 | page_size = pmap_pt_attr.pta_page_size | |
813fb2f6 A |
1207 | page_offset_mask = (page_size - 1) |
1208 | page_base_mask = ((1 << ARM64_VMADDR_BITS) - 1) & (~page_offset_mask) | |
f427ee49 A |
1209 | max_level = unsigned(pmap_pt_attr.pta_max_level) |
1210 | ||
813fb2f6 A |
1211 | for i in range(nttes): |
1212 | try: | |
1213 | tte = tt[i] | |
f427ee49 A |
1214 | if tte & 0x1 == 0x0: |
1215 | continue | |
1216 | ||
1217 | tt_next = None | |
1218 | paddr = unsigned(tte) & unsigned(page_base_mask) | |
1219 | ||
1220 | # Handle leaf entry | |
1221 | if tte & 0x2 == 0x0 or level == max_level: | |
1222 | type = 'block' if level < max_level else 'entry' | |
1223 | granule = PmapBlockOffsetMaskARM64(page_size, level) + 1 | |
1224 | else: | |
1225 | # Handle page table entry | |
1226 | type = 'table' | |
1227 | granule = page_size | |
1228 | tt_next = kern.GetValueFromAddress(kern.PhysToKernelVirt(paddr), 'tt_entry_t *') | |
1229 | ||
1230 | mapped_va = long(unsigned(va)) + ((PmapBlockOffsetMaskARM64(page_size, level) + 1) * i) | |
1231 | if action(pmap, level, type, addressof(tt[i]), paddr, mapped_va, granule): | |
1232 | if tt_next is not None: | |
1233 | FindMappingAtLevelARM64(pmap, tt_next, granule / ARM64_TTE_SIZE, level + 1, mapped_va, action) | |
1234 | ||
813fb2f6 A |
1235 | except Exception as exc: |
1236 | print "Unable to access tte {:#x}".format(unsigned(addressof(tt[i]))) | |
1237 | ||
1238 | def ScanPageTables(action, targetPmap=None): | |
1239 | """ Perform the specified action for all valid mappings in all page tables, | |
1240 | optionally restricted to a single pmap. | |
1241 | pmap: pmap whose page table should be scanned. If None, all pmaps on system will be scanned. | |
1242 | """ | |
1243 | print "Scanning all available translation tables. This may take a long time..." | |
1244 | def ScanPmap(pmap, action): | |
5ba3f43e | 1245 | if kern.arch.startswith('arm64'): |
f427ee49 A |
1246 | # Obtain pmap attributes |
1247 | pmap_pt_attr = pmap.pmap_pt_attr if hasattr(pmap, 'pmap_pt_attr') else kern.globals.native_pt_attr | |
1248 | granule = pmap_pt_attr.pta_page_size | |
1249 | level = unsigned(pmap_pt_attr.pta_root_level) | |
1250 | root_pgtable_num_ttes = (unsigned(pmap_pt_attr.pta_level_info[level].index_mask) >> \ | |
1251 | unsigned(pmap_pt_attr.pta_level_info[level].shift)) + 1 | |
813fb2f6 A |
1252 | elif kern.arch == 'arm': |
1253 | granule = pmap.tte_index_max * 4 | |
f427ee49 A |
1254 | |
1255 | if action(pmap, pmap_pt_attr.pta_root_level, 'root', pmap.tte, unsigned(pmap.ttep), pmap.min, granule): | |
1256 | if kern.arch.startswith('arm64'): | |
1257 | FindMappingAtLevelARM64(pmap, pmap.tte, root_pgtable_num_ttes, level, pmap.min, action) | |
1258 | elif kern.arch == 'arm': | |
1259 | FindMappingAtLevelARM(pmap, pmap.tte, pmap.tte_index_max, 1, pmap.min, action) | |
813fb2f6 A |
1260 | |
1261 | if targetPmap is not None: | |
1262 | ScanPmap(kern.GetValueFromAddress(targetPmap, 'pmap_t'), action) | |
1263 | else: | |
1264 | for pmap in IterateQueue(kern.globals.map_pmap_list, 'pmap_t', 'pmaps'): | |
1265 | ScanPmap(pmap, action) | |
1266 | ||
1267 | @lldb_command('showallmappings') | |
1268 | def ShowAllMappings(cmd_args=None): | |
1269 | """ Find and display all available mappings on the system for | |
1270 | <physical_address>. Optionally only searches the pmap | |
1271 | specified by [<pmap>] | |
1272 | Syntax: (lldb) showallmappings <physical_address> [<pmap>] | |
1273 | WARNING: this macro can take a long time (up to 30min.) to complete! | |
1274 | """ | |
1275 | if cmd_args == None or len(cmd_args) < 1: | |
1276 | raise ArgumentError("Too few arguments to showallmappings.") | |
5ba3f43e | 1277 | if not kern.arch.startswith('arm'): |
813fb2f6 A |
1278 | raise NotImplementedError("showallmappings does not support {0}".format(kern.arch)) |
1279 | pa = kern.GetValueFromAddress(cmd_args[0], 'unsigned long') | |
1280 | targetPmap = None | |
1281 | if len(cmd_args) > 1: | |
1282 | targetPmap = cmd_args[1] | |
f427ee49 | 1283 | def printMatchedMapping(pmap, level, type, tte, paddr, va, granule): |
813fb2f6 | 1284 | if paddr <= pa < (paddr + granule): |
f427ee49 A |
1285 | print "pmap: {:#x}: L{:d} {:s} at {:#x}: [{:#x}, {:#x}), maps va {:#x}".format(pmap, level, type, unsigned(tte), paddr, paddr + granule, va) |
1286 | return True | |
813fb2f6 A |
1287 | ScanPageTables(printMatchedMapping, targetPmap) |
1288 | ||
f427ee49 A |
1289 | @lldb_command('showptusage') |
1290 | def ShowPTUsage(cmd_args=None): | |
1291 | """ Display a summary of pagetable allocations for a given pmap. | |
1292 | Syntax: (lldb) showptusage [<pmap>] | |
1293 | WARNING: this macro can take a long time (> 1hr) to complete! | |
1294 | """ | |
1295 | if not kern.arch.startswith('arm'): | |
1296 | raise NotImplementedError("showptusage does not support {0}".format(kern.arch)) | |
1297 | targetPmap = None | |
1298 | if len(cmd_args) > 0: | |
1299 | targetPmap = cmd_args[0] | |
1300 | lastPmap = [None] | |
1301 | numTables = [0] | |
1302 | numUnnested = [0] | |
1303 | numPmaps = [0] | |
1304 | def printValidTTE(pmap, level, type, tte, paddr, va, granule): | |
1305 | unnested = "" | |
1306 | nested_region_addr = long(unsigned(pmap.nested_region_addr)) | |
1307 | nested_region_end = nested_region_addr + long(unsigned(pmap.nested_region_size)) | |
1308 | if lastPmap[0] is None or (pmap != lastPmap[0]): | |
1309 | lastPmap[0] = pmap | |
1310 | numPmaps[0] = numPmaps[0] + 1 | |
1311 | print ("pmap {:#x}:".format(pmap)) | |
1312 | if type == 'root': | |
1313 | return True | |
1314 | if (level == 2) and (va >= nested_region_addr) and (va < nested_region_end): | |
1315 | ptd = GetPtDesc(paddr) | |
1316 | if ptd.pmap != pmap: | |
1317 | return False | |
1318 | else: | |
1319 | numUnnested[0] = numUnnested[0] + 1 | |
1320 | unnested = " (likely unnested)" | |
1321 | numTables[0] = numTables[0] + 1 | |
1322 | print (" " * 4 * int(level)) + "L{:d} entry at {:#x}, maps {:#x}".format(level, unsigned(tte), va) + unnested | |
1323 | if level == 2: | |
1324 | return False | |
1325 | else: | |
1326 | return True | |
1327 | ScanPageTables(printValidTTE, targetPmap) | |
1328 | print("{:d} table(s), {:d} of them likely unnested, in {:d} pmap(s)".format(numTables[0], numUnnested[0], numPmaps[0])) | |
1329 | ||
1330 | def checkPVList(pmap, level, type, tte, paddr, va, granule): | |
d9a64523 | 1331 | """ Checks an ARM physical-to-virtual mapping list for consistency errors. |
813fb2f6 A |
1332 | pmap: owner of the translation table |
1333 | level: translation table level. PV lists will only be checked for L2 (arm32) or L3 (arm64) tables. | |
1334 | type: unused | |
1335 | tte: KVA of PTE to check for presence in PV list. If None, presence check will be skipped. | |
1336 | paddr: physical address whose PV list should be checked. Need not be page-aligned. | |
1337 | granule: unused | |
1338 | """ | |
1339 | vm_first_phys = unsigned(kern.globals.vm_first_phys) | |
1340 | vm_last_phys = unsigned(kern.globals.vm_last_phys) | |
d9a64523 | 1341 | page_size = kern.globals.page_size |
5ba3f43e | 1342 | if kern.arch.startswith('arm64'): |
813fb2f6 A |
1343 | page_offset_mask = (page_size - 1) |
1344 | page_base_mask = ((1 << ARM64_VMADDR_BITS) - 1) & (~page_offset_mask) | |
1345 | paddr = paddr & page_base_mask | |
1346 | max_level = 3 | |
d9a64523 | 1347 | pvh_set_bits = PVH_HIGH_FLAGS_ARM64 |
813fb2f6 A |
1348 | elif kern.arch == 'arm': |
1349 | page_base_mask = 0xFFFFF000 | |
1350 | paddr = paddr & page_base_mask | |
1351 | max_level = 2 | |
d9a64523 | 1352 | pvh_set_bits = PVH_HIGH_FLAGS_ARM32 |
813fb2f6 | 1353 | if level < max_level or paddr < vm_first_phys or paddr >= vm_last_phys: |
f427ee49 | 1354 | return True |
813fb2f6 | 1355 | pn = (paddr - vm_first_phys) / page_size |
d9a64523 | 1356 | pvh = unsigned(kern.globals.pv_head_table[pn]) | pvh_set_bits |
813fb2f6 A |
1357 | pvh_type = pvh & 0x3 |
1358 | if pmap is not None: | |
1359 | pmap_str = "pmap: {:#x}: ".format(pmap) | |
1360 | else: | |
1361 | pmap_str = '' | |
1362 | if tte is not None: | |
1363 | tte_str = "pte {:#x} ({:#x}): ".format(unsigned(tte), paddr) | |
1364 | else: | |
1365 | tte_str = "paddr {:#x}: ".format(paddr) | |
1366 | if pvh_type == 0 or pvh_type == 3: | |
1367 | print "{:s}{:s}unexpected PVH type {:d}".format(pmap_str, tte_str, pvh_type) | |
1368 | elif pvh_type == 2: | |
1369 | ptep = pvh & ~0x3 | |
1370 | if tte is not None and ptep != unsigned(tte): | |
1371 | print "{:s}{:s}PVH mismatch ({:#x})".format(pmap_str, tte_str, ptep) | |
1372 | try: | |
1373 | pte = long(unsigned(dereference(kern.GetValueFromAddress(ptep, 'pt_entry_t *')))) & page_base_mask | |
1374 | if (pte != paddr): | |
1375 | print "{:s}{:s}PVH {:#x} maps wrong page ({:#x}) ".format(pmap_str, tte_str, ptep, pte) | |
1376 | except Exception as exc: | |
1377 | print "{:s}{:s}Unable to read PVH {:#x}".format(pmap_str, tte_str, ptep) | |
1378 | elif pvh_type == 1: | |
1379 | pvep = pvh & ~0x3 | |
1380 | tte_match = False | |
1381 | while pvep != 0: | |
1382 | pve = kern.GetValueFromAddress(pvep, "pv_entry_t *") | |
1383 | pvep = unsigned(pve.pve_next) & ~0x1 | |
1384 | ptep = unsigned(pve.pve_ptep) & ~0x3 | |
1385 | if tte is not None and ptep == unsigned(tte): | |
1386 | tte_match = True | |
1387 | try: | |
1388 | pte = long(unsigned(dereference(kern.GetValueFromAddress(ptep, 'pt_entry_t *')))) & page_base_mask | |
1389 | if (pte != paddr): | |
1390 | print "{:s}{:s}PVE {:#x} maps wrong page ({:#x}) ".format(pmap_str, tte_str, ptep, pte) | |
1391 | except Exception as exc: | |
1392 | print "{:s}{:s}Unable to read PVE {:#x}".format(pmap_str, tte_str, ptep) | |
1393 | if tte is not None and not tte_match: | |
1394 | print "{:s}{:s}not found in PV list".format(pmap_str, tte_str, paddr) | |
f427ee49 | 1395 | return True |
813fb2f6 A |
1396 | |
1397 | @lldb_command('pv_check', 'P') | |
1398 | def PVCheck(cmd_args=None, cmd_options={}): | |
1399 | """ Check the physical-to-virtual mapping for a given PTE or physical address | |
1400 | Syntax: (lldb) pv_check <addr> [-p] | |
1401 | -P : Interpret <addr> as a physical address rather than a PTE | |
1402 | """ | |
1403 | if cmd_args == None or len(cmd_args) < 1: | |
d9a64523 | 1404 | raise ArgumentError("Too few arguments to pv_check.") |
813fb2f6 A |
1405 | if kern.arch == 'arm': |
1406 | level = 2 | |
5ba3f43e | 1407 | elif kern.arch.startswith('arm64'): |
813fb2f6 A |
1408 | level = 3 |
1409 | else: | |
f427ee49 | 1410 | raise NotImplementedError("pv_check does not support {0}".format(kern.arch)) |
813fb2f6 A |
1411 | if "-P" in cmd_options: |
1412 | pte = None | |
1413 | pa = long(unsigned(kern.GetValueFromAddress(cmd_args[0], "unsigned long"))) | |
1414 | else: | |
1415 | pte = kern.GetValueFromAddress(cmd_args[0], 'pt_entry_t *') | |
1416 | pa = long(unsigned(dereference(pte))) | |
f427ee49 | 1417 | checkPVList(None, level, None, pte, pa, 0, None) |
813fb2f6 A |
1418 | |
1419 | @lldb_command('check_pmaps') | |
1420 | def CheckPmapIntegrity(cmd_args=None): | |
1421 | """ Performs a system-wide integrity check of all PTEs and associated PV lists. | |
1422 | Optionally only checks the pmap specified by [<pmap>] | |
1423 | Syntax: (lldb) check_pmaps [<pmap>] | |
1424 | WARNING: this macro can take a HUGE amount of time (several hours) if you do not | |
1425 | specify [pmap] to limit it to a single pmap. It will also give false positives | |
1426 | for kernel_pmap, as we do not create PV entries for static kernel mappings on ARM. | |
1427 | Use of this macro without the [<pmap>] argument is heavily discouraged. | |
1428 | """ | |
5ba3f43e | 1429 | if not kern.arch.startswith('arm'): |
f427ee49 | 1430 | raise NotImplementedError("check_pmaps does not support {0}".format(kern.arch)) |
813fb2f6 A |
1431 | targetPmap = None |
1432 | if len(cmd_args) > 0: | |
1433 | targetPmap = cmd_args[0] | |
1434 | ScanPageTables(checkPVList, targetPmap) | |
1435 | ||
1436 | @lldb_command('pmapsforledger') | |
1437 | def PmapsForLedger(cmd_args=None): | |
1438 | """ Find and display all pmaps currently using <ledger>. | |
1439 | Syntax: (lldb) pmapsforledger <ledger> | |
1440 | """ | |
1441 | if cmd_args == None or len(cmd_args) < 1: | |
1442 | raise ArgumentError("Too few arguments to pmapsforledger.") | |
5ba3f43e | 1443 | if not kern.arch.startswith('arm'): |
813fb2f6 A |
1444 | raise NotImplementedError("pmapsforledger does not support {0}".format(kern.arch)) |
1445 | ledger = kern.GetValueFromAddress(cmd_args[0], 'ledger_t') | |
1446 | for pmap in IterateQueue(kern.globals.map_pmap_list, 'pmap_t', 'pmaps'): | |
1447 | if pmap.ledger == ledger: | |
1448 | print "pmap: {:#x}".format(pmap) | |
f427ee49 | 1449 |