| @@ -1,222 +1,398 @@ |
| 1 | # SPDX-License-Identifier: GPL-2.0-only | 1 | # SPDX-License-Identifier: GPL-2.0 |
| 2 | # | 2 | # |
| 3 | # gdb helper commands and functions for Linux kernel debugging | 3 | # Copyright (c) 2023 MediaTek Inc. |
| 4 | # | | |
| 5 | # routines to introspect page table | | |
| 6 | # | 4 | # |
| 7 | # Authors: | 5 | # Authors: |
| 8 | # Dmitrii Bundin <dmitrii.bundin.a@gmail.com> | 6 | # Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com> |
| 9 | # | 7 | # |
| 10 | | 8 | |
| 11 | import gdb | 9 | import gdb |
| | 10 | import math |
| | 11 | from linux import utils, constants |
| | 12 | |
| | 13 | def DIV_ROUND_UP(n,d): |
| | 14 | return ((n) + (d) - 1) // (d) |
| 12 | | 15 | |
| 13 | from linux import utils | 16 | def test_bit(nr, addr): |
| | 17 | if addr.dereference() & (0x1 << nr): |
| | 18 | return True |
| | 19 | else: |
| | 20 | return False |
| 14 | | 21 | |
| 15 | PHYSICAL_ADDRESS_MASK = gdb.parse_and_eval('0xfffffffffffff') | 22 | class page_ops(): |
| | 23 | ops = None |
| | 24 | def __init__(self): |
| | 25 | if not constants.LX_CONFIG_SPARSEMEM_VMEMMAP: |
| | 26 | raise gdb.GdbError('Only support CONFIG_SPARSEMEM_VMEMMAP now') |
| | 27 | if constants.LX_CONFIG_ARM64 and utils.is_target_arch('aarch64'): |
| | 28 | self.ops = aarch64_page_ops() |
| | 29 | else: |
| | 30 | raise gdb.GdbError('Only support aarch64 now') |
| 16 | | 31 | |
| | 32 | class aarch64_page_ops(): |
| | 33 | def __init__(self): |
| | 34 | self.SUBSECTION_SHIFT = 21 |
| | 35 | self.SEBSECTION_SIZE = 1 << self.SUBSECTION_SHIFT |
| | 36 | self.MODULES_VSIZE = 128 * 1024 * 1024 |
| 17 | | 37 | |
| 18 | def page_mask(level=1): | 38 | if constants.LX_CONFIG_ARM64_64K_PAGES: |
| 19 | # 4KB | 39 | self.SECTION_SIZE_BITS = 29 |
| 20 | if level == 1: | 40 | else: |
| 21 | return gdb.parse_and_eval('(u64) ~0xfff') | 41 | self.SECTION_SIZE_BITS = 27 |
| 22 | # 2MB | 42 | self.MAX_PHYSMEM_BITS = constants.LX_CONFIG_ARM64_VA_BITS |
| 23 | elif level == 2: | 43 | |
| 24 | return gdb.parse_and_eval('(u64) ~0x1fffff') | 44 | self.PAGE_SHIFT = constants.LX_CONFIG_ARM64_PAGE_SHIFT |
| 25 | # 1GB | 45 | self.PAGE_SIZE = 1 << self.PAGE_SHIFT |
| 26 | elif level == 3: | 46 | self.PAGE_MASK = (~(self.PAGE_SIZE - 1)) & ((1 << 64) - 1) |
| 27 | return gdb.parse_and_eval('(u64) ~0x3fffffff') | 47 | |
| 28 | else: | 48 | self.VA_BITS = constants.LX_CONFIG_ARM64_VA_BITS |
| 29 | raise Exception(f'Unknown page level: {level}') | 49 | if self.VA_BITS > 48: |
| 30 | | 50 | self.VA_BITS_MIN = 48 |
| 31 | | 51 | self.vabits_actual = gdb.parse_and_eval('vabits_actual') |
| 32 | #page_offset_base in case CONFIG_DYNAMIC_MEMORY_LAYOUT is disabled | 52 | else: |
| 33 | POB_NO_DYNAMIC_MEM_LAYOUT = '0xffff888000000000' | 53 | self.VA_BITS_MIN = self.VA_BITS |
| 34 | def _page_offset_base(): | 54 | self.vabits_actual = self.VA_BITS |
| 35 | pob_symbol = gdb.lookup_global_symbol('page_offset_base') | 55 | self.kimage_voffset = gdb.parse_and_eval('kimage_voffset') & ((1 << 64) - 1) |
| 36 | pob = pob_symbol.name if pob_symbol else POB_NO_DYNAMIC_MEM_LAYOUT | 56 | |
| 37 | return gdb.parse_and_eval(pob) | 57 | self.SECTIONS_SHIFT = self.MAX_PHYSMEM_BITS - self.SECTION_SIZE_BITS |
| 38 | | 58 | |
| 39 | | 59 | if str(constants.LX_CONFIG_ARCH_FORCE_MAX_ORDER).isdigit(): |
| 40 | def is_bit_defined_tupled(data, offset): | 60 | self.MAX_ORDER = constants.LX_CONFIG_ARCH_FORCE_MAX_ORDER |
| 41 | return offset, bool(data >> offset & 1) | 61 | else: |
| 42 | | 62 | self.MAX_ORDER = 11 |
| 43 | def content_tupled(data, bit_start, bit_end): | 63 | |
| 44 | return (bit_start, bit_end), data >> bit_start & ((1 << (1 + bit_end - bit_start)) - 1) | 64 | self.MAX_ORDER_NR_PAGES = 1 << (self.MAX_ORDER - 1) |
| 45 | | 65 | self.PFN_SECTION_SHIFT = self.SECTION_SIZE_BITS - self.PAGE_SHIFT |
| 46 | def entry_va(level, phys_addr, translating_va): | 66 | self.NR_MEM_SECTIONS = 1 << self.SECTIONS_SHIFT |
| 47 | def start_bit(level): | 67 | self.PAGES_PER_SECTION = 1 << self.PFN_SECTION_SHIFT |
| 48 | if level == 5: | 68 | self.PAGE_SECTION_MASK = (~(self.PAGES_PER_SECTION - 1)) & ((1 << 64) - 1) |
| 49 | return 48 | 69 | |
| 50 | elif level == 4: | 70 | if constants.LX_CONFIG_SPARSEMEM_EXTREME: |
| 51 | return 39 | 71 | self.SECTIONS_PER_ROOT = self.PAGE_SIZE // gdb.lookup_type("struct mem_section").sizeof |
| 52 | elif level == 3: | 72 | else: |
| 53 | return 30 | 73 | self.SECTIONS_PER_ROOT = 1 |
| 54 | elif level == 2: | 74 | |
| 55 | return 21 | 75 | self.NR_SECTION_ROOTS = DIV_ROUND_UP(self.NR_MEM_SECTIONS, self.SECTIONS_PER_ROOT) |
| 56 | elif level == 1: | 76 | self.SECTION_ROOT_MASK = self.SECTIONS_PER_ROOT - 1 |
| 57 | return 12 | 77 | self.SUBSECTION_SHIFT = 21 |
| | 78 | self.SEBSECTION_SIZE = 1 << self.SUBSECTION_SHIFT |
| | 79 | self.PFN_SUBSECTION_SHIFT = self.SUBSECTION_SHIFT - self.PAGE_SHIFT |
| | 80 | self.PAGES_PER_SUBSECTION = 1 << self.PFN_SUBSECTION_SHIFT |
| | 81 | |
| | 82 | self.SECTION_HAS_MEM_MAP = 1 << int(gdb.parse_and_eval('SECTION_HAS_MEM_MAP_BIT')) |
| | 83 | self.SECTION_IS_EARLY = 1 << int(gdb.parse_and_eval('SECTION_IS_EARLY_BIT')) |
| | 84 | |
| | 85 | self.struct_page_size = utils.get_page_type().sizeof |
| | 86 | self.STRUCT_PAGE_MAX_SHIFT = (int)(math.log(self.struct_page_size, 2)) |
| | 87 | |
| | 88 | self.PAGE_OFFSET = self._PAGE_OFFSET(self.VA_BITS) |
| | 89 | self.MODULES_VADDR = self._PAGE_END(self.VA_BITS_MIN) |
| | 90 | self.MODULES_END = self.MODULES_VADDR + self.MODULES_VSIZE |
| | 91 | |
| | 92 | self.VMEMMAP_SHIFT = (self.PAGE_SHIFT - self.STRUCT_PAGE_MAX_SHIFT) |
| | 93 | self.VMEMMAP_SIZE = ((self._PAGE_END(self.VA_BITS_MIN) - self.PAGE_OFFSET) >> self.VMEMMAP_SHIFT) |
| | 94 | self.VMEMMAP_START = (-(1 << (self.VA_BITS - self.VMEMMAP_SHIFT))) & 0xffffffffffffffff |
| | 95 | self.VMEMMAP_END = self.VMEMMAP_START + self.VMEMMAP_SIZE |
| | 96 | |
| | 97 | self.VMALLOC_START = self.MODULES_END |
| | 98 | self.VMALLOC_END = self.VMEMMAP_START - 256 * 1024 * 1024 |
| | 99 | |
| | 100 | self.memstart_addr = gdb.parse_and_eval("memstart_addr") |
| | 101 | self.PHYS_OFFSET = self.memstart_addr |
| | 102 | self.vmemmap = gdb.Value(self.VMEMMAP_START).cast(utils.get_page_type().pointer()) - (self.memstart_addr >> self.PAGE_SHIFT) |
| | 103 | |
| | 104 | self.KERNEL_START = gdb.parse_and_eval("_text") |
| | 105 | self.KERNEL_END = gdb.parse_and_eval("_end") |
| | 106 | |
| | 107 | if constants.LX_CONFIG_KASAN_GENERIC or constants.LX_CONFIG_KASAN_SW_TAGS: |
| | 108 | if constants.LX_CONFIG_KASAN_GENERIC: |
| | 109 | self.KASAN_SHADOW_SCALE_SHIFT = 3 |
| 58 | else: | 110 | else: |
| 59 | raise Exception(f'Unknown level {level}') | 111 | self.KASAN_SHADOW_SCALE_SHIFT = 4 |
| 60 | | 112 | self.KASAN_SHADOW_OFFSET = constants.LX_CONFIG_KASAN_SHADOW_OFFSET |
| 61 | entry_offset = ((translating_va >> start_bit(level)) & 511) * 8 | 113 | self.KASAN_SHADOW_END = (1 << (64 - self.KASAN_SHADOW_SCALE_SHIFT)) + self.KASAN_SHADOW_OFFSET |
| 62 | entry_va = _page_offset_base() + phys_addr + entry_offset | 114 | self.PAGE_END = self.KASAN_SHADOW_END - (1 << (self.vabits_actual - self.KASAN_SHADOW_SCALE_SHIFT)) |
| 63 | return entry_va | 115 | else: |
| 64 | | 116 | self.PAGE_END = self._PAGE_END(self.VA_BITS_MIN) |
| 65 | class Cr3(): | 117 | |
| 66 | def __init__(self, cr3, page_levels): | 118 | if constants.LX_CONFIG_NUMA and constants.LX_CONFIG_NODES_SHIFT: |
| 67 | self.cr3 = cr3 | 119 | self.NODE_SHIFT = constants.LX_CONFIG_NODES_SHIFT |
| 68 | self.page_levels = page_levels | 120 | else: |
| 69 | self.page_level_write_through = is_bit_defined_tupled(cr3, 3) | 121 | self.NODE_SHIFT = 0 |
| 70 | self.page_level_cache_disabled = is_bit_defined_tupled(cr3, 4) | 122 | |
| 71 | self.next_entry_physical_address = cr3 & PHYSICAL_ADDRESS_MASK & page_mask() | 123 | self.MAX_NUMNODES = 1 << self.NODE_SHIFT |
| 72 | | 124 | |
| 73 | def next_entry(self, va): | 125 | def SECTION_NR_TO_ROOT(self, sec): |
| 74 | next_level = self.page_levels | 126 | return sec // self.SECTIONS_PER_ROOT |
| 75 | return PageHierarchyEntry(entry_va(next_level, self.next_entry_physical_address, va), next_level) | 127 | |
| 76 | | 128 | def __nr_to_section(self, nr): |
| 77 | def mk_string(self): | 129 | root = self.SECTION_NR_TO_ROOT(nr) |
| 78 | return f"""\ | 130 | mem_section = gdb.parse_and_eval("mem_section") |
| 79 | cr3: | 131 | return mem_section[root][nr & self.SECTION_ROOT_MASK] |
| 80 | {'cr3 binary data': <30} {hex(self.cr3)} | 132 | |
| 81 | {'next entry physical address': <30} {hex(self.next_entry_physical_address)} | 133 | def pfn_to_section_nr(self, pfn): |
| 82 | --- | 134 | return pfn >> self.PFN_SECTION_SHIFT |
| 83 | {'bit' : <4} {self.page_level_write_through[0]: <10} {'page level write through': <30} {self.page_level_write_through[1]} | 135 | |
| 84 | {'bit' : <4} {self.page_level_cache_disabled[0]: <10} {'page level cache disabled': <30} {self.page_level_cache_disabled[1]} | 136 | def section_nr_to_pfn(self, sec): |
| 85 | """ | 137 | return sec << self.PFN_SECTION_SHIFT |
| 86 | | 138 | |
| 87 | | 139 | def __pfn_to_section(self, pfn): |
| 88 | class PageHierarchyEntry(): | 140 | return self.__nr_to_section(self.pfn_to_section_nr(pfn)) |
| 89 | def __init__(self, address, level): | 141 | |
| 90 | data = int.from_bytes( | 142 | def pfn_to_section(self, pfn): |
| 91 | memoryview(gdb.selected_inferior().read_memory(address, 8)), | 143 | return self.__pfn_to_section(pfn) |
| 92 | "little" | 144 | |
| 93 | ) | 145 | def subsection_map_index(self, pfn): |
| 94 | if level == 1: | 146 | return (pfn & ~(self.PAGE_SECTION_MASK)) // self.PAGES_PER_SUBSECTION |
| 95 | self.is_page = True | 147 | |
| 96 | self.entry_present = is_bit_defined_tupled(data, 0) | 148 | def pfn_section_valid(self, ms, pfn): |
| 97 | self.read_write = is_bit_defined_tupled(data, 1) | 149 | if constants.LX_CONFIG_SPARSEMEM_VMEMMAP: |
| 98 | self.user_access_allowed = is_bit_defined_tupled(data, 2) | 150 | idx = self.subsection_map_index(pfn) |
| 99 | self.page_level_write_through = is_bit_defined_tupled(data, 3) | 151 | return test_bit(idx, ms['usage']['subsection_map']) |
| 100 | self.page_level_cache_disabled = is_bit_defined_tupled(data, 4) | 152 | else: |
| 101 | self.entry_was_accessed = is_bit_defined_tupled(data, 5) | 153 | return True |
| 102 | self.dirty = is_bit_defined_tupled(data, 6) | 154 | |
| 103 | self.pat = is_bit_defined_tupled(data, 7) | 155 | def valid_section(self, mem_section): |
| 104 | self.global_translation = is_bit_defined_tupled(data, 8) | 156 | if mem_section != None and (mem_section['section_mem_map'] & self.SECTION_HAS_MEM_MAP): |
| 105 | self.page_physical_address = data & PHYSICAL_ADDRESS_MASK & page_mask(level) | 157 | return True |
| 106 | self.next_entry_physical_address = None | 158 | return False |
| 107 | self.hlat_restart_with_ordinary = is_bit_defined_tupled(data, 11) | 159 | |
| 108 | self.protection_key = content_tupled(data, 59, 62) | 160 | def early_section(self, mem_section): |
| 109 | self.executed_disable = is_bit_defined_tupled(data, 63) | 161 | if mem_section != None and (mem_section['section_mem_map'] & self.SECTION_IS_EARLY): |
| | 162 | return True |
| | 163 | return False |
| | 164 | |
| | 165 | def pfn_valid(self, pfn): |
| | 166 | ms = None |
| | 167 | if self.PHYS_PFN(self.PFN_PHYS(pfn)) != pfn: |
| | 168 | return False |
| | 169 | if self.pfn_to_section_nr(pfn) >= self.NR_MEM_SECTIONS: |
| | 170 | return False |
| | 171 | ms = self.__pfn_to_section(pfn) |
| | 172 | |
| | 173 | if not self.valid_section(ms): |
| | 174 | return False |
| | 175 | return self.early_section(ms) or self.pfn_section_valid(ms, pfn) |
| | 176 | |
| | 177 | def _PAGE_OFFSET(self, va): |
| | 178 | return (-(1 << (va))) & 0xffffffffffffffff |
| | 179 | |
| | 180 | def _PAGE_END(self, va): |
| | 181 | return (-(1 << (va - 1))) & 0xffffffffffffffff |
| | 182 | |
| | 183 | def kasan_reset_tag(self, addr): |
| | 184 | if constants.LX_CONFIG_KASAN_SW_TAGS or constants.LX_CONFIG_KASAN_HW_TAGS: |
| | 185 | return int(addr) | (0xff << 56) |
| | 186 | else: |
| | 187 | return addr |
| | 188 | |
| | 189 | def __is_lm_address(self, addr): |
| | 190 | if (addr - self.PAGE_OFFSET) < (self.PAGE_END - self.PAGE_OFFSET): |
| | 191 | return True |
| | 192 | else: |
| | 193 | return False |
| | 194 | def __lm_to_phys(self, addr): |
| | 195 | return addr - self.PAGE_OFFSET + self.PHYS_OFFSET |
| | 196 | |
| | 197 | def __kimg_to_phys(self, addr): |
| | 198 | return addr - self.kimage_voffset |
| | 199 | |
| | 200 | def __virt_to_phys_nodebug(self, va): |
| | 201 | untagged_va = self.kasan_reset_tag(va) |
| | 202 | if self.__is_lm_address(untagged_va): |
| | 203 | return self.__lm_to_phys(untagged_va) |
| | 204 | else: |
| | 205 | return self.__kimg_to_phys(untagged_va) |
| | 206 | |
| | 207 | def __virt_to_phys(self, va): |
| | 208 | if constants.LX_CONFIG_DEBUG_VIRTUAL: |
| | 209 | if not self.__is_lm_address(self.kasan_reset_tag(va)): |
| | 210 | raise gdb.GdbError("Warning: virt_to_phys used for non-linear address: 0x%lx\n" % va) |
| | 211 | return self.__virt_to_phys_nodebug(va) |
| | 212 | |
| | 213 | def virt_to_phys(self, va): |
| | 214 | return self.__virt_to_phys(va) |
| | 215 | |
| | 216 | def PFN_PHYS(self, pfn): |
| | 217 | return pfn << self.PAGE_SHIFT |
| | 218 | |
| | 219 | def PHYS_PFN(self, phys): |
| | 220 | return phys >> self.PAGE_SHIFT |
| | 221 | |
| | 222 | def __phys_to_virt(self, pa): |
| | 223 | return (pa - self.PHYS_OFFSET) | self.PAGE_OFFSET |
| | 224 | |
| | 225 | def __phys_to_pfn(self, pa): |
| | 226 | return self.PHYS_PFN(pa) |
| | 227 | |
| | 228 | def __pfn_to_phys(self, pfn): |
| | 229 | return self.PFN_PHYS(pfn) |
| | 230 | |
| | 231 | def __pa_symbol_nodebug(self, x): |
| | 232 | return self.__kimg_to_phys(x) |
| | 233 | |
| | 234 | def __phys_addr_symbol(self, x): |
| | 235 | if constants.LX_CONFIG_DEBUG_VIRTUAL: |
| | 236 | if x < self.KERNEL_START or x > self.KERNEL_END: |
| | 237 | raise gdb.GdbError("0x%x exceed kernel range" % x) |
| | 238 | return self.__pa_symbol_nodebug(x) |
| | 239 | |
| | 240 | def __pa_symbol(self, x): |
| | 241 | return self.__phys_addr_symbol(x) |
| | 242 | |
| | 243 | def __va(self, pa): |
| | 244 | return self.__phys_to_virt(pa) |
| | 245 | |
| | 246 | def pfn_to_kaddr(self, pfn): |
| | 247 | return self.__va(pfn << self.PAGE_SHIFT) |
| | 248 | |
| | 249 | def virt_to_pfn(self, va): |
| | 250 | return self.__phys_to_pfn(self.__virt_to_phys(va)) |
| | 251 | |
| | 252 | def sym_to_pfn(self, x): |
| | 253 | return self.__phys_to_pfn(self.__pa_symbol(x)) |
| | 254 | |
| | 255 | def page_to_pfn(self, page): |
| | 256 | return int(page.cast(utils.get_page_type().pointer()) - self.vmemmap.cast(utils.get_page_type().pointer())) |
| | 257 | |
| | 258 | def page_to_phys(self, page): |
| | 259 | return self.__pfn_to_phys(self.page_to_pfn(page)) |
| | 260 | |
| | 261 | def pfn_to_page(self, pfn): |
| | 262 | return (self.vmemmap + pfn).cast(utils.get_page_type().pointer()) |
| | 263 | |
| | 264 | def page_to_virt(self, page): |
| | 265 | if constants.LX_CONFIG_DEBUG_VIRTUAL: |
| | 266 | return self.__va(self.page_to_phys(page)) |
| 110 | else: | 267 | else: |
| 111 | page_size = is_bit_defined_tupled(data, 7) | 268 | __idx = int((page.cast(gdb.lookup_type("unsigned long")) - self.VMEMMAP_START).cast(utils.get_ulong_type())) // self.struct_page_size |
| 112 | page_size_bit = page_size[1] | 269 | return self.PAGE_OFFSET + (__idx * self.PAGE_SIZE) |
| 113 | self.is_page = page_size_bit | 270 | |
| 114 | self.entry_present = is_bit_defined_tupled(data, 0) | 271 | def virt_to_page(self, va): |
| 115 | self.read_write = is_bit_defined_tupled(data, 1) | 272 | if constants.LX_CONFIG_DEBUG_VIRTUAL: |
| 116 | self.user_access_allowed = is_bit_defined_tupled(data, 2) | 273 | return self.pfn_to_page(self.virt_to_pfn(va)) |
| 117 | self.page_level_write_through = is_bit_defined_tupled(data, 3) | | |
| 118 | self.page_level_cache_disabled = is_bit_defined_tupled(data, 4) | | |
| 119 | self.entry_was_accessed = is_bit_defined_tupled(data, 5) | | |
| 120 | self.page_size = page_size | | |
| 121 | self.dirty = is_bit_defined_tupled( | | |
| 122 | data, 6) if page_size_bit else None | | |
| 123 | self.global_translation = is_bit_defined_tupled( | | |
| 124 | data, 8) if page_size_bit else None | | |
| 125 | self.pat = is_bit_defined_tupled( | | |
| 126 | data, 12) if page_size_bit else None | | |
| 127 | self.page_physical_address = data & PHYSICAL_ADDRESS_MASK & page_mask(level) if page_size_bit else None | | |
| 128 | self.next_entry_physical_address = None if page_size_bit else data & PHYSICAL_ADDRESS_MASK & page_mask() | | |
| 129 | self.hlat_restart_with_ordinary = is_bit_defined_tupled(data, 11) | | |
| 130 | self.protection_key = content_tupled(data, 59, 62) if page_size_bit else None | | |
| 131 | self.executed_disable = is_bit_defined_tupled(data, 63) | | |
| 132 | self.address = address | | |
| 133 | self.page_entry_binary_data = data | | |
| 134 | self.page_hierarchy_level = level | | |
| 135 | | | |
| 136 | def next_entry(self, va): | | |
| 137 | if self.is_page or not self.entry_present[1]: | | |
| 138 | return None | | |
| 139 | | | |
| 140 | next_level = self.page_hierarchy_level - 1 | | |
| 141 | return PageHierarchyEntry(entry_va(next_level, self.next_entry_physical_address, va), next_level) | | |
| 142 | | | |
| 143 | | | |
| 144 | def mk_string(self): | | |
| 145 | if not self.entry_present[1]: | | |
| 146 | return f"""\ | | |
| 147 | level {self.page_hierarchy_level}: | | |
| 148 | {'entry address': <30} {hex(self.address)} | | |
| 149 | {'page entry binary data': <30} {hex(self.page_entry_binary_data)} | | |
| 150 | --- | | |
| 151 | PAGE ENTRY IS NOT PRESENT! | | |
| 152 | """ | | |
| 153 | elif self.is_page: | | |
| 154 | def page_size_line(ps_bit, ps, level): | | |
| 155 | return "" if level == 1 else f"{'bit': <3} {ps_bit: <5} {'page size': <30} {ps}" | | |
| 156 | | | |
| 157 | return f"""\ | | |
| 158 | level {self.page_hierarchy_level}: | | |
| 159 | {'entry address': <30} {hex(self.address)} | | |
| 160 | {'page entry binary data': <30} {hex(self.page_entry_binary_data)} | | |
| 161 | {'page size': <30} {'1GB' if self.page_hierarchy_level == 3 else '2MB' if self.page_hierarchy_level == 2 else '4KB' if self.page_hierarchy_level == 1 else 'Unknown page size for level:' + self.page_hierarchy_level} | | |
| 162 | {'page physical address': <30} {hex(self.page_physical_address)} | | |
| 163 | --- | | |
| 164 | {'bit': <4} {self.entry_present[0]: <10} {'entry present': <30} {self.entry_present[1]} | | |
| 165 | {'bit': <4} {self.read_write[0]: <10} {'read/write access allowed': <30} {self.read_write[1]} | | |
| 166 | {'bit': <4} {self.user_access_allowed[0]: <10} {'user access allowed': <30} {self.user_access_allowed[1]} | | |
| 167 | {'bit': <4} {self.page_level_write_through[0]: <10} {'page level write through': <30} {self.page_level_write_through[1]} | | |
| 168 | {'bit': <4} {self.page_level_cache_disabled[0]: <10} {'page level cache disabled': <30} {self.page_level_cache_disabled[1]} | | |
| 169 | {'bit': <4} {self.entry_was_accessed[0]: <10} {'entry has been accessed': <30} {self.entry_was_accessed[1]} | | |
| 170 | {"" if self.page_hierarchy_level == 1 else f"{'bit': <4} {self.page_size[0]: <10} {'page size': <30} {self.page_size[1]}"} | | |
| 171 | {'bit': <4} {self.dirty[0]: <10} {'page dirty': <30} {self.dirty[1]} | | |
| 172 | {'bit': <4} {self.global_translation[0]: <10} {'global translation': <30} {self.global_translation[1]} | | |
| 173 | {'bit': <4} {self.hlat_restart_with_ordinary[0]: <10} {'restart to ordinary': <30} {self.hlat_restart_with_ordinary[1]} | | |
| 174 | {'bit': <4} {self.pat[0]: <10} {'pat': <30} {self.pat[1]} | | |
| 175 | {'bits': <4} {str(self.protection_key[0]): <10} {'protection key': <30} {self.protection_key[1]} | | |
| 176 | {'bit': <4} {self.executed_disable[0]: <10} {'execute disable': <30} {self.executed_disable[1]} | | |
| 177 | """ | | |
| 178 | else: | 274 | else: |
| 179 | return f"""\ | 275 | __idx = int(self.kasan_reset_tag(va) - self.PAGE_OFFSET) // self.PAGE_SIZE |
| 180 | level {self.page_hierarchy_level}: | 276 | addr = self.VMEMMAP_START + (__idx * self.struct_page_size) |
| 181 | {'entry address': <30} {hex(self.address)} | 277 | return gdb.Value(addr).cast(utils.get_page_type().pointer()) |
| 182 | {'page entry binary data': <30} {hex(self.page_entry_binary_data)} | 278 | |
| 183 | {'next entry physical address': <30} {hex(self.next_entry_physical_address)} | 279 | def page_address(self, page): |
| 184 | --- | 280 | return self.page_to_virt(page) |
| 185 | {'bit': <4} {self.entry_present[0]: <10} {'entry present': <30} {self.entry_present[1]} | 281 | |
| 186 | {'bit': <4} {self.read_write[0]: <10} {'read/write access allowed': <30} {self.read_write[1]} | 282 | def folio_address(self, folio): |
| 187 | {'bit': <4} {self.user_access_allowed[0]: <10} {'user access allowed': <30} {self.user_access_allowed[1]} | 283 | return self.page_address(folio['page'].address) |
| 188 | {'bit': <4} {self.page_level_write_through[0]: <10} {'page level write through': <30} {self.page_level_write_through[1]} | 284 | |
| 189 | {'bit': <4} {self.page_level_cache_disabled[0]: <10} {'page level cache disabled': <30} {self.page_level_cache_disabled[1]} | 285 | class LxPFN2Page(gdb.Command): |
| 190 | {'bit': <4} {self.entry_was_accessed[0]: <10} {'entry has been accessed': <30} {self.entry_was_accessed[1]} | 286 | """PFN to struct page""" |
| 191 | {'bit': <4} {self.page_size[0]: <10} {'page size': <30} {self.page_size[1]} | | |
| 192 | {'bit': <4} {self.hlat_restart_with_ordinary[0]: <10} {'restart to ordinary': <30} {self.hlat_restart_with_ordinary[1]} | | |
| 193 | {'bit': <4} {self.executed_disable[0]: <10} {'execute disable': <30} {self.executed_disable[1]} | | |
| 194 | """ | | |
| 195 | | | |
| 196 | | | |
| 197 | class TranslateVM(gdb.Command): | | |
| 198 | """Prints the entire paging structure used to translate a given virtual address. | | |
| 199 | | | |
| 200 | Having an address space of the currently executed process translates the virtual address | | |
| 201 | and prints detailed information of all paging structure levels used for the transaltion. | | |
| 202 | Currently supported arch: x86""" | | |
| 203 | | 287 | |
| 204 | def __init__(self): | 288 | def __init__(self): |
| 205 | super(TranslateVM, self).__init__('translate-vm', gdb.COMMAND_USER) | 289 | super(LxPFN2Page, self).__init__("lx-pfn_to_page", gdb.COMMAND_USER) |
| 206 | | 290 | |
| 207 | def invoke(self, arg, from_tty): | 291 | def invoke(self, arg, from_tty): |
| 208 | if utils.is_target_arch("x86"): | 292 | argv = gdb.string_to_argv(arg) |
| 209 | vm_address = gdb.parse_and_eval(f'{arg}') | 293 | pfn = int(argv[0]) |
| 210 | cr3_data = gdb.parse_and_eval('$cr3') | 294 | page = page_ops().ops.pfn_to_page(pfn) |
| 211 | cr4 = gdb.parse_and_eval('$cr4') | 295 | gdb.write("pfn_to_page(0x%x) = 0x%x\n" % (pfn, page)) |
| 212 | page_levels = 5 if cr4 & (1 << 12) else 4 | 296 | |
| 213 | page_entry = Cr3(cr3_data, page_levels) | 297 | LxPFN2Page() |
| 214 | while page_entry: | 298 | |
| 215 | gdb.write(page_entry.mk_string()) | 299 | class LxPage2PFN(gdb.Command): |
| 216 | page_entry = page_entry.next_entry(vm_address) | 300 | """struct page to PFN""" |
| 217 | else: | 301 | |
| 218 | gdb.GdbError("Virtual address translation is not" | 302 | def __init__(self): |
| 219 | "supported for this arch") | 303 | super(LxPage2PFN, self).__init__("lx-page_to_pfn", gdb.COMMAND_USER) |
| | 304 | |
| | 305 | def invoke(self, arg, from_tty): |
| | 306 | argv = gdb.string_to_argv(arg) |
| | 307 | struct_page_addr = int(argv[0], 16) |
| | 308 | page = gdb.Value(struct_page_addr).cast(utils.get_page_type().pointer()) |
| | 309 | pfn = page_ops().ops.page_to_pfn(page) |
| | 310 | gdb.write("page_to_pfn(0x%x) = 0x%x\n" % (page, pfn)) |
| | 311 | |
| | 312 | LxPage2PFN() |
| | 313 | |
| | 314 | class LxPageAddress(gdb.Command): |
| | 315 | """struct page to linear mapping address""" |
| | 316 | |
| | 317 | def __init__(self): |
| | 318 | super(LxPageAddress, self).__init__("lx-page_address", gdb.COMMAND_USER) |
| | 319 | |
| | 320 | def invoke(self, arg, from_tty): |
| | 321 | argv = gdb.string_to_argv(arg) |
| | 322 | struct_page_addr = int(argv[0], 16) |
| | 323 | page = gdb.Value(struct_page_addr).cast(utils.get_page_type().pointer()) |
| | 324 | addr = page_ops().ops.page_address(page) |
| | 325 | gdb.write("page_address(0x%x) = 0x%x\n" % (page, addr)) |
| 220 | | 326 | |
| | 327 | LxPageAddress() |
| | 328 | |
| | 329 | class LxPage2Phys(gdb.Command): |
| | 330 | """struct page to physical address""" |
| | 331 | |
| | 332 | def __init__(self): |
| | 333 | super(LxPage2Phys, self).__init__("lx-page_to_phys", gdb.COMMAND_USER) |
| | 334 | |
| | 335 | def invoke(self, arg, from_tty): |
| | 336 | argv = gdb.string_to_argv(arg) |
| | 337 | struct_page_addr = int(argv[0], 16) |
| | 338 | page = gdb.Value(struct_page_addr).cast(utils.get_page_type().pointer()) |
| | 339 | phys_addr = page_ops().ops.page_to_phys(page) |
| | 340 | gdb.write("page_to_phys(0x%x) = 0x%x\n" % (page, phys_addr)) |
| | 341 | |
| | 342 | LxPage2Phys() |
| | 343 | |
| | 344 | class LxVirt2Phys(gdb.Command): |
| | 345 | """virtual address to physical address""" |
| | 346 | |
| | 347 | def __init__(self): |
| | 348 | super(LxVirt2Phys, self).__init__("lx-virt_to_phys", gdb.COMMAND_USER) |
| | 349 | |
| | 350 | def invoke(self, arg, from_tty): |
| | 351 | argv = gdb.string_to_argv(arg) |
| | 352 | linear_addr = int(argv[0], 16) |
| | 353 | phys_addr = page_ops().ops.virt_to_phys(linear_addr) |
| | 354 | gdb.write("virt_to_phys(0x%x) = 0x%x\n" % (linear_addr, phys_addr)) |
| | 355 | |
| | 356 | LxVirt2Phys() |
| | 357 | |
| | 358 | class LxVirt2Page(gdb.Command): |
| | 359 | """virtual address to struct page""" |
| | 360 | |
| | 361 | def __init__(self): |
| | 362 | super(LxVirt2Page, self).__init__("lx-virt_to_page", gdb.COMMAND_USER) |
| | 363 | |
| | 364 | def invoke(self, arg, from_tty): |
| | 365 | argv = gdb.string_to_argv(arg) |
| | 366 | linear_addr = int(argv[0], 16) |
| | 367 | page = page_ops().ops.virt_to_page(linear_addr) |
| | 368 | gdb.write("virt_to_page(0x%x) = 0x%x\n" % (linear_addr, page)) |
| | 369 | |
| | 370 | LxVirt2Page() |
| | 371 | |
| | 372 | class LxSym2PFN(gdb.Command): |
| | 373 | """symbol address to PFN""" |
| | 374 | |
| | 375 | def __init__(self): |
| | 376 | super(LxSym2PFN, self).__init__("lx-sym_to_pfn", gdb.COMMAND_USER) |
| | 377 | |
| | 378 | def invoke(self, arg, from_tty): |
| | 379 | argv = gdb.string_to_argv(arg) |
| | 380 | sym_addr = int(argv[0], 16) |
| | 381 | pfn = page_ops().ops.sym_to_pfn(sym_addr) |
| | 382 | gdb.write("sym_to_pfn(0x%x) = %d\n" % (sym_addr, pfn)) |
| | 383 | |
| | 384 | LxSym2PFN() |
| | 385 | |
| | 386 | class LxPFN2Kaddr(gdb.Command): |
| | 387 | """PFN to kernel address""" |
| | 388 | |
| | 389 | def __init__(self): |
| | 390 | super(LxPFN2Kaddr, self).__init__("lx-pfn_to_kaddr", gdb.COMMAND_USER) |
| | 391 | |
| | 392 | def invoke(self, arg, from_tty): |
| | 393 | argv = gdb.string_to_argv(arg) |
| | 394 | pfn = int(argv[0]) |
| | 395 | kaddr = page_ops().ops.pfn_to_kaddr(pfn) |
| | 396 | gdb.write("pfn_to_kaddr(%d) = 0x%x\n" % (pfn, kaddr)) |
| 221 | | 397 | |
| 222 | TranslateVM() | 398 | LxPFN2Kaddr() |