xref: /linux/scripts/update-intel-ucode-defs.py (revision 056e065a6b6e01ab54bb9770c0d5a15350e571e2)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3import argparse
4import re
5import shutil
6import subprocess
7import sys
8import os
9
10script = os.path.relpath(__file__)
11
12DESCRIPTION = f"""
13For Intel CPUs, update the microcode revisions that determine
14X86_BUG_OLD_MICROCODE.
15
16This script is intended to be run in response to releases of the
17official Intel microcode GitHub repository:
18https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files.git
19
20It takes the Intel microcode files as input and uses iucode-tool to
21extract the revision information. It prints the output in the format
22expected by intel-ucode-defs.h.
23
24Usage:
25    ./{script} /path/to/microcode/files > /path/to/intel-ucode-defs.h
26
27Typically, someone at Intel would see a new public release, wait for at
28least three months to ensure the update is stable, run this script to
29refresh the intel-ucode-defs.h file, and send a patch upstream to update
30the mainline and stable versions.
31
32Any exception to this process should be supported with an appropriate
33justification.
34"""
35
36SIG_RE = re.compile(r'sig (0x[0-9a-fA-F]+)')
37PFM_RE = re.compile(r'pf_mask (0x[0-9a-fA-F]+)')
38REV_RE = re.compile(r'rev (0x[0-9a-fA-F]+)')
39
40# Functions to extract family, model, and stepping
41def bits(val, bottom, top):
42    mask = (1 << (top + 1 - bottom)) - 1
43    return (val >> bottom) & mask
44
45def family(sig):
46    if bits(sig, 8, 11) == 0xf:
47        return bits(sig, 8, 11) + bits(sig, 20, 27)
48    return bits(sig, 8, 11)
49
50def model(sig):
51    return bits(sig, 4, 7) | (bits(sig, 16, 19) << 4)
52
53def step(sig):
54    return bits(sig, 0, 3)
55
56class Ucode:
57    def __init__(self, sig, pfm, rev):
58        self.family = family(sig)
59        self.model = model(sig)
60        self.steppings = 1 << step(sig)
61        self.platforms = pfm
62        self.rev = rev
63
64        self.key = (self.family, self.model, self.steppings, self.platforms)
65
66    def __eq__(self, other):
67        return self.key == other.key
68
69    def __hash__(self):
70        return hash(self.key)
71
72    def __str__(self):
73        return "{ .flags = X86_CPU_ID_FLAG_ENTRY_VALID, .vendor = X86_VENDOR_INTEL, .family = 0x%x, .model = 0x%02x, .steppings = 0x%04x, .platform_mask = 0x%02x, .driver_data = 0x%x }," % \
74                (self.family, self.model, self.steppings, self.platforms, self.rev)
75
76def main():
77    parser = argparse.ArgumentParser(description=DESCRIPTION,
78                                     formatter_class=argparse.RawDescriptionHelpFormatter)
79    parser.add_argument('ucode_files', nargs='+', help='Path(s) to the microcode files')
80
81    args = parser.parse_args()
82
83    # Process the microcode files using iucode-tool
84    iucode_tool = shutil.which("iucode-tool") or shutil.which("iucode_tool")
85    if iucode_tool is None:
86        print("Error: iucode-tool not found, please install it", file=sys.stderr)
87        sys.exit(1)
88
89    cmd = [iucode_tool, '--list-all'] + args.ucode_files
90
91    result = subprocess.run(cmd, capture_output=True, text=True)
92    if result.returncode != 0:
93        print("Error: iucode-tool ran into an error, exiting", file=sys.stderr)
94        if result.stderr:
95            print(result.stderr, file=sys.stderr, end='')
96        sys.exit(1)
97
98    ucodes = set()
99
100    # Parse the output of iucode-tool
101    for line in result.stdout.splitlines():
102        sig_match = SIG_RE.search(line)
103        pfm_match = PFM_RE.search(line)
104        rev_match = REV_RE.search(line)
105
106        if not (sig_match and pfm_match and rev_match):
107            continue
108
109        sig = int(sig_match.group(1), 16)
110        pfm = int(pfm_match.group(1), 16)
111        rev = int(rev_match.group(1), 16)
112        debug_rev = bits(rev, 31, 31)
113        if debug_rev != 0:
114            print("Error: Debug ucode file found, exiting", file=sys.stderr)
115            sys.exit(1)
116
117        ucodes.add(Ucode(sig, pfm, rev))
118
119    if not ucodes:
120        print("Error: No valid microcode files found, exiting", file=sys.stderr)
121        sys.exit(1)
122
123    # Sort and print the microcode entries
124    print("/* SPDX-License-Identifier: GPL-2.0 */")
125    print("/* Auto-generated by scripts/update-intel-ucode-defs.py */")
126    for u in sorted(ucodes, key=lambda x: x.key):
127        print(u)
128
129if __name__ == "__main__":
130    main()
131