1from xnu import *
2import xnudefines
3from kdp import *
4from utils import *
5
6def ReadPhysInt(phys_addr, bitsize = 64, cpuval = None):
7    """ Read a physical memory data based on address.
8        params:
9            phys_addr : int - Physical address to read
10            bitsize   : int - defines how many bytes to read. defaults to 64 bit
11            cpuval    : None (optional)
12        returns:
13            int - int value read from memory. in case of failure 0xBAD10AD is returned.
14    """
15    if "kdp" == GetConnectionProtocol():
16        return KDPReadPhysMEM(phys_addr, bitsize)
17
18    #NO KDP. Attempt to use physical memory
19    paddr_in_kva = kern.PhysToKernelVirt(long(phys_addr))
20    if paddr_in_kva :
21        if bitsize == 64 :
22            return kern.GetValueFromAddress(paddr_in_kva, 'uint64_t *').GetSBValue().Dereference().GetValueAsUnsigned()
23        if bitsize == 32 :
24            return kern.GetValueFromAddress(paddr_in_kva, 'uint32_t *').GetSBValue().Dereference().GetValueAsUnsigned()
25        if bitsize == 16 :
26            return kern.GetValueFromAddress(paddr_in_kva, 'uint16_t *').GetSBValue().Dereference().GetValueAsUnsigned()
27        if bitsize == 8 :
28            return kern.GetValueFromAddress(paddr_in_kva, 'uint8_t *').GetSBValue().Dereference().GetValueAsUnsigned()
29    return 0xBAD10AD
30
31@lldb_command('readphys')
32def ReadPhys(cmd_args = None):
33    """ Reads the specified untranslated address
34        The argument is interpreted as a physical address, and the 64-bit word
35        addressed is displayed.
36        usage: readphys <nbits> <address>
37        nbits: 8,16,32,64
38        address: 1234 or 0x1234
39    """
40    if cmd_args == None or len(cmd_args) < 2:
41        print "Insufficient arguments.", ReadPhys.__doc__
42        return False
43    else:
44        nbits = ArgumentStringToInt(cmd_args[0])
45        phys_addr = ArgumentStringToInt(cmd_args[1])
46        print "{0: <#x}".format(ReadPhysInt(phys_addr, nbits))
47    return True
48
49lldb_alias('readphys8', 'readphys 8 ')
50lldb_alias('readphys16', 'readphys 16 ')
51lldb_alias('readphys32', 'readphys 32 ')
52lldb_alias('readphys64', 'readphys 64 ')
53
54def KDPReadPhysMEM(address, bits):
55    """ Setup the state for READPHYSMEM64 commands for reading data via kdp
56        params:
57            address : int - address where to read the data from
58            bits : int - number of bits in the intval (8/16/32/64)
59        returns:
60            int: read value from memory.
61            0xBAD10AD: if failed to read data.
62    """
63    retval = 0xBAD10AD
64    if "kdp" != GetConnectionProtocol():
65        print "Target is not connected over kdp. Nothing to do here."
66        return retval
67
68    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
69    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
70    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
71    if not WriteInt32ToMemoryAddress(0, input_address):
72        return retval
73
74    kdp_pkt_size = GetType('kdp_readphysmem64_req_t').GetByteSize()
75    if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
76        return retval
77
78    data_addr = int(addressof(kern.globals.manual_pkt))
79    pkt = kern.GetValueFromAddress(data_addr, 'kdp_readphysmem64_req_t *')
80
81    header_value =GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_READPHYSMEM64'), length=kdp_pkt_size)
82
83    if ( WriteInt64ToMemoryAddress((header_value), int(addressof(pkt.hdr))) and
84         WriteInt64ToMemoryAddress(address, int(addressof(pkt.address))) and
85         WriteInt32ToMemoryAddress((bits/8), int(addressof(pkt.nbytes))) and
86         WriteInt16ToMemoryAddress(xnudefines.lcpu_self, int(addressof(pkt.lcpu)))
87         ):
88
89        if WriteInt32ToMemoryAddress(1, input_address):
90            # now read data from the kdp packet
91            data_address = unsigned(addressof(kern.GetValueFromAddress(int(addressof(kern.globals.manual_pkt.data)), 'kdp_readphysmem64_reply_t *').data))
92            if bits == 64 :
93                retval =  kern.GetValueFromAddress(data_address, 'uint64_t *').GetSBValue().Dereference().GetValueAsUnsigned()
94            if bits == 32 :
95                retval =  kern.GetValueFromAddress(data_address, 'uint32_t *').GetSBValue().Dereference().GetValueAsUnsigned()
96            if bits == 16 :
97                retval =  kern.GetValueFromAddress(data_address, 'uint16_t *').GetSBValue().Dereference().GetValueAsUnsigned()
98            if bits == 8 :
99                retval =  kern.GetValueFromAddress(data_address, 'uint8_t *').GetSBValue().Dereference().GetValueAsUnsigned()
100    return retval
101
102
103def KDPWritePhysMEM(address, intval, bits):
104    """ Setup the state for WRITEPHYSMEM64 commands for saving data in kdp
105        params:
106            address : int - address where to save the data
107            intval : int - integer value to be stored in memory
108            bits : int - number of bits in the intval (8/16/32/64)
109        returns:
110            boolean: True if the write succeeded.
111    """
112    if "kdp" != GetConnectionProtocol():
113        print "Target is not connected over kdp. Nothing to do here."
114        return False
115    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
116    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
117    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
118    if not WriteInt32ToMemoryAddress(0, input_address):
119        return False
120
121    kdp_pkt_size = GetType('kdp_writephysmem64_req_t').GetByteSize()
122    if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
123        return False
124
125    data_addr = int(addressof(kern.globals.manual_pkt))
126    pkt = kern.GetValueFromAddress(data_addr, 'kdp_writephysmem64_req_t *')
127
128    header_value =GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_WRITEPHYSMEM64'), length=kdp_pkt_size)
129
130    if ( WriteInt64ToMemoryAddress((header_value), int(addressof(pkt.hdr))) and
131         WriteInt64ToMemoryAddress(address, int(addressof(pkt.address))) and
132         WriteInt32ToMemoryAddress((bits/8), int(addressof(pkt.nbytes))) and
133         WriteInt16ToMemoryAddress(xnudefines.lcpu_self, int(addressof(pkt.lcpu)))
134         ):
135
136        if bits == 8:
137            if not WriteInt8ToMemoryAddress(intval, int(addressof(pkt.data))):
138                return False
139        if bits == 16:
140            if not WriteInt16ToMemoryAddress(intval, int(addressof(pkt.data))):
141                return False
142        if bits == 32:
143            if not WriteInt32ToMemoryAddress(intval, int(addressof(pkt.data))):
144                return False
145        if bits == 64:
146            if not WriteInt64ToMemoryAddress(intval, int(addressof(pkt.data))):
147                return False
148        if WriteInt32ToMemoryAddress(1, input_address):
149            return True
150    return False
151
152
153def WritePhysInt(phys_addr, int_val, bitsize = 64):
154    """ Write and integer value in a physical memory data based on address.
155        params:
156            phys_addr : int - Physical address to read
157            int_val   : int - int value to write in memory
158            bitsize   : int - defines how many bytes to read. defaults to 64 bit
159        returns:
160            bool - True if write was successful.
161    """
162    if "kdp" == GetConnectionProtocol():
163        if not KDPWritePhysMEM(phys_addr, int_val, bitsize):
164            print "Failed to write via KDP."
165            return False
166        return True
167    #We are not connected via KDP. So do manual math and savings.
168    print "Failed: Write to physical memory is not supported for %s connection." % GetConnectionProtocol()
169    return False
170
171@lldb_command('writephys')
172def WritePhys(cmd_args=None):
173    """ writes to the specified untranslated address
174        The argument is interpreted as a physical address, and the 64-bit word
175        addressed is displayed.
176        usage: writephys <nbits> <address> <value>
177        nbits: 8,16,32,64
178        address: 1234 or 0x1234
179        value: int value to be written
180        ex. (lldb)writephys 16 0x12345abcd 0x25
181    """
182    if cmd_args == None or len(cmd_args) < 3:
183        print "Invalid arguments.", WritePhys.__doc__
184    else:
185        nbits = ArgumentStringToInt(cmd_args[0])
186        phys_addr = ArgumentStringToInt(cmd_args[1])
187        int_value = ArgumentStringToInt(cmd_args[2])
188        print WritePhysInt(phys_addr, int_value, nbits)
189
190
191lldb_alias('writephys8', 'writephys 8 ')
192lldb_alias('writephys16', 'writephys 16 ')
193lldb_alias('writephys32', 'writephys 32 ')
194lldb_alias('writephys64', 'writephys 64 ')
195
196
197def _PT_Step(paddr, index, verbose_level = vSCRIPT):
198    """
199     Step to lower-level page table and print attributes
200       paddr: current page table entry physical address
201       index: current page table entry index (0..511)
202       verbose_level:    vHUMAN: print nothing
203                         vSCRIPT: print basic information
204                         vDETAIL: print basic information and hex table dump
205     returns: (pt_paddr, pt_valid, pt_large)
206       pt_paddr: next level page table entry physical address
207                      or null if invalid
208       pt_valid: 1 if $kgm_pt_paddr is valid, 0 if the walk
209                      should be aborted
210       pt_large: 1 if kgm_pt_paddr is a page frame address
211                      of a large page and not another page table entry
212    """
213    entry_addr = paddr + (8 * index)
214    entry = ReadPhysInt(entry_addr, 64, xnudefines.lcpu_self )
215    out_string = ''
216    if verbose_level >= vDETAIL:
217        for pte_loop in range(0, 512):
218            paddr_tmp = paddr + (8 * pte_loop)
219            out_string += "{0: <#020x}:\t {1: <#020x}\n".format(paddr_tmp, ReadPhysInt(paddr_tmp, 64, xnudefines.lcpu_self))
220    paddr_mask = ~((0xfff<<52) | 0xfff)
221    paddr_large_mask =  ~((0xfff<<52) | 0x1fffff)
222    pt_valid = False
223    pt_large = False
224    pt_paddr = 0
225    if verbose_level < vSCRIPT:
226        if entry & 0x1 :
227            pt_valid = True
228            pt_large = False
229            pt_paddr = entry & paddr_mask
230            if entry & (0x1 <<7):
231                pt_large = True
232                pt_paddr = entry & paddr_large_mask
233    else:
234        out_string+= "{0: <#020x}:\n\t{1:#020x}\n\t".format(entry_addr, entry)
235        if entry & 0x1:
236            out_string += " valid"
237            pt_paddr = entry & paddr_mask
238            pt_valid = True
239        else:
240            out_string += " invalid"
241            pt_paddr = 0
242            pt_valid = False
243            #Stop decoding other bits
244            entry = 0
245        if entry & (0x1 << 1):
246            out_string += " writable"
247        else:
248            out_string += " read-only"
249
250        if entry & (0x1 << 2):
251            out_string += " user"
252        else:
253            out_string += " supervisor"
254
255        if entry & (0x1 << 3):
256            out_string += " PWT"
257
258        if entry & (0x1 << 4):
259            out_string += " PCD"
260
261        if entry & (0x1 << 5):
262            out_string += " accessed"
263
264        if entry & (0x1 << 6):
265            out_string += " dirty"
266
267        if entry & (0x1 << 7):
268            out_string += " large"
269            pt_large = True
270        else:
271            pt_large = False
272
273        if entry & (0x1 << 8):
274            out_string += " global"
275
276        if entry & (0x3 << 9):
277            out_string += " avail:{0:x}".format((entry >> 9) & 0x3)
278
279        if entry & (0x1 << 63):
280            out_string += " noexec"
281    print out_string
282    return (pt_paddr, pt_valid, pt_large)
283
284
285
286
287def _PmapL4Walk(pmap_addr_val,vaddr, verbose_level = vSCRIPT):
288    """ Walk the l4 pmap entry.
289        params: pmap_addr_val - core.value representing kernel data of type pmap_addr_t
290        vaddr : int - virtual address to walk
291    """
292    is_cpu64_bit = int(kern.globals.cpu_64bit)
293    pt_paddr = unsigned(pmap_addr_val)
294    pt_valid = (unsigned(pmap_addr_val) != 0)
295    pt_large = 0
296    pframe_offset = 0
297    if pt_valid and is_cpu64_bit:
298        # Lookup bits 47:39 of linear address in PML4T
299        pt_index = (vaddr >> 39) & 0x1ff
300        pframe_offset = vaddr & 0x7fffffffff
301        if verbose_level > vHUMAN :
302            print "pml4 (index {0:d}):".format(pt_index)
303        (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level)
304    if pt_valid:
305        # Lookup bits 38:30 of the linear address in PDPT
306        pt_index = (vaddr >> 30) & 0x1ff
307        pframe_offset = vaddr & 0x3fffffff
308        if verbose_level > vHUMAN:
309            print "pdpt (index {0:d}):".format(pt_index)
310        (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level)
311    if pt_valid and not pt_large:
312        #Lookup bits 29:21 of the linear address in PDPT
313        pt_index = (vaddr >> 21) & 0x1ff
314        pframe_offset = vaddr & 0x1fffff
315        if verbose_level > vHUMAN:
316            print "pdt (index {0:d}):".format(pt_index)
317        (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level)
318    if pt_valid and not pt_large:
319        #Lookup bits 20:21 of linear address in PT
320        pt_index = (vaddr >> 12) & 0x1ff
321        pframe_offset = vaddr & 0xfff
322        if verbose_level > vHUMAN:
323            print "pt (index {0:d}):".format(pt_index)
324        (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level)
325    paddr = 0
326    paddr_isvalid = False
327    if pt_valid:
328        paddr = pt_paddr + pframe_offset
329        paddr_isvalid = True
330
331    if verbose_level > vHUMAN:
332        if paddr_isvalid:
333            pvalue = ReadPhysInt(paddr, 32, xnudefines.lcpu_self)
334            print "phys {0: <#020x}: {1: <#020x}".format(paddr, pvalue)
335        else:
336            print "no translation"
337
338    return paddr
339
340def _PmapWalkARMLevel1Section(tte, vaddr, verbose_level = vSCRIPT):
341    paddr = 0
342    out_string = ""
343    #Supersection or just section?
344    if (tte & 0x40000) == 0x40000:
345        paddr = ( (tte & 0xFF000000) | (vaddr & 0x00FFFFFF) )
346    else:
347        paddr = ( (tte & 0xFFF00000) | (vaddr & 0x000FFFFF) )
348
349    if verbose_level >= vSCRIPT:
350        out_string += "{0: <#020x}\n\t{1: <#020x}\n\t".format(addressof(tte), tte)
351        #bit [1:0] evaluated in PmapWalkARM
352        # B bit 2
353        b_bit = (tte & 0x4) >> 2
354        # C bit 3
355        c_bit = (tte & 0x8) >> 3
356        #XN bit 4
357        if (tte & 0x10) :
358            out_string += "no-execute"
359        else:
360            out_string += "execute"
361        #Domain bit [8:5] if not supersection
362        if (tte & 0x40000) == 0x0:
363            out_string += " domain ({:d})".format(((tte & 0x1e0) >> 5) )
364        #IMP bit 9
365        out_string += " imp({:d})".format( ((tte & 0x200) >> 9) )
366        # AP bit 15 and [11:10] merged to a single 3 bit value
367        access = ( (tte & 0xc00) >> 10 ) | ((tte & 0x8000) >> 13)
368        out_string += xnudefines.arm_level2_access_strings[access]
369
370        #TEX bit [14:12]
371        tex_bits = ((tte & 0x7000) >> 12)
372        #Print TEX, C , B all together
373        out_string += " TEX:C:B({:d}{:d}{:d}:{:d}:{:d})".format(
374                                                                    1 if (tex_bits & 0x4) else 0,
375                                                                    1 if (tex_bits & 0x2) else 0,
376                                                                    1 if (tex_bits & 0x1) else 0,
377                                                                    c_bit,
378                                                                    b_bit
379                                                                    )
380        # S bit 16
381        if tte & 0x10000:
382            out_string += " shareable"
383        else:
384            out_string += " not-shareable"
385        # nG bit 17
386        if tte & 0x20000 :
387            out_string += " not-global"
388        else:
389            out_string += " global"
390        # Supersection bit 18
391        if tte & 0x40000:
392            out_string += " supersection"
393        else:
394            out_string += " section"
395        #NS bit 19
396        if tte & 0x80000 :
397            out_string += " no-secure"
398        else:
399            out_string += " secure"
400
401    print out_string
402    return paddr
403
404
405
406def _PmapWalkARMLevel2(tte, vaddr, verbose_level = vSCRIPT):
407    """ Pmap walk the level 2 tte.
408        params:
409          tte - value object
410          vaddr - int
411        returns: str - description of the tte + additional informaiton based on verbose_level
412    """
413    pte_base = kern.PhysToKernelVirt(tte & 0xFFFFFC00)
414    pte_index = (vaddr >> 12) & 0xFF
415    pte_base_val = kern.GetValueFromAddress(pte_base, 'pt_entry_t *')
416    pte = pte_base_val[pte_index]
417    out_string = ''
418    if verbose_level >= vSCRIPT:
419        out_string += "{0: <#020x}\n\t{1: <#020x}\n\t".format(addressof(tte), tte)
420        # bit [1:0] evaluated in PmapWalkARM
421        # NS bit 3
422        if tte & 0x8:
423            out_string += ' no-secure'
424        else:
425            out_string += ' secure'
426        #Domain bit [8:5]
427        out_string += " domain({:d})".format(((tte & 0x1e0) >> 5))
428        # IMP bit 9
429        out_string += " imp({:d})".format( ((tte & 0x200) >> 9))
430        out_string += "\n"
431    if verbose_level >= vSCRIPT:
432        out_string += "second-level table (index {:d}):\n".format(pte_index)
433    if verbose_level >= vDETAIL:
434        for i in range(256):
435            tmp = pte_base_val[i]
436            out_string += "{0: <#020x}:\t{1: <#020x}\n".format(addressof(tmp), unsigned(tmp))
437
438    paddr = 0
439    if pte & 0x2:
440        paddr = (unsigned(pte) & 0xFFFFF000) | (vaddr & 0xFFF)
441
442    if verbose_level >= vSCRIPT:
443        out_string += " {0: <#020x}\n\t{1: <#020x}\n\t".format(addressof(pte), unsigned(pte))
444        if (pte & 0x3) == 0x0:
445            out_string += " invalid"
446        else:
447            if (pte & 0x3) == 0x1:
448                out_string += " large"
449                # XN bit 15
450                if pte & 0x8000 == 0x8000:
451                    out_string+= " no-execute"
452                else:
453                    out_string += " execute"
454            else:
455                out_string += " small"
456                # XN bit 0
457                if (pte & 0x1) == 0x01:
458                    out_string += " no-execute"
459                else:
460                    out_string += " execute"
461            # B bit 2
462            b_bit = (pte & 0x4) >> 2
463            c_bit = (pte & 0x8) >> 3
464            # AP bit 9 and [5:4], merged to a single 3-bit value
465            access = (pte & 0x30) >> 4 | (pte & 0x200) >> 7
466            out_string += xnudefines.arm_level2_access_strings[access]
467
468            #TEX bit [14:12] for large, [8:6] for small
469            tex_bits = ((pte & 0x1c0) >> 6)
470            if (pte & 0x3) == 0x1:
471                tex_bits = ((pte & 0x7000) >> 12)
472
473            # Print TEX, C , B alltogether
474            out_string += " TEX:C:B({:d}{:d}{:d}:{:d}:{:d})".format(
475                                                                    1 if (tex_bits & 0x4) else 0,
476                                                                    1 if (tex_bits & 0x2) else 0,
477                                                                    1 if (tex_bits & 0x1) else 0,
478                                                                    c_bit,
479                                                                    b_bit
480                                                                    )
481            # S bit 10
482            if pte & 0x400 :
483                out_string += " shareable"
484            else:
485                out_string += " not-shareable"
486
487            # nG bit 11
488            if pte & 0x800:
489                out_string += " not-global"
490            else:
491                out_string += " global"
492    print out_string
493    return paddr
494    #end of level 2 walking of arm
495
496
497def PmapWalkARM(pmap, vaddr, verbose_level = vHUMAN):
498    """ Pmap walking for ARM kernel.
499        params:
500          pmapval: core.value - representing pmap_t in kernel
501          vaddr:  int     - integer representing virtual address to walk
502    """
503    paddr = 0
504    # shift by TTESHIFT (20) to get tte index
505    tte_index = ((vaddr - unsigned(pmap.min)) >> 20 )
506    tte = pmap.tte[tte_index]
507    if verbose_level >= vSCRIPT:
508        print "First-level table (index {:d}):".format(tte_index)
509    if verbose_level >= vDETAIL:
510        for i in range(0, 4096):
511            ptr = unsigned(addressof(pmap.tte[i]))
512            val = unsigned(pmap.tte[i])
513            print "{0: <#020x}:\t {1: <#020x}".format(ptr, val)
514    if (tte & 0x3) == 0x1:
515        paddr = _PmapWalkARMLevel2(tte, vaddr, verbose_level)
516    elif (tte & 0x3) == 0x2 :
517        paddr = _PmapWalkARMLevel1Section(tte, vaddr, verbose_level)
518    else:
519        paddr = 0
520        if verbose_level >= vSCRIPT:
521            print "Invalid First-Level Translation Table Entry: {0: #020x}".format(tte)
522
523    if verbose_level >= vHUMAN:
524        if paddr:
525            print "Translation of {:#x} is {:#x}.".format(vaddr, paddr)
526        else:
527            print "(no translation)"
528
529    return paddr
530
531def PmapWalkX86_64(pmapval, vaddr):
532    """
533        params: pmapval - core.value representing pmap_t in kernel
534        vaddr:  int     - int representing virtual address to walk
535    """
536    return _PmapL4Walk(pmapval.pm_cr3, vaddr, config['verbosity'])
537
538def assert_64bit(val):
539    assert(val < 2**64)
540
541ARM64_TTE_SIZE = 8
542ARM64_VMADDR_BITS = 48
543
544def PmapBlockOffsetMaskARM64(level):
545    assert level >= 1 and level <= 3
546    page_size = kern.globals.page_size
547    ttentries = (page_size / ARM64_TTE_SIZE)
548    return page_size * (ttentries ** (3 - level)) - 1
549
550def PmapBlockBaseMaskARM64(level):
551    assert level >= 1 and level <= 3
552    page_size = kern.globals.page_size
553    return ((1 << ARM64_VMADDR_BITS) - 1) & ~PmapBlockOffsetMaskARM64(level)
554
555def PmapIndexMaskARM64(level):
556    assert level >= 1 and level <= 3
557    page_size = kern.globals.page_size
558    ttentries = (page_size / ARM64_TTE_SIZE)
559    return page_size * (ttentries ** (3 - level) * (ttentries - 1))
560
561def PmapIndexDivideARM64(level):
562    assert level >= 1 and level <= 3
563    page_size = kern.globals.page_size
564    ttentries = (page_size / ARM64_TTE_SIZE)
565    return page_size * (ttentries ** (3 - level))
566
567def PmapTTnIndexARM64(vaddr, level):
568    assert(type(vaddr) in (long, int))
569    assert_64bit(vaddr)
570
571    return (vaddr & PmapIndexMaskARM64(level)) // PmapIndexDivideARM64(level)
572
573def PmapDecodeTTEARM64(tte, level):
574    assert(type(tte) == long)
575    assert(type(level) == int)
576    assert_64bit(tte)
577
578    if tte & 0x1 == 0x1:
579        if (tte & 0x2 == 0x2) and (level != 0x3):
580            print "Type       = Table pointer."
581            print "Table addr = {:#x}.".format(tte & 0xfffffffff000)
582            print "PXN        = {:#x}.".format((tte >> 59) & 0x1)
583            print "XN         = {:#x}.".format((tte >> 60) & 0x1)
584            print "AP         = {:#x}.".format((tte >> 61) & 0x3)
585            print "NS         = {:#x}".format(tte >> 63)
586        else:
587            print "Type       = Block."
588            print "AttrIdx    = {:#x}.".format((tte >> 2) & 0x7)
589            print "NS         = {:#x}.".format((tte >> 5) & 0x1)
590            print "AP         = {:#x}.".format((tte >> 6) & 0x3)
591            print "SH         = {:#x}.".format((tte >> 8) & 0x3)
592            print "AF         = {:#x}.".format((tte >> 10) & 0x1)
593            print "nG         = {:#x}.".format((tte >> 11) & 0x1)
594            print "HINT       = {:#x}.".format((tte >> 52) & 0x1)
595            print "PXN        = {:#x}.".format((tte >> 53) & 0x1)
596            print "XN         = {:#x}.".format((tte >> 54) & 0x1)
597            print "SW Use     = {:#x}.".format((tte >> 55) & 0xf)
598    else:
599        print "Invalid."
600
601    return
602
603def PmapWalkARM64(pmap, vaddr, verbose_level = vHUMAN):
604    assert(type(pmap) == core.cvalue.value)
605    assert(type(vaddr) in (long, int))
606    page_size = kern.globals.page_size
607    page_offset_mask = (page_size - 1)
608    page_base_mask = ((1 << ARM64_VMADDR_BITS) - 1) & (~page_offset_mask)
609
610    assert_64bit(vaddr)
611    paddr = -1
612
613    tt1_index = PmapTTnIndexARM64(vaddr, 1)
614    tt2_index = PmapTTnIndexARM64(vaddr, 2)
615    tt3_index = PmapTTnIndexARM64(vaddr, 3)
616
617    # L1
618    tte = long(unsigned(pmap.tte[tt1_index]))
619    assert(type(tte) == long)
620    assert_64bit(tte)
621
622    if verbose_level >= vSCRIPT:
623        print "L1 entry: {:#x}".format(tte)
624    if verbose_level >= vDETAIL:
625        PmapDecodeTTEARM64(tte, 1)
626
627    if tte & 0x1 == 0x1:
628        # Check for L1 block entry
629        if tte & 0x2 == 0x0:
630            # Handle L1 block entry
631            paddr = tte & PmapBlockBaseMaskARM64(1)
632            paddr = paddr | (vaddr & PmapBlockOffsetMaskARM64(1))
633            print "phys: {:#x}".format(paddr)
634        else:
635            # Handle L1 table entry
636            l2_phys = (tte & page_base_mask) + (ARM64_TTE_SIZE * tt2_index)
637            assert(type(l2_phys) == long)
638
639            l2_virt = kern.PhysToKernelVirt(l2_phys)
640            assert(type(l2_virt) == long)
641
642            if verbose_level >= vDETAIL:
643                print "L2 physical address: {:#x}. L2 virtual address: {:#x}".format(l2_phys, l2_virt)
644
645            # L2
646            ttep = kern.GetValueFromAddress(l2_virt, "tt_entry_t*")
647            tte = long(unsigned(dereference(ttep)))
648            assert(type(tte) == long)
649
650            if verbose_level >= vSCRIPT:
651                print "L2 entry: {:#0x}".format(tte)
652            if verbose_level >= vDETAIL:
653                PmapDecodeTTEARM64(tte, 2)
654
655            if tte & 0x1 == 0x1:
656                # Check for L2 block entry
657                if tte & 0x2 == 0x0:
658                    # Handle L2 block entry
659                    paddr = tte & PmapBlockBaseMaskARM64(2)
660                    paddr = paddr | (vaddr & PmapBlockOffsetMaskARM64(2))
661                else:
662                    # Handle L2 table entry
663                    l3_phys = (tte & page_base_mask) + (ARM64_TTE_SIZE * tt3_index)
664                    assert(type(l3_phys) == long)
665
666                    l3_virt = kern.PhysToKernelVirt(l3_phys)
667                    assert(type(l3_virt) == long)
668
669                    if verbose_level >= vDETAIL:
670                        print "L3 physical address: {:#x}. L3 virtual address: {:#x}".format(l3_phys, l3_virt)
671
672                    # L3
673                    ttep = kern.GetValueFromAddress(l3_virt, "tt_entry_t*")
674                    tte = long(unsigned(dereference(ttep)))
675                    assert(type(tte) == long)
676
677                    if verbose_level >= vSCRIPT:
678                        print "L3 entry: {:#0x}".format(tte)
679                    if verbose_level >= vDETAIL:
680                        PmapDecodeTTEARM64(tte, 3)
681
682                    if tte & 0x3 == 0x3:
683                        paddr = tte & page_base_mask
684                        paddr = paddr | (vaddr & page_offset_mask)
685                    elif verbose_level >= vHUMAN:
686                        print "L3 entry invalid: {:#x}\n".format(tte)
687            elif verbose_level >= vHUMAN: # tte & 0x1 == 0x1
688                print "L2 entry invalid: {:#x}\n".format(tte)
689    elif verbose_level >= vHUMAN:
690        print "L1 entry invalid: {:#x}\n".format(tte)
691
692    if verbose_level >= vHUMAN:
693        if paddr:
694            print "Translation of {:#x} is {:#x}.".format(vaddr, paddr)
695        else:
696            print "(no translation)"
697
698    return paddr
699
700def PmapWalk(pmap, vaddr, verbose_level = vHUMAN):
701    if kern.arch == 'x86_64':
702        return PmapWalkX86_64(pmap, vaddr)
703    elif kern.arch == 'arm':
704        return PmapWalkARM(pmap, vaddr, verbose_level)
705    elif kern.arch == 'arm64':
706        return PmapWalkARM64(pmap, vaddr, verbose_level)
707    else:
708        raise NotImplementedError("PmapWalk does not support {0}".format(kern.arch))
709
710@lldb_command('pmap_walk')
711def PmapWalkHelper(cmd_args=None):
712    """ Perform a page-table walk in <pmap> for <virtual_address>.
713        Syntax: (lldb) pmap_walk <pmap> <virtual_address> [-v]
714            Multiple -v's can be specified for increased verbosity
715    """
716    if cmd_args == None or len(cmd_args) < 2:
717        raise ArgumentError("Too few arguments to pmap_walk.")
718
719    pmap = kern.GetValueAsType(cmd_args[0], 'pmap_t')
720    addr = unsigned(kern.GetValueFromAddress(cmd_args[1], 'void *'))
721    PmapWalk(pmap, addr, config['verbosity'])
722    return
723