xref: /linux-6.15/scripts/gdb/linux/pgtable.py (revision eb985b5d)
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