]> git.saurik.com Git - apple/xnu.git/blobdiff - tools/lldbmacros/pmap.py
xnu-2422.1.72.tar.gz
[apple/xnu.git] / tools / lldbmacros / pmap.py
diff --git a/tools/lldbmacros/pmap.py b/tools/lldbmacros/pmap.py
new file mode 100644 (file)
index 0000000..fc3a005
--- /dev/null
@@ -0,0 +1,560 @@
+from xnu import *
+import xnudefines
+from kdp import *
+from utils import *
+
+def ReadPhysInt(phys_addr, bitsize = 64, cpuval = None):
+    """ 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
+            cpuval    : None (optional)
+        returns:
+            int - int value read from memory. in case of failure 0xBAD10AD is returned.
+    """
+    if "kdp" == GetConnectionProtocol():
+        return KDPReadPhysMEM(phys_addr, bitsize)
+
+    #NO KDP. Attempt to use physical memory
+    paddr_in_kva = kern.PhysToKernelVirt(long(phys_addr))
+    if paddr_in_kva :
+        if bitsize == 64 :
+            return kern.GetValueFromAddress(paddr_in_kva, 'uint64_t *').GetSBValue().Dereference().GetValueAsUnsigned()
+        if bitsize == 32 :
+            return kern.GetValueFromAddress(paddr_in_kva, 'uint32_t *').GetSBValue().Dereference().GetValueAsUnsigned()
+        if bitsize == 16 :
+            return kern.GetValueFromAddress(paddr_in_kva, 'uint16_t *').GetSBValue().Dereference().GetValueAsUnsigned()
+        if bitsize == 8 :
+            return kern.GetValueFromAddress(paddr_in_kva, 'uint8_t *').GetSBValue().Dereference().GetValueAsUnsigned()
+    return 0xBAD10AD
+
+@lldb_command('readphys')
+def ReadPhys(cmd_args = None):
+    """ 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 
+    """
+    if cmd_args == None or len(cmd_args) < 2:
+        print "Insufficient arguments.", ReadPhys.__doc__
+        return False
+    else:
+        nbits = ArgumentStringToInt(cmd_args[0])
+        phys_addr = ArgumentStringToInt(cmd_args[1])
+        print "{0: <#x}".format(ReadPhysInt(phys_addr, nbits))
+    return True
+
+lldb_alias('readphys8', 'readphys 8 ')
+lldb_alias('readphys16', 'readphys 16 ')
+lldb_alias('readphys32', 'readphys 32 ')
+lldb_alias('readphys64', 'readphys 64 ')
+
+def KDPReadPhysMEM(address, bits):
+    """ Setup the state for READPHYSMEM64 commands for reading data via kdp
+        params:
+            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. 
+            0xBAD10AD: if failed to read data.
+    """
+    retval = 0xBAD10AD
+    if "kdp" != GetConnectionProtocol():
+        print "Target is not connected over kdp. Nothing to do here."
+        return retval
+
+    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
+    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
+    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
+    if not WriteInt32ToMemoryAddress(0, input_address):
+        return retval
+
+    kdp_pkt_size = GetType('kdp_readphysmem64_req_t').GetByteSize()
+    if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
+        return retval
+
+    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))
+            if bits == 64 :
+                retval =  kern.GetValueFromAddress(data_address, 'uint64_t *').GetSBValue().Dereference().GetValueAsUnsigned()
+            if bits == 32 :
+                retval =  kern.GetValueFromAddress(data_address, 'uint32_t *').GetSBValue().Dereference().GetValueAsUnsigned()
+            if bits == 16 :
+                retval =  kern.GetValueFromAddress(data_address, 'uint16_t *').GetSBValue().Dereference().GetValueAsUnsigned()
+            if bits == 8 :
+                retval =  kern.GetValueFromAddress(data_address, 'uint8_t *').GetSBValue().Dereference().GetValueAsUnsigned()
+    return retval
+
+
+def KDPWritePhysMEM(address, intval, bits):
+    """ Setup the state for WRITEPHYSMEM64 commands for saving data in kdp
+        params:
+            address : int - address where to save the data
+            intval : int - integer value to be stored in memory
+            bits : int - number of bits in the intval (8/16/32/64)
+        returns:
+            boolean: True if the write succeeded.
+    """
+    if "kdp" != GetConnectionProtocol():
+        print "Target is not connected over kdp. Nothing to do here."
+        return False
+    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
+    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
+    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
+    if not WriteInt32ToMemoryAddress(0, input_address):
+        return False
+
+    kdp_pkt_size = GetType('kdp_writephysmem64_req_t').GetByteSize()
+    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
+        if bits == 16:
+            if not WriteInt16ToMemoryAddress(intval, int(addressof(pkt.data))):
+                return False
+        if bits == 32:
+            if not WriteInt32ToMemoryAddress(intval, int(addressof(pkt.data))):
+                return False
+        if bits == 64:
+            if not WriteInt64ToMemoryAddress(intval, int(addressof(pkt.data))):
+                return False
+        if WriteInt32ToMemoryAddress(1, input_address):
+            return True
+    return False
+
+
+def WritePhysInt(phys_addr, int_val, bitsize = 64):
+    """ 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
+            bitsize   : int - defines how many bytes to read. defaults to 64 bit
+        returns:
+            bool - True if write was successful.
+    """
+    if "kdp" == GetConnectionProtocol():
+        if not KDPWritePhysMEM(phys_addr, int_val, bitsize):
+            print "Failed to write via KDP."
+            return False
+        return True
+    #We are not connected via KDP. So do manual math and savings.
+    print "Failed: Write to physical memory is not supported for %s connection." % GetConnectionProtocol()
+    return False
+
+@lldb_command('writephys')
+def WritePhys(cmd_args=None):
+    """ 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 
+    """
+    if cmd_args == None or len(cmd_args) < 3:
+        print "Invalid arguments.", WritePhys.__doc__
+    else:
+        nbits = ArgumentStringToInt(cmd_args[0])
+        phys_addr = ArgumentStringToInt(cmd_args[1])
+        int_value = ArgumentStringToInt(cmd_args[2])
+        print WritePhysInt(phys_addr, int_value, nbits)
+
+
+lldb_alias('writephys8', 'writephys 8 ')
+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
+       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 & 0x1 :
+            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 & 0x1:
+            out_string += " valid"
+            pt_paddr = entry & paddr_mask
+            pt_valid = True
+        else:
+            out_string += " invalid"
+            pt_paddr = 0
+            pt_valid = False
+            #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):
+    """ 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:
+        # 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 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 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 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)
+    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)
+            print "phys {0: <#020x}: {1: <#020x}".format(paddr, pvalue)
+        else:
+            print "no translation"
+
+    return
+def _PmapWalkARMLevel1Section(tte, vaddr, verbose_level = vSCRIPT):
+    paddr = 0
+    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)
+        #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) : 
+            out_string += "no-execute"
+        else:
+            out_string += "execute"
+        #Domain bit [8:5] if not supersection
+        if (tte & 0x40000) == 0x0:
+            out_string += " domain ({:d})".format(((tte & 0x1e0) >> 5) )
+        #IMP bit 9
+        out_string += " imp({:d})".format( ((tte & 0x200) >> 9) )
+        # 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
+        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 16
+        if tte & 0x10000:
+            out_string += " shareable"
+        else:
+            out_string += " not-shareable"
+        # nG bit 17
+        if tte & 0x20000 :
+            out_string += " not-global"
+        else:
+            out_string += " global"
+        # Supersection bit 18
+        if tte & 0x40000:
+            out_string += " supersection"
+        else:
+            out_string += " section"
+        #NS bit 19
+        if tte & 0x80000 :
+            out_string += " no-secure"
+        else:
+            out_string += " secure"
+        
+    print out_string
+    return paddr
+    
+        
+    
+def _PmapWalkARMLevel2(tte, vaddr, verbose_level = vSCRIPT):
+    """ Pmap walk the level 2 tte. 
+        params: 
+          tte - value object
+          vaddr - int
+        returns: str - description of the tte + additional informaiton based on verbose_level
+    """
+    pte_base = kern.PhysToKernelVirt(tte & 0xFFFFFC00)
+    pte_index = (vaddr >> 12) & 0xFF
+    pte_base_val = kern.GetValueFromAddress(pte_base, 'pt_entry_t *')
+    pte = pte_base_val[pte_index]
+    out_string = ''
+    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"
+    if verbose_level >= vSCRIPT:
+        out_string += "second-level table (index {:d}):\n".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)
+
+    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
+    return paddr
+    #end of level 2 walking of arm
+
+
+def PmapWalkARM(pmap, vaddr, verbose_level = vHUMAN):
+    """ Pmap walking for ARM kernel.
+        params:
+          pmapval: core.value - representing pmap_t in kernel
+          vaddr:  int     - integer representing virtual address to walk
+    """
+    paddr = 0
+    # shift by TTESHIFT (20) to get tte index
+    tte_index = ((vaddr - unsigned(pmap.min)) >> 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):
+            ptr = unsigned(addressof(pmap.tte[i]))
+            val = unsigned(pmap.tte[i])
+            print "{0: <#020x}:\t {1: <#020x}".format(ptr, val)
+    if (tte & 0x3) == 0x1:
+        paddr = _PmapWalkARMLevel2(tte, vaddr, verbose_level)
+    elif (tte & 0x3) == 0x2 :
+        paddr = _PmapWalkARMLevel1Section(tte, vaddr, verbose_level)
+    else:
+        paddr = 0
+        if verbose_level >= vSCRIPT:
+            print "Invalid First-Level Translation Table Entry: {0: #020x}".format(tte)
+
+    if verbose_level >= vHUMAN:
+        if paddr:
+            print "Translation of {:#x} is {:#x}.".format(vaddr, paddr)
+        else:
+            print "(no translation)"
+
+    return paddr
+
+def PmapWalkX86_64(pmapval, vaddr):
+    """
+        params: pmapval - core.value representing pmap_t in kernel
+        vaddr:  int     - int representing virtual address to walk
+    """
+    _PmapL4Walk(pmapval.pm_cr3, vaddr, config['verbosity'])
+
+def assert_64bit(val):
+    assert(val < 2**64)
+
+def PmapWalk(pmap, vaddr, verbose_level = vHUMAN):
+    if kern.arch == 'x86_64':
+        return PmapWalkX86_64(pmap, vaddr)
+    elif kern.arch == 'arm':
+        return PmapWalkARM(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]
+            Multiple -v's can be specified for increased verbosity
+    """
+    if cmd_args == None or len(cmd_args) < 2:
+        raise ArgumentError("Too few arguments to pmap_walk.")
+
+    pmap = kern.GetValueAsType(cmd_args[0], 'pmap_t')
+    addr = unsigned(kern.GetValueFromAddress(cmd_args[1], 'void *'))
+    PmapWalk(pmap, addr, config['verbosity'])
+    return