~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/scripts/gdb/linux/pgtable.py

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 # SPDX-License-Identifier: GPL-2.0-only
  2 #
  3 # gdb helper commands and functions for Linux kernel debugging
  4 #
  5 #  routines to introspect page table
  6 #
  7 # Authors:
  8 #  Dmitrii Bundin <dmitrii.bundin.a@gmail.com>
  9 #
 10 
 11 import gdb
 12 
 13 from linux import utils
 14 
 15 PHYSICAL_ADDRESS_MASK = gdb.parse_and_eval('0xfffffffffffff')
 16 
 17 
 18 def page_mask(level=1):
 19     # 4KB
 20     if level == 1:
 21         return gdb.parse_and_eval('(u64) ~0xfff')
 22     # 2MB
 23     elif level == 2:
 24         return gdb.parse_and_eval('(u64) ~0x1fffff')
 25     # 1GB
 26     elif level == 3:
 27         return gdb.parse_and_eval('(u64) ~0x3fffffff')
 28     else:
 29         raise Exception(f'Unknown page level: {level}')
 30 
 31 
 32 #page_offset_base in case CONFIG_DYNAMIC_MEMORY_LAYOUT is disabled
 33 POB_NO_DYNAMIC_MEM_LAYOUT = '0xffff888000000000'
 34 def _page_offset_base():
 35     pob_symbol = gdb.lookup_global_symbol('page_offset_base')
 36     pob = pob_symbol.name if pob_symbol else POB_NO_DYNAMIC_MEM_LAYOUT
 37     return gdb.parse_and_eval(pob)
 38 
 39 
 40 def is_bit_defined_tupled(data, offset):
 41     return offset, bool(data >> offset & 1)
 42 
 43 def content_tupled(data, bit_start, bit_end):
 44     return (bit_start, bit_end), data >> bit_start & ((1 << (1 + bit_end - bit_start)) - 1)
 45 
 46 def entry_va(level, phys_addr, translating_va):
 47         def start_bit(level):
 48             if level == 5:
 49                 return 48
 50             elif level == 4:
 51                 return 39
 52             elif level == 3:
 53                 return 30
 54             elif level == 2:
 55                 return 21
 56             elif level == 1:
 57                 return 12
 58             else:
 59                 raise Exception(f'Unknown level {level}')
 60 
 61         entry_offset =  ((translating_va >> start_bit(level)) & 511) * 8
 62         entry_va = _page_offset_base() + phys_addr + entry_offset
 63         return entry_va
 64 
 65 class Cr3():
 66     def __init__(self, cr3, page_levels):
 67         self.cr3 = cr3
 68         self.page_levels = page_levels
 69         self.page_level_write_through = is_bit_defined_tupled(cr3, 3)
 70         self.page_level_cache_disabled = is_bit_defined_tupled(cr3, 4)
 71         self.next_entry_physical_address = cr3 & PHYSICAL_ADDRESS_MASK & page_mask()
 72 
 73     def next_entry(self, va):
 74         next_level = self.page_levels
 75         return PageHierarchyEntry(entry_va(next_level, self.next_entry_physical_address, va), next_level)
 76 
 77     def mk_string(self):
 78             return f"""\
 79 cr3:
 80     {'cr3 binary data': <30} {hex(self.cr3)}
 81     {'next entry physical address': <30} {hex(self.next_entry_physical_address)}
 82     ---
 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_disabled[0]: <10} {'page level cache disabled': <30} {self.page_level_cache_disabled[1]}
 85 """
 86 
 87 
 88 class PageHierarchyEntry():
 89     def __init__(self, address, level):
 90         data = int.from_bytes(
 91             memoryview(gdb.selected_inferior().read_memory(address, 8)),
 92             "little"
 93         )
 94         if level == 1:
 95             self.is_page = True
 96             self.entry_present = is_bit_defined_tupled(data, 0)
 97             self.read_write = is_bit_defined_tupled(data, 1)
 98             self.user_access_allowed = is_bit_defined_tupled(data, 2)
 99             self.page_level_write_through = is_bit_defined_tupled(data, 3)
100             self.page_level_cache_disabled = is_bit_defined_tupled(data, 4)
101             self.entry_was_accessed = is_bit_defined_tupled(data, 5)
102             self.dirty = is_bit_defined_tupled(data, 6)
103             self.pat = is_bit_defined_tupled(data, 7)
104             self.global_translation = is_bit_defined_tupled(data, 8)
105             self.page_physical_address = data & PHYSICAL_ADDRESS_MASK & page_mask(level)
106             self.next_entry_physical_address = None
107             self.hlat_restart_with_ordinary = is_bit_defined_tupled(data, 11)
108             self.protection_key = content_tupled(data, 59, 62)
109             self.executed_disable = is_bit_defined_tupled(data, 63)
110         else:
111             page_size = is_bit_defined_tupled(data, 7)
112             page_size_bit = page_size[1]
113             self.is_page = page_size_bit
114             self.entry_present = is_bit_defined_tupled(data, 0)
115             self.read_write = is_bit_defined_tupled(data, 1)
116             self.user_access_allowed = is_bit_defined_tupled(data, 2)
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:
179             return f"""\
180 level {self.page_hierarchy_level}:
181     {'entry address': <30} {hex(self.address)}
182     {'page entry binary data': <30} {hex(self.page_entry_binary_data)}
183     {'next entry physical address': <30} {hex(self.next_entry_physical_address)}
184     ---
185     {'bit': <4} {self.entry_present[0]: <10} {'entry present': <30} {self.entry_present[1]}
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]: <10} {'user access allowed': <30} {self.user_access_allowed[1]}
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_disabled[0]: <10} {'page level cache disabled': <30} {self.page_level_cache_disabled[1]}
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} {'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 
204     def __init__(self):
205         super(TranslateVM, self).__init__('translate-vm', gdb.COMMAND_USER)
206 
207     def invoke(self, arg, from_tty):
208         if utils.is_target_arch("x86"):
209             vm_address = gdb.parse_and_eval(f'{arg}')
210             cr3_data = gdb.parse_and_eval('$cr3')
211             cr4 = gdb.parse_and_eval('$cr4')
212             page_levels = 5 if cr4 & (1 << 12) else 4
213             page_entry = Cr3(cr3_data, page_levels)
214             while page_entry:
215                 gdb.write(page_entry.mk_string())
216                 page_entry = page_entry.next_entry(vm_address)
217         else:
218             gdb.GdbError("Virtual address translation is not"
219                          "supported for this arch")
220 
221 
222 TranslateVM()

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php