# Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. # Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 and # only version 2 as published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. from bitops import bm, bvalsel from register import Register import sizes class MMU(object): """Represents an MMU. Does virtual-to-physical address lookups, caching the results in a TLB. This is an abstract class that should not be used directly. Concrete subclasses should override the following methods: - load_page_tables() - page_table_walk(addr) - dump_page_tables(file_object) Interesting properties that will be set for usage in derived classes: - ramdump:: The RamDump instance being parsed """ def __init__(self, ramdump, ttbr=None): self._tlb = {} self.ramdump = ramdump self.s2_walk = self.ramdump.s2_walk if self.s2_walk: self._tlbv2 = {} self.ttbr = self.ramdump.ttbr self.vttbr = self.ramdump.vttbr else: if ttbr is not None: self.ttbr = ttbr else: self.ttbr = self.ramdump.kernel_virt_to_phys( self.ramdump.swapper_pg_dir_addr) self.get_pgtable_index() self.load_page_tables() return def virt_to_phys(self, addr, skip_tlb=False, save_in_tlb=True): """Do a virtual to physical address lookup and possibly cache the result in the "TLB". """ if addr is None: return None page_addr = (addr >> self.ramdump.page_shift) << self.ramdump.page_shift page_offset = addr & ((1 << self.ramdump.page_shift) - 1) if not skip_tlb: if page_addr in self._tlb: return self._tlb[page_addr] + page_offset phys_addr = self.page_table_walk(page_addr) if phys_addr is None: return None if self.s2_walk: ipa = phys_addr + page_offset phys_addr = (ipa >> 12) << 12 page_offset = ipa & 0xFFF ipa2 = self.page_table_walkel2(phys_addr) pa = ipa2 + page_offset if save_in_tlb: self._tlb[phys_addr] = pa return pa else: if save_in_tlb: self._tlb[page_addr] = phys_addr return phys_addr + page_offset def get_swap_pte(self, addr): if addr is None: return None page_addr = (addr >> self.ramdump.page_shift) << self.ramdump.page_shift pte = self.page_table_walk_to_get_swap_pte(page_addr) return pte def load_page_tables(self): raise NotImplementedError def page_table_walk(self, virt): raise NotImplementedError def dump_page_tables(self, f): raise NotImplementedError def page_table_walk_to_get_swap_pte(self, virt): raise NotImplementedError def get_pgtable_index(self): return None class Armv7MMU(MMU): """An MMU for ARMv7 (no LPAE).""" def __init__(self, ramdump, ttbr=None): super().__init__(ramdump, ttbr) def load_page_tables(self): entry_size = self.ramdump.sizeof("void *") self.global_page_table = [0 for i in range(4096)] self.secondary_page_tables = [[0 for col in range(256)] for row in range(4096)] l1_index = 0 for l1_pte_ptr in range(self.ttbr, self.ttbr + (4096 * entry_size), entry_size): l1_pte = self.ramdump.read_pointer(l1_pte_ptr, False) if l1_pte is None or l1_pte == 0: l1_index += 1 continue self.global_page_table[l1_index] = l1_pte # pointer to 2 level page table if bvalsel(1, 0, l1_pte) == 1: # clean low 10 bit l2_pt_base = l1_pte & ~((1 << 10) - 1) l2_index = 0 for l2_pte_ptr in range(l2_pt_base, l2_pt_base + (256 * entry_size), entry_size): l2_pte = self.ramdump.read_pointer(l2_pte_ptr, False) if l2_pte is None or l2_pte == 0: l2_index += 1 continue self.secondary_page_tables[l1_index][l2_index] = l2_pte l2_index += 1 l1_index += 1 # here look up hw PTE # arm32 page table # -------------------------------------------------------- # | 12 | 8 | 12 | # -------------------------------------------------------- def page_table_walk(self, virt): global_offset = bvalsel(31, 20, virt) l1_pte = self.global_page_table[global_offset] if l1_pte is None: return None # pointer to 2 level page table if bvalsel(1, 0, l1_pte) == 1: l2_offset = bvalsel(19, 12, virt) l2_pte = self.secondary_page_tables[global_offset][l2_offset] # find out the hw pte # sometime the hw pte is 0, but the sw pte is not 0 if l2_pte is None or l2_pte == 0: l2_pte = self.page_table_walk_to_get_swap_pte(virt) # find out the linux pte again if l2_pte is None or l2_pte == 0: return None entry4kb = (l2_pte & bm(31, 12)) + bvalsel(11, 0, virt) return entry4kb else: # small page if (bvalsel(1, 0, l2_pte) == 2) or (bvalsel(1, 0, l2_pte) == 3): entry4kb = (l2_pte & bm(31, 12)) + bvalsel(11, 0, virt) return entry4kb # large page elif (bvalsel(1, 0, l2_pte) == 1): entry64kb = (l2_pte & bm(31, 16)) + bvalsel(15, 0, virt) return entry64kb # section or supersection if bvalsel(1, 0, l1_pte) == 2: onemb_entry = bm(31, 20) & l1_pte onemb_entry += bvalsel(19, 0, virt) return onemb_entry return 0 def dump_page_tables(self, f): f.write( 'Dumping page tables is not currently supported for Armv7MMU\n') f.flush() def SW_PGD_OFFSET(self, virt): PGDIR_SHIFT = 21 return virt >> PGDIR_SHIFT # get hight 11 bit def SW_PTE_OFFSET(self, virt): PTRS_PER_PTE = 512 # get the internal 9 bit return (virt >> self.ramdump.page_shift) & (PTRS_PER_PTE - 1) def SW_pmd_page_addr(self, pmd): # clean low 12 bit return pmd & ~(self.ramdump.get_page_size() -1) # here look up Linux PTE # Linux page table # -------------------------------------------------------- # | 11 | 9 | 12 | # -------------------------------------------------------- # * Starting from 2.6.38 # * # * PGD PTE # * +---------+ # * | | 0 ----> +------------+ # * +- - - - -+ | Linux pt 0 | # * | | 4 ----> +------------+ +1024 # * +- - - - -+ | Linux pt 1 | # * . . +------------+ +2048 # * . . | h/w pt 0 | # * . . +------------+ +3072 # * | | 4095 | h/w pt 1 | # * +---------+ +------------+ +4096 def page_table_walk_to_get_swap_pte(self, virt): debug = False # per pgd entry is 4 byte entry_size = self.ramdump.sizeof("void *") if debug: print("PAGE DIRECTORY: {0:x}".format(self.ttbr)) page_dir = self.ttbr + (entry_size * 2 * self.SW_PGD_OFFSET(virt)) # The unity-mapped region is mapped using 1MB pages hence 1-level translation # if bit 20 is set; if we are 1MB apart physically, we move the page_dir in case bit 20 is set. # if ((virt) >> (20)) & 1: # page_dir = page_dir + entry_size pgd_pte = self.ramdump.read_pointer(page_dir, False) if debug: print(" PGD: {0:x} => {1:x}".format(page_dir, pgd_pte)) if pgd_pte is None or pgd_pte == 0: return None pmd_pte = pgd_pte page_middle = page_dir if debug: print(" PMD: {0:x} => {1:x}".format(page_middle, pmd_pte)) page_table = self.SW_pmd_page_addr(pmd_pte) + (entry_size * self.SW_PTE_OFFSET(virt)) sw_pte = self.ramdump.read_pointer(page_table, False) if debug: print(" PTE: {0:x} => {1:x}".format(page_table, sw_pte)) return sw_pte class Armv7LPAEMMU(MMU): """An MMU for ARMv7 (with LPAE)""" # Descriptor types DESCRIPTOR_INVALID = 0x0 DESCRIPTOR_BLOCK = 0x1 DESCRIPTOR_TABLE = 0x3 TL_DESCRIPTOR_RESERVED = 0x1 TL_DESCRIPTOR_PAGE = 0x3 # Mapping classes class MappingInfo(object): pass class LeafMapping(MappingInfo): def __init__(self, virt_r, descriptor, page_size, n): self.virt_r = virt_r self.descriptor = descriptor self.attributes = Register( descriptor.value, software=(58, 55), XN=(54, 54), PXN=(53, 53), contiguous_hint=(52, 52), nG=(11, 11), AF=(10, 10), sh_10=(9, 8), ap_21=(7, 6), ns=(5, 5), attr_index_20=(4, 2), ) self.page_size = page_size self.leaf = True p = Register(output_address=(39, n), page_offset=(n - 1, 0)) p.output_address = self.descriptor.output_address self.virt_r.add_field('rest', (n - 1, 0)) p.page_offset |= self.virt_r.rest self.phys = p.value def __repr__(self): pstart, pend = self.phys_addr_range() return '[{:x}-{:x}][{:}]'.format( pstart, pend, ','.join(self.get_attributes_strings()) ) def phys_addr_range(self): return (self.phys, self.phys + self.page_size) def get_attributes_strings(self): attrs = [ self.get_xn_string(), self.get_pxn_string(), self.get_contiguous_hint_string(), self.get_nG_string(), self.get_AF_string(), self.get_sh_string(), self.get_ap_21_string(), self.get_ns_string(), self.get_attr_index_20_string(), ] return [a for a in attrs if a != ''] def get_xn_string(self): if self.attributes.XN == 1: return 'XN' return '' def get_pxn_string(self): if self.attributes.PXN == 1: return 'PXN' return '' def get_contiguous_hint_string(self): if self.attributes.contiguous_hint == 1: return 'Contiguous' return '' def get_nG_string(self): if self.attributes.nG == 1: return 'nG' return '' def get_AF_string(self): if self.attributes.AF == 1: return 'AF' return '' def get_sh_string(self): if self.attributes.sh_10 == 0b00: return 'Non-shareable' elif self.attributes.sh_10 == 0b01: return 'UNPREDICTABLE' elif self.attributes.sh_10 == 0b10: return 'Outer Shareable' elif self.attributes.sh_10 == 0b11: return 'Inner Shareable' raise ValueError('Impossible sh[1:0]: 0x%x' % self.attributes.sh_10) def get_ap_21_string(self): if self.attributes.ap_21 == 0b00: return 'R/W@PL1' elif self.attributes.ap_21 == 0b01: return 'R/W' elif self.attributes.ap_21 == 0b10: return 'R/O@PL1' elif self.attributes.ap_21 == 0b11: return 'R/O' raise ValueError('Impossible ap[2:1]: 0x%x' % self.attributes.ap_21) def get_ns_string(self): if self.attributes.ns == 1: return 'NS' return '' def get_attr_index_20_string(self): return 'AI=0x%x' % self.attributes.attr_index_20 class TableMapping(MappingInfo): def __init__(self, next_table_addr): self.next_table_addr = next_table_addr self.leaf = False def __repr__(self): return '[Next Table: 0x%x]' % ( self.next_table_addr ) class FLBlockMapping(LeafMapping): def __init__(self, virt_r, desc): super(Armv7LPAEMMU.FLBlockMapping, self).__init__( virt_r, desc, sizes.SZ_1G, 30) class SLBlockMapping(LeafMapping): def __init__(self, virt_r, desc): super(Armv7LPAEMMU.SLBlockMapping, self).__init__( virt_r, desc, sizes.SZ_2M, 21) class TLPageMapping(LeafMapping): def __init__(self, virt_r, desc): super(Armv7LPAEMMU.TLPageMapping, self).__init__( virt_r, desc, sizes.SZ_4K, 12) class FLTableMapping(TableMapping): pass class SLTableMapping(TableMapping): pass # Exceptions class LookupException(Exception): pass class LookupExceptionFLSL(LookupException): pass class LookupExceptionTL(LookupException): pass def __init__(self, ramdump, pgtbl, txsz, virt_for_fl=False): """Constructor for Armv7LPAEMMU. - ramdump: RamDump instance - pgtbl: base address of page table - txsz: t0sz or t1sz (see ARM ARM B3.6.6 (rev 0406C.b)) - virt_for_fl: whether we should do a virtual address lookup for the first-level page table. Note that it wouldn't make any sense to pass `True' here if this is the "main" mmu instance for a RamDump, because then the RamDump would try to invoke this very object to do the lookup, and we would recursively discover the higgs boson. This option is useful, though, for parsing LPAE page tables whose first-level page table is sitting in kernel address space (as is the case for the IOMMU LPAE page tables). """ super(Armv7LPAEMMU, self).__init__(ramdump) self.pgtbl = pgtbl self.txsz = txsz self.virt_for_fl = virt_for_fl if (32 - txsz) > 30: self.initial_lkup_level = 1 self.initial_block_split = 12 else: self.initial_lkup_level = 2 self.initial_block_split = 21 if self.initial_lkup_level == 1: # see the ARMv7 ARM B3.6.6 (rev 0406C.b): self.input_addr_split = 5 - self.txsz if self.input_addr_split not in [4, 5]: raise ValueError("Invalid stage 1 first-level `n' value: 0x%x. Please check txsz." % self.input_addr_split) else: # see the ARMv7 ARM B3.6.6 (rev 0406C.b): self.input_addr_split = 14 - self.txsz if self.input_addr_split not in range(7, 13): raise ValueError("Invalid stage 1 second-level (initial) `n' value: 0x%x. Please check txsz." % self.input_addr_split) def do_fl_sl_level_lookup(self, table_base_address, table_index, block_split, virtual=False): descriptor, addr = self.do_level_lookup( table_base_address, table_index, virtual=virtual) if descriptor.dtype == Armv7LPAEMMU.DESCRIPTOR_BLOCK: descriptor.add_field('output_address', (39, block_split)) elif descriptor.dtype == Armv7LPAEMMU.DESCRIPTOR_TABLE: # we have bits 39:12 of the next-level table in # next_level_base_addr_upper descriptor.add_field('next_level_base_addr_upper', (39, 12)) else: raise Armv7LPAEMMU.LookupExceptionFLSL( 'Invalid stage 1 first- or second-level translation\ndescriptor: (%s)\naddr: (%s)' % (str(descriptor), str(addr)) ) return descriptor def do_tl_level_lookup(self, table_base_address, table_index): descriptor, addr = self.do_level_lookup( table_base_address, table_index) if descriptor.dtype == Armv7LPAEMMU.TL_DESCRIPTOR_PAGE: descriptor.add_field('output_address', (39, 12)) else: raise Armv7LPAEMMU.LookupExceptionTL( 'Invalid stage 1 third-level translation\ndescriptor: (%s)\naddr: (%s)' % (str(descriptor), str(addr)) ) return descriptor def do_level_lookup(self, table_base_address, table_index, virtual=False): """Does a base + index descriptor lookup. Returns a tuple with the Register object representing the found descriptor and a Register object representing the the computed descriptor address. """ n = self.input_addr_split # these Registers are overkill but nice documentation:). table_base = Register(table_base_address, base=(39, n)) descriptor_addr = Register(base=(39, n), offset=(n - 1, 3)) descriptor_addr.base = table_base.base descriptor_addr.offset = table_index descriptor_val = self.ramdump.read_dword( descriptor_addr.value, virtual=virtual) descriptor = Register(descriptor_val, dtype=(1, 0)) return descriptor, descriptor_addr def load_page_tables(self): pass def page_table_walk(self, virt): info = self.translate(virt) return info.phys if info is not None else None def translate_first_level(self, virt_r): try: fl_desc = self.do_fl_sl_level_lookup(self.pgtbl, virt_r.fl_index, 30, virtual=self.virt_for_fl) except Armv7LPAEMMU.LookupExceptionFLSL: return None # if we got a block descriptor we're done: if fl_desc.dtype == Armv7LPAEMMU.DESCRIPTOR_BLOCK: return Armv7LPAEMMU.FLBlockMapping(virt_r, fl_desc) base = Register(base=(39, 12)) base.base = fl_desc.next_level_base_addr_upper return Armv7LPAEMMU.FLTableMapping(base.value) def translate_second_level(self, virt_r, level2_table_addr, block_split=None): if block_split is None: block_split = self.initial_block_split try: sl_desc = self.do_fl_sl_level_lookup( level2_table_addr, virt_r.sl_index, block_split) # res.next_table_addr, virt_r.sl_index, 12, 21) except Armv7LPAEMMU.LookupExceptionFLSL: return None # if we got a block descriptor we're done: if sl_desc.dtype == Armv7LPAEMMU.DESCRIPTOR_BLOCK: return Armv7LPAEMMU.SLBlockMapping(virt_r, sl_desc) base = Register(base=(39, 12)) base.base = sl_desc.next_level_base_addr_upper return Armv7LPAEMMU.SLTableMapping(base.value) def translate_third_level(self, virt_r, level3_table_addr): try: tl_desc = self.do_tl_level_lookup( level3_table_addr, virt_r.tl_index) except Armv7LPAEMMU.LookupExceptionTL: return None return Armv7LPAEMMU.TLPageMapping(virt_r, tl_desc) def translate(self, virt): """Does a page table walk and returns a LeafMapping that describes the mapping (including the physical address and mapping attributes) """ if self.initial_lkup_level == 1: virt_r = Register(virt, fl_index=(self.input_addr_split + 26, 30), sl_index=(29, 21), tl_index=(20, 12), page_index=(11, 0)) res = self.translate_first_level(virt_r) if res is None or res.leaf: return res level2_table_addr = res.next_table_addr elif self.initial_lkup_level == 2: virt_r = Register(virt, sl_index=(self.input_addr_split + 17, 21), tl_index=(20, 12), page_index=(11, 0)) level2_table_addr = self.pgtbl else: raise ValueError('Invalid initial lookup level (0x%x). Should be 1 or 2.' % self.initial_lkup_level) res = self.translate_second_level(virt_r, level2_table_addr) if res is None or res.leaf: return res level3_table_addr = res.next_table_addr return self.translate_third_level(virt_r, level3_table_addr) def dump_page_tables(self, f): f.write( 'Dumping page tables is not currently supported for Armv7LPAEMMU\n') f.flush() def page_table_walk_to_get_swap_pte(self, virt): return None class Armv8MMU(MMU): """An MMU for ARMv8 VMSA""" # Descriptor types DESCRIPTOR_INVALID = 0x0 DESCRIPTOR_BLOCK = 0x1 DESCRIPTOR_TABLE = 0x3 TL_DESCRIPTOR_RESERVED = 0x1 TL_DESCRIPTOR_PAGE = 0x3 def do_fl_sl_level_lookup(self, table_base_address, table_index, input_addr_split, block_split): descriptor, addr = self.do_level_lookup( table_base_address, table_index, input_addr_split) if descriptor.dtype == Armv8MMU.DESCRIPTOR_BLOCK: descriptor.add_field('output_address', (self.ramdump.va_bits-1, block_split)) elif descriptor.dtype == Armv8MMU.DESCRIPTOR_TABLE: descriptor.add_field('next_level_base_addr_upper', (self.ramdump.va_bits-1, self.l3_index)) else: raise Exception( 'Invalid stage 1 first- or second-level translation\ndescriptor: (%s)\naddr: (%s)' % (str(descriptor), str(addr)) ) return descriptor def do_fl_sl_level_lookupel2v2(self, table_base_address, table_index, input_addr_split, block_split): #print "====do_fl_sl_level_lookupel2v2" #print hex(table_base_address) #print hex(table_index) #print input_addr_split #print block_split descriptor, addr = self.do_level_lookupel2v2( table_base_address, table_index, input_addr_split) #print "descriptor do_fl_sl_level_lookupel2v2" #print descriptor if descriptor.dtype == Armv8MMU.DESCRIPTOR_BLOCK: descriptor.add_field('output_address', (47, block_split)) elif descriptor.dtype == Armv8MMU.DESCRIPTOR_TABLE: # we have bits 39:12 of the next-level table in # next_level_base_addr_upper descriptor.add_field('next_level_base_addr_upper', (47, 12)) #print "descriptor addeding filed" #print descriptor else: raise Exception( 'Invalid stage 1 first- or second-level translation\ndescriptor: (%s)\naddr: (%s)' % (str(descriptor), str(addr)) ) return descriptor def do_fl_sl_level_lookupel2(self, table_base_address, table_index, input_addr_split, block_split): #print "====" #print hex(table_base_address) #print hex(table_index) #print input_addr_split #print block_split descriptor, addr = self.do_level_lookupel2v2( table_base_address, table_index, input_addr_split) #print "descriptor do_fl_sl_level_lookupel2" #print descriptor if descriptor.dtype == Armv8MMU.DESCRIPTOR_BLOCK: descriptor.add_field('output_address', (47, block_split)) elif descriptor.dtype == Armv8MMU.DESCRIPTOR_TABLE: # we have bits 39:12 of the next-level table in # next_level_base_addr_upper descriptor.add_field('next_level_base_addr_upper', (47, 12)) #print "descriptor addeding filed" #print descriptor else: raise Exception( 'Invalid stage 1 first- or second-level translation\ndescriptor: (%s)\naddr: (%s)' % (str(descriptor), str(addr)) ) return descriptor def do_fl_level_lookup(self, table_base_address, table_index, input_addr_split): return self.do_fl_sl_level_lookup(table_base_address, table_index, input_addr_split, 30) def do_fl_level_lookupel2(self, table_base_address, table_index, input_addr_split): return self.do_fl_sl_level_lookupel2(table_base_address, table_index, input_addr_split, 30) def do_sl_level_lookup(self, table_base_address, table_index): return self.do_fl_sl_level_lookup(table_base_address, table_index, 12, 21) def do_sl_level_lookupel2(self, table_base_address, table_index): #print "do_sl_level_lookupel2 entry" return self.do_fl_sl_level_lookupel2v2(table_base_address, table_index, 12, 21) def do_tl_level_lookup(self, table_base_address, table_index): #print "+++++ do_tl_level_lookup entry" descriptor, addr = self.do_level_lookup( table_base_address, table_index, 12) #print "descriptor in do_tl_level_lookup" #print descriptor #print descriptor.dtype if descriptor.dtype == Armv8MMU.TL_DESCRIPTOR_PAGE: #print "output_address in do_tl_level_lookup" descriptor.add_field('output_address', (47, 12)) else: raise Exception( 'Invalid stage 1 third-level translation\ndescriptor: (%s)\naddr: (%s)' % (str(descriptor), str(addr)) ) return descriptor def do_tl_level_lookupel2(self, table_base_address, table_index): descriptor, addr = self.do_level_lookupel2v2( table_base_address, table_index, 12) if descriptor.dtype == Armv8MMU.TL_DESCRIPTOR_PAGE: descriptor.add_field('output_address', (47, 12)) else: raise Exception( 'Invalid stage 1 third-level translation\ndescriptor: (%s)\naddr: (%s)' % (str(descriptor), str(addr)) ) return descriptor def do_level_lookupel2v2(self, table_base_address, table_index, input_addr_split): """Does a base + index descriptor lookup. Returns a tuple with the Register object representing the found descriptor and a Register object representing the the computed descriptor address. """ n = input_addr_split # these Registers are overkill but nice documentation:). table_base = Register(table_base_address, base=(47, n)) descriptor_addr = Register(table_base_address, base=(47, n), offset=(n - 1, 3)) descriptor_addr.offset = table_index descriptor_val = self.read_phys_dword(descriptor_addr.value) descriptor = Register(descriptor_val, dtype=(1, 0)) return descriptor, descriptor_addr def do_level_lookupel2(self, table_base_address, table_index, input_addr_split): """Does a base + index descriptor lookup. Returns a tuple with the Register object representing the found descriptor and a Register object representing the the computed descriptor address. """ n = input_addr_split table_base = Register(table_base_address, base=(47, n)) descriptor_addr = Register(table_base_address, base=(47, n), offset=(n - 1, 3)) descriptor_val = self.read_phys_dword(descriptor_addr.value) descriptor = Register(descriptor_val, dtype=(1, 0)) return descriptor, descriptor_addr def do_level_lookup(self, table_base_address, table_index, input_addr_split): """Does a base + index descriptor lookup. Returns a tuple with the Register object representing the found descriptor and a Register object representing the the computed descriptor address. """ n = input_addr_split if self.s2_walk: ttbr_phy = self.virt_to_physel2(table_base_address) table_base_address = ttbr_phy # these Registers are overkill but nice documentation:). table_base = Register(table_base_address, base=(self.ramdump.va_bits-1, n)) descriptor_addr = Register(table_base_address, base=(self.ramdump.va_bits-1, n), offset=(n - 1, 3)) descriptor_addr.offset = table_index descriptor_val = self.read_phys_dword(descriptor_addr.value) descriptor = Register(descriptor_val, dtype=(1, 0)) return descriptor, descriptor_addr def block_or_page_desc_2_phys(self, desc, virt_r, n): phys = Register(output_address=(47, n), page_offset=(n - 1, 0)) phys.output_address = desc.output_address virt_r.add_field('rest', (n - 1, 0)) phys.page_offset |= virt_r.rest return phys.value def block_or_page_desc_2_physel2(self, desc, virt_r, n): phys = Register(output_address=(38, n), page_offset=(n - 1, 0)) phys.output_address = desc.output_address virt_r.add_field('rest', (n - 1, 0)) phys.page_offset |= virt_r.rest return phys.value def fl_block_desc_2_phys(self, desc, virt_r): """Block descriptor to physical address.""" return self.block_or_page_desc_2_phys(desc, virt_r, 30) def fl_block_desc_2_physel2(self, desc, virt_r): """Block descriptor to physical address.""" return self.block_or_page_desc_2_phys(desc, virt_r, 30) def sl_block_desc_2_phys(self, desc, virt_r): """Block descriptor to physical address.""" return self.block_or_page_desc_2_phys(desc, virt_r, 21) def sl_block_desc_2_physel2(self, desc, virt_r): """Block descriptor to physical address.""" return self.block_or_page_desc_2_phys(desc, virt_r, 21) def tl_page_desc_2_phys(self, desc, virt_r): """Page descriptor to physical address.""" return self.block_or_page_desc_2_phys(desc, virt_r, 12) def tl_page_desc_2_physel2(self, desc, virt_r): """Page descriptor to physical address.""" return self.block_or_page_desc_2_phys(desc, virt_r, 12) def read_phys_dword(self, physaddr): return self.ramdump.read_dword(physaddr, virtual=False) def load_page_tables(self): pass def page_table_walk(self, virt): virt_r = Register(virt, zl_index=(self.ramdump.va_bits-1,self.l0_index), fl_index=(self.l0_index-1,self.l1_index), sl_index=(self.l1_index-1,self.l2_index), tl_index=(self.l2_index-1,self.l3_index), page_index=(self.l3_index-1,0)) base = Register(base=(self.ramdump.va_bits-1, self.l3_index)) base.value = self.ttbr if self.ramdump.pgtable_levels >= 4: try: zl_desc = self.do_fl_sl_level_lookup(base.value, virt_r.zl_index, self.l3_index, self.l0_index) except: return None if zl_desc.dtype == Armv8MMU.DESCRIPTOR_BLOCK: return None base.base = zl_desc.next_level_base_addr_upper try: fl_desc = self.do_fl_sl_level_lookup(base.value, virt_r.fl_index, self.l3_index, self.l1_index) except: return None if fl_desc.dtype == Armv8MMU.DESCRIPTOR_BLOCK: return self.fl_block_desc_2_phys(fl_desc, virt_r) base.base = fl_desc.next_level_base_addr_upper try: sl_desc = self.do_sl_level_lookup( base.value, virt_r.sl_index) except: return None if sl_desc.dtype == Armv8MMU.DESCRIPTOR_BLOCK: r = self.sl_block_desc_2_phys(sl_desc, virt_r) return r base.base = sl_desc.next_level_base_addr_upper try: tl_desc = self.do_tl_level_lookup(base.value, virt_r.tl_index) except: return None r = self.tl_page_desc_2_phys(tl_desc, virt_r) return r def page_table_walk_to_get_swap_pte(self, virt): virt_r = Register(virt, zl_index=(self.ramdump.va_bits-1,self.l0_index), fl_index=(self.l0_index-1,self.l1_index), sl_index=(self.l1_index-1,self.l2_index), tl_index=(self.l2_index-1,self.l3_index), page_index=(self.l3_index-1,0)) base = Register(base=(self.ramdump.va_bits-1, self.l3_index)) base.value = self.ttbr if self.ramdump.pgtable_levels >= 4: try: zl_desc = self.do_fl_sl_level_lookup(base.value, virt_r.zl_index, self.l3_index, self.l0_index) except: return None if zl_desc.dtype == Armv8MMU.DESCRIPTOR_BLOCK: return None base.base = zl_desc.next_level_base_addr_upper try: fl_desc = self.do_fl_sl_level_lookup(base.value, virt_r.fl_index, self.l3_index, self.l1_index) except: return None if fl_desc.dtype == Armv8MMU.DESCRIPTOR_BLOCK: return self.fl_block_desc_2_phys(fl_desc, virt_r) base.base = fl_desc.next_level_base_addr_upper try: sl_desc = self.do_sl_level_lookup( base.value, virt_r.sl_index) except: return None if sl_desc.dtype == Armv8MMU.DESCRIPTOR_BLOCK: r = self.sl_block_desc_2_phys(sl_desc, virt_r) return r base.base = sl_desc.next_level_base_addr_upper descriptor, addr = self.do_level_lookup( base.value, virt_r.tl_index, 12) if descriptor.dtype == Armv8MMU.DESCRIPTOR_INVALID and descriptor.value != 0: return descriptor.value return None def page_table_walkel2(self, virt): #print "page_table_walkel2 virt address = {0}".format(hex(virt)) virt_r = Register(virt, zl_index=(47,37), fl_index=(36,30), sl_index=(29,21), tl_index=(20,12), page_index=(11,0)) #print "page_table_walk entry" try: fl_desc = self.do_fl_sl_level_lookupel2(self.vttbr, virt_r.fl_index, 9, 30) #print "fl_desc.." #print fl_desc #print fl_desc.dtype except Exception as err: #print "err" #print err return None if fl_desc.dtype == Armv8MMU.DESCRIPTOR_BLOCK: #print "return from here ....." return self.fl_block_desc_2_physel2(fl_desc, virt_r) base = Register(base=(47, 12)) #print "fl_desc.next_level_base_addr_upper = {0}".format(hex(fl_desc.next_level_base_addr_upper)) base.base = fl_desc.next_level_base_addr_upper try: sl_desc = self.do_sl_level_lookupel2( base.value, virt_r.sl_index) except Exception as err: #print "err...+++" #print err return None if sl_desc.dtype == Armv8MMU.DESCRIPTOR_BLOCK: r = self.sl_block_desc_2_physel2(sl_desc, virt_r) return r base.base = sl_desc.next_level_base_addr_upper try: tl_desc = self.do_tl_level_lookupel2(base.value, virt_r.tl_index) except: return None r = self.tl_page_desc_2_physel2(tl_desc, virt_r) return r def virt_to_physel2(self, addr, skip_tlb=False, save_in_tlb=True): """Do a virtual to physical address lookup and possibly cache the result in the "TLB". """ #print "virt_to_physel2 entry.. " if addr is None: return None page_addr = (addr >> 12) << 12 page_offset = addr & 0xFFF if not skip_tlb: if page_addr in self._tlbv2: ##print "return from here..." return self._tlbv2[page_addr] + page_offset #print "page_addr virt_to_physel2 = {0}".format(hex(page_addr)) phys_addr = self.page_table_walkel2(page_addr) if phys_addr is None: return None if save_in_tlb: self._tlbv2[page_addr] = phys_addr return phys_addr + page_offset def stage2_translation(self,ipa_addr,pa_page_offset): #print "stage2_translation.... {0}".format(hex(ipa_addr)) pa = self.page_table_walkel2(ipa_addr) #print "stage2_translation.. {0}".format(hex(pa)) pa2 = pa + pa_page_offset #print "stage2_translation.. pa2 = {0}".format(hex(pa2)) return pa2 def dump_page_tables(self, f): f.write( 'Dumping page tables is not currently supported for Armv8MMU\n') f.flush() def get_pgtable_index(self): self.l3_index = self.ramdump.page_shift self.l2_index = self.pmd_shift = self.pgtable_level_shift(2) self.pud_shift = self.pgtable_level_shift(1) self.pgdir_shift = self.pgtable_level_shift(4 - self.ramdump.pgtable_levels) if self.ramdump.pgtable_levels >= 4: self.l1_index = self.pud_shift self.l0_index = self.pgdir_shift else: self.l1_index = self.pgdir_shift self.l0_index = self.ramdump.va_bits return def pgtable_level_shift(self, n): return ((self.ramdump.page_shift - 3) * (4 - (n)) + 3)