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