diff mbox

[U-Boot,v2,1/6] x86: Add a script to process Intel microcode files

Message ID 1418706162-31354-2-git-send-email-sjg@chromium.org
State Superseded
Delegated to: Simon Glass
Headers show

Commit Message

Simon Glass Dec. 16, 2014, 5:02 a.m. UTC
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 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

Comments

Bin Meng Dec. 16, 2014, 8:04 a.m. UTC | #1
Hi Simon,

On Tue, Dec 16, 2014 at 1:02 PM, 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 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?

Regards,
Bin
Simon Glass Dec. 16, 2014, 4:32 p.m. UTC | #2
Hi Bin,

On 16 December 2014 at 01:04, Bin Meng <bmeng.cn@gmail.com> wrote:
> Hi Simon,
>
> On Tue, Dec 16, 2014 at 1:02 PM, 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 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
diff mbox

Patch

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)