Created
July 10, 2022 13:55
-
-
Save benagricola/00c5f052e2611a0f0f7325639b762be3 to your computer and use it in GitHub Desktop.
Script to convert QEMU VFIO Intel HD Audio traces to a more human readable format for debugging card initialisation issues.
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 | |
import sys | |
import re | |
tre = r"^(?P<function>[^\s]+)\s+\((?P<device>[0-9a-fA-F:\.]+):region(?P<region>[0-9]+)\+(?P<offset>(0x)?[0-9a-fA-F]+),\s(?P<arg1>(0x)?[0-9a-zA-Z]+)(,\s(?P<arg2>(0x)?[0-9a-zA-Z]+))?\)(\s=\s(?P<ret>(0x)?[0-9a-zA-Z]+))?$" | |
str_to_int = lambda x: int(x) | |
str_to_hex = lambda x: int(x, 16) | |
def extract_bits(v, o, c=1): | |
return v >> (o-1) & (~(~0 << c)) | |
def set_bits(v, o, c=1): | |
v |= ((~(~0 << c))<<o) | |
gctl = 0x0 | |
gcap = 0x0 | |
corbctl = 0x0 | |
rirbctl = 0x0 | |
def write_gctl(data): | |
global gctl | |
value = str_to_hex(data["arg1"]) | |
size = str_to_hex(data["arg2"]) | |
gctl = gctl ^ value | |
return "WR " + print_gctl() | |
def read_gctl(data): | |
global gctl | |
gctl = str_to_hex(data["ret"]) | |
return "RD " + print_gctl() | |
def print_gctl(): | |
return "GCTL: {}".format({ | |
"UNSOL": extract_bits(gctl, 8), # Bit 8 | |
"FCNTRL": extract_bits(gctl, 1) # Bit 1 | |
}) | |
def write_gcap(data): | |
global gcap | |
value = str_to_hex(data["arg1"]) | |
size = str_to_hex(data["arg2"]) | |
gcap = gcap ^ value | |
return "WR " + print_gcap() | |
def read_gcap(data): | |
global gcap | |
gcap = str_to_hex(data["ret"]) | |
return "RD " + print_gcap() | |
def print_gcap(): | |
global gcap | |
return "GCAP: {}".format({ | |
"OSS": extract_bits(gcap, 15, 3), # Bit 15-12 | |
"ISS": extract_bits(gcap, 11, 3), # Bit 11-8 | |
"BSS": extract_bits(gcap, 7, 4), # Bit 7-3 | |
"NSDO": extract_bits(gcap, 2), # Bit 2 | |
"64OK": extract_bits(gcap, 1), # Bit 1 | |
}) | |
def write_corbctl(data): | |
global corbctl | |
value = str_to_hex(data["arg1"]) | |
size = str_to_hex(data["arg2"]) | |
corbctl = value | |
return "WR {} (Wrote {}b: {})".format(print_corbctl(), size, value) | |
def print_corbctl(): | |
global corbctl | |
return "CORBCTL: {}".format({ | |
"CORBRUN": extract_bits(corbctl, 2), # Bit 2 | |
"CMEIE": extract_bits(corbctl, 1), # Bit 1 | |
}) | |
def read_corbctl(data): | |
global corbctl | |
corbctl = str_to_hex(data["ret"]) | |
return "RD " + print_corbctl() | |
def write_rirbctl(data): | |
global rirbctl | |
value = str_to_hex(data["arg1"]) | |
size = str_to_hex(data["arg2"]) | |
rirbctl = value | |
return "WR {} (Wrote {}b: {})".format(print_rirbctl(), size, value) | |
def print_rirbctl(): | |
global rirbctl | |
return "RIRBCTL: {}".format({ | |
"RIRBOIC": extract_bits(rirbctl, 3), # Bit 3 | |
"RIRBDMAEN": extract_bits(rirbctl, 2), # Bit 2 | |
"RINTCTL": extract_bits(rirbctl, 1), # Bit 1 | |
}) | |
def read_rirbctl(data): | |
global rirbctl | |
rirbctl = str_to_hex(data["ret"]) | |
return "RD " + print_rirbctl() | |
decoder = { | |
"function": { | |
"vfio_region_read": { | |
"region": { | |
"__convert": str_to_int, | |
0: { | |
"offset": { | |
"__convert": str_to_hex, | |
0x0: (lambda data: read_gcap(data)), | |
0x2: (lambda data: "RD VMIN: {}".format(str_to_hex(data["ret"]))), | |
0x3: (lambda data: "RD VMAJ: {}".format(str_to_hex(data["ret"]))), | |
0x8: (lambda data: read_gctl(data)), | |
0x4c: (lambda data: read_corbctl(data)), | |
0x5c: (lambda data: read_rirbctl(data)) | |
} | |
} | |
} | |
}, | |
"vfio_region_write": { | |
"region": { | |
"__convert": str_to_int, | |
0: { | |
"offset": { | |
"__convert": str_to_hex, | |
0x0: (lambda data: write_gcap(data)), | |
0x8: (lambda data: write_gctl(data)), | |
0x4c: (lambda data: write_corbctl(data)), | |
0x5c: (lambda data: write_rirbctl(data)) | |
} | |
} | |
} | |
} | |
} | |
} | |
def decode_level(level, vars): | |
# Track current level decoded and depth | |
cl = level | |
depth = 1 | |
# Convert items to list of tuples for zipping | |
vl = list(vars.items()) | |
# For each pair in tuples | |
for k, v in vl: | |
# Check if the current "level" is a callable function | |
if callable(cl): | |
# If so, call it with remaining tuples as arguments | |
return cl(dict(vl[depth:])) | |
# Otherwise if the key is in the current level | |
elif k in cl: | |
# Run any conversion function available and overwrite the current value | |
if "__convert" in cl[k]: | |
v = cl[k]["__convert"](v) | |
# Check if (maybe) converted value is in current level's key | |
if v in cl[k]: | |
# If it is, increment the depth and set the current level to the new one | |
depth += 1 | |
cl = cl[k][v] | |
return None | |
def main(args): | |
with open(args[1],'r') as f: | |
for l in f: | |
m = re.match(tre, l) | |
if not m: | |
continue | |
gd = m.groupdict() | |
r = decode_level(decoder, gd) | |
if r: | |
print(r) | |
else: | |
print(gd) | |
if __name__ == '__main__': | |
main(sys.argv) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment