summaryrefslogtreecommitdiff
path: root/tools/intel-gfx-fw-info
blob: 5eb733120bc7680563fda446918ebde0cbbd10a2 (plain)
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#!/usr/bin/env python3
# pylint: disable=C0301
# SPDX-License-Identifier: (GPL-2.0 OR MIT)
#
# Copyright (C) 2023 Intel Corporation

import argparse
import logging
import sys
import typing

# struct definition below should match the one from i915
# (drivers/gpu/drm/i915/gt/uc/intel_uc_fw_abi.h) and xe
# (drivers/gpu/drm/xe/xe_uc_fw_abi.h).
#
# For compatibility reasons with dissect.cstruct python module, the following
# things are changed from the original kernel definition:
#
#       1) #define in the middle of the struct removed: comment them out
#       2) No anonymous union - not compatible with the
#          dumpstruct(): give it a name

CDEF = """
typedef uint32 u32;

struct uc_css_header {
	u32 module_type;
	/*
	 * header_size includes all non-uCode bits, including css_header, rsa
	 * key, modulus key and exponent data.
	 */
	u32 header_size_dw;
	u32 header_version;
	u32 module_id;
	u32 module_vendor;
	u32 date;
// #define CSS_DATE_DAY				(0xFF << 0)
// #define CSS_DATE_MONTH			(0xFF << 8)
// #define CSS_DATE_YEAR			(0xFFFF << 16)
	u32 size_dw; /* uCode plus header_size_dw */
	u32 key_size_dw;
	u32 modulus_size_dw;
	u32 exponent_size_dw;
	u32 time;
// #define CSS_TIME_HOUR			(0xFF << 0)
// #define CSS_DATE_MIN				(0xFF << 8)
// #define CSS_DATE_SEC				(0xFFFF << 16)
	char username[8];
	char buildnumber[12];
	u32 sw_version;
// #define CSS_SW_VERSION_UC_MAJOR		(0xFF << 16)
// #define CSS_SW_VERSION_UC_MINOR		(0xFF << 8)
// #define CSS_SW_VERSION_UC_PATCH		(0xFF << 0)
	u32 vf_version;
	u32 reserved0[12];
	union {
		u32 private_data_size; /* only applies to GuC */
		u32 reserved1;
	} rsvd;
	u32 header_info;
};

#define HUC_GSC_VERSION_HI_DW		44
#define   HUC_GSC_MAJOR_VER_HI_MASK	(0xFF << 0)
#define   HUC_GSC_MINOR_VER_HI_MASK	(0xFF << 16)
#define HUC_GSC_VERSION_LO_DW		45
#define   HUC_GSC_PATCH_VER_LO_MASK	(0xFF << 0)

// Add a fake definition for the GSC's header so this script can still
// check the version

struct uc_huc_gsc_header {
	u32 rsvd[HUC_GSC_VERSION_HI_DW];
	u32 ver_hi;
	u32 ver_lo;
};

struct magic {
	char data[4];
};
"""

logging.basicConfig(format="%(levelname)s: %(message)s")

try:
    from dissect import cstruct
except:
    logging.critical(
        "Could not import dissect.cstruct module. See https://github.com/fox-it/dissect.cstruct for installation options"
    )
    raise SystemExit(1)


def ffs(x: int) -> int:
    """Returns the index, counting from 0, of the
    least significant set bit in `x`.
    """
    return (x & -x).bit_length() - 1


def FIELD_GET(mask: int, value: int) -> int:
    return (value & mask) >> ffs(mask)


class Fw:
    def __init__(self, fw):
        self.fw = fw


class FwCss(Fw):
    def decode(self):
        data = []

        CSS_SW_VERSION_UC_MAJOR = 0xFF << 16
        CSS_SW_VERSION_UC_MINOR = 0xFF << 8
        CSS_SW_VERSION_UC_PATCH = 0xFF
        major = FIELD_GET(CSS_SW_VERSION_UC_MAJOR, self.fw.sw_version)
        minor = FIELD_GET(CSS_SW_VERSION_UC_MINOR, self.fw.sw_version)
        patch = FIELD_GET(CSS_SW_VERSION_UC_PATCH, self.fw.sw_version)
        data += [f"version: {major}.{minor}.{patch}"]

        CSS_DATE_DAY = 0xFF
        CSS_DATE_MONTH = 0xFF << 8
        CSS_DATE_YEAR = 0xFFFF << 16
        day = FIELD_GET(CSS_DATE_DAY, self.fw.date)
        month = FIELD_GET(CSS_DATE_MONTH, self.fw.date)
        year = FIELD_GET(CSS_DATE_YEAR, self.fw.date)
        data += [f"date: {year:02x}-{month:02x}-{day:02x}"]

        return data


class FwGsc(Fw):
    def decode(self):
        data = []

        HUC_GSC_MINOR_VER_HI_MASK = 0xFF << 16
        HUC_GSC_MAJOR_VER_HI_MASK = 0xFF
        HUC_GSC_PATCH_VER_LO_MASK = 0xFF
        major = FIELD_GET(HUC_GSC_MAJOR_VER_HI_MASK, self.fw.ver_hi)
        minor = FIELD_GET(HUC_GSC_MINOR_VER_HI_MASK, self.fw.ver_hi)
        patch = FIELD_GET(HUC_GSC_PATCH_VER_LO_MASK, self.fw.ver_lo)
        data += [f"version: {major}.{minor}.{patch}"]

        return data


def parse_args(argv: typing.List[str]) -> argparse.Namespace:
    description = "Dump GuC/HuC firmware header"
    parser = argparse.ArgumentParser(prog="intel-gfx-fw-info", description=description)

    parser.add_argument("filename", help="GuC/HuC firmware file")

    return parser.parse_args(argv)


def main(argv: typing.List[str]) -> int:
    args = parse_args(argv)

    cparser = cstruct.cstruct()
    cparser.load(CDEF)

    try:
        with open(args.filename, mode="rb") as f:
            magic = cparser.magic(f)
            f.seek(0, 0)
            if magic.data == b"$CPD":
                fw = FwGsc(cparser.uc_huc_gsc_header(f))
            else:
                fw = FwCss(cparser.uc_css_header(f))
    except FileNotFoundError as e:
        logging.fatal(e)
        return 1

    print(*fw.decode(), sep="\n")
    print("raw dump:", end="")
    cstruct.dumpstruct(fw.fw, color=sys.stdout.isatty())

    return 0


if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]))