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> Tested-by: Bin Meng <bmeng.cn@gmail.com>master
parent
255fd5caa5
commit
d2c6181d2d
@ -0,0 +1 @@ |
|||||||
|
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() |
Loading…
Reference in new issue