Skip to content

Instantly share code, notes, and snippets.

@benagricola
Created July 10, 2022 13:55
Show Gist options
  • Save benagricola/00c5f052e2611a0f0f7325639b762be3 to your computer and use it in GitHub Desktop.
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.
#!/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