1 # SPDX-License-Identifier: GPL-2.0-only 1 # SPDX-License-Identifier: GPL-2.0-only 2 # 2 # 3 # gdb helper commands and functions for Linux 3 # gdb helper commands and functions for Linux kernel debugging 4 # 4 # 5 # routines to introspect page table 5 # routines to introspect page table 6 # 6 # 7 # Authors: 7 # Authors: 8 # Dmitrii Bundin <dmitrii.bundin.a@gmail.com> 8 # Dmitrii Bundin <dmitrii.bundin.a@gmail.com> 9 # 9 # 10 10 11 import gdb 11 import gdb 12 12 13 from linux import utils 13 from linux import utils 14 14 15 PHYSICAL_ADDRESS_MASK = gdb.parse_and_eval('0x 15 PHYSICAL_ADDRESS_MASK = gdb.parse_and_eval('0xfffffffffffff') 16 16 17 17 18 def page_mask(level=1): 18 def page_mask(level=1): 19 # 4KB 19 # 4KB 20 if level == 1: 20 if level == 1: 21 return gdb.parse_and_eval('(u64) ~0xff 21 return gdb.parse_and_eval('(u64) ~0xfff') 22 # 2MB 22 # 2MB 23 elif level == 2: 23 elif level == 2: 24 return gdb.parse_and_eval('(u64) ~0x1f 24 return gdb.parse_and_eval('(u64) ~0x1fffff') 25 # 1GB 25 # 1GB 26 elif level == 3: 26 elif level == 3: 27 return gdb.parse_and_eval('(u64) ~0x3f 27 return gdb.parse_and_eval('(u64) ~0x3fffffff') 28 else: 28 else: 29 raise Exception(f'Unknown page level: 29 raise Exception(f'Unknown page level: {level}') 30 30 31 31 32 #page_offset_base in case CONFIG_DYNAMIC_MEMOR 32 #page_offset_base in case CONFIG_DYNAMIC_MEMORY_LAYOUT is disabled 33 POB_NO_DYNAMIC_MEM_LAYOUT = '0xffff88800000000 33 POB_NO_DYNAMIC_MEM_LAYOUT = '0xffff888000000000' 34 def _page_offset_base(): 34 def _page_offset_base(): 35 pob_symbol = gdb.lookup_global_symbol('pag 35 pob_symbol = gdb.lookup_global_symbol('page_offset_base') 36 pob = pob_symbol.name if pob_symbol else P 36 pob = pob_symbol.name if pob_symbol else POB_NO_DYNAMIC_MEM_LAYOUT 37 return gdb.parse_and_eval(pob) 37 return gdb.parse_and_eval(pob) 38 38 39 39 40 def is_bit_defined_tupled(data, offset): 40 def is_bit_defined_tupled(data, offset): 41 return offset, bool(data >> offset & 1) 41 return offset, bool(data >> offset & 1) 42 42 43 def content_tupled(data, bit_start, bit_end): 43 def content_tupled(data, bit_start, bit_end): 44 return (bit_start, bit_end), data >> bit_s 44 return (bit_start, bit_end), data >> bit_start & ((1 << (1 + bit_end - bit_start)) - 1) 45 45 46 def entry_va(level, phys_addr, translating_va) 46 def entry_va(level, phys_addr, translating_va): 47 def start_bit(level): 47 def start_bit(level): 48 if level == 5: 48 if level == 5: 49 return 48 49 return 48 50 elif level == 4: 50 elif level == 4: 51 return 39 51 return 39 52 elif level == 3: 52 elif level == 3: 53 return 30 53 return 30 54 elif level == 2: 54 elif level == 2: 55 return 21 55 return 21 56 elif level == 1: 56 elif level == 1: 57 return 12 57 return 12 58 else: 58 else: 59 raise Exception(f'Unknown leve 59 raise Exception(f'Unknown level {level}') 60 60 61 entry_offset = ((translating_va >> st 61 entry_offset = ((translating_va >> start_bit(level)) & 511) * 8 62 entry_va = _page_offset_base() + phys_ 62 entry_va = _page_offset_base() + phys_addr + entry_offset 63 return entry_va 63 return entry_va 64 64 65 class Cr3(): 65 class Cr3(): 66 def __init__(self, cr3, page_levels): 66 def __init__(self, cr3, page_levels): 67 self.cr3 = cr3 67 self.cr3 = cr3 68 self.page_levels = page_levels 68 self.page_levels = page_levels 69 self.page_level_write_through = is_bit 69 self.page_level_write_through = is_bit_defined_tupled(cr3, 3) 70 self.page_level_cache_disabled = is_bi 70 self.page_level_cache_disabled = is_bit_defined_tupled(cr3, 4) 71 self.next_entry_physical_address = cr3 71 self.next_entry_physical_address = cr3 & PHYSICAL_ADDRESS_MASK & page_mask() 72 72 73 def next_entry(self, va): 73 def next_entry(self, va): 74 next_level = self.page_levels 74 next_level = self.page_levels 75 return PageHierarchyEntry(entry_va(nex 75 return PageHierarchyEntry(entry_va(next_level, self.next_entry_physical_address, va), next_level) 76 76 77 def mk_string(self): 77 def mk_string(self): 78 return f"""\ 78 return f"""\ 79 cr3: 79 cr3: 80 {'cr3 binary data': <30} {hex(self.cr3)} 80 {'cr3 binary data': <30} {hex(self.cr3)} 81 {'next entry physical address': <30} {hex( 81 {'next entry physical address': <30} {hex(self.next_entry_physical_address)} 82 --- 82 --- 83 {'bit' : <4} {self.page_level_write_throug 83 {'bit' : <4} {self.page_level_write_through[0]: <10} {'page level write through': <30} {self.page_level_write_through[1]} 84 {'bit' : <4} {self.page_level_cache_disabl 84 {'bit' : <4} {self.page_level_cache_disabled[0]: <10} {'page level cache disabled': <30} {self.page_level_cache_disabled[1]} 85 """ 85 """ 86 86 87 87 88 class PageHierarchyEntry(): 88 class PageHierarchyEntry(): 89 def __init__(self, address, level): 89 def __init__(self, address, level): 90 data = int.from_bytes( 90 data = int.from_bytes( 91 memoryview(gdb.selected_inferior() 91 memoryview(gdb.selected_inferior().read_memory(address, 8)), 92 "little" 92 "little" 93 ) 93 ) 94 if level == 1: 94 if level == 1: 95 self.is_page = True 95 self.is_page = True 96 self.entry_present = is_bit_define 96 self.entry_present = is_bit_defined_tupled(data, 0) 97 self.read_write = is_bit_defined_t 97 self.read_write = is_bit_defined_tupled(data, 1) 98 self.user_access_allowed = is_bit_ 98 self.user_access_allowed = is_bit_defined_tupled(data, 2) 99 self.page_level_write_through = is 99 self.page_level_write_through = is_bit_defined_tupled(data, 3) 100 self.page_level_cache_disabled = i 100 self.page_level_cache_disabled = is_bit_defined_tupled(data, 4) 101 self.entry_was_accessed = is_bit_d 101 self.entry_was_accessed = is_bit_defined_tupled(data, 5) 102 self.dirty = is_bit_defined_tupled 102 self.dirty = is_bit_defined_tupled(data, 6) 103 self.pat = is_bit_defined_tupled(d 103 self.pat = is_bit_defined_tupled(data, 7) 104 self.global_translation = is_bit_d 104 self.global_translation = is_bit_defined_tupled(data, 8) 105 self.page_physical_address = data 105 self.page_physical_address = data & PHYSICAL_ADDRESS_MASK & page_mask(level) 106 self.next_entry_physical_address = 106 self.next_entry_physical_address = None 107 self.hlat_restart_with_ordinary = 107 self.hlat_restart_with_ordinary = is_bit_defined_tupled(data, 11) 108 self.protection_key = content_tupl 108 self.protection_key = content_tupled(data, 59, 62) 109 self.executed_disable = is_bit_def 109 self.executed_disable = is_bit_defined_tupled(data, 63) 110 else: 110 else: 111 page_size = is_bit_defined_tupled( 111 page_size = is_bit_defined_tupled(data, 7) 112 page_size_bit = page_size[1] 112 page_size_bit = page_size[1] 113 self.is_page = page_size_bit 113 self.is_page = page_size_bit 114 self.entry_present = is_bit_define 114 self.entry_present = is_bit_defined_tupled(data, 0) 115 self.read_write = is_bit_defined_t 115 self.read_write = is_bit_defined_tupled(data, 1) 116 self.user_access_allowed = is_bit_ 116 self.user_access_allowed = is_bit_defined_tupled(data, 2) 117 self.page_level_write_through = is 117 self.page_level_write_through = is_bit_defined_tupled(data, 3) 118 self.page_level_cache_disabled = i 118 self.page_level_cache_disabled = is_bit_defined_tupled(data, 4) 119 self.entry_was_accessed = is_bit_d 119 self.entry_was_accessed = is_bit_defined_tupled(data, 5) 120 self.page_size = page_size 120 self.page_size = page_size 121 self.dirty = is_bit_defined_tupled 121 self.dirty = is_bit_defined_tupled( 122 data, 6) if page_size_bit else 122 data, 6) if page_size_bit else None 123 self.global_translation = is_bit_d 123 self.global_translation = is_bit_defined_tupled( 124 data, 8) if page_size_bit else 124 data, 8) if page_size_bit else None 125 self.pat = is_bit_defined_tupled( 125 self.pat = is_bit_defined_tupled( 126 data, 12) if page_size_bit els 126 data, 12) if page_size_bit else None 127 self.page_physical_address = data 127 self.page_physical_address = data & PHYSICAL_ADDRESS_MASK & page_mask(level) if page_size_bit else None 128 self.next_entry_physical_address = 128 self.next_entry_physical_address = None if page_size_bit else data & PHYSICAL_ADDRESS_MASK & page_mask() 129 self.hlat_restart_with_ordinary = 129 self.hlat_restart_with_ordinary = is_bit_defined_tupled(data, 11) 130 self.protection_key = content_tupl 130 self.protection_key = content_tupled(data, 59, 62) if page_size_bit else None 131 self.executed_disable = is_bit_def 131 self.executed_disable = is_bit_defined_tupled(data, 63) 132 self.address = address 132 self.address = address 133 self.page_entry_binary_data = data 133 self.page_entry_binary_data = data 134 self.page_hierarchy_level = level 134 self.page_hierarchy_level = level 135 135 136 def next_entry(self, va): 136 def next_entry(self, va): 137 if self.is_page or not self.entry_pres 137 if self.is_page or not self.entry_present[1]: 138 return None 138 return None 139 139 140 next_level = self.page_hierarchy_level 140 next_level = self.page_hierarchy_level - 1 141 return PageHierarchyEntry(entry_va(nex 141 return PageHierarchyEntry(entry_va(next_level, self.next_entry_physical_address, va), next_level) 142 142 143 143 144 def mk_string(self): 144 def mk_string(self): 145 if not self.entry_present[1]: 145 if not self.entry_present[1]: 146 return f"""\ 146 return f"""\ 147 level {self.page_hierarchy_level}: 147 level {self.page_hierarchy_level}: 148 {'entry address': <30} {hex(self.address)} 148 {'entry address': <30} {hex(self.address)} 149 {'page entry binary data': <30} {hex(self. 149 {'page entry binary data': <30} {hex(self.page_entry_binary_data)} 150 --- 150 --- 151 PAGE ENTRY IS NOT PRESENT! 151 PAGE ENTRY IS NOT PRESENT! 152 """ 152 """ 153 elif self.is_page: 153 elif self.is_page: 154 def page_size_line(ps_bit, ps, lev 154 def page_size_line(ps_bit, ps, level): 155 return "" if level == 1 else f 155 return "" if level == 1 else f"{'bit': <3} {ps_bit: <5} {'page size': <30} {ps}" 156 156 157 return f"""\ 157 return f"""\ 158 level {self.page_hierarchy_level}: 158 level {self.page_hierarchy_level}: 159 {'entry address': <30} {hex(self.address)} 159 {'entry address': <30} {hex(self.address)} 160 {'page entry binary data': <30} {hex(self. 160 {'page entry binary data': <30} {hex(self.page_entry_binary_data)} 161 {'page size': <30} {'1GB' if self.page_hie 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.p 162 {'page physical address': <30} {hex(self.page_physical_address)} 163 --- 163 --- 164 {'bit': <4} {self.entry_present[0]: <10} { 164 {'bit': <4} {self.entry_present[0]: <10} {'entry present': <30} {self.entry_present[1]} 165 {'bit': <4} {self.read_write[0]: <10} {'re 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]: 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 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_disable 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]: < 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 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 di 171 {'bit': <4} {self.dirty[0]: <10} {'page dirty': <30} {self.dirty[1]} 172 {'bit': <4} {self.global_translation[0]: < 172 {'bit': <4} {self.global_translation[0]: <10} {'global translation': <30} {self.global_translation[1]} 173 {'bit': <4} {self.hlat_restart_with_ordina 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 174 {'bit': <4} {self.pat[0]: <10} {'pat': <30} {self.pat[1]} 175 {'bits': <4} {str(self.protection_key[0]): 175 {'bits': <4} {str(self.protection_key[0]): <10} {'protection key': <30} {self.protection_key[1]} 176 {'bit': <4} {self.executed_disable[0]: <10 176 {'bit': <4} {self.executed_disable[0]: <10} {'execute disable': <30} {self.executed_disable[1]} 177 """ 177 """ 178 else: 178 else: 179 return f"""\ 179 return f"""\ 180 level {self.page_hierarchy_level}: 180 level {self.page_hierarchy_level}: 181 {'entry address': <30} {hex(self.address)} 181 {'entry address': <30} {hex(self.address)} 182 {'page entry binary data': <30} {hex(self. 182 {'page entry binary data': <30} {hex(self.page_entry_binary_data)} 183 {'next entry physical address': <30} {hex( 183 {'next entry physical address': <30} {hex(self.next_entry_physical_address)} 184 --- 184 --- 185 {'bit': <4} {self.entry_present[0]: <10} { 185 {'bit': <4} {self.entry_present[0]: <10} {'entry present': <30} {self.entry_present[1]} 186 {'bit': <4} {self.read_write[0]: <10} {'re 186 {'bit': <4} {self.read_write[0]: <10} {'read/write access allowed': <30} {self.read_write[1]} 187 {'bit': <4} {self.user_access_allowed[0]: 187 {'bit': <4} {self.user_access_allowed[0]: <10} {'user access allowed': <30} {self.user_access_allowed[1]} 188 {'bit': <4} {self.page_level_write_through 188 {'bit': <4} {self.page_level_write_through[0]: <10} {'page level write through': <30} {self.page_level_write_through[1]} 189 {'bit': <4} {self.page_level_cache_disable 189 {'bit': <4} {self.page_level_cache_disabled[0]: <10} {'page level cache disabled': <30} {self.page_level_cache_disabled[1]} 190 {'bit': <4} {self.entry_was_accessed[0]: < 190 {'bit': <4} {self.entry_was_accessed[0]: <10} {'entry has been accessed': <30} {self.entry_was_accessed[1]} 191 {'bit': <4} {self.page_size[0]: <10} {'pag 191 {'bit': <4} {self.page_size[0]: <10} {'page size': <30} {self.page_size[1]} 192 {'bit': <4} {self.hlat_restart_with_ordina 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 193 {'bit': <4} {self.executed_disable[0]: <10} {'execute disable': <30} {self.executed_disable[1]} 194 """ 194 """ 195 195 196 196 197 class TranslateVM(gdb.Command): 197 class TranslateVM(gdb.Command): 198 """Prints the entire paging structure used 198 """Prints the entire paging structure used to translate a given virtual address. 199 199 200 Having an address space of the currently execu 200 Having an address space of the currently executed process translates the virtual address 201 and prints detailed information of all paging 201 and prints detailed information of all paging structure levels used for the transaltion. 202 Currently supported arch: x86""" 202 Currently supported arch: x86""" 203 203 204 def __init__(self): 204 def __init__(self): 205 super(TranslateVM, self).__init__('tra 205 super(TranslateVM, self).__init__('translate-vm', gdb.COMMAND_USER) 206 206 207 def invoke(self, arg, from_tty): 207 def invoke(self, arg, from_tty): 208 if utils.is_target_arch("x86"): 208 if utils.is_target_arch("x86"): 209 vm_address = gdb.parse_and_eval(f' 209 vm_address = gdb.parse_and_eval(f'{arg}') 210 cr3_data = gdb.parse_and_eval('$cr 210 cr3_data = gdb.parse_and_eval('$cr3') 211 cr4 = gdb.parse_and_eval('$cr4') 211 cr4 = gdb.parse_and_eval('$cr4') 212 page_levels = 5 if cr4 & (1 << 12) 212 page_levels = 5 if cr4 & (1 << 12) else 4 213 page_entry = Cr3(cr3_data, page_le 213 page_entry = Cr3(cr3_data, page_levels) 214 while page_entry: 214 while page_entry: 215 gdb.write(page_entry.mk_string 215 gdb.write(page_entry.mk_string()) 216 page_entry = page_entry.next_e 216 page_entry = page_entry.next_entry(vm_address) 217 else: 217 else: 218 gdb.GdbError("Virtual address tran 218 gdb.GdbError("Virtual address translation is not" 219 "supported for this a 219 "supported for this arch") 220 220 221 221 222 TranslateVM() 222 TranslateVM()
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.