mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 10:26:10 +01:00 
			
		
		
		
	The microcode header files in the Intel Chief River FSP package have a license comment block. Update the microcode-tool to support parsing it and extract the license text to the .dtsi file. Signed-off-by: Bin Meng <bmeng.cn@gmail.com> Acked-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			318 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			318 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/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 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:
 | |
|             license_start = False
 | |
|             license_end = False
 | |
|             for line in fd:
 | |
|                 line = line.rstrip()
 | |
| 
 | |
|                 if len(line) >= 2:
 | |
|                     if line[0] == '/' and line[1] == '*':
 | |
|                         license_start = True
 | |
|                         continue
 | |
|                     if line[0] == '*' and line[1] == '/':
 | |
|                         license_end = True
 | |
|                         continue
 | |
|                 if license_start and not license_end:
 | |
|                     # Ignore blank line
 | |
|                     if len(line) > 0:
 | |
|                         license_text.append(line)
 | |
|                     continue
 | |
|                 # 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
 | |
| 
 | |
|     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, 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
 | |
|         mcodes:      Microcode objects to write (normally only 1)
 | |
|         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 = ''
 | |
|     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 = ''
 | |
|     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'" % (
 | |
|                 ', '.join([mcode.name for mcode in mcodes]), 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('-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 ('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)
 | |
|     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 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)
 | |
|     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()
 | |
|         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 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, options.outfile)
 | |
|     else:
 | |
|         parser.error("Unknown command '%s'" % cmd)
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     MicrocodeTool()
 |