xref: /xnu-8019.80.24/tools/lldbmacros/pmap.py (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
1from xnu import *
2import xnudefines
3from kdp import *
4from utils import *
5import struct
6
7def ReadPhysInt(phys_addr, bitsize = 64, cpuval = None):
8    """ Read a physical memory data based on address.
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')
33def ReadPhys(cmd_args = None):
34    """ Reads the specified untranslated address
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
39        address: 1234 or 0x1234
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
50lldb_alias('readphys8', 'readphys 8 ')
51lldb_alias('readphys16', 'readphys 16 ')
52lldb_alias('readphys32', 'readphys 32 ')
53lldb_alias('readphys64', 'readphys 64 ')
54
55def 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:
61            int: read value from memory.
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
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)
74
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)
78
79        if ret_obj.Succeeded():
80            value = ret_obj.GetOutput()
81
82            if bits == 64 :
83                pack_fmt = "<Q"
84                unpack_fmt = ">Q"
85            if bits == 32 :
86                pack_fmt = "<I"
87                unpack_fmt = ">I"
88            if bits == 16 :
89                pack_fmt = "<H"
90                unpack_fmt = ">H"
91            if bits == 8 :
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
132    return retval
133
134
135def 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
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
178
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
185
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
216
217
218def WritePhysInt(phys_addr, int_val, bitsize = 64):
219    """ Write and integer value in a physical memory data based on address.
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')
237def WritePhys(cmd_args=None):
238    """ writes to the specified untranslated address
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
245        ex. (lldb)writephys 16 0x12345abcd 0x25
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
256lldb_alias('writephys8', 'writephys 8 ')
257lldb_alias('writephys16', 'writephys 16 ')
258lldb_alias('writephys32', 'writephys 32 ')
259lldb_alias('writephys64', 'writephys 64 ')
260
261
262def _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
308            if entry & (0x1 << 62):
309                out_string += " compressed"
310            #Stop decoding other bits
311            entry = 0
312        if entry & (0x1 << 1):
313            out_string += " writable"
314        else:
315            out_string += " read-only"
316
317        if entry & (0x1 << 2):
318            out_string += " user"
319        else:
320            out_string += " supervisor"
321
322        if entry & (0x1 << 3):
323            out_string += " PWT"
324
325        if entry & (0x1 << 4):
326            out_string += " PCD"
327
328        if entry & (0x1 << 5):
329            out_string += " accessed"
330
331        if entry & (0x1 << 6):
332            out_string += " dirty"
333
334        if entry & (0x1 << 7):
335            out_string += " large"
336            pt_large = True
337        else:
338            pt_large = False
339
340        if entry & (0x1 << 8):
341            out_string += " global"
342
343        if entry & (0x3 << 9):
344            out_string += " avail:{0:x}".format((entry >> 9) & 0x3)
345
346        if entry & (0x1 << 63):
347            out_string += " noexec"
348    print out_string
349    return (pt_paddr, pt_valid, pt_large)
350
351def _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"
429
430        if (entry & 0x100) == 0x100:
431            out_string += " accessed"
432
433        if (entry & 0x200) == 0x200:
434            out_string += " dirty"
435
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
444def _PmapL4Walk(pmap_addr_val,vaddr, ept_pmap, verbose_level = vSCRIPT):
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    """
449    pt_paddr = unsigned(pmap_addr_val)
450    pt_valid = (unsigned(pmap_addr_val) != 0)
451    pt_large = 0
452    pframe_offset = 0
453    if pt_valid:
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)
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)
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)
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)
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)
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)
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)
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)
493    paddr = 0
494    paddr_isvalid = False
495    if pt_valid:
496        paddr = pt_paddr + pframe_offset
497        paddr_isvalid = True
498
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
506    return paddr
507
508def 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    """
515    out_string = ""
516    if level == 1 and (tte & 0x3) == 0x2:
517        if verbose_level < vSCRIPT:
518            return
519
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
526        if (tte & 0x10) :
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]
538
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"
569
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
637    print out_string
638
639
640def _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
653    return paddr
654
655
656
657def _PmapWalkARMLevel2(tte, vaddr, verbose_level = vSCRIPT):
658    """ Pmap walk the level 2 tte.
659        params:
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]
668
669    paddr = 0
670    if pte & 0x2:
671        paddr = (unsigned(pte) & 0xFFFFF000) | (vaddr & 0xFFF)
672
673    if verbose_level >= vSCRIPT:
674        print "{0: <#020x}\n\t{1: <#020x}\n\t".format(addressof(tte), tte),
675
676    PmapDecodeTTEARM(tte, 1, verbose_level)
677    if verbose_level >= vSCRIPT:
678        print "second-level table (index {:d}):".format(pte_index)
679    if verbose_level >= vDETAIL:
680        for i in range(256):
681            tmp = pte_base_val[i]
682            print "{0: <#020x}:\t{1: <#020x}".format(addressof(tmp), unsigned(tmp))
683
684    if verbose_level >= vSCRIPT:
685        print " {0: <#020x}\n\t{1: <#020x}\n\t".format(addressof(pte), unsigned(pte)),
686
687    PmapDecodeTTEARM(pte, 2, verbose_level)
688
689    return paddr
690    #end of level 2 walking of arm
691
692
693def 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
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
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:
709        for i in range(0, pmap.tte_index_max):
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
730def PmapWalkX86_64(pmapval, vaddr, verbose_level = vSCRIPT):
731    """
732        params: pmapval - core.value representing pmap_t in kernel
733        vaddr:  int     - int representing virtual address to walk
734    """
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'])
743
744def assert_64bit(val):
745    assert(val < 2**64)
746
747ARM64_TTE_SIZE = 8
748ARM64_TTE_SHIFT = 3
749ARM64_VMADDR_BITS = 48
750
751def PmapBlockOffsetMaskARM64(page_size, level):
752    assert level >= 0 and level <= 3
753    ttentries = (page_size / ARM64_TTE_SIZE)
754    return page_size * (ttentries ** (3 - level)) - 1
755
756def PmapBlockBaseMaskARM64(page_size, level):
757    assert level >= 0 and level <= 3
758    return ((1 << ARM64_VMADDR_BITS) - 1) & ~PmapBlockOffsetMaskARM64(page_size, level)
759
760def PmapDecodeTTEARM64(tte, level, stage2 = False, is_iommu_tte = False):
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        is_iommu_tte: True if the TTE is from an IOMMU's page table, False otherwise.
766    """
767    assert(type(level) == int)
768    assert_64bit(tte)
769
770    if tte & 0x1 == 0x0:
771        print("Invalid.")
772        return
773
774    if (tte & 0x2 == 0x2) and (level != 0x3):
775        print "Type       = Table pointer."
776        print "Table addr = {:#x}.".format(tte & 0xfffffffff000)
777
778        if not stage2:
779            print "PXN        = {:#x}.".format((tte >> 59) & 0x1)
780            print "XN         = {:#x}.".format((tte >> 60) & 0x1)
781            print "AP         = {:#x}.".format((tte >> 61) & 0x3)
782            print "NS         = {:#x}.".format(tte >> 63)
783    else:
784        print "Type       = Block."
785
786        if stage2:
787            print "S2 MemAttr = {:#x}.".format((tte >> 2) & 0xf)
788        else:
789            attr_index = (tte >> 2) & 0x7
790            attr_string = { 0: 'WRITEBACK', 1: 'WRITECOMB', 2: 'WRITETHRU',
791                3: 'CACHE DISABLE', 4: 'INNERWRITEBACK', 5: 'POSTED',
792                6: 'POSTED_REORDERED', 7: 'POSTED_COMBINED_REORDERED' }
793
794            # Only show the string version of the AttrIdx for CPU mappings since
795            # these values don't apply to IOMMU mappings.
796            if is_iommu_tte:
797                print "AttrIdx    = {:#x}.".format(attr_index)
798            else:
799                print "AttrIdx    = {:#x} ({:s}).".format(attr_index, attr_string[attr_index])
800            print "NS         = {:#x}.".format((tte >> 5) & 0x1)
801
802        if stage2:
803            print "S2AP       = {:#x}.".format((tte >> 6) & 0x3)
804        else:
805            print "AP         = {:#x}.".format((tte >> 6) & 0x3)
806
807        print "SH         = {:#x}.".format((tte >> 8) & 0x3)
808        print "AF         = {:#x}.".format((tte >> 10) & 0x1)
809
810        if not stage2:
811            print "nG         = {:#x}.".format((tte >> 11) & 0x1)
812
813        print "HINT       = {:#x}.".format((tte >> 52) & 0x1)
814
815        if stage2:
816            print "S2XN       = {:#x}.".format((tte >> 53) & 0x3)
817        else:
818            print "PXN        = {:#x}.".format((tte >> 53) & 0x1)
819            print "XN         = {:#x}.".format((tte >> 54) & 0x1)
820
821        print "SW Use     = {:#x}.".format((tte >> 55) & 0xf)
822
823    return
824
825def PmapTTnIndexARM64(vaddr, pmap_pt_attr):
826    pta_max_level = unsigned(pmap_pt_attr.pta_max_level)
827
828    tt_index = []
829    for i in range(pta_max_level + 1):
830        tt_index.append((vaddr & unsigned(pmap_pt_attr.pta_level_info[i].index_mask)) \
831            >> unsigned(pmap_pt_attr.pta_level_info[i].shift))
832
833    return tt_index
834
835def PmapWalkARM64(pmap_pt_attr, root_tte, vaddr, verbose_level = vHUMAN):
836    assert(type(vaddr) in (long, int))
837    assert_64bit(vaddr)
838    assert_64bit(root_tte)
839
840    # Obtain pmap attributes
841    page_size = pmap_pt_attr.pta_page_size
842    page_offset_mask = (page_size - 1)
843    page_base_mask = ((1 << ARM64_VMADDR_BITS) - 1) & (~page_offset_mask)
844    tt_index = PmapTTnIndexARM64(vaddr, pmap_pt_attr)
845    stage2 = bool(pmap_pt_attr.stage2 if hasattr(pmap_pt_attr, 'stage2') else False)
846
847    # The pmap starts at a page table level that is defined by register
848    # values; the root level can be obtained from the attributes structure
849    level = unsigned(pmap_pt_attr.pta_root_level)
850
851    root_tt_index = tt_index[level]
852    root_pgtable_num_ttes = (unsigned(pmap_pt_attr.pta_level_info[level].index_mask) >> \
853        unsigned(pmap_pt_attr.pta_level_info[level].shift)) + 1
854    tte = long(unsigned(root_tte[root_tt_index]))
855
856    # Walk the page tables
857    paddr = -1
858    max_level = unsigned(pmap_pt_attr.pta_max_level)
859    is_valid = True
860    is_leaf = False
861
862    while (level <= max_level):
863        if verbose_level >= vSCRIPT:
864            print "L{} entry: {:#x}".format(level, tte)
865        if verbose_level >= vDETAIL:
866            PmapDecodeTTEARM64(tte, level, stage2)
867
868        if tte & 0x1 == 0x0:
869            if verbose_level >= vHUMAN:
870                print "L{} entry invalid: {:#x}\n".format(level, tte)
871
872            is_valid = False
873            break
874
875        # Handle leaf entry
876        if tte & 0x2 == 0x0 or level == max_level:
877            base_mask = page_base_mask if level == max_level else PmapBlockBaseMaskARM64(page_size, level)
878            offset_mask = page_offset_mask if level == max_level else PmapBlockOffsetMaskARM64(page_size, level)
879            paddr = tte & base_mask
880            paddr = paddr | (vaddr & offset_mask)
881
882            if level != max_level:
883                print "phys: {:#x}".format(paddr)
884
885            is_leaf = True
886            break
887        else:
888        # Handle page table entry
889            next_phys = (tte & page_base_mask) + (ARM64_TTE_SIZE * tt_index[level + 1])
890            assert(type(next_phys) == long)
891
892            next_virt = kern.PhysToKernelVirt(next_phys)
893            assert(type(next_virt) == long)
894
895            if verbose_level >= vDETAIL:
896                print "L{} physical address: {:#x}. L{} virtual address: {:#x}".format(level + 1, next_phys, level + 1, next_virt)
897
898            ttep = kern.GetValueFromAddress(next_virt, "tt_entry_t*")
899            tte = long(unsigned(dereference(ttep)))
900            assert(type(tte) == long)
901
902        # We've parsed one level, so go to the next level
903        assert(level <= 3)
904        level = level + 1
905
906
907    if verbose_level >= vHUMAN:
908        if paddr:
909            print "Translation of {:#x} is {:#x}.".format(vaddr, paddr)
910        else:
911            print "(no translation)"
912
913    return paddr
914
915def PmapWalk(pmap, vaddr, verbose_level = vHUMAN):
916    if kern.arch == 'x86_64':
917        return PmapWalkX86_64(pmap, vaddr, verbose_level)
918    elif kern.arch == 'arm':
919        return PmapWalkARM(pmap, vaddr, verbose_level)
920    elif kern.arch.startswith('arm64'):
921        # Obtain pmap attributes from pmap structure
922        pmap_pt_attr = pmap.pmap_pt_attr if hasattr(pmap, 'pmap_pt_attr') else kern.globals.native_pt_attr
923        return PmapWalkARM64(pmap_pt_attr, pmap.tte, vaddr, verbose_level)
924    else:
925        raise NotImplementedError("PmapWalk does not support {0}".format(kern.arch))
926
927@lldb_command('pmap_walk')
928def PmapWalkHelper(cmd_args=None):
929    """ Perform a page-table walk in <pmap> for <virtual_address>.
930        Syntax: (lldb) pmap_walk <pmap> <virtual_address> [-v] [-e]
931            Multiple -v's can be specified for increased verbosity
932    """
933    if cmd_args == None or len(cmd_args) < 2:
934        raise ArgumentError("Too few arguments to pmap_walk.")
935
936    pmap = kern.GetValueAsType(cmd_args[0], 'pmap_t')
937    addr = ArgumentStringToInt(cmd_args[1])
938    PmapWalk(pmap, addr, config['verbosity'])
939    return
940
941def GetMemoryAttributesFromUser(requested_type):
942    pmap_attr_dict = {
943        '4k' : kern.globals.pmap_pt_attr_4k,
944        '16k' : kern.globals.pmap_pt_attr_16k,
945        '16k_s2' : kern.globals.pmap_pt_attr_16k_stage2 if hasattr(kern.globals, 'pmap_pt_attr_16k_stage2') else None,
946    }
947
948    requested_type = requested_type.lower()
949    if requested_type not in pmap_attr_dict:
950        return None
951
952    return pmap_attr_dict[requested_type]
953
954@lldb_command('ttep_walk')
955def TTEPWalkPHelper(cmd_args=None):
956    """ Perform a page-table walk in <root_ttep> for <virtual_address>.
957        Syntax: (lldb) ttep_walk <root_ttep> <virtual_address> [4k|16k|16k_s2] [-v] [-e]
958        Multiple -v's can be specified for increased verbosity
959        """
960    if cmd_args == None or len(cmd_args) < 2:
961        raise ArgumentError("Too few arguments to ttep_walk.")
962
963    if not kern.arch.startswith('arm64'):
964        raise NotImplementedError("ttep_walk does not support {0}".format(kern.arch))
965
966    tte = kern.GetValueFromAddress(kern.PhysToKernelVirt(ArgumentStringToInt(cmd_args[0])), 'unsigned long *')
967    addr = ArgumentStringToInt(cmd_args[1])
968
969    pmap_pt_attr = kern.globals.native_pt_attr if len(cmd_args) < 3 else GetMemoryAttributesFromUser(cmd_args[2])
970    if pmap_pt_attr is None:
971        raise ArgumentError("Invalid translation attribute type.")
972
973    return PmapWalkARM64(pmap_pt_attr, tte, addr, config['verbosity'])
974
975@lldb_command('decode_tte')
976def DecodeTTE(cmd_args=None):
977    """ Decode the bits in the TTE/PTE value specified <tte_val> for translation level <level> and stage [s1|s2]
978        Syntax: (lldb) decode_tte <tte_val> <level> [s1|s2]
979    """
980    if cmd_args == None or len(cmd_args) < 2:
981        raise ArgumentError("Too few arguments to decode_tte.")
982    if len(cmd_args) > 2 and cmd_args[2] not in ["s1", "s2"]:
983        raise ArgumentError("{} is not a valid stage of translation.".format(cmd_args[2]))
984    if kern.arch == 'arm':
985        PmapDecodeTTEARM(kern.GetValueFromAddress(cmd_args[0], "unsigned long"), ArgumentStringToInt(cmd_args[1]), vSCRIPT)
986    elif kern.arch.startswith('arm64'):
987        stage2 = True if len(cmd_args) > 2 and cmd_args[2] == "s2" else False
988        PmapDecodeTTEARM64(ArgumentStringToInt(cmd_args[0]), ArgumentStringToInt(cmd_args[1]), stage2)
989    else:
990        raise NotImplementedError("decode_tte does not support {0}".format(kern.arch))
991
992
993PVH_HIGH_FLAGS_ARM64 = (1 << 62) | (1 << 61) | (1 << 60) | (1 << 59) | (1 << 58) | (1 << 57) | (1 << 56)
994PVH_HIGH_FLAGS_ARM32 = (1 << 31)
995
996def PVDumpPTE(pvep, ptep, verbose_level = vHUMAN):
997    """ Dump information about a single mapping retrieved by the pv_head_table.
998
999        pvep: Either a pointer to the PVE object if the PVH entry is PVH_TYPE_PVEP,
1000              or None if type PVH_TYPE_PTEP.
1001        ptep: For type PVH_TYPE_PTEP this should just be the raw PVH entry with
1002              the high flags already set (the type bits don't need to be cleared).
1003              For type PVH_TYPE_PVEP this will be the value retrieved from the
1004              pve_ptep[] array.
1005    """
1006    if kern.arch.startswith('arm64'):
1007        iommu_flag = 0x4
1008        iommu_table_flag = 1 << 63
1009    else:
1010        iommu_flag = 0
1011        iommu_table_flag = 0
1012
1013    # AltAcct status is only stored in the ptep for PVH_TYPE_PVEP entries.
1014    if pvep is not None and (ptep & 0x1):
1015        # Note: It's not possible for IOMMU mappings to be marked as alt acct so
1016        # setting this string is mutually exclusive with setting the IOMMU strings.
1017        pte_str = ' (alt acct)'
1018    else:
1019        pte_str = ''
1020
1021    if pvep is not None:
1022        pve_str = 'PVEP {:#x}, '.format(pvep)
1023    else:
1024        pve_str = ''
1025
1026    # For PVH_TYPE_PTEP, this clears out the type bits. For PVH_TYPE_PVEP, this
1027    # either does nothing or clears out the AltAcct bit.
1028    ptep = ptep & ~0x3
1029
1030    # When printing with extra verbosity, print an extra newline that describes
1031    # who owns the mapping.
1032    extra_str = ''
1033
1034    if ptep & iommu_flag:
1035        # The mapping is an IOMMU Mapping
1036        ptep = ptep & ~iommu_flag
1037
1038        # Due to LLDB automatically setting all the high bits of pointers, when
1039        # ptep is retrieved from the pve_ptep[] array, LLDB will automatically set
1040        # the iommu_table_flag, which means this check only works for PVH entries
1041        # of type PVH_TYPE_PTEP (since those PTEPs come directly from the PVH
1042        # entry which has the right casting applied to avoid this issue).
1043        #
1044        # Why don't we just do the same casting for pve_ptep[] you ask? Well not
1045        # for a lack of trying, that's for sure. If you can figure out how to
1046        # cast that array correctly, then be my guest.
1047        if ptep & iommu_table_flag:
1048            pte_str = ' (IOMMU table), entry'
1049
1050            ptd = GetPtDesc(KVToPhysARM(ptep))
1051            iommu = dereference(ptd.iommu)
1052        else:
1053            # Instead of dumping the PTE (since we don't have that), dump the
1054            # descriptor object used by the IOMMU state (t8020dart/nvme_ppl/etc).
1055            #
1056            # This works because later on when the "ptep" is dereferenced as a
1057            # PTE pointer (uint64_t pointer), the descriptor pointer will be
1058            # dumped as that's the first 64-bit value in the IOMMU state object.
1059            pte_str = ' (IOMMU state), descriptor'
1060            ptep = ptep | iommu_table_flag
1061            iommu = dereference(kern.GetValueFromAddress(ptep, 'ppl_iommu_state *'))
1062
1063        # For IOMMU mappings, dump who owns the mapping as the extra string.
1064        extra_str = 'Mapped by {:s}'.format(dereference(iommu.desc).name)
1065        if unsigned(iommu.name) is not 0:
1066            extra_str += '/{:s}'.format(iommu.name)
1067        extra_str += ' (iommu state: {:x})'.format(addressof(iommu))
1068    else:
1069        # The mapping is a CPU Mapping
1070        pte_str += ', entry'
1071        ptd = GetPtDesc(KVToPhysARM(ptep))
1072        if ptd.pmap == kern.globals.kernel_pmap:
1073            extra_str = "Mapped by kernel task (kernel_pmap: {:#x})".format(ptd.pmap)
1074        elif verbose_level >= vDETAIL:
1075            task = TaskForPmapHelper(ptd.pmap)
1076            extra_str = "Mapped by user task (pmap: {:#x}, task: {:s})".format(ptd.pmap, "{:#x}".format(task) if task is not None else "<unknown>")
1077    try:
1078        print "{:s}PTEP {:#x}{:s}: {:#x}".format(pve_str, ptep, pte_str, dereference(kern.GetValueFromAddress(ptep, 'pt_entry_t *')))
1079    except:
1080        print "{:s}PTEP {:#x}{:s}: <unavailable>".format(pve_str, ptep, pte_str)
1081
1082    if verbose_level >= vDETAIL:
1083        print "    |-- {:s}".format(extra_str)
1084
1085def PVWalkARM(pai, verbose_level = vHUMAN):
1086    """ Walk a physical-to-virtual reverse mapping list maintained by the arm pmap.
1087
1088        pai: physical address index (PAI) corresponding to the pv_head_table
1089             entry to walk.
1090        verbose_level: Set to vSCRIPT or higher to print extra info around the
1091                       the pv_head_table/pp_attr_table flags and to dump the
1092                       pt_desc_t object if the type is a PTD.
1093    """
1094    # LLDB will automatically try to make pointer values dereferencable by
1095    # setting the upper bits if they aren't set. We need to parse the flags
1096    # stored in the upper bits later, so cast the pv_head_table to an array of
1097    # integers to get around this "feature". We'll add the upper bits back
1098    # manually before deref'ing anything.
1099    pv_head_table = cast(kern.GetGlobalVariable('pv_head_table'), "uintptr_t*")
1100    pvh_raw = unsigned(pv_head_table[pai])
1101    pvh = pvh_raw
1102    pvh_type = pvh & 0x3
1103
1104    print "PVH raw value: {:#x}".format(pvh_raw)
1105    if kern.arch.startswith('arm64'):
1106        pvh = pvh | PVH_HIGH_FLAGS_ARM64
1107    else:
1108        pvh = pvh | PVH_HIGH_FLAGS_ARM32
1109
1110    if pvh_type == 0:
1111        print "PVH type: NULL"
1112    elif pvh_type == 3:
1113        print "PVH type: page-table descriptor ({:#x})".format(pvh & ~0x3)
1114    elif pvh_type == 2:
1115        print "PVH type: single PTE"
1116        PVDumpPTE(None, pvh, verbose_level)
1117    elif pvh_type == 1:
1118        pvep = pvh & ~0x3
1119        print "PVH type: PTE list"
1120        pve_ptep_idx = 0
1121        while pvep != 0:
1122            pve = kern.GetValueFromAddress(pvep, "pv_entry_t *")
1123
1124            if pve.pve_ptep[pve_ptep_idx] != 0:
1125                PVDumpPTE(pvep, pve.pve_ptep[pve_ptep_idx], verbose_level)
1126
1127            pve_ptep_idx += 1
1128            if pve_ptep_idx == 2:
1129                pve_ptep_idx = 0
1130                pvep = unsigned(pve.pve_next)
1131
1132    if verbose_level >= vDETAIL:
1133        if (pvh_type == 1) or (pvh_type == 2):
1134            # Dump pv_head_table flags when there's a valid mapping.
1135            pvh_flags = []
1136
1137            if pvh_raw & (1 << 62):
1138                pvh_flags.append("CPU")
1139            if pvh_raw & (1 << 60):
1140                pvh_flags.append("EXEC")
1141            if pvh_raw & (1 << 59):
1142                pvh_flags.append("LOCKDOWN_KC")
1143            if pvh_raw & (1 << 58):
1144                pvh_flags.append("HASHED")
1145            if pvh_raw & (1 << 57):
1146                pvh_flags.append("LOCKDOWN_CS")
1147            if pvh_raw & (1 << 56):
1148                pvh_flags.append("LOCKDOWN_RO")
1149            if kern.arch.startswith('arm64') and pvh_raw & (1 << 61):
1150                pvh_flags.append("LOCK")
1151            elif kern.arch == 'arm' and pvh_raw & (1 << 31):
1152                pvh_flags.append("LOCK")
1153
1154            print "PVH Flags: {}".format(pvh_flags)
1155
1156        # Always dump pp_attr_table flags (these can be updated even if there aren't mappings).
1157        ppattr = unsigned(kern.globals.pp_attr_table[pai])
1158        print "PPATTR raw value: {:#x}".format(ppattr)
1159
1160        ppattr_flags = ["WIMG ({:#x})".format(ppattr & 0x3F)]
1161        if ppattr & 0x40:
1162            ppattr_flags.append("REFERENCED")
1163        if ppattr & 0x80:
1164            ppattr_flags.append("MODIFIED")
1165        if ppattr & 0x100:
1166            ppattr_flags.append("INTERNAL")
1167        if ppattr & 0x200:
1168            ppattr_flags.append("REUSABLE")
1169        if ppattr & 0x400:
1170            ppattr_flags.append("ALTACCT")
1171        if ppattr & 0x800:
1172            ppattr_flags.append("NOENCRYPT")
1173        if ppattr & 0x1000:
1174            ppattr_flags.append("REFFAULT")
1175        if ppattr & 0x2000:
1176            ppattr_flags.append("MODFAULT")
1177        if ppattr & 0x4000:
1178            ppattr_flags.append("MONITOR")
1179        if ppattr & 0x8000:
1180            ppattr_flags.append("NO_MONITOR")
1181
1182        print "PPATTR Flags: {}".format(ppattr_flags)
1183
1184        if pvh_type == 3:
1185            def RunLldbCmdHelper(command):
1186                """Helper for dumping an LLDB command right before executing it
1187                and printing the results.
1188                command: The LLDB command (as a string) to run.
1189
1190                Example input: "p/x kernel_pmap".
1191                """
1192                print "\nExecuting: {:s}\n{:s}".format(command, lldb_run_command(command))
1193            # Dump the page table descriptor object
1194            ptd = kern.GetValueFromAddress(pvh & ~0x3, 'pt_desc_t *')
1195            RunLldbCmdHelper("p/x *(pt_desc_t*)" + hex(ptd))
1196
1197            # Depending on the system, more than one ptd_info can be associated
1198            # with a single PTD. Only dump the first PTD info and assume the
1199            # user knows to dump the rest if they're on one of those systems.
1200            RunLldbCmdHelper("p/x ((pt_desc_t*)" + hex(ptd) + ")->ptd_info[0]")
1201
1202@lldb_command('pv_walk')
1203def PVWalk(cmd_args=None):
1204    """ Show mappings for <physical_address | PAI> tracked in the PV list.
1205        Syntax: (lldb) pv_walk <physical_address | PAI> [-vv]
1206
1207        Extra verbosity will pretty print the pv_head_table/pp_attr_table flags
1208        as well as dump the page table descriptor (PTD) struct if the entry is a
1209        PTD.
1210    """
1211    if cmd_args == None or len(cmd_args) < 1:
1212        raise ArgumentError("Too few arguments to pv_walk.")
1213    if not kern.arch.startswith('arm'):
1214        raise NotImplementedError("pv_walk does not support {0}".format(kern.arch))
1215
1216    pa = kern.GetValueFromAddress(cmd_args[0], 'unsigned long')
1217
1218    # If the input is already a PAI, this function will return the input unchanged.
1219    # This function also ensures that the physical address is kernel-managed.
1220    pai = ConvertPhysAddrToPai(pa)
1221
1222    PVWalkARM(pai, config['verbosity'])
1223
1224@lldb_command('kvtophys')
1225def KVToPhys(cmd_args=None):
1226    """ Translate a kernel virtual address to the corresponding physical address.
1227        Assumes the virtual address falls within the kernel static region.
1228        Syntax: (lldb) kvtophys <kernel virtual address>
1229    """
1230    if cmd_args == None or len(cmd_args) < 1:
1231        raise ArgumentError("Too few arguments to kvtophys.")
1232    if kern.arch.startswith('arm'):
1233        print "{:#x}".format(KVToPhysARM(long(unsigned(kern.GetValueFromAddress(cmd_args[0], 'unsigned long')))))
1234    elif kern.arch == 'x86_64':
1235        print "{:#x}".format(long(unsigned(kern.GetValueFromAddress(cmd_args[0], 'unsigned long'))) - unsigned(kern.globals.physmap_base))
1236
1237@lldb_command('phystokv')
1238def PhysToKV(cmd_args=None):
1239    """ Translate a physical address to the corresponding static kernel virtual address.
1240        Assumes the physical address corresponds to managed DRAM.
1241        Syntax: (lldb) phystokv <physical address>
1242    """
1243    if cmd_args == None or len(cmd_args) < 1:
1244        raise ArgumentError("Too few arguments to phystokv.")
1245    print "{:#x}".format(kern.PhysToKernelVirt(long(unsigned(kern.GetValueFromAddress(cmd_args[0], 'unsigned long')))))
1246
1247def KVToPhysARM(addr):
1248    if kern.arch.startswith('arm64'):
1249        ptov_table = kern.globals.ptov_table
1250        for i in range(0, kern.globals.ptov_index):
1251            if (addr >= long(unsigned(ptov_table[i].va))) and (addr < (long(unsigned(ptov_table[i].va)) + long(unsigned(ptov_table[i].len)))):
1252                return (addr - long(unsigned(ptov_table[i].va)) + long(unsigned(ptov_table[i].pa)))
1253    return (addr - unsigned(kern.globals.gVirtBase) + unsigned(kern.globals.gPhysBase))
1254
1255
1256def GetPtDesc(paddr):
1257    pn = (paddr - unsigned(kern.globals.vm_first_phys)) / kern.globals.page_size
1258    pvh = unsigned(kern.globals.pv_head_table[pn])
1259    if kern.arch.startswith('arm64'):
1260        pvh = pvh | PVH_HIGH_FLAGS_ARM64
1261    else:
1262        pvh = pvh | PVH_HIGH_FLAGS_ARM32
1263    pvh_type = pvh & 0x3
1264    if pvh_type != 0x3:
1265        raise ValueError("PV head {:#x} does not correspond to a page-table descriptor".format(pvh))
1266    ptd = kern.GetValueFromAddress(pvh & ~0x3, 'pt_desc_t *')
1267    return ptd
1268
1269def ShowPTEARM(pte, page_size, stage2 = False):
1270    """ Display vital information about an ARM page table entry
1271        pte: kernel virtual address of the PTE.  Should be L3 PTE.  May also work with L2 TTEs for certain devices.
1272    """
1273    ptd = GetPtDesc(KVToPhysARM(pte))
1274    pt_index = (pte % kern.globals.page_size) / page_size
1275    refcnt = ptd.ptd_info[pt_index].refcnt
1276    wiredcnt = ptd.ptd_info[pt_index].wiredcnt
1277    print "descriptor: {:#x} (refcnt: {:#x}, wiredcnt: {:#x})".format(ptd, refcnt, wiredcnt)
1278
1279    # PTDs used to describe IOMMU pages always have a refcnt of 0x8000/0x8001.
1280    is_iommu_pte = (refcnt & 0x8000) == 0x8000
1281
1282    # The pmap/iommu field is a union, so only print the correct one.
1283    if is_iommu_pte:
1284        iommu_desc_name = '{:s}'.format(dereference(dereference(ptd.iommu).desc).name)
1285        if unsigned(dereference(ptd.iommu).name) is not 0:
1286            iommu_desc_name += '/{:s}'.format(dereference(ptd.iommu).name)
1287
1288        print "iommu state: {:#x} ({:s})".format(ptd.iommu, iommu_desc_name)
1289    else:
1290        if ptd.pmap == kern.globals.kernel_pmap:
1291            pmap_str = "(kernel_pmap)"
1292        else:
1293            task = TaskForPmapHelper(ptd.pmap)
1294            pmap_str = "(User Task: {:s})".format("{:#x}".format(task) if task is not None else "<unknown>")
1295        print "pmap: {:#x} {:s}".format(ptd.pmap, pmap_str)
1296
1297    pte_pgoff = pte % page_size
1298    if kern.arch.startswith('arm64'):
1299        pte_pgoff = pte_pgoff / 8
1300        nttes = page_size / 8
1301    else:
1302        pte_pgoff = pte_pgoff / 4
1303        nttes = page_size / 4
1304    if ptd.ptd_info[pt_index].refcnt == 0x4000:
1305        level = 2
1306        granule = nttes * page_size
1307    else:
1308        level = 3
1309        granule = page_size
1310    print "maps {}: {:#x}".format("IPA" if stage2 else "VA", long(unsigned(ptd.va[pt_index])) + (pte_pgoff * granule))
1311    pteval = long(unsigned(dereference(kern.GetValueFromAddress(unsigned(pte), 'pt_entry_t *'))))
1312    print "value: {:#x}".format(pteval)
1313    if kern.arch.startswith('arm64'):
1314        print "level: {:d}".format(level)
1315        PmapDecodeTTEARM64(pteval, level, stage2, is_iommu_pte)
1316
1317    elif kern.arch == 'arm':
1318        PmapDecodeTTEARM(pteval, 2, vSCRIPT)
1319
1320@lldb_command('showpte')
1321def ShowPTE(cmd_args=None):
1322    """ Display vital information about the page table entry at VA <pte>
1323        Syntax: (lldb) showpte <pte_va> [4k|16k|16k_s2]
1324    """
1325    if cmd_args == None or len(cmd_args) < 1:
1326        raise ArgumentError("Too few arguments to showpte.")
1327
1328    if kern.arch == 'arm':
1329        ShowPTEARM(kern.GetValueFromAddress(cmd_args[0], 'unsigned long'), kern.globals.page_size)
1330    elif kern.arch.startswith('arm64'):
1331        pmap_pt_attr = kern.globals.native_pt_attr if len(cmd_args) < 2 else GetMemoryAttributesFromUser(cmd_args[1])
1332        if pmap_pt_attr is None:
1333            raise ArgumentError("Invalid translation attribute type.")
1334
1335        stage2 = bool(pmap_pt_attr.stage2 if hasattr(pmap_pt_attr, 'stage2') else False)
1336        ShowPTEARM(kern.GetValueFromAddress(cmd_args[0], 'unsigned long'), pmap_pt_attr.pta_page_size, stage2)
1337    else:
1338        raise NotImplementedError("showpte does not support {0}".format(kern.arch))
1339
1340def FindMappingAtLevelARM(pmap, tt, nttes, level, va, action):
1341    """ Perform the specified action for all valid mappings in an ARM translation table
1342        pmap: owner of the translation table
1343        tt: translation table or page table
1344        nttes: number of entries in tt
1345        level: translation table level, 1 or 2
1346        action: callback for each valid TTE
1347    """
1348    for i in range(nttes):
1349        try:
1350            tte = tt[i]
1351            va_size = None
1352            if level == 1:
1353                if tte & 0x3 == 0x1:
1354                    type = 'table'
1355                    granule = 1024
1356                    va_size = kern.globals.page_size * 256
1357                    paddr = tte & 0xFFFFFC00
1358                elif tte & 0x3 == 0x2:
1359                    type = 'block'
1360                    if (tte & 0x40000) == 0x40000:
1361                        granule = 1 << 24
1362                        paddr = tte & 0xFF000000
1363                    else:
1364                        granule = 1 << 20
1365                        paddr = tte & 0xFFF00000
1366                else:
1367                    continue
1368            elif (tte & 0x3) == 0x1:
1369                type = 'entry'
1370                granule = 1 << 16
1371                paddr = tte & 0xFFFF0000
1372            elif (tte & 0x3) != 0:
1373                type = 'entry'
1374                granule = 1 << 12
1375                paddr = tte & 0xFFFFF000
1376            else:
1377                continue
1378            if va_size is None:
1379                va_size = granule
1380            mapped_va = va + (va_size * i)
1381            if action(pmap, level, type, addressof(tt[i]), paddr, mapped_va, granule):
1382                if level == 1 and (tte & 0x3) == 0x1:
1383                    tt_next = kern.GetValueFromAddress(kern.PhysToKernelVirt(paddr), 'tt_entry_t *')
1384                    FindMappingAtLevelARM(pmap, tt_next, granule / 4, level + 1, mapped_va, action)
1385        except Exception as exc:
1386            print "Unable to access tte {:#x}".format(unsigned(addressof(tt[i])))
1387
1388def FindMappingAtLevelARM64(pmap, tt, nttes, level, va, action):
1389    """ Perform the specified action for all valid mappings in an ARM64 translation table
1390        pmap: owner of the translation table
1391        tt: translation table or page table
1392        nttes: number of entries in tt
1393        level: translation table level, 1 2 or 3
1394        action: callback for each valid TTE
1395    """
1396    # Obtain pmap attributes
1397    pmap_pt_attr = pmap.pmap_pt_attr if hasattr(pmap, 'pmap_pt_attr') else kern.globals.native_pt_attr
1398    page_size = pmap_pt_attr.pta_page_size
1399    page_offset_mask = (page_size - 1)
1400    page_base_mask = ((1 << ARM64_VMADDR_BITS) - 1) & (~page_offset_mask)
1401    max_level = unsigned(pmap_pt_attr.pta_max_level)
1402
1403    for i in range(nttes):
1404        try:
1405            tte = tt[i]
1406            if tte & 0x1 == 0x0:
1407                continue
1408
1409            tt_next = None
1410            paddr = unsigned(tte) & unsigned(page_base_mask)
1411
1412            # Handle leaf entry
1413            if tte & 0x2 == 0x0 or level == max_level:
1414                type = 'block' if level < max_level else 'entry'
1415                granule = PmapBlockOffsetMaskARM64(page_size, level) + 1
1416            else:
1417            # Handle page table entry
1418                type = 'table'
1419                granule = page_size
1420                tt_next = kern.GetValueFromAddress(kern.PhysToKernelVirt(paddr), 'tt_entry_t *')
1421
1422            mapped_va = long(unsigned(va)) + ((PmapBlockOffsetMaskARM64(page_size, level) + 1) * i)
1423            if action(pmap, level, type, addressof(tt[i]), paddr, mapped_va, granule):
1424                if tt_next is not None:
1425                    FindMappingAtLevelARM64(pmap, tt_next, granule / ARM64_TTE_SIZE, level + 1, mapped_va, action)
1426
1427        except Exception as exc:
1428            print "Unable to access tte {:#x}".format(unsigned(addressof(tt[i])))
1429
1430def ScanPageTables(action, targetPmap=None):
1431    """ Perform the specified action for all valid mappings in all page tables,
1432        optionally restricted to a single pmap.
1433        pmap: pmap whose page table should be scanned.  If None, all pmaps on system will be scanned.
1434    """
1435    print "Scanning all available translation tables.  This may take a long time..."
1436    def ScanPmap(pmap, action):
1437        if kern.arch.startswith('arm64'):
1438            # Obtain pmap attributes
1439            pmap_pt_attr = pmap.pmap_pt_attr if hasattr(pmap, 'pmap_pt_attr') else kern.globals.native_pt_attr
1440            granule = pmap_pt_attr.pta_page_size
1441            level = unsigned(pmap_pt_attr.pta_root_level)
1442            root_pgtable_num_ttes = (unsigned(pmap_pt_attr.pta_level_info[level].index_mask) >> \
1443                unsigned(pmap_pt_attr.pta_level_info[level].shift)) + 1
1444        elif kern.arch == 'arm':
1445            granule = pmap.tte_index_max * 4
1446
1447        if action(pmap, pmap_pt_attr.pta_root_level, 'root', pmap.tte, unsigned(pmap.ttep), pmap.min, granule):
1448            if kern.arch.startswith('arm64'):
1449                FindMappingAtLevelARM64(pmap, pmap.tte, root_pgtable_num_ttes, level, pmap.min, action)
1450            elif kern.arch == 'arm':
1451                FindMappingAtLevelARM(pmap, pmap.tte, pmap.tte_index_max, 1, pmap.min, action)
1452
1453    if targetPmap is not None:
1454        ScanPmap(kern.GetValueFromAddress(targetPmap, 'pmap_t'), action)
1455    else:
1456        for pmap in IterateQueue(kern.globals.map_pmap_list, 'pmap_t', 'pmaps'):
1457            ScanPmap(pmap, action)
1458
1459@lldb_command('showallmappings')
1460def ShowAllMappings(cmd_args=None):
1461    """ Find and display all available mappings on the system for
1462        <physical_address>.  Optionally only searches the pmap
1463        specified by [<pmap>]
1464        Syntax: (lldb) showallmappings <physical_address> [<pmap>]
1465        WARNING: this macro can take a long time (up to 30min.) to complete!
1466    """
1467    if cmd_args == None or len(cmd_args) < 1:
1468        raise ArgumentError("Too few arguments to showallmappings.")
1469    if not kern.arch.startswith('arm'):
1470        raise NotImplementedError("showallmappings does not support {0}".format(kern.arch))
1471    pa = kern.GetValueFromAddress(cmd_args[0], 'unsigned long')
1472    targetPmap = None
1473    if len(cmd_args) > 1:
1474        targetPmap = cmd_args[1]
1475    def printMatchedMapping(pmap, level, type, tte, paddr, va, granule):
1476        if paddr <= pa < (paddr + granule):
1477            print "pmap: {:#x}: L{:d} {:s} at {:#x}: [{:#x}, {:#x}), maps va {:#x}".format(pmap, level, type, unsigned(tte), paddr, paddr + granule, va)
1478        return True
1479    ScanPageTables(printMatchedMapping, targetPmap)
1480
1481@lldb_command('showptusage')
1482def ShowPTUsage(cmd_args=None):
1483    """ Display a summary of pagetable allocations for a given pmap.
1484        Syntax: (lldb) showptusage [<pmap>]
1485        WARNING: this macro can take a long time (> 1hr) to complete!
1486    """
1487    if not kern.arch.startswith('arm'):
1488        raise NotImplementedError("showptusage does not support {0}".format(kern.arch))
1489    targetPmap = None
1490    if len(cmd_args) > 0:
1491        targetPmap = cmd_args[0]
1492    lastPmap = [None]
1493    numTables = [0]
1494    numUnnested = [0]
1495    numPmaps = [0]
1496    def printValidTTE(pmap, level, type, tte, paddr, va, granule):
1497        unnested = ""
1498        nested_region_addr = long(unsigned(pmap.nested_region_addr))
1499        nested_region_end = nested_region_addr + long(unsigned(pmap.nested_region_size))
1500        if lastPmap[0] is None or (pmap != lastPmap[0]):
1501            lastPmap[0] = pmap
1502            numPmaps[0] = numPmaps[0] + 1
1503            print ("pmap {:#x}:".format(pmap))
1504        if type == 'root':
1505            return True
1506        if (level == 2) and (va >= nested_region_addr) and (va < nested_region_end):
1507            ptd = GetPtDesc(paddr)
1508            if ptd.pmap != pmap:
1509                return False
1510            else:
1511                numUnnested[0] = numUnnested[0] + 1
1512                unnested = " (likely unnested)"
1513        numTables[0] = numTables[0] + 1
1514        print (" " * 4 * int(level)) + "L{:d} entry at {:#x}, maps {:#x}".format(level, unsigned(tte), va) + unnested
1515        if level == 2:
1516            return False
1517        else:
1518            return True
1519    ScanPageTables(printValidTTE, targetPmap)
1520    print("{:d} table(s), {:d} of them likely unnested, in {:d} pmap(s)".format(numTables[0], numUnnested[0], numPmaps[0]))
1521
1522def checkPVList(pmap, level, type, tte, paddr, va, granule):
1523    """ Checks an ARM physical-to-virtual mapping list for consistency errors.
1524        pmap: owner of the translation table
1525        level: translation table level.  PV lists will only be checked for L2 (arm32) or L3 (arm64) tables.
1526        type: unused
1527        tte: KVA of PTE to check for presence in PV list.  If None, presence check will be skipped.
1528        paddr: physical address whose PV list should be checked.  Need not be page-aligned.
1529        granule: unused
1530    """
1531    vm_first_phys = unsigned(kern.globals.vm_first_phys)
1532    vm_last_phys = unsigned(kern.globals.vm_last_phys)
1533    page_size = kern.globals.page_size
1534    if kern.arch.startswith('arm64'):
1535        page_offset_mask = (page_size - 1)
1536        page_base_mask = ((1 << ARM64_VMADDR_BITS) - 1) & (~page_offset_mask)
1537        paddr = paddr & page_base_mask
1538        max_level = 3
1539        pvh_set_bits = PVH_HIGH_FLAGS_ARM64
1540    elif kern.arch == 'arm':
1541        page_base_mask = 0xFFFFF000
1542        paddr = paddr & page_base_mask
1543        max_level = 2
1544        pvh_set_bits = PVH_HIGH_FLAGS_ARM32
1545    if level < max_level or paddr < vm_first_phys or paddr >= vm_last_phys:
1546        return True
1547    pn = (paddr - vm_first_phys) / page_size
1548    pvh = unsigned(kern.globals.pv_head_table[pn]) | pvh_set_bits
1549    pvh_type = pvh & 0x3
1550    if pmap is not None:
1551        pmap_str = "pmap: {:#x}: ".format(pmap)
1552    else:
1553        pmap_str = ''
1554    if tte is not None:
1555        tte_str = "pte {:#x} ({:#x}): ".format(unsigned(tte), paddr)
1556    else:
1557        tte_str = "paddr {:#x}: ".format(paddr)
1558    if pvh_type == 0 or pvh_type == 3:
1559        print "{:s}{:s}unexpected PVH type {:d}".format(pmap_str, tte_str, pvh_type)
1560    elif pvh_type == 2:
1561        ptep = pvh & ~0x3
1562        if tte is not None and ptep != unsigned(tte):
1563            print "{:s}{:s}PVH mismatch ({:#x})".format(pmap_str, tte_str, ptep)
1564        try:
1565            pte = long(unsigned(dereference(kern.GetValueFromAddress(ptep, 'pt_entry_t *')))) & page_base_mask
1566            if (pte != paddr):
1567                print "{:s}{:s}PVH {:#x} maps wrong page ({:#x}) ".format(pmap_str, tte_str, ptep, pte)
1568        except Exception as exc:
1569            print "{:s}{:s}Unable to read PVH {:#x}".format(pmap_str, tte_str, ptep)
1570    elif pvh_type == 1:
1571        pvep = pvh & ~0x3
1572        tte_match = False
1573        pve_ptep_idx = 0
1574        while pvep != 0:
1575            pve = kern.GetValueFromAddress(pvep, "pv_entry_t *")
1576            ptep = unsigned(pve.pve_ptep[pve_ptep_idx]) & ~0x3
1577            pve_ptep_idx += 1
1578            if pve_ptep_idx == 2:
1579                pve_ptep_idx = 0
1580                pvep = unsigned(pve.pve_next)
1581            if ptep == 0:
1582                continue
1583            if tte is not None and ptep == unsigned(tte):
1584                tte_match = True
1585            try:
1586                pte = long(unsigned(dereference(kern.GetValueFromAddress(ptep, 'pt_entry_t *')))) & page_base_mask
1587                if (pte != paddr):
1588                    print "{:s}{:s}PVE {:#x} maps wrong page ({:#x}) ".format(pmap_str, tte_str, ptep, pte)
1589            except Exception as exc:
1590                print "{:s}{:s}Unable to read PVE {:#x}".format(pmap_str, tte_str, ptep)
1591        if tte is not None and not tte_match:
1592            print "{:s}{:s}not found in PV list".format(pmap_str, tte_str, paddr)
1593    return True
1594
1595@lldb_command('pv_check', 'P')
1596def PVCheck(cmd_args=None, cmd_options={}):
1597    """ Check the physical-to-virtual mapping for a given PTE or physical address
1598        Syntax: (lldb) pv_check <addr> [-p]
1599            -P        : Interpret <addr> as a physical address rather than a PTE
1600    """
1601    if cmd_args == None or len(cmd_args) < 1:
1602        raise ArgumentError("Too few arguments to pv_check.")
1603    if kern.arch == 'arm':
1604        level = 2
1605    elif kern.arch.startswith('arm64'):
1606        level = 3
1607    else:
1608        raise NotImplementedError("pv_check does not support {0}".format(kern.arch))
1609    if "-P" in cmd_options:
1610        pte = None
1611        pa = long(unsigned(kern.GetValueFromAddress(cmd_args[0], "unsigned long")))
1612    else:
1613        pte = kern.GetValueFromAddress(cmd_args[0], 'pt_entry_t *')
1614        pa = long(unsigned(dereference(pte)))
1615    checkPVList(None, level, None, pte, pa, 0, None)
1616
1617@lldb_command('check_pmaps')
1618def CheckPmapIntegrity(cmd_args=None):
1619    """ Performs a system-wide integrity check of all PTEs and associated PV lists.
1620        Optionally only checks the pmap specified by [<pmap>]
1621        Syntax: (lldb) check_pmaps [<pmap>]
1622        WARNING: this macro can take a HUGE amount of time (several hours) if you do not
1623        specify [pmap] to limit it to a single pmap.  It will also give false positives
1624        for kernel_pmap, as we do not create PV entries for static kernel mappings on ARM.
1625        Use of this macro without the [<pmap>] argument is heavily discouraged.
1626    """
1627    if not kern.arch.startswith('arm'):
1628        raise NotImplementedError("check_pmaps does not support {0}".format(kern.arch))
1629    targetPmap = None
1630    if len(cmd_args) > 0:
1631        targetPmap = cmd_args[0]
1632    ScanPageTables(checkPVList, targetPmap)
1633
1634@lldb_command('pmapsforledger')
1635def PmapsForLedger(cmd_args=None):
1636    """ Find and display all pmaps currently using <ledger>.
1637        Syntax: (lldb) pmapsforledger <ledger>
1638    """
1639    if cmd_args == None or len(cmd_args) < 1:
1640        raise ArgumentError("Too few arguments to pmapsforledger.")
1641    if not kern.arch.startswith('arm'):
1642        raise NotImplementedError("pmapsforledger does not support {0}".format(kern.arch))
1643    ledger = kern.GetValueFromAddress(cmd_args[0], 'ledger_t')
1644    for pmap in IterateQueue(kern.globals.map_pmap_list, 'pmap_t', 'pmaps'):
1645        if pmap.ledger == ledger:
1646            print "pmap: {:#x}".format(pmap)
1647
1648
1649def IsValidPai(pai):
1650    """ Given an unsigned value, detect whether that value is a valid physical
1651        address index (PAI). It does this by first computing the last possible
1652        PAI and comparing the input to that.
1653
1654        All contemporary SoCs reserve the bottom part of the address space, so
1655        there shouldn't be any valid physical addresses between zero and the
1656        last PAI either.
1657    """
1658    page_size = unsigned(kern.globals.page_size)
1659    vm_first_phys = unsigned(kern.globals.vm_first_phys)
1660    vm_last_phys = unsigned(kern.globals.vm_last_phys)
1661
1662    last_pai = (vm_last_phys - vm_first_phys) / page_size
1663    if (pai < 0) or (pai >= last_pai):
1664        return False
1665
1666    return True
1667
1668def ConvertPaiToPhysAddr(pai):
1669    """ Convert the given Physical Address Index (PAI) into a physical address.
1670
1671        If the input isn't a valid PAI (it's most likely already a physical
1672        address), then just return back the input unchanged.
1673    """
1674    pa = pai
1675
1676    # If the value is a valid PAI, then convert it into a physical address.
1677    if IsValidPai(pai):
1678        pa = (pai * unsigned(kern.globals.page_size)) + unsigned(kern.globals.vm_first_phys)
1679
1680    return pa
1681
1682def ConvertPhysAddrToPai(pa):
1683    """ Convert the given physical address into a Physical Address Index (PAI).
1684
1685        If the input is already a valid PAI, then just return back the input
1686        unchanged.
1687    """
1688    vm_first_phys = unsigned(kern.globals.vm_first_phys)
1689    vm_last_phys = unsigned(kern.globals.vm_last_phys)
1690    pai = pa
1691
1692    if not IsValidPai(pa) and (pa < vm_first_phys or pa >= vm_last_phys):
1693        raise ArgumentError("{:#x} is neither a valid PAI nor a kernel-managed address: [{:#x}, {:#x})".format(pa, vm_first_phys, vm_last_phys))
1694    elif not IsValidPai(pa):
1695        # If the value isn't already a valid PAI, then convert it into one.
1696        pai = (pa - vm_first_phys) / unsigned(kern.globals.page_size)
1697
1698    return pai
1699
1700@lldb_command('pmappaindex')
1701def PmapPaIndex(cmd_args=None):
1702    """ Display both a physical address and physical address index (PAI) when
1703        provided with only one of those values.
1704
1705        Syntax: (lldb) pmappaindex <physical address | PAI>
1706
1707        NOTE: This macro will throw an exception if the input isn't a valid PAI
1708              and is also not a kernel-managed physical address.
1709    """
1710    if (cmd_args == None) or (len(cmd_args) < 1):
1711        raise ArgumentError("Too few arguments to pmappaindex.")
1712
1713    if not kern.arch.startswith('arm'):
1714        raise NotImplementedError("pmappaindex is only supported on ARM devices.")
1715
1716    value = kern.GetValueFromAddress(cmd_args[0], 'unsigned long')
1717    pai = value
1718    phys_addr = value
1719
1720    if IsValidPai(value):
1721        # Input is a PAI, calculate the physical address.
1722        phys_addr = ConvertPaiToPhysAddr(value)
1723    else:
1724        # Input is a physical address, calculate the PAI
1725        pai = ConvertPhysAddrToPai(value)
1726
1727    print "Physical Address: {:#x}".format(phys_addr)
1728    print "PAI: {:d}".format(pai)
1729