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