1
0

pgtable.py 9.9 KB

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