Last active
November 25, 2020 14:26
-
-
Save andy-shev/ccc216b826ff1790d05044e12127ebeb to your computer and use it in GitHub Desktop.
Dumb SFI table dumper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
# vim: ts=4 sw=4 et ai si | |
from __future__ import print_function | |
import operator | |
import os | |
import struct | |
import sys | |
TABLE_HEADER = [ | |
['signature', '4s'], | |
['length', 'I'], | |
['revision', 'B'], | |
['checksum', 'B'], | |
['oemid', '6s'], | |
['oemtableid', '8s'], | |
] | |
# Registered device types | |
DEVS_TYPES = [ | |
'SPI', | |
'I2C', | |
'UART', | |
'HSI', | |
'IPC', | |
'SD', | |
'xxx', | |
] | |
TABLE_APIC_ENTRY = [ | |
['phys', 'Q'], | |
] | |
TABLE_CPUS_ENTRY = [ | |
['id', 'I'], | |
] | |
TABLE_DEVS_ENTRY = [ | |
['type', 'B'], | |
['num', 'B'], | |
['addr', 'H'], | |
['irq', 'B'], | |
['freq', 'I'], | |
['name', '16s'], | |
] | |
TABLE_FREQ_ENTRY = [ | |
['freq', 'I'], | |
['latency', 'I'], | |
['value', 'I'], | |
] | |
TABLE_GPIO_ENTRY = [ | |
['controller', '16s'], | |
['pin', 'H'], | |
['name', '16s'], | |
] | |
TABLE_MTMR_ENTRY = [ | |
['phys', 'Q'], | |
['freq', 'I'], | |
['irq', 'I'], | |
] | |
TABLE_MCFG_HEADER = [ | |
['oemrevision', 'I'], | |
['creatorid', 'I'], | |
['creatorrevision', 'I'], | |
['reserved', 'Q'], | |
] | |
TABLE_MCFG_ENTRY = [ | |
['base', 'Q'], | |
['segment', 'H'], | |
['start', 'B'], | |
['end', 'B'], | |
['reserved', 'I'], | |
] | |
MMAP_TYPES = [ | |
'MEM RESERVED', | |
'LOADER CODE', | |
'LOADER DATA', | |
'BOOT SERVICE CODE', | |
'BOOT SERVICE DATA', | |
'RUNTIME SERVICE CODE', | |
'RUNTIME SERVICE DATA', | |
'MEM CONV', | |
'MEM UNUSABLE', | |
'ACPI RECLAIM', | |
'ACPI NVS', | |
'MEM MMIO', | |
'MEM IOPORT', | |
'PAL CODE', | |
'MEM TYPEMAX', | |
] | |
EFI_MEMORY_ATTRS = [ | |
("UC", 0x0000000000000001), | |
("WC", 0x0000000000000002), | |
("WT", 0x0000000000000004), | |
("WB", 0x0000000000000008), | |
("UCE", 0x0000000000000010), | |
("WP", 0x0000000000001000), | |
("RP", 0x0000000000002000), | |
("XP", 0x0000000000004000), | |
("NV", 0x0000000000008000), | |
("MORE_RELIABLE", 0x0000000000010000), | |
("RO", 0x0000000000020000), | |
("RUNTIME", 0x8000000000000000), | |
] | |
TABLE_EFI_MEMORY_DESCRIPTOR = [ | |
['type', 'I'], | |
['physical', 'Q'], | |
['virtual', 'Q'], | |
['pages', 'Q'], | |
['attribute', 'Q'], | |
] | |
TABLE_ENTRIES = { | |
'APIC': TABLE_APIC_ENTRY, | |
'CPUS': TABLE_CPUS_ENTRY, | |
'DEVS': TABLE_DEVS_ENTRY, | |
'FREQ': TABLE_FREQ_ENTRY, | |
'GPIO': TABLE_GPIO_ENTRY, | |
'MTMR': TABLE_MTMR_ENTRY, | |
'MCFG': TABLE_MCFG_ENTRY, | |
'MMAP': TABLE_EFI_MEMORY_DESCRIPTOR, | |
} | |
def get_format_str(desc): | |
return '<' + ''.join(operator.itemgetter(1)(field) for field in desc) | |
def get_string_from_field(field): | |
return field.split(b'\0')[0].decode() | |
def parse_APIC(table): | |
fmt = get_format_str(TABLE_APIC_ENTRY) | |
size = struct.calcsize(fmt) | |
while len(table) >= size: | |
d = struct.unpack(fmt, table[:size]) | |
print("\tAPIC Physical address 0x%016x" % d[0]) | |
table = table[size:] | |
def parse_CPUS(table): | |
fmt = get_format_str(TABLE_CPUS_ENTRY) | |
size = struct.calcsize(fmt) | |
while len(table) >= size: | |
d = struct.unpack(fmt, table[:size]) | |
print("\tAPIC ID 0x%08x" % d[0]) | |
table = table[size:] | |
def parse_DEVS(table): | |
fmt = get_format_str(TABLE_DEVS_ENTRY) | |
size = struct.calcsize(fmt) | |
while len(table) >= size: | |
d = struct.unpack(fmt, table[:size]) | |
print("\t[%s]\t0x%02x:%04x\tIRQ:%d\t%10dHz\t%-16s" % (DEVS_TYPES[d[0]], d[1], d[2], d[3], d[4], get_string_from_field(d[5]))) | |
table = table[size:] | |
def parse_FREQ(table): | |
fmt = get_format_str(TABLE_FREQ_ENTRY) | |
size = struct.calcsize(fmt) | |
while len(table) >= size: | |
d = struct.unpack(fmt, table[:size]) | |
print("\tTransition to %u MHz in %u us: 0x%08x" % (d[0], d[1], d[2])) | |
table = table[size:] | |
def parse_GPIO(table): | |
fmt = get_format_str(TABLE_GPIO_ENTRY) | |
size = struct.calcsize(fmt) | |
gpio = {} | |
while len(table) >= size: | |
d = struct.unpack(fmt, table[:size]) | |
controller = get_string_from_field(d[0]) | |
if controller not in gpio: | |
gpio[controller] = [] | |
gpio[controller].append(d[1:3]) | |
table = table[size:] | |
for controller in gpio: | |
print("\t%s:" % controller) | |
gpio[controller].sort(key=operator.itemgetter(0)) | |
for entry in gpio[controller]: | |
print("\t\t%3d\t%s" % (entry[0], get_string_from_field(entry[1]))) | |
def parse_MTMR(table): | |
fmt = get_format_str(TABLE_MTMR_ENTRY) | |
size = struct.calcsize(fmt) | |
while len(table) >= size: | |
d = struct.unpack(fmt, table[:size]) | |
print("\tTimer 0x%016x, %uHz, IRQ: %u" % (d[0], d[1], d[2])) | |
table = table[size:] | |
def get_efi_memory_attr(attribute): | |
result = [] | |
for k, v in EFI_MEMORY_ATTRS: | |
if attribute & v: | |
result.append(k) | |
return '|'.join(result) | |
def parse_MCFG(table): | |
# Skip header | |
fmt = get_format_str(TABLE_MCFG_HEADER) | |
size = struct.calcsize(fmt) | |
table = table[size:] | |
fmt = get_format_str(TABLE_MCFG_ENTRY) | |
size = struct.calcsize(fmt) | |
while len(table) >= size: | |
d = struct.unpack(fmt, table[:size]) | |
attr = get_efi_memory_attr(d[4]) | |
print("\t%04x [bus%02x-%02x]\t%016x" % (d[1], d[2], d[3], d[0])) | |
table = table[size:] | |
def parse_MMAP(table): | |
fmt = get_format_str(TABLE_EFI_MEMORY_DESCRIPTOR) | |
size = struct.calcsize(fmt) | |
while len(table) >= size: | |
d = struct.unpack(fmt, table[:size]) | |
attr = get_efi_memory_attr(d[4]) | |
print("\t%016x-%016x\t[%s]\t%s" % (d[1], d[1] + d[3] * 4096 - 1, attr, MMAP_TYPES[d[0]])) | |
table = table[size:] | |
# https://codereview.stackexchange.com/a/161620 | |
def hexdump(data, base=0, offset=0, rowsize=16, dumpascii=True): | |
not_shown = [' '] | |
leader = (base + offset) % rowsize | |
next_n = offset - leader + rowsize | |
while data[offset:]: | |
col0 = '%08x' % (base + offset - leader) | |
col1 = not_shown * leader | |
col2 = ' ' * leader | |
leader = 0 | |
for i in bytearray(data[offset:next_n]): | |
col1 += ['%02x' % i] | |
col2 += chr(i) if 0x1f < i < 0x7f else '.' | |
trailer = rowsize - len(col1) | |
if trailer: | |
col1 += not_shown * trailer | |
col2 += ' ' * trailer | |
if dumpascii: | |
yield ' '.join((col0, ' '.join(col1), '', col2)) | |
else: | |
yield ' '.join((col0, ' '.join(col1))) | |
offset = next_n | |
next_n += rowsize | |
MODULE = sys.modules[globals()['__name__']] | |
def list_parse_functions(): | |
""" List supported parsers """ | |
for attr in dir(MODULE): | |
if attr.startswith('parse_'): | |
yield attr.replace('parse_', '') | |
def do_parse(name, table): | |
""" Get parse function by its name """ | |
for func in list_parse_functions(): | |
if func.upper() == name.upper(): | |
return getattr(MODULE, "parse_" + func.upper())(table) | |
print("Don't know how to parse %s..." % name) | |
print('\n'.join(hexdump(table, 0x18))) | |
def parse_HEADER(table): | |
fmt = get_format_str(TABLE_HEADER) | |
header = list(struct.unpack(fmt, table[:struct.calcsize(fmt)])) | |
for idx in (0, 4, 5): | |
header[idx] = get_string_from_field(header[idx]) | |
print("Parsing table %s for %s %s:" % (header[0], header[4], header[5])) | |
table = table[struct.calcsize(fmt):] | |
do_parse(header[0], table) | |
def usage(): | |
return "Usage: %s <TABLE>" % os.path.basename(sys.argv[0]) | |
def parse_one_table(filename): | |
data = open(filename, 'rb').read() | |
parse_HEADER(data) | |
def main(argv): | |
"""MAIN.""" | |
if len(argv) < 2: | |
raise SystemExit(usage()) | |
pathname = os.path.realpath(argv[1]) | |
if os.path.isfile(pathname): | |
parse_one_table(pathname) | |
elif os.path.isdir(pathname): | |
for root, dummy, files in os.walk(pathname): | |
for table in files: | |
parse_one_table(os.path.join(root, table)) | |
if __name__ == '__main__': | |
sys.exit(main(sys.argv)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment