]> git.saurik.com Git - apple/xnu.git/blobdiff - tools/lldbmacros/pmap.py
xnu-6153.81.5.tar.gz
[apple/xnu.git] / tools / lldbmacros / pmap.py
old mode 100644 (file)
new mode 100755 (executable)
index fc3a005..8bff268
@@ -4,7 +4,7 @@ from kdp import *
 from utils import *
 
 def ReadPhysInt(phys_addr, bitsize = 64, cpuval = None):
-    """ Read a physical memory data based on address. 
+    """ Read a physical memory data based on address.
         params:
             phys_addr : int - Physical address to read
             bitsize   : int - defines how many bytes to read. defaults to 64 bit
@@ -30,12 +30,12 @@ def ReadPhysInt(phys_addr, bitsize = 64, cpuval = None):
 
 @lldb_command('readphys')
 def ReadPhys(cmd_args = None):
-    """ Reads the specified untranslated address 
+    """ Reads the specified untranslated address
         The argument is interpreted as a physical address, and the 64-bit word
         addressed is displayed.
         usage: readphys <nbits> <address>
         nbits: 8,16,32,64
-        address: 1234 or 0x1234 
+        address: 1234 or 0x1234
     """
     if cmd_args == None or len(cmd_args) < 2:
         print "Insufficient arguments.", ReadPhys.__doc__
@@ -57,7 +57,7 @@ def KDPReadPhysMEM(address, bits):
             address : int - address where to read the data from
             bits : int - number of bits in the intval (8/16/32/64)
         returns:
-            int: read value from memory. 
+            int: read value from memory.
             0xBAD10AD: if failed to read data.
     """
     retval = 0xBAD10AD
@@ -77,15 +77,15 @@ def KDPReadPhysMEM(address, bits):
 
     data_addr = int(addressof(kern.globals.manual_pkt))
     pkt = kern.GetValueFromAddress(data_addr, 'kdp_readphysmem64_req_t *')
-    
+
     header_value =GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_READPHYSMEM64'), length=kdp_pkt_size)
-    
+
     if ( WriteInt64ToMemoryAddress((header_value), int(addressof(pkt.hdr))) and
          WriteInt64ToMemoryAddress(address, int(addressof(pkt.address))) and
          WriteInt32ToMemoryAddress((bits/8), int(addressof(pkt.nbytes))) and
          WriteInt16ToMemoryAddress(xnudefines.lcpu_self, int(addressof(pkt.lcpu)))
          ):
-         
+
         if WriteInt32ToMemoryAddress(1, input_address):
             # now read data from the kdp packet
             data_address = unsigned(addressof(kern.GetValueFromAddress(int(addressof(kern.globals.manual_pkt.data)), 'kdp_readphysmem64_reply_t *').data))
@@ -118,21 +118,21 @@ def KDPWritePhysMEM(address, intval, bits):
     if not WriteInt32ToMemoryAddress(0, input_address):
         return False
 
-    kdp_pkt_size = GetType('kdp_writephysmem64_req_t').GetByteSize()
+    kdp_pkt_size = GetType('kdp_writephysmem64_req_t').GetByteSize() + (bits / 8)
     if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
         return False
 
     data_addr = int(addressof(kern.globals.manual_pkt))
     pkt = kern.GetValueFromAddress(data_addr, 'kdp_writephysmem64_req_t *')
-    
+
     header_value =GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_WRITEPHYSMEM64'), length=kdp_pkt_size)
-    
+
     if ( WriteInt64ToMemoryAddress((header_value), int(addressof(pkt.hdr))) and
          WriteInt64ToMemoryAddress(address, int(addressof(pkt.address))) and
          WriteInt32ToMemoryAddress((bits/8), int(addressof(pkt.nbytes))) and
          WriteInt16ToMemoryAddress(xnudefines.lcpu_self, int(addressof(pkt.lcpu)))
          ):
-         
+
         if bits == 8:
             if not WriteInt8ToMemoryAddress(intval, int(addressof(pkt.data))):
                 return False
@@ -151,7 +151,7 @@ def KDPWritePhysMEM(address, intval, bits):
 
 
 def WritePhysInt(phys_addr, int_val, bitsize = 64):
-    """ Write and integer value in a physical memory data based on address. 
+    """ Write and integer value in a physical memory data based on address.
         params:
             phys_addr : int - Physical address to read
             int_val   : int - int value to write in memory
@@ -170,14 +170,14 @@ def WritePhysInt(phys_addr, int_val, bitsize = 64):
 
 @lldb_command('writephys')
 def WritePhys(cmd_args=None):
-    """ writes to the specified untranslated address 
+    """ writes to the specified untranslated address
         The argument is interpreted as a physical address, and the 64-bit word
         addressed is displayed.
         usage: writephys <nbits> <address> <value>
         nbits: 8,16,32,64
         address: 1234 or 0x1234
         value: int value to be written
-        ex. (lldb)writephys 16 0x12345abcd 0x25 
+        ex. (lldb)writephys 16 0x12345abcd 0x25
     """
     if cmd_args == None or len(cmd_args) < 3:
         print "Invalid arguments.", WritePhys.__doc__
@@ -193,6 +193,7 @@ lldb_alias('writephys16', 'writephys 16 ')
 lldb_alias('writephys32', 'writephys 32 ')
 lldb_alias('writephys64', 'writephys 64 ')
 
+
 def _PT_Step(paddr, index, verbose_level = vSCRIPT):
     """
      Step to lower-level page table and print attributes
@@ -239,94 +240,197 @@ def _PT_Step(paddr, index, verbose_level = vSCRIPT):
             out_string += " invalid"
             pt_paddr = 0
             pt_valid = False
+            if entry & (0x1 << 62):
+                out_string += " compressed"
             #Stop decoding other bits
             entry = 0
         if entry & (0x1 << 1):
             out_string += " writable"
         else:
             out_string += " read-only"
-        
+
         if entry & (0x1 << 2):
             out_string += " user"
         else:
             out_string += " supervisor"
-        
+
         if entry & (0x1 << 3):
             out_string += " PWT"
-        
+
         if entry & (0x1 << 4):
             out_string += " PCD"
-        
+
         if entry & (0x1 << 5):
             out_string += " accessed"
-        
+
         if entry & (0x1 << 6):
             out_string += " dirty"
-        
+
         if entry & (0x1 << 7):
             out_string += " large"
             pt_large = True
         else:
             pt_large = False
-        
+
         if entry & (0x1 << 8):
             out_string += " global"
-        
+
         if entry & (0x3 << 9):
             out_string += " avail:{0:x}".format((entry >> 9) & 0x3)
-        
+
         if entry & (0x1 << 63):
             out_string += " noexec"
     print out_string
     return (pt_paddr, pt_valid, pt_large)
-        
-            
-        
-    
-def _PmapL4Walk(pmap_addr_val,vaddr, verbose_level = vSCRIPT):
+
+def _PT_StepEPT(paddr, index, verbose_level = vSCRIPT):
+    """
+     Step to lower-level page table and print attributes for EPT pmap
+       paddr: current page table entry physical address
+       index: current page table entry index (0..511)
+       verbose_level:    vHUMAN: print nothing
+                         vSCRIPT: print basic information
+                         vDETAIL: print basic information and hex table dump
+     returns: (pt_paddr, pt_valid, pt_large)
+       pt_paddr: next level page table entry physical address
+                      or null if invalid
+       pt_valid: 1 if $kgm_pt_paddr is valid, 0 if the walk
+                      should be aborted
+       pt_large: 1 if kgm_pt_paddr is a page frame address
+                      of a large page and not another page table entry
+    """
+    entry_addr = paddr + (8 * index)
+    entry = ReadPhysInt(entry_addr, 64, xnudefines.lcpu_self )
+    out_string = ''
+    if verbose_level >= vDETAIL:
+        for pte_loop in range(0, 512):
+            paddr_tmp = paddr + (8 * pte_loop)
+            out_string += "{0: <#020x}:\t {1: <#020x}\n".format(paddr_tmp, ReadPhysInt(paddr_tmp, 64, xnudefines.lcpu_self))
+    paddr_mask = ~((0xfff<<52) | 0xfff)
+    paddr_large_mask =  ~((0xfff<<52) | 0x1fffff)
+    pt_valid = False
+    pt_large = False
+    pt_paddr = 0
+    if verbose_level < vSCRIPT:
+        if entry & 0x7 :
+            pt_valid = True
+            pt_large = False
+            pt_paddr = entry & paddr_mask
+            if entry & (0x1 <<7):
+                pt_large = True
+                pt_paddr = entry & paddr_large_mask
+    else:
+        out_string+= "{0: <#020x}:\n\t{1:#020x}\n\t".format(entry_addr, entry)
+        if entry & 0x7:
+            out_string += "valid"
+            pt_paddr = entry & paddr_mask
+            pt_valid = True
+        else:
+            out_string += "invalid"
+            pt_paddr = 0
+            pt_valid = False
+            if entry & (0x1 << 62):
+                out_string += " compressed"
+            #Stop decoding other bits
+            entry = 0
+        if entry & 0x1:
+            out_string += " readable"
+        else:
+            out_string += " no read"
+        if entry & (0x1 << 1):
+            out_string += " writable"
+        else:
+            out_string += " no write"
+
+        if entry & (0x1 << 2):
+            out_string += " executable"
+        else:
+            out_string += " no exec"
+
+        ctype = entry & 0x38
+        if ctype == 0x30:
+            out_string += " cache-WB"
+        elif ctype == 0x28:
+            out_string += " cache-WP"
+        elif ctype == 0x20:
+            out_string += " cache-WT"
+        elif ctype == 0x8:
+            out_string += " cache-WC"
+        else:
+            out_string += " cache-NC"
+
+        if (entry & 0x40) == 0x40:
+            out_string += " Ignore-PTA"
+
+        if (entry & 0x100) == 0x100:
+            out_string += " accessed"
+
+        if (entry & 0x200) == 0x200:
+            out_string += " dirty"
+
+        if entry & (0x1 << 7):
+            out_string += " large"
+            pt_large = True
+        else:
+            pt_large = False
+    print out_string
+    return (pt_paddr, pt_valid, pt_large)
+
+def _PmapL4Walk(pmap_addr_val,vaddr, ept_pmap, verbose_level = vSCRIPT):
     """ Walk the l4 pmap entry.
         params: pmap_addr_val - core.value representing kernel data of type pmap_addr_t
         vaddr : int - virtual address to walk
     """
-    is_cpu64_bit = int(kern.globals.cpu_64bit)
     pt_paddr = unsigned(pmap_addr_val)
     pt_valid = (unsigned(pmap_addr_val) != 0)
     pt_large = 0
     pframe_offset = 0
-    if pt_valid and is_cpu64_bit:
+    if pt_valid:
         # Lookup bits 47:39 of linear address in PML4T
         pt_index = (vaddr >> 39) & 0x1ff
         pframe_offset = vaddr & 0x7fffffffff
         if verbose_level > vHUMAN :
             print "pml4 (index {0:d}):".format(pt_index)
-        (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level)
+        if not(ept_pmap):
+            (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level)
+        else:
+            (pt_paddr, pt_valid, pt_large) = _PT_StepEPT(pt_paddr, pt_index, verbose_level)
     if pt_valid:
         # Lookup bits 38:30 of the linear address in PDPT
         pt_index = (vaddr >> 30) & 0x1ff
         pframe_offset = vaddr & 0x3fffffff
         if verbose_level > vHUMAN:
             print "pdpt (index {0:d}):".format(pt_index)
-        (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level)
+        if not(ept_pmap):
+            (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level)
+        else:
+            (pt_paddr, pt_valid, pt_large) = _PT_StepEPT(pt_paddr, pt_index, verbose_level)
     if pt_valid and not pt_large:
         #Lookup bits 29:21 of the linear address in PDPT
         pt_index = (vaddr >> 21) & 0x1ff
         pframe_offset = vaddr & 0x1fffff
         if verbose_level > vHUMAN:
             print "pdt (index {0:d}):".format(pt_index)
-        (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level)
+        if not(ept_pmap):
+            (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level)
+        else:
+            (pt_paddr, pt_valid, pt_large) = _PT_StepEPT(pt_paddr, pt_index, verbose_level)
     if pt_valid and not pt_large:
         #Lookup bits 20:21 of linear address in PT
         pt_index = (vaddr >> 12) & 0x1ff
         pframe_offset = vaddr & 0xfff
         if verbose_level > vHUMAN:
             print "pt (index {0:d}):".format(pt_index)
-        (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level)
+        if not(ept_pmap):
+            (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level)
+        else:
+            (pt_paddr, pt_valid, pt_large) = _PT_StepEPT(pt_paddr, pt_index, verbose_level)
     paddr = 0
     paddr_isvalid = False
     if pt_valid:
         paddr = pt_paddr + pframe_offset
         paddr_isvalid = True
-    
+
     if verbose_level > vHUMAN:
         if paddr_isvalid:
             pvalue = ReadPhysInt(paddr, 32, xnudefines.lcpu_self)
@@ -334,26 +438,27 @@ def _PmapL4Walk(pmap_addr_val,vaddr, verbose_level = vSCRIPT):
         else:
             print "no translation"
 
-    return
-def _PmapWalkARMLevel1Section(tte, vaddr, verbose_level = vSCRIPT):
-    paddr = 0
+    return paddr
+
+def PmapDecodeTTEARM(tte, level, verbose_level):
+    """ Display the bits of an ARM translation table or page table entry
+        in human-readable form.
+        tte: integer value of the TTE/PTE
+        level: translation table level.  Valid values are 1 or 2.
+        verbose_level: verbosity. vHUMAN, vSCRIPT, vDETAIL
+    """
     out_string = ""
-    #Supersection or just section?
-    if (tte & 0x40000) == 0x40000:
-        paddr = ( (tte & 0xFF000000) | (vaddr & 0x00FFFFFF) )
-    else:
-        paddr = ( (tte & 0xFFF00000) | (vaddr & 0x000FFFFF) )
-        
-    if verbose_level >= vSCRIPT:
-        out_string += "{0: <#020x}\n\t{1: <#020x}\n\t".format(addressof(tte), tte)
+    if level == 1 and (tte & 0x3) == 0x2:
+        if verbose_level < vSCRIPT:
+            return
+
         #bit [1:0] evaluated in PmapWalkARM
         # B bit 2
         b_bit = (tte & 0x4) >> 2
         # C bit 3
         c_bit = (tte & 0x8) >> 3
         #XN bit 4
-        if (tte & 0x10) : 
+        if (tte & 0x10) :
             out_string += "no-execute"
         else:
             out_string += "execute"
@@ -365,7 +470,7 @@ def _PmapWalkARMLevel1Section(tte, vaddr, verbose_level = vSCRIPT):
         # AP bit 15 and [11:10] merged to a single 3 bit value
         access = ( (tte & 0xc00) >> 10 ) | ((tte & 0x8000) >> 13)
         out_string += xnudefines.arm_level2_access_strings[access]
-        
+
         #TEX bit [14:12]
         tex_bits = ((tte & 0x7000) >> 12)
         #Print TEX, C , B all together
@@ -396,15 +501,97 @@ def _PmapWalkARMLevel1Section(tte, vaddr, verbose_level = vSCRIPT):
             out_string += " no-secure"
         else:
             out_string += " secure"
-        
+
+    elif level == 1 and (tte & 0x3) == 0x1:
+
+        if verbose_level >= vSCRIPT:
+            # bit [1:0] evaluated in PmapWalkARM
+            # NS bit 3
+            if tte & 0x8:
+                out_string += ' no-secure'
+            else:
+                out_string += ' secure'
+            #Domain bit [8:5]
+            out_string += " domain({:d})".format(((tte & 0x1e0) >> 5))
+            # IMP bit 9
+            out_string += " imp({:d})".format( ((tte & 0x200) >> 9))
+            out_string += "\n"
+
+    elif level == 2:
+        pte = tte
+        if verbose_level >= vSCRIPT:
+            if (pte & 0x3) == 0x0:
+                out_string += " invalid"
+            else:
+                if (pte & 0x3) == 0x1:
+                    out_string += " large"
+                    # XN bit 15
+                    if pte & 0x8000 == 0x8000:
+                        out_string+= " no-execute"
+                    else:
+                        out_string += " execute"
+                else:
+                    out_string += " small"
+                    # XN bit 0
+                    if (pte & 0x1) == 0x01:
+                        out_string += " no-execute"
+                    else:
+                        out_string += " execute"
+                # B bit 2
+                b_bit = (pte & 0x4) >> 2
+                c_bit = (pte & 0x8) >> 3
+                # AP bit 9 and [5:4], merged to a single 3-bit value
+                access = (pte & 0x30) >> 4 | (pte & 0x200) >> 7
+                out_string += xnudefines.arm_level2_access_strings[access]
+
+                #TEX bit [14:12] for large, [8:6] for small
+                tex_bits = ((pte & 0x1c0) >> 6)
+                if (pte & 0x3) == 0x1:
+                    tex_bits = ((pte & 0x7000) >> 12)
+
+                # Print TEX, C , B alltogether
+                out_string += " TEX:C:B({:d}{:d}{:d}:{:d}:{:d})".format(
+                                                                        1 if (tex_bits & 0x4) else 0,
+                                                                        1 if (tex_bits & 0x2) else 0,
+                                                                        1 if (tex_bits & 0x1) else 0,
+                                                                        c_bit,
+                                                                        b_bit
+                                                                        )
+                # S bit 10
+                if pte & 0x400 :
+                    out_string += " shareable"
+                else:
+                    out_string += " not-shareable"
+
+                # nG bit 11
+                if pte & 0x800:
+                    out_string += " not-global"
+                else:
+                    out_string += " global"
+    
     print out_string
+
+
+def _PmapWalkARMLevel1Section(tte, vaddr, verbose_level = vSCRIPT):
+    paddr = 0
+    #Supersection or just section?
+    if (tte & 0x40000) == 0x40000:
+        paddr = ( (tte & 0xFF000000) | (vaddr & 0x00FFFFFF) )
+    else:
+        paddr = ( (tte & 0xFFF00000) | (vaddr & 0x000FFFFF) )
+
+    if verbose_level >= vSCRIPT:
+        print "{0: <#020x}\n\t{1: <#020x}\n\t".format(addressof(tte), tte),
+
+    PmapDecodeTTEARM(tte, 1, verbose_level)
+
     return paddr
-    
-        
-    
+
+
+
 def _PmapWalkARMLevel2(tte, vaddr, verbose_level = vSCRIPT):
-    """ Pmap walk the level 2 tte. 
-        params: 
+    """ Pmap walk the level 2 tte.
+        params:
           tte - value object
           vaddr - int
         returns: str - description of the tte + additional informaiton based on verbose_level
@@ -413,82 +600,27 @@ def _PmapWalkARMLevel2(tte, vaddr, verbose_level = vSCRIPT):
     pte_index = (vaddr >> 12) & 0xFF
     pte_base_val = kern.GetValueFromAddress(pte_base, 'pt_entry_t *')
     pte = pte_base_val[pte_index]
-    out_string = ''
+
+    paddr = 0
+    if pte & 0x2:
+        paddr = (unsigned(pte) & 0xFFFFF000) | (vaddr & 0xFFF)
+
     if verbose_level >= vSCRIPT:
-        out_string += "{0: <#020x}\n\t{1: <#020x}\n\t".format(addressof(tte), tte)
-        # bit [1:0] evaluated in PmapWalkARM
-        # NS bit 3
-        if tte & 0x8:
-            out_string += ' no-secure'
-        else:
-            out_string += ' secure'
-        #Domain bit [8:5]
-        out_string += " domain({:d})".format(((tte & 0x1e0) >> 5))
-        # IMP bit 9
-        out_string += " imp({:d})".format( ((tte & 0x200) >> 9))
-        out_string += "\n"
+        print "{0: <#020x}\n\t{1: <#020x}\n\t".format(addressof(tte), tte),
+
+    PmapDecodeTTEARM(tte, 1, verbose_level)
     if verbose_level >= vSCRIPT:
-        out_string += "second-level table (index {:d}):\n".format(pte_index)
+        print "second-level table (index {:d}):".format(pte_index)
     if verbose_level >= vDETAIL:
         for i in range(256):
             tmp = pte_base_val[i]
-            out_string += "{0: <#020x}:\t{1: <#020x}\n".format(addressof(tmp), unsigned(tmp))
-
-    paddr = 0
-    if pte & 0x2:
-        paddr = (unsigned(pte) & 0xFFFFF000) | (vaddr & 0xFFF)
+            print "{0: <#020x}:\t{1: <#020x}".format(addressof(tmp), unsigned(tmp))
 
     if verbose_level >= vSCRIPT:
-        out_string += " {0: <#020x}\n\t{1: <#020x}\n\t".format(addressof(pte), unsigned(pte))
-        if (pte & 0x3) == 0x0:
-            out_string += " invalid"
-        else:
-            if (pte & 0x3) == 0x1:
-                out_string += " large"
-                # XN bit 15
-                if pte & 0x8000 == 0x8000: 
-                    out_string+= " no-execute" 
-                else: 
-                    out_string += " execute"
-            else:
-                out_string += " small"
-                # XN bit 0
-                if (pte & 0x1) == 0x01:
-                    out_string += " no-execute"
-                else:
-                    out_string += " execute"
-            # B bit 2
-            b_bit = (pte & 0x4) >> 2
-            c_bit = (pte & 0x8) >> 3
-            # AP bit 9 and [5:4], merged to a single 3-bit value
-            access = (pte & 0x30) >> 4 | (pte & 0x200) >> 7
-            out_string += xnudefines.arm_level2_access_strings[access]
-            
-            #TEX bit [14:12] for large, [8:6] for small
-            tex_bits = ((pte & 0x1c0) >> 6)
-            if (pte & 0x3) == 0x1:
-                tex_bits = ((pte & 0x7000) >> 12)    
-            
-            # Print TEX, C , B alltogether
-            out_string += " TEX:C:B({:d}{:d}{:d}:{:d}:{:d})".format(
-                                                                    1 if (tex_bits & 0x4) else 0,
-                                                                    1 if (tex_bits & 0x2) else 0,
-                                                                    1 if (tex_bits & 0x1) else 0,
-                                                                    c_bit,
-                                                                    b_bit
-                                                                    )
-            # S bit 10
-            if pte & 0x400 :
-                out_string += " shareable"
-            else:
-                out_string += " not-shareable"
-            
-            # nG bit 11
-            if pte & 0x800:
-                out_string += " not-global"
-            else:
-                out_string += " global"
-    print out_string
+        print " {0: <#020x}\n\t{1: <#020x}\n\t".format(addressof(pte), unsigned(pte)),
+
+    PmapDecodeTTEARM(pte, 2, verbose_level)
+
     return paddr
     #end of level 2 walking of arm
 
@@ -501,12 +633,15 @@ def PmapWalkARM(pmap, vaddr, verbose_level = vHUMAN):
     """
     paddr = 0
     # shift by TTESHIFT (20) to get tte index
-    tte_index = ((vaddr - unsigned(pmap.min)) >> 20 )
+    # Assume all L1 indexing starts at VA 0...for our purposes it does,
+    # as that's where all user pmaps start, and the kernel pmap contains
+    # 4 L1 pages (the lower 2 of which are unused after bootstrap)
+    tte_index = vaddr >> 20
     tte = pmap.tte[tte_index]
     if verbose_level >= vSCRIPT:
         print "First-level table (index {:d}):".format(tte_index)
     if verbose_level >= vDETAIL:
-        for i in range(0, 4096):
+        for i in range(0, pmap.tte_index_max):
             ptr = unsigned(addressof(pmap.tte[i]))
             val = unsigned(pmap.tte[i])
             print "{0: <#020x}:\t {1: <#020x}".format(ptr, val)
@@ -527,28 +662,239 @@ def PmapWalkARM(pmap, vaddr, verbose_level = vHUMAN):
 
     return paddr
 
-def PmapWalkX86_64(pmapval, vaddr):
+def PmapWalkX86_64(pmapval, vaddr, verbose_level = vSCRIPT):
     """
         params: pmapval - core.value representing pmap_t in kernel
         vaddr:  int     - int representing virtual address to walk
     """
-    _PmapL4Walk(pmapval.pm_cr3, vaddr, config['verbosity'])
+    if pmapval.pm_cr3 != 0:
+        if verbose_level > vHUMAN:
+            print "Using normal Intel PMAP from pm_cr3\n"
+        return _PmapL4Walk(pmapval.pm_cr3, vaddr, 0, config['verbosity'])
+    else:
+        if verbose_level > vHUMAN:
+            print "Using EPT pmap from pm_eptp\n"
+        return _PmapL4Walk(pmapval.pm_eptp, vaddr, 1, config['verbosity'])
 
 def assert_64bit(val):
     assert(val < 2**64)
 
+ARM64_TTE_SIZE = 8
+ARM64_TTE_SHIFT = 3
+ARM64_VMADDR_BITS = 48
+
+def PmapBlockOffsetMaskARM64(level):
+    assert level >= 1 and level <= 3
+    page_size = kern.globals.arm_hardware_page_size
+    ttentries = (page_size / ARM64_TTE_SIZE)
+    return page_size * (ttentries ** (3 - level)) - 1
+
+def PmapBlockBaseMaskARM64(level):
+    assert level >= 1 and level <= 3
+    page_size = kern.globals.arm_hardware_page_size
+    return ((1 << ARM64_VMADDR_BITS) - 1) & ~PmapBlockOffsetMaskARM64(level)
+
+def PmapIndexMaskARM64(level):
+    assert level >= 1 and level <= 3
+    page_size = kern.globals.arm_hardware_page_size
+    ttentries = (page_size / ARM64_TTE_SIZE)
+    return page_size * (ttentries ** (3 - level) * (ttentries - 1))
+
+def PmapIndexDivideARM64(level):
+    assert level >= 1 and level <= 3
+    page_size = kern.globals.arm_hardware_page_size
+    ttentries = (page_size / ARM64_TTE_SIZE)
+    return page_size * (ttentries ** (3 - level))
+
+def PmapTTnIndexARM64(vaddr, level):
+    assert(type(vaddr) in (long, int))
+    assert_64bit(vaddr)
+
+    return (vaddr & PmapIndexMaskARM64(level)) // PmapIndexDivideARM64(level)
+
+def PmapDecodeTTEARM64(tte, level):
+    """ Display the bits of an ARM64 translation table or page table entry
+        in human-readable form.
+        tte: integer value of the TTE/PTE
+        level: translation table level.  Valid values are 1, 2, or 3.
+    """
+    assert(type(tte) == long)
+    assert(type(level) == int)
+    assert_64bit(tte)
+
+    if tte & 0x1 == 0x1:
+        if (tte & 0x2 == 0x2) and (level != 0x3):
+            print "Type       = Table pointer."
+            print "Table addr = {:#x}.".format(tte & 0xfffffffff000)
+            print "PXN        = {:#x}.".format((tte >> 59) & 0x1)
+            print "XN         = {:#x}.".format((tte >> 60) & 0x1)
+            print "AP         = {:#x}.".format((tte >> 61) & 0x3)
+            print "NS         = {:#x}".format(tte >> 63)
+        else:
+            print "Type       = Block."
+            print "AttrIdx    = {:#x}.".format((tte >> 2) & 0x7)
+            print "NS         = {:#x}.".format((tte >> 5) & 0x1)
+            print "AP         = {:#x}.".format((tte >> 6) & 0x3)
+            print "SH         = {:#x}.".format((tte >> 8) & 0x3)
+            print "AF         = {:#x}.".format((tte >> 10) & 0x1)
+            print "nG         = {:#x}.".format((tte >> 11) & 0x1)
+            print "HINT       = {:#x}.".format((tte >> 52) & 0x1)
+            print "PXN        = {:#x}.".format((tte >> 53) & 0x1)
+            print "XN         = {:#x}.".format((tte >> 54) & 0x1)
+            print "SW Use     = {:#x}.".format((tte >> 55) & 0xf)
+    else:
+        print "Invalid."
+
+    return
+
+def PmapWalkARM64(pmap, vaddr, verbose_level = vHUMAN):
+    assert(type(pmap) == core.cvalue.value)
+    assert(type(vaddr) in (long, int))
+    page_size = kern.globals.arm_hardware_page_size
+    page_offset_mask = (page_size - 1)
+    page_base_mask = ((1 << ARM64_VMADDR_BITS) - 1) & (~page_offset_mask)
+
+    assert_64bit(vaddr)
+    paddr = -1
+
+    tt0_index = 0
+    tt1_index = PmapTTnIndexARM64(vaddr, 1)
+    tt2_index = PmapTTnIndexARM64(vaddr, 2)
+    tt3_index = PmapTTnIndexARM64(vaddr, 3)
+
+    # The pmap starts at a page tabel level that is defined by register
+    # values; the kernel exports the root level for LLDB
+    level = kern.globals.arm64_root_pgtable_level
+    assert(level <= 3)
+
+    if level == 0:
+        root_tt_index = tt0_index
+    elif level == 1:
+        root_tt_index = tt1_index
+    elif level == 2:
+        root_tt_index = tt2_index
+    elif level == 3:
+        root_tt_index = tt3_index
+
+    # If the root of the page table is not a full page, we need to
+    # truncate the index
+    root_tt_index = root_tt_index % unsigned(kern.globals.arm64_root_pgtable_num_ttes)
+
+    tte = long(unsigned(pmap.tte[root_tt_index]))
+    assert(type(tte) == long)
+    assert_64bit(tte)
+
+    while (True):
+        if (level == 0):
+            # L0
+            # This is unsupported at the moment, as no kernel configurations use L0
+            assert(False)
+
+        elif (level == 1):
+            # L1
+            if verbose_level >= vSCRIPT:
+                print "L1 entry: {:#x}".format(tte)
+            if verbose_level >= vDETAIL:
+                PmapDecodeTTEARM64(tte, 1)
+
+            if tte & 0x1 == 0x1:
+                # Check for L1 block entry
+                if tte & 0x2 == 0x0:
+                    # Handle L1 block entry
+                    paddr = tte & PmapBlockBaseMaskARM64(1)
+                    paddr = paddr | (vaddr & PmapBlockOffsetMaskARM64(1))
+                    print "phys: {:#x}".format(paddr)
+                    break
+                else:
+                    # Handle L1 table entry
+                    l2_phys = (tte & page_base_mask) + (ARM64_TTE_SIZE * tt2_index)
+                    assert(type(l2_phys) == long)
+
+                    l2_virt = kern.PhysToKernelVirt(l2_phys)
+                    assert(type(l2_virt) == long)
+
+                    if verbose_level >= vDETAIL:
+                        print "L2 physical address: {:#x}. L2 virtual address: {:#x}".format(l2_phys, l2_virt)
+
+                    ttep = kern.GetValueFromAddress(l2_virt, "tt_entry_t*")
+                    tte = long(unsigned(dereference(ttep)))
+                    assert(type(tte) == long)
+            elif verbose_level >= vHUMAN:
+                print "L1 entry invalid: {:#x}\n".format(tte)
+
+        elif (level == 2):
+            # L2
+            if verbose_level >= vSCRIPT:
+                print "L2 entry: {:#0x}".format(tte)
+            if verbose_level >= vDETAIL:
+                PmapDecodeTTEARM64(tte, 2)
+
+            if tte & 0x1 == 0x1:
+                # Check for L2 block entry
+                if tte & 0x2 == 0x0:
+                    # Handle L2 block entry
+                    paddr = tte & PmapBlockBaseMaskARM64(2)
+                    paddr = paddr | (vaddr & PmapBlockOffsetMaskARM64(2))
+                    break
+                else:
+                    # Handle L2 table entry
+                    l3_phys = (tte & page_base_mask) + (ARM64_TTE_SIZE * tt3_index)
+                    assert(type(l3_phys) == long)
+
+                    l3_virt = kern.PhysToKernelVirt(l3_phys)
+                    assert(type(l3_virt) == long)
+
+                    if verbose_level >= vDETAIL:
+                        print "L3 physical address: {:#x}. L3 virtual address: {:#x}".format(l3_phys, l3_virt)
+
+                    ttep = kern.GetValueFromAddress(l3_virt, "tt_entry_t*")
+                    tte = long(unsigned(dereference(ttep)))
+                    assert(type(tte) == long)
+            elif verbose_level >= vHUMAN: # tte & 0x1 == 0x1
+                print "L2 entry invalid: {:#x}\n".format(tte)
+
+        elif (level == 3):
+            # L3
+            if verbose_level >= vSCRIPT:
+                print "L3 entry: {:#0x}".format(tte)
+            if verbose_level >= vDETAIL:
+                PmapDecodeTTEARM64(tte, 3)
+
+            if tte & 0x3 == 0x3:
+                paddr = tte & page_base_mask
+                paddr = paddr | (vaddr & page_offset_mask)
+            elif verbose_level >= vHUMAN:
+                print "L3 entry invalid: {:#x}\n".format(tte)
+
+            # This was the leaf page table page for this request; we're done
+            break
+
+        # We've parsed one level, so go to the next level
+        assert(level <= 3)
+        level = level + 1
+
+    if verbose_level >= vHUMAN:
+        if paddr:
+            print "Translation of {:#x} is {:#x}.".format(vaddr, paddr)
+        else:
+            print "(no translation)"
+
+    return paddr
+
 def PmapWalk(pmap, vaddr, verbose_level = vHUMAN):
     if kern.arch == 'x86_64':
-        return PmapWalkX86_64(pmap, vaddr)
+        return PmapWalkX86_64(pmap, vaddr, verbose_level)
     elif kern.arch == 'arm':
         return PmapWalkARM(pmap, vaddr, verbose_level)
+    elif kern.arch.startswith('arm64'):
+        return PmapWalkARM64(pmap, vaddr, verbose_level)
     else:
         raise NotImplementedError("PmapWalk does not support {0}".format(kern.arch))
 
 @lldb_command('pmap_walk')
 def PmapWalkHelper(cmd_args=None):
     """ Perform a page-table walk in <pmap> for <virtual_address>.
-        Syntax: (lldb) pmap_walk <pmap> <virtual_address> [-v]
+        Syntax: (lldb) pmap_walk <pmap> <virtual_address> [-v] [-e]
             Multiple -v's can be specified for increased verbosity
     """
     if cmd_args == None or len(cmd_args) < 2:
@@ -558,3 +904,415 @@ def PmapWalkHelper(cmd_args=None):
     addr = unsigned(kern.GetValueFromAddress(cmd_args[1], 'void *'))
     PmapWalk(pmap, addr, config['verbosity'])
     return
+
+@lldb_command('decode_tte')
+def DecodeTTE(cmd_args=None):
+    """ Decode the bits in the TTE/PTE value specified <tte_val> for translation level <level>
+        Syntax: (lldb) decode_tte <tte_val> <level>
+    """
+    if cmd_args == None or len(cmd_args) < 2:
+        raise ArgumentError("Too few arguments to decode_tte.")
+    if kern.arch == 'arm':
+        PmapDecodeTTEARM(kern.GetValueFromAddress(cmd_args[0], "unsigned long"), ArgumentStringToInt(cmd_args[1]), vSCRIPT)
+    elif kern.arch.startswith('arm64'):
+        PmapDecodeTTEARM64(long(kern.GetValueFromAddress(cmd_args[0], "unsigned long")), ArgumentStringToInt(cmd_args[1]))
+    else:
+        raise NotImplementedError("decode_tte does not support {0}".format(kern.arch))
+
+
+PVH_HIGH_FLAGS_ARM64 = (1 << 62) | (1 << 61) | (1 << 60) | (1 << 59)
+PVH_HIGH_FLAGS_ARM32 = (1 << 31)
+
+def PVWalkARM(pa):
+    """ Walk a physical-to-virtual reverse mapping list maintained by the arm pmap
+        pa: physical address (NOT page number).  Does not need to be page-aligned 
+    """
+    vm_first_phys = unsigned(kern.globals.vm_first_phys)
+    vm_last_phys = unsigned(kern.globals.vm_last_phys)
+    if pa < vm_first_phys or pa >= vm_last_phys:
+        raise ArgumentError("PA {:#x} is outside range of managed physical addresses: [{:#x}, {:#x})".format(pa, vm_first_phys, vm_last_phys))
+    page_size = kern.globals.page_size
+    pn = (pa - unsigned(kern.globals.vm_first_phys)) / page_size
+    pvh = unsigned(kern.globals.pv_head_table[pn])
+    pvh_type = pvh & 0x3
+    print "PVH raw value: ({:#x})".format(pvh)
+    if kern.arch.startswith('arm64'):
+        iommu_flag = 0x4
+        iommu_table_flag = 1 << 63
+        pvh = pvh | PVH_HIGH_FLAGS_ARM64
+    else:
+        iommu_flag = 0
+        iommu_table_flag = 0 
+        pvh = pvh | PVH_HIGH_FLAGS_ARM32
+    if pvh_type == 0:
+        print "PVH type: NULL"
+        return
+    elif pvh_type == 3:
+        print "PVH type: page-table descriptor ({:#x})".format(pvh & ~0x3)
+        return
+    elif pvh_type == 2:
+        ptep = pvh & ~0x3
+        pte_str = ''
+        print "PVH type: single PTE"
+        if ptep & iommu_flag:
+            ptep = ptep & ~iommu_flag
+            if ptep & iommu_table_flag:
+                pte_str = ' (IOMMU table), entry'
+            else:
+                pte_str = ' (IOMMU state), descriptor'
+                ptep = ptep | iommu_table_flag
+        print "PTE {:#x}{:s}: {:#x}".format(ptep, pte_str, dereference(kern.GetValueFromAddress(ptep, 'pt_entry_t *')))
+    elif pvh_type == 1:
+        pvep = pvh & ~0x3
+        print "PVH type: PTE list"
+        while pvep != 0:
+            pve = kern.GetValueFromAddress(pvep, "pv_entry_t *")
+            if unsigned(pve.pve_next) & 0x1:
+                pve_str = ' (alt acct) '
+            else:
+                pve_str = ''
+            current_pvep = pvep
+            pvep = unsigned(pve.pve_next) & ~0x1
+            ptep = unsigned(pve.pve_ptep) & ~0x3
+            if ptep & iommu_flag:
+                ptep = ptep & ~iommu_flag
+                if ptep & iommu_table_flag:
+                    pve_str = ' (IOMMU table), entry'
+                else:
+                    pve_str = ' (IOMMU state), descriptor'
+                    ptep = ptep | iommu_table_flag
+            print "PVE {:#x}, PTE {:#x}{:s}: {:#x}".format(current_pvep, ptep, pve_str, dereference(kern.GetValueFromAddress(ptep, 'pt_entry_t *')))
+
+@lldb_command('pv_walk')
+def PVWalk(cmd_args=None):
+    """ Show mappings for <physical_address> tracked in the PV list.
+        Syntax: (lldb) pv_walk <physical_address>
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        raise ArgumentError("Too few arguments to pv_walk.")
+    if not kern.arch.startswith('arm'):
+        raise NotImplementedError("pv_walk does not support {0}".format(kern.arch))
+    PVWalkARM(kern.GetValueFromAddress(cmd_args[0], 'unsigned long'))
+
+@lldb_command('kvtophys')
+def KVToPhys(cmd_args=None):
+    """ Translate a kernel virtual address to the corresponding physical address.
+        Assumes the virtual address falls within the kernel static region.
+        Syntax: (lldb) kvtophys <kernel virtual address>
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        raise ArgumentError("Too few arguments to kvtophys.")
+    if kern.arch.startswith('arm'):
+        print "{:#x}".format(KVToPhysARM(long(unsigned(kern.GetValueFromAddress(cmd_args[0], 'unsigned long')))))
+    elif kern.arch == 'x86_64':
+        print "{:#x}".format(long(unsigned(kern.GetValueFromAddress(cmd_args[0], 'unsigned long'))) - unsigned(kern.globals.physmap_base))
+
+@lldb_command('phystokv')
+def PhysToKV(cmd_args=None):
+    """ Translate a physical address to the corresponding static kernel virtual address.
+        Assumes the physical address corresponds to managed DRAM.
+        Syntax: (lldb) phystokv <physical address>
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        raise ArgumentError("Too few arguments to phystokv.")
+    print "{:#x}".format(kern.PhysToKernelVirt(long(unsigned(kern.GetValueFromAddress(cmd_args[0], 'unsigned long')))))
+
+def KVToPhysARM(addr):
+    if kern.arch.startswith('arm64'):
+        ptov_table = kern.globals.ptov_table
+        for i in range(0, kern.globals.ptov_index):
+            if (addr >= long(unsigned(ptov_table[i].va))) and (addr < (long(unsigned(ptov_table[i].va)) + long(unsigned(ptov_table[i].len)))):
+                return (addr - long(unsigned(ptov_table[i].va)) + long(unsigned(ptov_table[i].pa)))
+    return (addr - unsigned(kern.globals.gVirtBase) + unsigned(kern.globals.gPhysBase))
+
+def ShowPTEARM(pte):
+    """ Display vital information about an ARM page table entry
+        pte: kernel virtual address of the PTE.  Should be L3 PTE.  May also work with L2 TTEs for certain devices.
+    """
+    page_size = kern.globals.arm_hardware_page_size
+    pn = (KVToPhysARM(pte) - unsigned(kern.globals.vm_first_phys)) / page_size
+    pvh = unsigned(kern.globals.pv_head_table[pn])
+    if kern.arch.startswith('arm64'):
+        pvh = pvh | PVH_HIGH_FLAGS_ARM64
+    else:
+        pvh = pvh | PVH_HIGH_FLAGS_ARM32
+    pvh_type = pvh & 0x3
+    if pvh_type != 0x3:
+        raise ValueError("PV head {:#x} does not correspond to a page-table descriptor".format(pvh))
+    ptd = kern.GetValueFromAddress(pvh & ~0x3, 'pt_desc_t *')
+    print "descriptor: {:#x}".format(ptd)
+    print "pmap: {:#x}".format(ptd.pmap)
+    pt_index = (pte % kern.globals.page_size) / page_size
+    pte_pgoff = pte % page_size
+    if kern.arch.startswith('arm64'):
+        pte_pgoff = pte_pgoff / 8
+        nttes = page_size / 8
+    else:
+        pte_pgoff = pte_pgoff / 4
+        nttes = page_size / 4
+    if ptd.ptd_info[pt_index].refcnt == 0x4000:
+        level = 2
+        granule = nttes * page_size
+    else:
+        level = 3
+        granule = page_size
+    print "maps VA: {:#x}".format(long(unsigned(ptd.ptd_info[pt_index].va)) + (pte_pgoff * granule))
+    pteval = long(unsigned(dereference(kern.GetValueFromAddress(unsigned(pte), 'pt_entry_t *'))))
+    print "value: {:#x}".format(pteval)
+    if kern.arch.startswith('arm64'):
+        print "level: {:d}".format(level)
+        PmapDecodeTTEARM64(pteval, level)
+    elif kern.arch == 'arm':
+        PmapDecodeTTEARM(pteval, 2, vSCRIPT)
+
+@lldb_command('showpte')
+def ShowPTE(cmd_args=None):
+    """ Display vital information about the page table entry at VA <pte>
+        Syntax: (lldb) showpte <pte_va>
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        raise ArgumentError("Too few arguments to showpte.")
+    if not kern.arch.startswith('arm'):
+        raise NotImplementedError("showpte does not support {0}".format(kern.arch))
+    ShowPTEARM(kern.GetValueFromAddress(cmd_args[0], 'unsigned long'))
+
+def FindMappingAtLevelARM(pmap, tt, nttes, level, action):
+    """ Perform the specified action for all valid mappings in an ARM translation table
+        pmap: owner of the translation table
+        tt: translation table or page table
+        nttes: number of entries in tt
+        level: translation table level, 1 or 2
+        action: callback for each valid TTE
+    """
+    for i in range(nttes):
+        try:
+            tte = tt[i]
+            if level == 1:
+                if tte & 0x3 == 0x1:
+                    type = 'table'
+                    granule = 1024
+                    paddr = tte & 0xFFFFFC00
+                elif tte & 0x3 == 0x2:
+                    type = 'block'
+                    if (tte & 0x40000) == 0x40000:
+                        granule = 1 << 24
+                        paddr = tte & 0xFF000000
+                    else:
+                        granule = 1 << 20
+                        paddr = tte & 0xFFF00000
+                else:
+                    continue
+            elif (tte & 0x3) == 0x1:
+                type = 'entry'
+                granule = 1 << 16
+                paddr = tte & 0xFFFF0000
+            elif (tte & 0x3) != 0:
+                type = 'entry' 
+                granule = 1 << 12
+                paddr = tte & 0xFFFFF000
+            else:
+                continue
+            action(pmap, level, type, addressof(tt[i]), paddr, granule)
+            if level == 1 and (tte & 0x3) == 0x1:
+                tt_next = kern.GetValueFromAddress(kern.PhysToKernelVirt(paddr), 'tt_entry_t *')
+                FindMappingAtLevelARM(pmap, tt_next, granule / 4, level + 1, action)
+        except Exception as exc:
+            print "Unable to access tte {:#x}".format(unsigned(addressof(tt[i])))
+
+def FindMappingAtLevelARM64(pmap, tt, nttes, level, action):
+    """ Perform the specified action for all valid mappings in an ARM64 translation table
+        pmap: owner of the translation table
+        tt: translation table or page table
+        nttes: number of entries in tt
+        level: translation table level, 1 2 or 3
+        action: callback for each valid TTE
+    """
+    page_size = kern.globals.arm_hardware_page_size
+    page_offset_mask = (page_size - 1)
+    page_base_mask = ((1 << ARM64_VMADDR_BITS) - 1) & (~page_offset_mask)
+    for i in range(nttes):
+        try:
+            tte = tt[i]
+            if tte & 0x1 == 0x1:
+                if tte & 0x2 == 0x2:
+                    if level < 3:
+                        type = 'table'
+                    else:
+                        type = 'entry'
+                    granule = page_size
+                    paddr = tte & page_base_mask
+                elif level < 3:
+                    type = 'block'
+                    granule = PmapBlockOffsetMaskARM64(level) + 1
+                    paddr = tte & PmapBlockBaseMaskARM64(level)
+                else:
+                    continue
+                action(pmap, level, type, addressof(tt[i]), paddr, granule)
+                if level < 3 and (tte & 0x2 == 0x2):
+                    tt_next = kern.GetValueFromAddress(kern.PhysToKernelVirt(paddr), 'tt_entry_t *')
+                    FindMappingAtLevelARM64(pmap, tt_next, granule / ARM64_TTE_SIZE, level + 1, action)
+        except Exception as exc:
+            print "Unable to access tte {:#x}".format(unsigned(addressof(tt[i]))) 
+
+def ScanPageTables(action, targetPmap=None):
+    """ Perform the specified action for all valid mappings in all page tables,
+        optionally restricted to a single pmap.
+        pmap: pmap whose page table should be scanned.  If None, all pmaps on system will be scanned.
+    """
+    print "Scanning all available translation tables.  This may take a long time..."
+    def ScanPmap(pmap, action):
+        if kern.arch.startswith('arm64'):
+            granule = kern.globals.arm64_root_pgtable_num_ttes * 8
+        elif kern.arch == 'arm':
+            granule = pmap.tte_index_max * 4
+        action(pmap, 1, 'root', pmap.tte, unsigned(pmap.ttep), granule)
+        if kern.arch.startswith('arm64'):
+            FindMappingAtLevelARM64(pmap, pmap.tte, kern.globals.arm64_root_pgtable_num_ttes, kern.globals.arm64_root_pgtable_level, action)
+        elif kern.arch == 'arm':
+            FindMappingAtLevelARM(pmap, pmap.tte, pmap.tte_index_max, 1, action)
+
+    if targetPmap is not None:
+        ScanPmap(kern.GetValueFromAddress(targetPmap, 'pmap_t'), action)
+    else:
+        for pmap in IterateQueue(kern.globals.map_pmap_list, 'pmap_t', 'pmaps'):
+            ScanPmap(pmap, action)        
+
+@lldb_command('showallmappings')
+def ShowAllMappings(cmd_args=None):
+    """ Find and display all available mappings on the system for
+        <physical_address>.  Optionally only searches the pmap
+        specified by [<pmap>]
+        Syntax: (lldb) showallmappings <physical_address> [<pmap>]
+        WARNING: this macro can take a long time (up to 30min.) to complete!
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        raise ArgumentError("Too few arguments to showallmappings.")
+    if not kern.arch.startswith('arm'):
+        raise NotImplementedError("showallmappings does not support {0}".format(kern.arch))
+    pa = kern.GetValueFromAddress(cmd_args[0], 'unsigned long')
+    targetPmap = None
+    if len(cmd_args) > 1:
+        targetPmap = cmd_args[1]
+    def printMatchedMapping(pmap, level, type, tte, paddr, granule):
+        if paddr <= pa < (paddr + granule):
+            print "pmap: {:#x}: L{:d} {:s} at {:#x}: [{:#x}, {:#x})".format(pmap, level, type, unsigned(tte), paddr, paddr + granule)
+    ScanPageTables(printMatchedMapping, targetPmap)
+
+def checkPVList(pmap, level, type, tte, paddr, granule):
+    """ Checks an ARM physical-to-virtual mapping list for consistency errors.
+        pmap: owner of the translation table
+        level: translation table level.  PV lists will only be checked for L2 (arm32) or L3 (arm64) tables.
+        type: unused
+        tte: KVA of PTE to check for presence in PV list.  If None, presence check will be skipped.
+        paddr: physical address whose PV list should be checked.  Need not be page-aligned.
+        granule: unused
+    """
+    vm_first_phys = unsigned(kern.globals.vm_first_phys)
+    vm_last_phys = unsigned(kern.globals.vm_last_phys)
+    page_size = kern.globals.page_size
+    if kern.arch.startswith('arm64'):
+        page_offset_mask = (page_size - 1)
+        page_base_mask = ((1 << ARM64_VMADDR_BITS) - 1) & (~page_offset_mask)
+        paddr = paddr & page_base_mask
+        max_level = 3
+        pvh_set_bits = PVH_HIGH_FLAGS_ARM64
+    elif kern.arch == 'arm':
+        page_base_mask = 0xFFFFF000
+        paddr = paddr & page_base_mask
+        max_level = 2
+        pvh_set_bits = PVH_HIGH_FLAGS_ARM32
+    if level < max_level or paddr < vm_first_phys or paddr >= vm_last_phys:
+        return
+    pn = (paddr - vm_first_phys) / page_size
+    pvh = unsigned(kern.globals.pv_head_table[pn]) | pvh_set_bits
+    pvh_type = pvh & 0x3
+    if pmap is not None:
+        pmap_str = "pmap: {:#x}: ".format(pmap)
+    else:
+        pmap_str = ''
+    if tte is not None:
+        tte_str = "pte {:#x} ({:#x}): ".format(unsigned(tte), paddr)
+    else:
+        tte_str = "paddr {:#x}: ".format(paddr) 
+    if pvh_type == 0 or pvh_type == 3:
+        print "{:s}{:s}unexpected PVH type {:d}".format(pmap_str, tte_str, pvh_type)
+    elif pvh_type == 2:
+        ptep = pvh & ~0x3
+        if tte is not None and ptep != unsigned(tte):
+            print "{:s}{:s}PVH mismatch ({:#x})".format(pmap_str, tte_str, ptep)
+        try:
+            pte = long(unsigned(dereference(kern.GetValueFromAddress(ptep, 'pt_entry_t *')))) & page_base_mask 
+            if (pte != paddr):
+                print "{:s}{:s}PVH {:#x} maps wrong page ({:#x}) ".format(pmap_str, tte_str, ptep, pte)
+        except Exception as exc:
+            print "{:s}{:s}Unable to read PVH {:#x}".format(pmap_str, tte_str, ptep)
+    elif pvh_type == 1:
+        pvep = pvh & ~0x3
+        tte_match = False
+        while pvep != 0:
+            pve = kern.GetValueFromAddress(pvep, "pv_entry_t *")
+            pvep = unsigned(pve.pve_next) & ~0x1
+            ptep = unsigned(pve.pve_ptep) & ~0x3
+            if tte is not None and ptep == unsigned(tte):
+                tte_match = True
+            try:
+                pte = long(unsigned(dereference(kern.GetValueFromAddress(ptep, 'pt_entry_t *')))) & page_base_mask 
+                if (pte != paddr):
+                    print "{:s}{:s}PVE {:#x} maps wrong page ({:#x}) ".format(pmap_str, tte_str, ptep, pte)
+            except Exception as exc:
+                print "{:s}{:s}Unable to read PVE {:#x}".format(pmap_str, tte_str, ptep)
+        if tte is not None and not tte_match:
+            print "{:s}{:s}not found in PV list".format(pmap_str, tte_str, paddr)
+
+@lldb_command('pv_check', 'P')
+def PVCheck(cmd_args=None, cmd_options={}):
+    """ Check the physical-to-virtual mapping for a given PTE or physical address
+        Syntax: (lldb) pv_check <addr> [-p]
+            -P        : Interpret <addr> as a physical address rather than a PTE
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        raise ArgumentError("Too few arguments to pv_check.")
+    if kern.arch == 'arm':
+        level = 2
+    elif kern.arch.startswith('arm64'):
+        level = 3
+    else:
+        raise NotImplementedError("showallmappings does not support {0}".format(kern.arch))
+    if "-P" in cmd_options:
+        pte = None
+        pa = long(unsigned(kern.GetValueFromAddress(cmd_args[0], "unsigned long")))
+    else:
+        pte = kern.GetValueFromAddress(cmd_args[0], 'pt_entry_t *')
+        pa = long(unsigned(dereference(pte)))
+    checkPVList(None, level, None, pte, pa, None)
+
+@lldb_command('check_pmaps')
+def CheckPmapIntegrity(cmd_args=None):
+    """ Performs a system-wide integrity check of all PTEs and associated PV lists.
+        Optionally only checks the pmap specified by [<pmap>]
+        Syntax: (lldb) check_pmaps [<pmap>]
+        WARNING: this macro can take a HUGE amount of time (several hours) if you do not
+        specify [pmap] to limit it to a single pmap.  It will also give false positives
+        for kernel_pmap, as we do not create PV entries for static kernel mappings on ARM.
+        Use of this macro without the [<pmap>] argument is heavily discouraged.
+    """
+    if not kern.arch.startswith('arm'):
+        raise NotImplementedError("showallmappings does not support {0}".format(kern.arch))
+    targetPmap = None
+    if len(cmd_args) > 0:
+        targetPmap = cmd_args[0]
+    ScanPageTables(checkPVList, targetPmap)
+
+@lldb_command('pmapsforledger')
+def PmapsForLedger(cmd_args=None):
+    """ Find and display all pmaps currently using <ledger>.
+        Syntax: (lldb) pmapsforledger <ledger>
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        raise ArgumentError("Too few arguments to pmapsforledger.")
+    if not kern.arch.startswith('arm'):
+        raise NotImplementedError("pmapsforledger does not support {0}".format(kern.arch))
+    ledger = kern.GetValueFromAddress(cmd_args[0], 'ledger_t')
+    for pmap in IterateQueue(kern.globals.map_pmap_list, 'pmap_t', 'pmaps'):
+        if pmap.ledger == ledger:
+            print "pmap: {:#x}".format(pmap)