x86: Enhance the microcode tool to support header files as input

Sometimes microcode is delivered as a header file. Allow the tool to
support this as well as collecting multiple microcode blocks into a
single update.

Signed-off-by: Simon Glass <sjg@chromium.org>
Tested-by: Bin Meng <bmeng.cn@gmail.com>
diff --git a/tools/microcode-tool.py b/tools/microcode-tool.py
index 003716d..71c2e91 100755
--- a/tools/microcode-tool.py
+++ b/tools/microcode-tool.py
@@ -76,6 +76,35 @@
         microcodes[name] = Microcode(name, data)
     return date, license_text, microcodes
 
+def ParseHeaderFiles(fname_list):
+    """Parse a list of header files and return the component parts
+
+    Args:
+        fname_list: List of files to parse
+    Returns:
+            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
+    """
+    microcodes = {}
+    license_text = []
+    date = ''
+    name = None
+    for fname in fname_list:
+        name = os.path.basename(fname).lower()
+        name = os.path.splitext(name)[0]
+        data = []
+        with open(fname) as fd:
+            for line in fd:
+                line = line.rstrip()
+
+                # Omit anything after the last comma
+                words = line.split(',')[:-1]
+                data += [word + ',' for word in words]
+        microcodes[name] = Microcode(name, data)
+    return date, license_text, microcodes
+
+
 def List(date, microcodes, model):
     """List the available microcode chunks
 
@@ -129,13 +158,13 @@
             break
     return found, tried
 
-def CreateFile(date, license_text, mcode, outfile):
+def CreateFile(date, license_text, mcodes, 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
+        mcodes:      Microcode objects to write (normally only 1)
         outfile:    Filename to write to ('-' for stdout)
     """
     out = '''/*%s
@@ -159,15 +188,22 @@
 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
+    add_comments = len(mcodes) > 1
+    for mcode in mcodes:
+        if add_comments:
+            words += '\n/* %s */' % mcode.name
+        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
+
+    # Use the first microcode for the headers
+    mcode = mcodes[0]
 
     # Take care to avoid adding a space before a tab
     text = ''
@@ -187,8 +223,8 @@
                 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)
+        print >> sys.stderr, "Writing microcode for '%s' to '%s'" % (
+                ', '.join([mcode.name for mcode in mcodes]), outfile)
         with open(outfile, 'w') as fd:
             print >> fd, out % tuple(args)
 
@@ -198,8 +234,12 @@
     parser = OptionParser()
     parser.add_option('-d', '--mcfile', type='string', action='store',
                     help='Name of microcode.dat file')
+    parser.add_option('-H', '--headerfile', type='string', action='append',
+                    help='Name of .h file containing microcode')
     parser.add_option('-m', '--model', type='string', action='store',
-                    help='Model name to extract')
+                    help="Model name to extract ('all' for all)")
+    parser.add_option('-M', '--multiple', type='string', action='store',
+                    help="Allow output of multiple models")
     parser.add_option('-o', '--outfile', type='string', action='store',
                     help='Filename to use for output (- for stdout), default is'
                     ' %s/<name>.dtsi' % MICROCODE_DIR)
@@ -224,9 +264,14 @@
     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 (not not options.mcfile) != (not not options.mcfile):
+        parser.error("You must specify either header files or a microcode file, not both")
+    if options.headerfile:
+        date, license_text, microcodes = ParseHeaderFiles(options.headerfile)
+    elif options.mcfile:
+        date, license_text, microcodes = ParseFile(options.mcfile)
+    else:
+        parser.error('You must specify a microcode file (or header files)')
 
     if cmd == 'list':
         List(date, microcodes, options.model)
@@ -236,16 +281,21 @@
         if not options.model:
             parser.error('You must specify a model to create')
         model = options.model.lower()
-        mcode_list, tried = FindMicrocode(microcodes, model)
+        if options.model == 'all':
+            options.multiple = True
+            mcode_list = microcodes.values()
+            tried = []
+        else:
+            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:
+        if not options.multiple and 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)
+        CreateFile(date, license_text, mcode_list, options.outfile)
     else:
         parser.error("Unknown command '%s'" % cmd)