2 # SPDX-License-Identifier: GPL-2.0+
4 # Copyright (c) 2014 Google, Inc
6 # Intel microcode update tool
8 from optparse import OptionParser
14 MICROCODE_DIR = 'arch/x86/dts/microcode'
17 """Holds information about the microcode for a particular model of CPU.
20 name: Name of the CPU this microcode is for, including any version
21 information (e.g. 'm12206a7_00000029')
22 model: Model code string (this is cpuid(1).eax, e.g. '206a7')
23 words: List of hex words containing the microcode. The first 16 words
24 are the public header.
26 def __init__(self, name, data):
28 # Convert data into a list of hex words
30 for value in ''.join(data).split(','):
31 hexval = value.strip()
33 self.words.append(int(hexval, 0))
35 # The model is in the 4rd hex word
36 self.model = '%x' % self.words[3]
39 """Parse a micrcode.dat file and return the component parts
42 fname: Filename to parse
45 date: String containing date from the file's header
46 license_text: List of text lines for the license file
47 microcodes: List of Microcode objects from the file
49 re_date = re.compile('/\* *(.* [0-9]{4}) *\*/$')
50 re_license = re.compile('/[^-*+] *(.*)$')
51 re_name = re.compile('/\* *(.*)\.inc *\*/', re.IGNORECASE)
57 with open(fname) as fd:
60 m_date = re_date.match(line)
61 m_license = re_license.match(line)
62 m_name = re_name.match(line)
65 microcodes[name] = Microcode(name, data)
66 name = m_name.group(1).lower()
69 license_text.append(m_license.group(1))
71 date = m_date.group(1)
75 microcodes[name] = Microcode(name, data)
76 return date, license_text, microcodes
78 def ParseHeaderFiles(fname_list):
79 """Parse a list of header files and return the component parts
82 fname_list: List of files to parse
84 date: String containing date from the file's header
85 license_text: List of text lines for the license file
86 microcodes: List of Microcode objects from the file
92 for fname in fname_list:
93 name = os.path.basename(fname).lower()
94 name = os.path.splitext(name)[0]
96 with open(fname) as fd:
103 if line[0] == '/' and line[1] == '*':
106 if line[0] == '*' and line[1] == '/':
109 if license_start and not license_end:
112 license_text.append(line)
114 # Omit anything after the last comma
115 words = line.split(',')[:-1]
116 data += [word + ',' for word in words]
117 microcodes[name] = Microcode(name, data)
118 return date, license_text, microcodes
121 def List(date, microcodes, model):
122 """List the available microcode chunks
125 date: Date of the microcode file
126 microcodes: Dict of Microcode objects indexed by name
127 model: Model string to search for, or None
129 print 'Date: %s' % date
131 mcode_list, tried = FindMicrocode(microcodes, model.lower())
132 print 'Matching models %s:' % (', '.join(tried))
135 mcode_list = [microcodes[m] for m in microcodes.keys()]
136 for mcode in mcode_list:
137 print '%-20s: model %s' % (mcode.name, mcode.model)
139 def FindMicrocode(microcodes, model):
140 """Find all the microcode chunks which match the given model.
142 This model is something like 306a9 (the value returned in eax from
143 cpuid(1) when running on Intel CPUs). But we allow a partial match,
144 omitting the last 1 or two characters to allow many families to have the
147 If the model name is ambiguous we return a list of matches.
150 microcodes: Dict of Microcode objects indexed by name
151 model: String containing model name to find
154 List of matching Microcode objects
155 List of abbreviations we tried
157 # Allow a full name to be used
158 mcode = microcodes.get(model)
165 abbrev = model[:-i] if i else model
167 for mcode in microcodes.values():
168 if mcode.model.startswith(abbrev):
174 def CreateFile(date, license_text, mcodes, outfile):
175 """Create a microcode file in U-Boot's .dtsi format
178 date: String containing date of original microcode file
179 license: List of text lines for the license file
180 mcodes: Microcode objects to write (normally only 1)
181 outfile: Filename to write to ('-' for stdout)
185 * This is a device tree fragment. Use #include to add these properties to a
191 compatible = "intel,microcode";
192 intel,header-version = <%d>;
193 intel,update-revision = <%#x>;
194 intel,date-code = <%#x>;
195 intel,processor-signature = <%#x>;
196 intel,checksum = <%#x>;
197 intel,loader-revision = <%d>;
198 intel,processor-flags = <%#x>;
200 /* The first 48-bytes are the public header which repeats the above data */
204 add_comments = len(mcodes) > 1
207 words += '\n/* %s */' % mcode.name
208 for i in range(len(mcode.words)):
212 # Change each word so it will be little-endian in the FDT
213 # This data is needed before RAM is available on some platforms so
214 # we cannot do an endianness swap on boot.
215 val = struct.unpack("<I", struct.pack(">I", val))[0]
216 words += '\t%#010x' % val
218 # Use the first microcode for the headers
221 # Take care to avoid adding a space before a tab
223 for line in license_text:
225 text += '\n *' + line
227 text += '\n * ' + line
229 args += [mcode.words[i] for i in range(7)]
232 print out % tuple(args)
235 if not os.path.exists(MICROCODE_DIR):
236 print >> sys.stderr, "Creating directory '%s'" % MICROCODE_DIR
237 os.makedirs(MICROCODE_DIR)
238 outfile = os.path.join(MICROCODE_DIR, mcode.name + '.dtsi')
239 print >> sys.stderr, "Writing microcode for '%s' to '%s'" % (
240 ', '.join([mcode.name for mcode in mcodes]), outfile)
241 with open(outfile, 'w') as fd:
242 print >> fd, out % tuple(args)
245 """Run the microcode tool"""
246 commands = 'create,license,list'.split(',')
247 parser = OptionParser()
248 parser.add_option('-d', '--mcfile', type='string', action='store',
249 help='Name of microcode.dat file')
250 parser.add_option('-H', '--headerfile', type='string', action='append',
251 help='Name of .h file containing microcode')
252 parser.add_option('-m', '--model', type='string', action='store',
253 help="Model name to extract ('all' for all)")
254 parser.add_option('-M', '--multiple', type='string', action='store',
255 help="Allow output of multiple models")
256 parser.add_option('-o', '--outfile', type='string', action='store',
257 help='Filename to use for output (- for stdout), default is'
258 ' %s/<name>.dtsi' % MICROCODE_DIR)
259 parser.usage += """ command
261 Process an Intel microcode file (use -h for help). Commands:
263 create Create microcode .dtsi file for a model
264 list List available models in microcode file
265 license Print the license
269 ./tools/microcode-tool -d microcode.dat -m 306a create
271 This will find the appropriate file and write it to %s.""" % MICROCODE_DIR
273 (options, args) = parser.parse_args()
275 parser.error('Please specify a command')
277 if cmd not in commands:
278 parser.error("Unknown command '%s'" % cmd)
280 if (not not options.mcfile) != (not not options.mcfile):
281 parser.error("You must specify either header files or a microcode file, not both")
282 if options.headerfile:
283 date, license_text, microcodes = ParseHeaderFiles(options.headerfile)
285 date, license_text, microcodes = ParseFile(options.mcfile)
287 parser.error('You must specify a microcode file (or header files)')
290 List(date, microcodes, options.model)
291 elif cmd == 'license':
292 print '\n'.join(license_text)
293 elif cmd == 'create':
294 if not options.model:
295 parser.error('You must specify a model to create')
296 model = options.model.lower()
297 if options.model == 'all':
298 options.multiple = True
299 mcode_list = microcodes.values()
302 mcode_list, tried = FindMicrocode(microcodes, model)
304 parser.error("Unknown model '%s' (%s) - try 'list' to list" %
305 (model, ', '.join(tried)))
306 if not options.multiple and len(mcode_list) > 1:
307 parser.error("Ambiguous model '%s' (%s) matched %s - try 'list' "
308 "to list or specify a particular file" %
309 (model, ', '.join(tried),
310 ', '.join([m.name for m in mcode_list])))
311 CreateFile(date, license_text, mcode_list, options.outfile)
313 parser.error("Unknown command '%s'" % cmd)
315 if __name__ == "__main__":