[U-Boot] [PATCH v2 1/6] x86: Add a script to process Intel microcode files
Simon Glass
sjg at chromium.org
Tue Dec 16 17:32:24 CET 2014
Hi Bin,
On 16 December 2014 at 01:04, Bin Meng <bmeng.cn at gmail.com> wrote:
> Hi Simon,
>
> On Tue, Dec 16, 2014 at 1:02 PM, Simon Glass <sjg at 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 at chromium.org>
>> ---
>>
>> Changes in v2:
>> - 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 | 245 ++++++++++++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 246 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..5dbb0db
>> --- /dev/null
>> +++ b/tools/microcode-tool.py
>> @@ -0,0 +1,245 @@
>> +#!/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: 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 = []
>> + 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.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, 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, 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.
>> + */
>> +
>> +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:
>> + if line[0] == '\t':
>> + text += '\n *' + line
>> + else:
>> + text += '\n * ' + line
>> + args = [text]
>> + args += [mcode.words[i] for i in range(7)]
>> + args.append(words)
>> + if outfile == '-':
>> + print out % tuple(args)
>> + else:
>> + if not outfile:
>> + outfile = os.path.join(MICROCODE_DIR, mcode.name + '.dtsi')
>> + print >>sys.stderr, "Writing micrcode for '%s' to '%s'" % (
>> + mcode.name, outfile)
>> + with open(outfile, 'w') as fd:
>> + print >>fd, out % tuple(args)
>> +
>> +
>> +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, microcodes = ParseFile(options.mcfile)
>> +
>> +if cmd == 'list':
>> + List(date, microcodes, options.model)
>> +elif cmd == 'license':
>> + print '\n'.join(license)
>> +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, mcode_list[0], options.outfile)
>> +else:
>> + parser.error("Unknown command '%s'" % cmd)
>> --
>
> $ ./tools/microcode-tool -d microcode.dat -m m0220661105_cv create
> Writing micrcode for 'm0220661105_cv' to
> 'arch/x86/dts/microcode/m0220661105_cv.dtsi'
> Traceback (most recent call last):
> File "./tools/microcode-tool", line 243, in <module>
> CreateFile(date, license, mcode_list[0], options.outfile)
> File "./tools/microcode-tool", line 188, in CreateFile
> with open(outfile, 'w') as fd:
> IOError: [Errno 2] No such file or directory:
> 'arch/x86/dts/microcode/m0220661105_cv.dtsi'
>
> My understanding is that when calling microcode-tool without '-o'
> option, a default dtsi file will be created in arch/x86/dts/microcode/
> directory with the model number as the file name. However testing
> above shows some python error. Could you please either print some user
> friendly message, or just create a default dtsi file?
I think it just needs to create the directory automatically. In my
tree it already has this directory. The message seems OK to me though.
I'll fix up the script a bit more.
Regards,
Simon
More information about the U-Boot
mailing list