]> git.sur5r.net Git - u-boot/blob - tools/microcode-tool.py
bootvx: x86: Explicitly clear the bootloader image size
[u-boot] / tools / microcode-tool.py
1 #!/usr/bin/env python2
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             license_start = False
99             license_end = False
100             for line in fd:
101                 line = line.rstrip()
102
103                 if len(line) >= 2:
104                     if line[0] == '/' and line[1] == '*':
105                         license_start = True
106                         continue
107                     if line[0] == '*' and line[1] == '/':
108                         license_end = True
109                         continue
110                 if license_start and not license_end:
111                     # Ignore blank line
112                     if len(line) > 0:
113                         license_text.append(line)
114                     continue
115                 # Omit anything after the last comma
116                 words = line.split(',')[:-1]
117                 data += [word + ',' for word in words]
118         microcodes[name] = Microcode(name, data)
119     return date, license_text, microcodes
120
121
122 def List(date, microcodes, model):
123     """List the available microcode chunks
124
125     Args:
126         date:           Date of the microcode file
127         microcodes:     Dict of Microcode objects indexed by name
128         model:          Model string to search for, or None
129     """
130     print 'Date: %s' % date
131     if model:
132         mcode_list, tried = FindMicrocode(microcodes, model.lower())
133         print 'Matching models %s:' % (', '.join(tried))
134     else:
135         print 'All models:'
136         mcode_list = [microcodes[m] for m in microcodes.keys()]
137     for mcode in mcode_list:
138         print '%-20s: model %s' % (mcode.name, mcode.model)
139
140 def FindMicrocode(microcodes, model):
141     """Find all the microcode chunks which match the given model.
142
143     This model is something like 306a9 (the value returned in eax from
144     cpuid(1) when running on Intel CPUs). But we allow a partial match,
145     omitting the last 1 or two characters to allow many families to have the
146     same microcode.
147
148     If the model name is ambiguous we return a list of matches.
149
150     Args:
151         microcodes: Dict of Microcode objects indexed by name
152         model:      String containing model name to find
153     Returns:
154         Tuple:
155             List of matching Microcode objects
156             List of abbreviations we tried
157     """
158     # Allow a full name to be used
159     mcode = microcodes.get(model)
160     if mcode:
161         return [mcode], []
162
163     tried = []
164     found = []
165     for i in range(3):
166         abbrev = model[:-i] if i else model
167         tried.append(abbrev)
168         for mcode in microcodes.values():
169             if mcode.model.startswith(abbrev):
170                 found.append(mcode)
171         if found:
172             break
173     return found, tried
174
175 def CreateFile(date, license_text, mcodes, outfile):
176     """Create a microcode file in U-Boot's .dtsi format
177
178     Args:
179         date:       String containing date of original microcode file
180         license:    List of text lines for the license file
181         mcodes:      Microcode objects to write (normally only 1)
182         outfile:    Filename to write to ('-' for stdout)
183     """
184     out = '''/*%s
185  * ---
186  * This is a device tree fragment. Use #include to add these properties to a
187  * node.
188  *
189  * Date: %s
190  */
191
192 compatible = "intel,microcode";
193 intel,header-version = <%d>;
194 intel,update-revision = <%#x>;
195 intel,date-code = <%#x>;
196 intel,processor-signature = <%#x>;
197 intel,checksum = <%#x>;
198 intel,loader-revision = <%d>;
199 intel,processor-flags = <%#x>;
200
201 /* The first 48-bytes are the public header which repeats the above data */
202 data = <%s
203 \t>;'''
204     words = ''
205     add_comments = len(mcodes) > 1
206     for mcode in mcodes:
207         if add_comments:
208             words += '\n/* %s */' % mcode.name
209         for i in range(len(mcode.words)):
210             if not (i & 3):
211                 words += '\n'
212             val = mcode.words[i]
213             # Change each word so it will be little-endian in the FDT
214             # This data is needed before RAM is available on some platforms so
215             # we cannot do an endianness swap on boot.
216             val = struct.unpack("<I", struct.pack(">I", val))[0]
217             words += '\t%#010x' % val
218
219     # Use the first microcode for the headers
220     mcode = mcodes[0]
221
222     # Take care to avoid adding a space before a tab
223     text = ''
224     for line in license_text:
225         if line[0] == '\t':
226             text += '\n *' + line
227         else:
228             text += '\n * ' + line
229     args = [text, date]
230     args += [mcode.words[i] for i in range(7)]
231     args.append(words)
232     if outfile == '-':
233         print out % tuple(args)
234     else:
235         if not outfile:
236             if not os.path.exists(MICROCODE_DIR):
237                 print >> sys.stderr, "Creating directory '%s'" % MICROCODE_DIR
238                 os.makedirs(MICROCODE_DIR)
239             outfile = os.path.join(MICROCODE_DIR, mcode.name + '.dtsi')
240         print >> sys.stderr, "Writing microcode for '%s' to '%s'" % (
241                 ', '.join([mcode.name for mcode in mcodes]), outfile)
242         with open(outfile, 'w') as fd:
243             print >> fd, out % tuple(args)
244
245 def MicrocodeTool():
246     """Run the microcode tool"""
247     commands = 'create,license,list'.split(',')
248     parser = OptionParser()
249     parser.add_option('-d', '--mcfile', type='string', action='store',
250                     help='Name of microcode.dat file')
251     parser.add_option('-H', '--headerfile', type='string', action='append',
252                     help='Name of .h file containing microcode')
253     parser.add_option('-m', '--model', type='string', action='store',
254                     help="Model name to extract ('all' for all)")
255     parser.add_option('-M', '--multiple', type='string', action='store',
256                     help="Allow output of multiple models")
257     parser.add_option('-o', '--outfile', type='string', action='store',
258                     help='Filename to use for output (- for stdout), default is'
259                     ' %s/<name>.dtsi' % MICROCODE_DIR)
260     parser.usage += """ command
261
262     Process an Intel microcode file (use -h for help). Commands:
263
264        create     Create microcode .dtsi file for a model
265        list       List available models in microcode file
266        license    Print the license
267
268     Typical usage:
269
270        ./tools/microcode-tool -d microcode.dat -m 306a create
271
272     This will find the appropriate file and write it to %s.""" % MICROCODE_DIR
273
274     (options, args) = parser.parse_args()
275     if not args:
276         parser.error('Please specify a command')
277     cmd = args[0]
278     if cmd not in commands:
279         parser.error("Unknown command '%s'" % cmd)
280
281     if (not not options.mcfile) != (not not options.mcfile):
282         parser.error("You must specify either header files or a microcode file, not both")
283     if options.headerfile:
284         date, license_text, microcodes = ParseHeaderFiles(options.headerfile)
285     elif options.mcfile:
286         date, license_text, microcodes = ParseFile(options.mcfile)
287     else:
288         parser.error('You must specify a microcode file (or header files)')
289
290     if cmd == 'list':
291         List(date, microcodes, options.model)
292     elif cmd == 'license':
293         print '\n'.join(license_text)
294     elif cmd == 'create':
295         if not options.model:
296             parser.error('You must specify a model to create')
297         model = options.model.lower()
298         if options.model == 'all':
299             options.multiple = True
300             mcode_list = microcodes.values()
301             tried = []
302         else:
303             mcode_list, tried = FindMicrocode(microcodes, model)
304         if not mcode_list:
305             parser.error("Unknown model '%s' (%s) - try 'list' to list" %
306                         (model, ', '.join(tried)))
307         if not options.multiple and len(mcode_list) > 1:
308             parser.error("Ambiguous model '%s' (%s) matched %s - try 'list' "
309                         "to list or specify a particular file" %
310                         (model, ', '.join(tried),
311                         ', '.join([m.name for m in mcode_list])))
312         CreateFile(date, license_text, mcode_list, options.outfile)
313     else:
314         parser.error("Unknown command '%s'" % cmd)
315
316 if __name__ == "__main__":
317     MicrocodeTool()