1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
|
#! /usr/bin/python3
#
# This script extracts the complete page table hierarchy from an existing
# dump file in a format that is accepted as test case data.
#
# It requires pykdumpfile, which is maintained here:
# https://github.com/ptesarik/pykdumpfile
#
import sys
from argparse import ArgumentParser
import kdumpfile
class Compress:
def __init__(self):
self.prev = list()
self.repeat = 1
def output(self, s):
print(s)
def restart(self):
if len(self.prev) >= 2:
diff = self.prev[0] - self.prev[1]
if not diff:
self.output('{:016X}*{:d}'.format(self.prev[0], self.repeat + 1))
self.prev.clear()
elif self.repeat > 1:
first = self.prev[0] - diff * self.repeat
self.output('{:016X}*{:d}{:+X}'.format(first, self.repeat + 1, diff))
self.prev.clear()
if len(self.prev) >= 2:
self.output('{:016X}'.format(self.prev[1]))
self.prev.pop()
self.repeat = 1
def insert(self, val):
if len(self.prev) >= 2:
diff = self.prev[0] - self.prev[1]
if val - self.prev[0] == diff:
self.repeat += 1
else:
self.restart()
self.prev.insert(0, val)
def flush(self):
self.restart()
if len(self.prev):
self.output('{:016X}'.format(self.prev[0]))
class PageDumper:
_byteordermap = {
kdumpfile.BIG_ENDIAN: 'big',
kdumpfile.LITTLE_ENDIAN: 'little',
}
def __init__(self, kdump):
self.kdump = kdump
self.byteorder = self._byteordermap[kdump.attr[kdumpfile.ATTR_BYTE_ORDER]]
self.pagesize = kdump.attr[kdumpfile.ATTR_PAGE_SIZE]
self.pte_size = kdump.attr['arch.pteval_size']
self.compress = Compress()
self.subtables = list()
# TODO: x86_64-specific
def ispgt(self, val):
# PRESENT and not PSE
return val & (1 << 0) and not val & (1 << 7)
# TODO: x86_64-specific
def val2addr(self, val):
return val & -self.pagesize & ~(1 << 63)
def dump(self, addr):
table = ctx.read(kdumpfile.MACHPHYSADDR, addr, self.pagesize)
for off in range(0, self.pagesize, self.pte_size):
val = int.from_bytes(table[off:off+8], self.byteorder)
if self.ispgt(val):
self.subtables.append(self.val2addr(val))
self.compress.insert(val)
self.compress.flush()
seen = set()
def dump(ctx, addr, level):
if addr in seen:
return False
seen.add(addr)
dumper = PageDumper(ctx)
print('@0x{:X}'.format(addr))
dumper.dump(addr)
print()
if level < 4:
for addr in dumper.subtables:
dump(ctx, addr, level + 1)
return True
parser = ArgumentParser(
description='Dump a complete page table hierarchy')
parser.register('type', 'numeric', lambda s: int(s, base=0))
parser.add_argument('filename')
parser.add_argument('rootpgt', type='numeric')
args = parser.parse_args()
ctx = kdumpfile.Context()
ctx.open(args.filename)
dump(ctx, args.rootpgt, 1)
|