aboutsummaryrefslogtreecommitdiffstats
path: root/dmcub/dmcub_parse.py
blob: 58b6426c58e7159ba9b2a3719f302f0ed8ba44cb (plain) (blame)
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
#!/usr/bin/env python3
"""Parse an amdgpu `*_dmcub.bin` container: common ucode header, the DMCUB
extension fields, and the trailing `dmub_fw_meta_info` block.

Field names follow the kernel's `struct dmcub_firmware_header_v1_0`
(drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.h) and `struct dmub_fw_meta_info`
(drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h).

Usage: dmcub_parse.py fw/*.bin
"""
import struct
import sys
import hashlib
import zlib

META_MAGIC = 0x444D5542  # 'DMUB'
META_OFFSET_FROM_END = 0x24  # DMUB_FW_META_OFFSET


def parse_container(blob):
    o = {}
    (o['size_bytes'], o['header_size_bytes'], o['hdr_ver_major'],
     o['hdr_ver_minor'], o['ip_ver_major'], o['ip_ver_minor'],
     o['ucode_version'], o['ucode_size_bytes'], o['ucode_array_offset_bytes'],
     o['crc32']) = struct.unpack_from('<IIHHHHIIII', blob, 0)
    if o['header_size_bytes'] >= 0x28:
        o['inst_const_bytes'], o['bss_data_bytes'] = struct.unpack_from('<II', blob, 0x20)
    return o


def find_meta(blob, hdr):
    """The meta block lives at a fixed offset from the end of the *payload*.
    Locate it that way, then fall back to a magic scan."""
    cands = []
    payload_end = hdr['ucode_array_offset_bytes'] + hdr['ucode_size_bytes']
    for end in (payload_end, len(blob)):
        off = end - META_OFFSET_FROM_END
        if 0 <= off and struct.unpack_from('<I', blob, off)[0] == META_MAGIC:
            cands.append(('end-0x24', off))
    if not cands:
        pat = struct.pack('<I', META_MAGIC)
        start = 0
        while True:
            i = blob.find(pat, start)
            if i < 0:
                break
            cands.append(('scan', i))
            start = i + 1
    return cands


def parse_meta(blob, off):
    m = {}
    (m['magic_value'], m['fw_region_size'], m['trace_buffer_size'],
     m['fw_version']) = struct.unpack_from('<IIII', blob, off)
    m['dal_fw'] = blob[off + 16]
    m['shared_state_size'], = struct.unpack_from('<I', blob, off + 20)
    m['shared_state_features'], = struct.unpack_from('<H', blob, off + 24)
    m['feature_bits'], = struct.unpack_from('<I', blob, off + 28)
    return m


def ver(v):
    return '%d.%d.%d' % ((v >> 24) & 0xff, (v >> 16) & 0xff, v & 0xffff)


for path in sys.argv[1:]:
    blob = open(path, 'rb').read()
    h = parse_container(blob)
    print('=' * 78)
    print(path)
    print('  file size            %d (0x%x)' % (len(blob), len(blob)))
    print('  sha256               %s' % hashlib.sha256(blob).hexdigest())
    print('  size_bytes           0x%08x  %s' %
          (h['size_bytes'], 'OK' if h['size_bytes'] == len(blob) else 'MISMATCH'))
    print('  header_size_bytes    0x%x' % h['header_size_bytes'])
    print('  header version       %d.%d' % (h['hdr_ver_major'], h['hdr_ver_minor']))
    print('  ip version           %d.%d' % (h['ip_ver_major'], h['ip_ver_minor']))
    print('  ucode_version        0x%08x' % h['ucode_version'])
    print('  ucode_size_bytes     0x%x' % h['ucode_size_bytes'])
    print('  ucode_array_offset   0x%x' % h['ucode_array_offset_bytes'])
    payload = blob[h['ucode_array_offset_bytes']:
                   h['ucode_array_offset_bytes'] + h['ucode_size_bytes']]
    calc = zlib.crc32(payload) & 0xffffffff
    print('  crc32 (header)       0x%08x   crc32(payload)=0x%08x  %s' %
          (h['crc32'], calc, 'MATCH' if calc == h['crc32'] else 'no-match'))
    if 'inst_const_bytes' in h:
        print('  inst_const_bytes     0x%x' % h['inst_const_bytes'])
        print('  bss_data_bytes       0x%x' % h['bss_data_bytes'])
    print('  payload span         0x%x..0x%x   trailing bytes: %d' % (
        h['ucode_array_offset_bytes'],
        h['ucode_array_offset_bytes'] + h['ucode_size_bytes'],
        len(blob) - h['ucode_array_offset_bytes'] - h['ucode_size_bytes']))
    for how, off in find_meta(blob, h):
        m = parse_meta(blob, off)
        print('  -- dmub_fw_meta_info @ 0x%x (%s)' % (off, how))
        print('     fw_version          0x%08x  (%s)' % (m['fw_version'], ver(m['fw_version'])))
        print('     fw_region_size      0x%x' % m['fw_region_size'])
        print('     trace_buffer_size   0x%x' % m['trace_buffer_size'])
        print('     dal_fw              %d' % m['dal_fw'])
        print('     shared_state_size   0x%x  features=0x%x' %
              (m['shared_state_size'], m['shared_state_features']))
        print('     feature_bits        0x%08x' % m['feature_bits'])