
On Wed, Dec 17, 2014 at 11:21 AM, Simon Glass sjg@chromium.org wrote:
Intel delivers microcode updates in a microcode.dat file which must be split up into individual files for each CPU. Add a tool which performs this task. It can list available microcode updates for each model and produce a new microcode update in U-Boot's .dtsi format.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3:
- Drop patches that have already been applied
- Fix pylint problems
- Add date string to microcode
- Automatically create microcode directory if needed
- Move microcode program into a function
Changes in v2:
- Drop patches that have already been applied
- Add function comments
- Fix problem with specifying the full microcode filename
- Allow 'list' command to display a list of matching microcode chunks
- Print a message when generating microcode
- Expand the help a little
- Print an error when an ambiguous microcode model is given
tools/microcode-tool | 1 + tools/microcode-tool.py | 253 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 120000 tools/microcode-tool create mode 100755 tools/microcode-tool.py
diff --git a/tools/microcode-tool b/tools/microcode-tool new file mode 120000 index 0000000..8be8507 --- /dev/null +++ b/tools/microcode-tool @@ -0,0 +1 @@ +microcode-tool.py \ No newline at end of file diff --git a/tools/microcode-tool.py b/tools/microcode-tool.py new file mode 100755 index 0000000..003716d --- /dev/null +++ b/tools/microcode-tool.py @@ -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()
--
Tested-by: Bin Meng bmeng.cn@gmail.com