3 # Copyright (c) 2014 Google, Inc
5 # SPDX-License-Identifier: GPL-2.0+
7 # Intel microcode update tool
9 from optparse import OptionParser
15 MICROCODE_DIR = 'arch/x86/dts/microcode'
18 """Holds information about the microcode for a particular model of CPU.
21 name: Name of the CPU this microcode is for, including any version
22 information (e.g. 'm12206a7_00000029')
23 model: Model code string (this is cpuid(1).eax, e.g. '206a7')
24 words: List of hex words containing the microcode. The first 16 words
25 are the public header.
27 def __init__(self, name, data):
29 # Convert data into a list of hex words
31 for value in ''.join(data).split(','):
32 hexval = value.strip()
34 self.words.append(int(hexval, 0))
36 # The model is in the 4rd hex word
37 self.model = '%x' % self.words[3]
40 """Parse a micrcode.dat file and return the component parts
43 fname: Filename to parse
46 date: String containing date from the file's header
47 license_text: List of text lines for the license file
48 microcodes: List of Microcode objects from the file
50 re_date = re.compile('/\* *(.* [0-9]{4}) *\*/$')
51 re_license = re.compile('/[^-*+] *(.*)$')
52 re_name = re.compile('/\* *(.*)\.inc *\*/', re.IGNORECASE)
58 with open(fname) as fd:
61 m_date = re_date.match(line)
62 m_license = re_license.match(line)
63 m_name = re_name.match(line)
66 microcodes[name] = Microcode(name, data)
67 name = m_name.group(1).lower()
70 license_text.append(m_license.group(1))
72 date = m_date.group(1)
76 microcodes[name] = Microcode(name, data)
77 return date, license_text, microcodes
79 def ParseHeaderFiles(fname_list):
80 """Parse a list of header files and return the component parts
83 fname_list: List of files to parse
85 date: String containing date from the file's header
86 license_text: List of text lines for the license file
87 microcodes: List of Microcode objects from the file
93 for fname in fname_list:
94 name = os.path.basename(fname).lower()
95 name = os.path.splitext(name)[0]
97 with open(fname) as fd:
101 # Omit anything after the last comma
102 words = line.split(',')[:-1]
103 data += [word + ',' for word in words]
104 microcodes[name] = Microcode(name, data)
105 return date, license_text, microcodes
108 def List(date, microcodes, model):
109 """List the available microcode chunks
112 date: Date of the microcode file
113 microcodes: Dict of Microcode objects indexed by name
114 model: Model string to search for, or None
116 print 'Date: %s' % date
118 mcode_list, tried = FindMicrocode(microcodes, model.lower())
119 print 'Matching models %s:' % (', '.join(tried))
122 mcode_list = [microcodes[m] for m in microcodes.keys()]
123 for mcode in mcode_list:
124 print '%-20s: model %s' % (mcode.name, mcode.model)
126 def FindMicrocode(microcodes, model):
127 """Find all the microcode chunks which match the given model.
129 This model is something like 306a9 (the value returned in eax from
130 cpuid(1) when running on Intel CPUs). But we allow a partial match,
131 omitting the last 1 or two characters to allow many families to have the
134 If the model name is ambiguous we return a list of matches.
137 microcodes: Dict of Microcode objects indexed by name
138 model: String containing model name to find
141 List of matching Microcode objects
142 List of abbreviations we tried
144 # Allow a full name to be used
145 mcode = microcodes.get(model)
152 abbrev = model[:-i] if i else model
154 for mcode in microcodes.values():
155 if mcode.model.startswith(abbrev):
161 def CreateFile(date, license_text, mcodes, outfile):
162 """Create a microcode file in U-Boot's .dtsi format
165 date: String containing date of original microcode file
166 license: List of text lines for the license file
167 mcodes: Microcode objects to write (normally only 1)
168 outfile: Filename to write to ('-' for stdout)
172 * This is a device tree fragment. Use #include to add these properties to a
178 compatible = "intel,microcode";
179 intel,header-version = <%d>;
180 intel,update-revision = <%#x>;
181 intel,date-code = <%#x>;
182 intel,processor-signature = <%#x>;
183 intel,checksum = <%#x>;
184 intel,loader-revision = <%d>;
185 intel,processor-flags = <%#x>;
187 /* The first 48-bytes are the public header which repeats the above data */
191 add_comments = len(mcodes) > 1
194 words += '\n/* %s */' % mcode.name
195 for i in range(len(mcode.words)):
199 # Change each word so it will be little-endian in the FDT
200 # This data is needed before RAM is available on some platforms so
201 # we cannot do an endianness swap on boot.
202 val = struct.unpack("<I", struct.pack(">I", val))[0]
203 words += '\t%#010x' % val
205 # Use the first microcode for the headers
208 # Take care to avoid adding a space before a tab
210 for line in license_text:
212 text += '\n *' + line
214 text += '\n * ' + line
216 args += [mcode.words[i] for i in range(7)]
219 print out % tuple(args)
222 if not os.path.exists(MICROCODE_DIR):
223 print >> sys.stderr, "Creating directory '%s'" % MICROCODE_DIR
224 os.makedirs(MICROCODE_DIR)
225 outfile = os.path.join(MICROCODE_DIR, mcode.name + '.dtsi')
226 print >> sys.stderr, "Writing microcode for '%s' to '%s'" % (
227 ', '.join([mcode.name for mcode in mcodes]), outfile)
228 with open(outfile, 'w') as fd:
229 print >> fd, out % tuple(args)
232 """Run the microcode tool"""
233 commands = 'create,license,list'.split(',')
234 parser = OptionParser()
235 parser.add_option('-d', '--mcfile', type='string', action='store',
236 help='Name of microcode.dat file')
237 parser.add_option('-H', '--headerfile', type='string', action='append',
238 help='Name of .h file containing microcode')
239 parser.add_option('-m', '--model', type='string', action='store',
240 help="Model name to extract ('all' for all)")
241 parser.add_option('-M', '--multiple', type='string', action='store',
242 help="Allow output of multiple models")
243 parser.add_option('-o', '--outfile', type='string', action='store',
244 help='Filename to use for output (- for stdout), default is'
245 ' %s/<name>.dtsi' % MICROCODE_DIR)
246 parser.usage += """ command
248 Process an Intel microcode file (use -h for help). Commands:
250 create Create microcode .dtsi file for a model
251 list List available models in microcode file
252 license Print the license
256 ./tools/microcode-tool -d microcode.dat -m 306a create
258 This will find the appropriate file and write it to %s.""" % MICROCODE_DIR
260 (options, args) = parser.parse_args()
262 parser.error('Please specify a command')
264 if cmd not in commands:
265 parser.error("Unknown command '%s'" % cmd)
267 if (not not options.mcfile) != (not not options.mcfile):
268 parser.error("You must specify either header files or a microcode file, not both")
269 if options.headerfile:
270 date, license_text, microcodes = ParseHeaderFiles(options.headerfile)
272 date, license_text, microcodes = ParseFile(options.mcfile)
274 parser.error('You must specify a microcode file (or header files)')
277 List(date, microcodes, options.model)
278 elif cmd == 'license':
279 print '\n'.join(license_text)
280 elif cmd == 'create':
281 if not options.model:
282 parser.error('You must specify a model to create')
283 model = options.model.lower()
284 if options.model == 'all':
285 options.multiple = True
286 mcode_list = microcodes.values()
289 mcode_list, tried = FindMicrocode(microcodes, model)
291 parser.error("Unknown model '%s' (%s) - try 'list' to list" %
292 (model, ', '.join(tried)))
293 if not options.multiple and len(mcode_list) > 1:
294 parser.error("Ambiguous model '%s' (%s) matched %s - try 'list' "
295 "to list or specify a particular file" %
296 (model, ', '.join(tried),
297 ', '.join([m.name for m in mcode_list])))
298 CreateFile(date, license_text, mcode_list, options.outfile)
300 parser.error("Unknown command '%s'" % cmd)
302 if __name__ == "__main__":