|
@@ -0,0 +1,253 @@
|
|
|
+#!/usr/bin/env python
|
|
|
+#
|
|
|
+# Copyright (c) 2014 Google, Inc
|
|
|
+#
|
|
|
+# SPDX-License-Identifier: GPL-2.0+
|
|
|
+#
|
|
|
+# Intel microcode update tool
|
|
|
+
|
|
|
+from optparse import OptionParser
|
|
|
+import os
|
|
|
+import re
|
|
|
+import struct
|
|
|
+import sys
|
|
|
+
|
|
|
+MICROCODE_DIR = 'arch/x86/dts/microcode'
|
|
|
+
|
|
|
+class Microcode:
|
|
|
+ """Holds information about the microcode for a particular model of CPU.
|
|
|
+
|
|
|
+ Attributes:
|
|
|
+ name: Name of the CPU this microcode is for, including any version
|
|
|
+ information (e.g. 'm12206a7_00000029')
|
|
|
+ model: Model code string (this is cpuid(1).eax, e.g. '206a7')
|
|
|
+ words: List of hex words containing the microcode. The first 16 words
|
|
|
+ are the public header.
|
|
|
+ """
|
|
|
+ def __init__(self, name, data):
|
|
|
+ self.name = name
|
|
|
+ # Convert data into a list of hex words
|
|
|
+ self.words = []
|
|
|
+ for value in ''.join(data).split(','):
|
|
|
+ hexval = value.strip()
|
|
|
+ if hexval:
|
|
|
+ self.words.append(int(hexval, 0))
|
|
|
+
|
|
|
+ # The model is in the 4rd hex word
|
|
|
+ self.model = '%x' % self.words[3]
|
|
|
+
|
|
|
+def ParseFile(fname):
|
|
|
+ """Parse a micrcode.dat file and return the component parts
|
|
|
+
|
|
|
+ Args:
|
|
|
+ fname: Filename to parse
|
|
|
+ Returns:
|
|
|
+ 3-Tuple:
|
|
|
+ date: String containing date from the file's header
|
|
|
+ license_text: List of text lines for the license file
|
|
|
+ microcodes: List of Microcode objects from the file
|
|
|
+ """
|
|
|
+ re_date = re.compile('/\* *(.* [0-9]{4}) *\*/$')
|
|
|
+ re_license = re.compile('/[^-*+] *(.*)$')
|
|
|
+ re_name = re.compile('/\* *(.*)\.inc *\*/', re.IGNORECASE)
|
|
|
+ microcodes = {}
|
|
|
+ license_text = []
|
|
|
+ date = ''
|
|
|
+ data = []
|
|
|
+ name = None
|
|
|
+ with open(fname) as fd:
|
|
|
+ for line in fd:
|
|
|
+ line = line.rstrip()
|
|
|
+ m_date = re_date.match(line)
|
|
|
+ m_license = re_license.match(line)
|
|
|
+ m_name = re_name.match(line)
|
|
|
+ if m_name:
|
|
|
+ if name:
|
|
|
+ microcodes[name] = Microcode(name, data)
|
|
|
+ name = m_name.group(1).lower()
|
|
|
+ data = []
|
|
|
+ elif m_license:
|
|
|
+ license_text.append(m_license.group(1))
|
|
|
+ elif m_date:
|
|
|
+ date = m_date.group(1)
|
|
|
+ else:
|
|
|
+ data.append(line)
|
|
|
+ if name:
|
|
|
+ microcodes[name] = Microcode(name, data)
|
|
|
+ return date, license_text, microcodes
|
|
|
+
|
|
|
+def List(date, microcodes, model):
|
|
|
+ """List the available microcode chunks
|
|
|
+
|
|
|
+ Args:
|
|
|
+ date: Date of the microcode file
|
|
|
+ microcodes: Dict of Microcode objects indexed by name
|
|
|
+ model: Model string to search for, or None
|
|
|
+ """
|
|
|
+ print 'Date: %s' % date
|
|
|
+ if model:
|
|
|
+ mcode_list, tried = FindMicrocode(microcodes, model.lower())
|
|
|
+ print 'Matching models %s:' % (', '.join(tried))
|
|
|
+ else:
|
|
|
+ print 'All models:'
|
|
|
+ mcode_list = [microcodes[m] for m in microcodes.keys()]
|
|
|
+ for mcode in mcode_list:
|
|
|
+ print '%-20s: model %s' % (mcode.name, mcode.model)
|
|
|
+
|
|
|
+def FindMicrocode(microcodes, model):
|
|
|
+ """Find all the microcode chunks which match the given model.
|
|
|
+
|
|
|
+ This model is something like 306a9 (the value returned in eax from
|
|
|
+ cpuid(1) when running on Intel CPUs). But we allow a partial match,
|
|
|
+ omitting the last 1 or two characters to allow many families to have the
|
|
|
+ same microcode.
|
|
|
+
|
|
|
+ If the model name is ambiguous we return a list of matches.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ microcodes: Dict of Microcode objects indexed by name
|
|
|
+ model: String containing model name to find
|
|
|
+ Returns:
|
|
|
+ Tuple:
|
|
|
+ List of matching Microcode objects
|
|
|
+ List of abbreviations we tried
|
|
|
+ """
|
|
|
+ # Allow a full name to be used
|
|
|
+ mcode = microcodes.get(model)
|
|
|
+ if mcode:
|
|
|
+ return [mcode], []
|
|
|
+
|
|
|
+ tried = []
|
|
|
+ found = []
|
|
|
+ for i in range(3):
|
|
|
+ abbrev = model[:-i] if i else model
|
|
|
+ tried.append(abbrev)
|
|
|
+ for mcode in microcodes.values():
|
|
|
+ if mcode.model.startswith(abbrev):
|
|
|
+ found.append(mcode)
|
|
|
+ if found:
|
|
|
+ break
|
|
|
+ return found, tried
|
|
|
+
|
|
|
+def CreateFile(date, license_text, mcode, outfile):
|
|
|
+ """Create a microcode file in U-Boot's .dtsi format
|
|
|
+
|
|
|
+ Args:
|
|
|
+ date: String containing date of original microcode file
|
|
|
+ license: List of text lines for the license file
|
|
|
+ mcode: Microcode object to write
|
|
|
+ outfile: Filename to write to ('-' for stdout)
|
|
|
+ """
|
|
|
+ out = '''/*%s
|
|
|
+ * ---
|
|
|
+ * This is a device tree fragment. Use #include to add these properties to a
|
|
|
+ * node.
|
|
|
+ *
|
|
|
+ * Date: %s
|
|
|
+ */
|
|
|
+
|
|
|
+compatible = "intel,microcode";
|
|
|
+intel,header-version = <%d>;
|
|
|
+intel,update-revision = <%#x>;
|
|
|
+intel,date-code = <%#x>;
|
|
|
+intel,processor-signature = <%#x>;
|
|
|
+intel,checksum = <%#x>;
|
|
|
+intel,loader-revision = <%d>;
|
|
|
+intel,processor-flags = <%#x>;
|
|
|
+
|
|
|
+/* The first 48-bytes are the public header which repeats the above data */
|
|
|
+data = <%s
|
|
|
+\t>;'''
|
|
|
+ words = ''
|
|
|
+ for i in range(len(mcode.words)):
|
|
|
+ if not (i & 3):
|
|
|
+ words += '\n'
|
|
|
+ val = mcode.words[i]
|
|
|
+ # Change each word so it will be little-endian in the FDT
|
|
|
+ # This data is needed before RAM is available on some platforms so we
|
|
|
+ # cannot do an endianness swap on boot.
|
|
|
+ val = struct.unpack("<I", struct.pack(">I", val))[0]
|
|
|
+ words += '\t%#010x' % val
|
|
|
+
|
|
|
+ # Take care to avoid adding a space before a tab
|
|
|
+ text = ''
|
|
|
+ for line in license_text:
|
|
|
+ if line[0] == '\t':
|
|
|
+ text += '\n *' + line
|
|
|
+ else:
|
|
|
+ text += '\n * ' + line
|
|
|
+ args = [text, date]
|
|
|
+ args += [mcode.words[i] for i in range(7)]
|
|
|
+ args.append(words)
|
|
|
+ if outfile == '-':
|
|
|
+ print out % tuple(args)
|
|
|
+ else:
|
|
|
+ if not outfile:
|
|
|
+ if not os.path.exists(MICROCODE_DIR):
|
|
|
+ print >> sys.stderr, "Creating directory '%s'" % MICROCODE_DIR
|
|
|
+ os.makedirs(MICROCODE_DIR)
|
|
|
+ outfile = os.path.join(MICROCODE_DIR, mcode.name + '.dtsi')
|
|
|
+ print >> sys.stderr, "Writing microcode for '%s' to '%s'" % (
|
|
|
+ mcode.name, outfile)
|
|
|
+ with open(outfile, 'w') as fd:
|
|
|
+ print >> fd, out % tuple(args)
|
|
|
+
|
|
|
+def MicrocodeTool():
|
|
|
+ """Run the microcode tool"""
|
|
|
+ commands = 'create,license,list'.split(',')
|
|
|
+ parser = OptionParser()
|
|
|
+ parser.add_option('-d', '--mcfile', type='string', action='store',
|
|
|
+ help='Name of microcode.dat file')
|
|
|
+ parser.add_option('-m', '--model', type='string', action='store',
|
|
|
+ help='Model name to extract')
|
|
|
+ parser.add_option('-o', '--outfile', type='string', action='store',
|
|
|
+ help='Filename to use for output (- for stdout), default is'
|
|
|
+ ' %s/<name>.dtsi' % MICROCODE_DIR)
|
|
|
+ parser.usage += """ command
|
|
|
+
|
|
|
+ Process an Intel microcode file (use -h for help). Commands:
|
|
|
+
|
|
|
+ create Create microcode .dtsi file for a model
|
|
|
+ list List available models in microcode file
|
|
|
+ license Print the license
|
|
|
+
|
|
|
+ Typical usage:
|
|
|
+
|
|
|
+ ./tools/microcode-tool -d microcode.dat -m 306a create
|
|
|
+
|
|
|
+ This will find the appropriate file and write it to %s.""" % MICROCODE_DIR
|
|
|
+
|
|
|
+ (options, args) = parser.parse_args()
|
|
|
+ if not args:
|
|
|
+ parser.error('Please specify a command')
|
|
|
+ cmd = args[0]
|
|
|
+ if cmd not in commands:
|
|
|
+ parser.error("Unknown command '%s'" % cmd)
|
|
|
+
|
|
|
+ if not options.mcfile:
|
|
|
+ parser.error('You must specify a microcode file')
|
|
|
+ date, license_text, microcodes = ParseFile(options.mcfile)
|
|
|
+
|
|
|
+ if cmd == 'list':
|
|
|
+ List(date, microcodes, options.model)
|
|
|
+ elif cmd == 'license':
|
|
|
+ print '\n'.join(license_text)
|
|
|
+ elif cmd == 'create':
|
|
|
+ if not options.model:
|
|
|
+ parser.error('You must specify a model to create')
|
|
|
+ model = options.model.lower()
|
|
|
+ mcode_list, tried = FindMicrocode(microcodes, model)
|
|
|
+ if not mcode_list:
|
|
|
+ parser.error("Unknown model '%s' (%s) - try 'list' to list" %
|
|
|
+ (model, ', '.join(tried)))
|
|
|
+ if len(mcode_list) > 1:
|
|
|
+ parser.error("Ambiguous model '%s' (%s) matched %s - try 'list' "
|
|
|
+ "to list or specify a particular file" %
|
|
|
+ (model, ', '.join(tried),
|
|
|
+ ', '.join([m.name for m in mcode_list])))
|
|
|
+ CreateFile(date, license_text, mcode_list[0], options.outfile)
|
|
|
+ else:
|
|
|
+ parser.error("Unknown command '%s'" % cmd)
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ MicrocodeTool()
|