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'])
|