]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - tools/microcode-tool.py
karo: tx6: replace open coded interpretation of cpurev by calls to is_cpu_type()
[karo-tx-uboot.git] / tools / microcode-tool.py
1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2014 Google, Inc
4 #
5 # SPDX-License-Identifier:      GPL-2.0+
6 #
7 # Intel microcode update tool
8
9 from optparse import OptionParser
10 import os
11 import re
12 import struct
13 import sys
14
15 MICROCODE_DIR = 'arch/x86/dts/microcode'
16
17 class Microcode:
18     """Holds information about the microcode for a particular model of CPU.
19
20     Attributes:
21         name:  Name of the CPU this microcode is for, including any version
22                    information (e.g. 'm12206a7_00000029')
23         model: Model code string (this is cpuid(1).eax, e.g. '206a7')
24         words: List of hex words containing the microcode. The first 16 words
25                    are the public header.
26     """
27     def __init__(self, name, data):
28         self.name = name
29         # Convert data into a list of hex words
30         self.words = []
31         for value in ''.join(data).split(','):
32             hexval = value.strip()
33             if hexval:
34                 self.words.append(int(hexval, 0))
35
36         # The model is in the 4rd hex word
37         self.model = '%x' % self.words[3]
38
39 def ParseFile(fname):
40     """Parse a micrcode.dat file and return the component parts
41
42     Args:
43         fname: Filename to parse
44     Returns:
45         3-Tuple:
46             date:         String containing date from the file's header
47             license_text: List of text lines for the license file
48             microcodes:   List of Microcode objects from the file
49     """
50     re_date = re.compile('/\* *(.* [0-9]{4}) *\*/$')
51     re_license = re.compile('/[^-*+] *(.*)$')
52     re_name = re.compile('/\* *(.*)\.inc *\*/', re.IGNORECASE)
53     microcodes = {}
54     license_text = []
55     date = ''
56     data = []
57     name = None
58     with open(fname) as fd:
59         for line in fd:
60             line = line.rstrip()
61             m_date = re_date.match(line)
62             m_license = re_license.match(line)
63             m_name = re_name.match(line)
64             if m_name:
65                 if name:
66                     microcodes[name] = Microcode(name, data)
67                 name = m_name.group(1).lower()
68                 data = []
69             elif m_license:
70                 license_text.append(m_license.group(1))
71             elif m_date:
72                 date = m_date.group(1)
73             else:
74                 data.append(line)
75     if name:
76         microcodes[name] = Microcode(name, data)
77     return date, license_text, microcodes
78
79 def ParseHeaderFiles(fname_list):
80     """Parse a list of header files and return the component parts
81
82     Args:
83         fname_list: List of files to parse
84     Returns:
85             date:         String containing date from the file's header
86             license_text: List of text lines for the license file
87             microcodes:   List of Microcode objects from the file
88     """
89     microcodes = {}
90     license_text = []
91     date = ''
92     name = None
93     for fname in fname_list:
94         name = os.path.basename(fname).lower()
95         name = os.path.splitext(name)[0]
96         data = []
97         with open(fname) as fd:
98             for line in fd:
99                 line = line.rstrip()
100
101                 # Omit anything after the last comma
102                 words = line.split(',')[:-1]
103                 data += [word + ',' for word in words]
104         microcodes[name] = Microcode(name, data)
105     return date, license_text, microcodes
106
107
108 def List(date, microcodes, model):
109     """List the available microcode chunks
110
111     Args:
112         date:           Date of the microcode file
113         microcodes:     Dict of Microcode objects indexed by name
114         model:          Model string to search for, or None
115     """
116     print 'Date: %s' % date
117     if model:
118         mcode_list, tried = FindMicrocode(microcodes, model.lower())
119         print 'Matching models %s:' % (', '.join(tried))
120     else:
121         print 'All models:'
122         mcode_list = [microcodes[m] for m in microcodes.keys()]
123     for mcode in mcode_list:
124         print '%-20s: model %s' % (mcode.name, mcode.model)
125
126 def FindMicrocode(microcodes, model):
127     """Find all the microcode chunks which match the given model.
128
129     This model is something like 306a9 (the value returned in eax from
130     cpuid(1) when running on Intel CPUs). But we allow a partial match,
131     omitting the last 1 or two characters to allow many families to have the
132     same microcode.
133
134     If the model name is ambiguous we return a list of matches.
135
136     Args:
137         microcodes: Dict of Microcode objects indexed by name
138         model:      String containing model name to find
139     Returns:
140         Tuple:
141             List of matching Microcode objects
142             List of abbreviations we tried
143     """
144     # Allow a full name to be used
145     mcode = microcodes.get(model)
146     if mcode:
147         return [mcode], []
148
149     tried = []
150     found = []
151     for i in range(3):
152         abbrev = model[:-i] if i else model
153         tried.append(abbrev)
154         for mcode in microcodes.values():
155             if mcode.model.startswith(abbrev):
156                 found.append(mcode)
157         if found:
158             break
159     return found, tried
160
161 def CreateFile(date, license_text, mcodes, outfile):
162     """Create a microcode file in U-Boot's .dtsi format
163
164     Args:
165         date:       String containing date of original microcode file
166         license:    List of text lines for the license file
167         mcodes:      Microcode objects to write (normally only 1)
168         outfile:    Filename to write to ('-' for stdout)
169     """
170     out = '''/*%s
171  * ---
172  * This is a device tree fragment. Use #include to add these properties to a
173  * node.
174  *
175  * Date: %s
176  */
177
178 compatible = "intel,microcode";
179 intel,header-version = <%d>;
180 intel,update-revision = <%#x>;
181 intel,date-code = <%#x>;
182 intel,processor-signature = <%#x>;
183 intel,checksum = <%#x>;
184 intel,loader-revision = <%d>;
185 intel,processor-flags = <%#x>;
186
187 /* The first 48-bytes are the public header which repeats the above data */
188 data = <%s
189 \t>;'''
190     words = ''
191     add_comments = len(mcodes) > 1
192     for mcode in mcodes:
193         if add_comments:
194             words += '\n/* %s */' % mcode.name
195         for i in range(len(mcode.words)):
196             if not (i & 3):
197                 words += '\n'
198             val = mcode.words[i]
199             # Change each word so it will be little-endian in the FDT
200             # This data is needed before RAM is available on some platforms so
201             # we cannot do an endianness swap on boot.
202             val = struct.unpack("<I", struct.pack(">I", val))[0]
203             words += '\t%#010x' % val
204
205     # Use the first microcode for the headers
206     mcode = mcodes[0]
207
208     # Take care to avoid adding a space before a tab
209     text = ''
210     for line in license_text:
211         if line[0] == '\t':
212             text += '\n *' + line
213         else:
214             text += '\n * ' + line
215     args = [text, date]
216     args += [mcode.words[i] for i in range(7)]
217     args.append(words)
218     if outfile == '-':
219         print out % tuple(args)
220     else:
221         if not outfile:
222             if not os.path.exists(MICROCODE_DIR):
223                 print >> sys.stderr, "Creating directory '%s'" % MICROCODE_DIR
224                 os.makedirs(MICROCODE_DIR)
225             outfile = os.path.join(MICROCODE_DIR, mcode.name + '.dtsi')
226         print >> sys.stderr, "Writing microcode for '%s' to '%s'" % (
227                 ', '.join([mcode.name for mcode in mcodes]), outfile)
228         with open(outfile, 'w') as fd:
229             print >> fd, out % tuple(args)
230
231 def MicrocodeTool():
232     """Run the microcode tool"""
233     commands = 'create,license,list'.split(',')
234     parser = OptionParser()
235     parser.add_option('-d', '--mcfile', type='string', action='store',
236                     help='Name of microcode.dat file')
237     parser.add_option('-H', '--headerfile', type='string', action='append',
238                     help='Name of .h file containing microcode')
239     parser.add_option('-m', '--model', type='string', action='store',
240                     help="Model name to extract ('all' for all)")
241     parser.add_option('-M', '--multiple', type='string', action='store',
242                     help="Allow output of multiple models")
243     parser.add_option('-o', '--outfile', type='string', action='store',
244                     help='Filename to use for output (- for stdout), default is'
245                     ' %s/<name>.dtsi' % MICROCODE_DIR)
246     parser.usage += """ command
247
248     Process an Intel microcode file (use -h for help). Commands:
249
250        create     Create microcode .dtsi file for a model
251        list       List available models in microcode file
252        license    Print the license
253
254     Typical usage:
255
256        ./tools/microcode-tool -d microcode.dat -m 306a create
257
258     This will find the appropriate file and write it to %s.""" % MICROCODE_DIR
259
260     (options, args) = parser.parse_args()
261     if not args:
262         parser.error('Please specify a command')
263     cmd = args[0]
264     if cmd not in commands:
265         parser.error("Unknown command '%s'" % cmd)
266
267     if (not not options.mcfile) != (not not options.mcfile):
268         parser.error("You must specify either header files or a microcode file, not both")
269     if options.headerfile:
270         date, license_text, microcodes = ParseHeaderFiles(options.headerfile)
271     elif options.mcfile:
272         date, license_text, microcodes = ParseFile(options.mcfile)
273     else:
274         parser.error('You must specify a microcode file (or header files)')
275
276     if cmd == 'list':
277         List(date, microcodes, options.model)
278     elif cmd == 'license':
279         print '\n'.join(license_text)
280     elif cmd == 'create':
281         if not options.model:
282             parser.error('You must specify a model to create')
283         model = options.model.lower()
284         if options.model == 'all':
285             options.multiple = True
286             mcode_list = microcodes.values()
287             tried = []
288         else:
289             mcode_list, tried = FindMicrocode(microcodes, model)
290         if not mcode_list:
291             parser.error("Unknown model '%s' (%s) - try 'list' to list" %
292                         (model, ', '.join(tried)))
293         if not options.multiple and len(mcode_list) > 1:
294             parser.error("Ambiguous model '%s' (%s) matched %s - try 'list' "
295                         "to list or specify a particular file" %
296                         (model, ', '.join(tried),
297                         ', '.join([m.name for m in mcode_list])))
298         CreateFile(date, license_text, mcode_list, options.outfile)
299     else:
300         parser.error("Unknown command '%s'" % cmd)
301
302 if __name__ == "__main__":
303     MicrocodeTool()