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:
104 if line[0] == '/' and line[1] == '*':
107 if line[0] == '*' and line[1] == '/':
110 if license_start and not license_end:
113 license_text.append(line)
115 # Omit anything after the last comma
116 words = line.split(',')[:-1]
117 data += [word + ',' for word in words]
118 microcodes[name] = Microcode(name, data)
119 return date, license_text, microcodes
122 def List(date, microcodes, model):
123 """List the available microcode chunks
126 date: Date of the microcode file
127 microcodes: Dict of Microcode objects indexed by name
128 model: Model string to search for, or None
130 print 'Date: %s' % date
132 mcode_list, tried = FindMicrocode(microcodes, model.lower())
133 print 'Matching models %s:' % (', '.join(tried))
136 mcode_list = [microcodes[m] for m in microcodes.keys()]
137 for mcode in mcode_list:
138 print '%-20s: model %s' % (mcode.name, mcode.model)
140 def FindMicrocode(microcodes, model):
141 """Find all the microcode chunks which match the given model.
143 This model is something like 306a9 (the value returned in eax from
144 cpuid(1) when running on Intel CPUs). But we allow a partial match,
145 omitting the last 1 or two characters to allow many families to have the
148 If the model name is ambiguous we return a list of matches.
151 microcodes: Dict of Microcode objects indexed by name
152 model: String containing model name to find
155 List of matching Microcode objects
156 List of abbreviations we tried
158 # Allow a full name to be used
159 mcode = microcodes.get(model)
166 abbrev = model[:-i] if i else model
168 for mcode in microcodes.values():
169 if mcode.model.startswith(abbrev):
175 def CreateFile(date, license_text, mcodes, outfile):
176 """Create a microcode file in U-Boot's .dtsi format
179 date: String containing date of original microcode file
180 license: List of text lines for the license file
181 mcodes: Microcode objects to write (normally only 1)
182 outfile: Filename to write to ('-' for stdout)
186 * This is a device tree fragment. Use #include to add these properties to a
192 compatible = "intel,microcode";
193 intel,header-version = <%d>;
194 intel,update-revision = <%#x>;
195 intel,date-code = <%#x>;
196 intel,processor-signature = <%#x>;
197 intel,checksum = <%#x>;
198 intel,loader-revision = <%d>;
199 intel,processor-flags = <%#x>;
201 /* The first 48-bytes are the public header which repeats the above data */
205 add_comments = len(mcodes) > 1
208 words += '\n/* %s */' % mcode.name
209 for i in range(len(mcode.words)):
213 # Change each word so it will be little-endian in the FDT
214 # This data is needed before RAM is available on some platforms so
215 # we cannot do an endianness swap on boot.
216 val = struct.unpack("<I", struct.pack(">I", val))[0]
217 words += '\t%#010x' % val
219 # Use the first microcode for the headers
222 # Take care to avoid adding a space before a tab
224 for line in license_text:
226 text += '\n *' + line
228 text += '\n * ' + line
230 args += [mcode.words[i] for i in range(7)]
233 print out % tuple(args)
236 if not os.path.exists(MICROCODE_DIR):
237 print >> sys.stderr, "Creating directory '%s'" % MICROCODE_DIR
238 os.makedirs(MICROCODE_DIR)
239 outfile = os.path.join(MICROCODE_DIR, mcode.name + '.dtsi')
240 print >> sys.stderr, "Writing microcode for '%s' to '%s'" % (
241 ', '.join([mcode.name for mcode in mcodes]), outfile)
242 with open(outfile, 'w') as fd:
243 print >> fd, out % tuple(args)
246 """Run the microcode tool"""
247 commands = 'create,license,list'.split(',')
248 parser = OptionParser()
249 parser.add_option('-d', '--mcfile', type='string', action='store',
250 help='Name of microcode.dat file')
251 parser.add_option('-H', '--headerfile', type='string', action='append',
252 help='Name of .h file containing microcode')
253 parser.add_option('-m', '--model', type='string', action='store',
254 help="Model name to extract ('all' for all)")
255 parser.add_option('-M', '--multiple', type='string', action='store',
256 help="Allow output of multiple models")
257 parser.add_option('-o', '--outfile', type='string', action='store',
258 help='Filename to use for output (- for stdout), default is'
259 ' %s/<name>.dtsi' % MICROCODE_DIR)
260 parser.usage += """ command
262 Process an Intel microcode file (use -h for help). Commands:
264 create Create microcode .dtsi file for a model
265 list List available models in microcode file
266 license Print the license
270 ./tools/microcode-tool -d microcode.dat -m 306a create
272 This will find the appropriate file and write it to %s.""" % MICROCODE_DIR
274 (options, args) = parser.parse_args()
276 parser.error('Please specify a command')
278 if cmd not in commands:
279 parser.error("Unknown command '%s'" % cmd)
281 if (not not options.mcfile) != (not not options.mcfile):
282 parser.error("You must specify either header files or a microcode file, not both")
283 if options.headerfile:
284 date, license_text, microcodes = ParseHeaderFiles(options.headerfile)
286 date, license_text, microcodes = ParseFile(options.mcfile)
288 parser.error('You must specify a microcode file (or header files)')
291 List(date, microcodes, options.model)
292 elif cmd == 'license':
293 print '\n'.join(license_text)
294 elif cmd == 'create':
295 if not options.model:
296 parser.error('You must specify a model to create')
297 model = options.model.lower()
298 if options.model == 'all':
299 options.multiple = True
300 mcode_list = microcodes.values()
303 mcode_list, tried = FindMicrocode(microcodes, model)
305 parser.error("Unknown model '%s' (%s) - try 'list' to list" %
306 (model, ', '.join(tried)))
307 if not options.multiple and len(mcode_list) > 1:
308 parser.error("Ambiguous model '%s' (%s) matched %s - try 'list' "
309 "to list or specify a particular file" %
310 (model, ', '.join(tried),
311 ', '.join([m.name for m in mcode_list])))
312 CreateFile(date, license_text, mcode_list, options.outfile)
314 parser.error("Unknown command '%s'" % cmd)
316 if __name__ == "__main__":