Blame view

tools/microcode-tool.py 10.8 KB
94b13bbae   Masahiro Yamada   host-tools: use p...
1
  #!/usr/bin/env python2
83d290c56   Tom Rini   SPDX: Convert all...
2
  # SPDX-License-Identifier: GPL-2.0+
d2c6181d2   Simon Glass   x86: Add a script...
3
4
5
  #
  # Copyright (c) 2014 Google, Inc
  #
d2c6181d2   Simon Glass   x86: Add a script...
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  # 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
cfcf8ea2d   Simon Glass   x86: Enhance the ...
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
  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:
aefba6f1b   Bin Meng   tools: microcode-...
96
97
              license_start = False
              license_end = False
cfcf8ea2d   Simon Glass   x86: Enhance the ...
98
99
              for line in fd:
                  line = line.rstrip()
aefba6f1b   Bin Meng   tools: microcode-...
100
101
102
103
104
105
106
107
108
109
110
111
                  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
cfcf8ea2d   Simon Glass   x86: Enhance the ...
112
113
114
115
116
                  # 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
d2c6181d2   Simon Glass   x86: Add a script...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
  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
cfcf8ea2d   Simon Glass   x86: Enhance the ...
169
  def CreateFile(date, license_text, mcodes, outfile):
d2c6181d2   Simon Glass   x86: Add a script...
170
171
172
173
174
      """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
cfcf8ea2d   Simon Glass   x86: Enhance the ...
175
          mcodes:      Microcode objects to write (normally only 1)
d2c6181d2   Simon Glass   x86: Add a script...
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
          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 = ''
cfcf8ea2d   Simon Glass   x86: Enhance the ...
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
      add_comments = len(mcodes) > 1
      for mcode in mcodes:
          if add_comments:
              words += '
  /* %s */' % mcode.name
          for i in range(len(mcode.words)):
              if not (i & 3):
                  words += '
  '
              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]
d2c6181d2   Simon Glass   x86: Add a script...
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
  
      # Take care to avoid adding a space before a tab
      text = ''
      for line in license_text:
          if line[0] == '\t':
              text += '
   *' + line
          else:
              text += '
   * ' + 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')
cfcf8ea2d   Simon Glass   x86: Enhance the ...
238
239
          print >> sys.stderr, "Writing microcode for '%s' to '%s'" % (
                  ', '.join([mcode.name for mcode in mcodes]), outfile)
d2c6181d2   Simon Glass   x86: Add a script...
240
241
242
243
244
245
246
247
248
          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')
cfcf8ea2d   Simon Glass   x86: Enhance the ...
249
250
      parser.add_option('-H', '--headerfile', type='string', action='append',
                      help='Name of .h file containing microcode')
d2c6181d2   Simon Glass   x86: Add a script...
251
      parser.add_option('-m', '--model', type='string', action='store',
cfcf8ea2d   Simon Glass   x86: Enhance the ...
252
253
254
                      help="Model name to extract ('all' for all)")
      parser.add_option('-M', '--multiple', type='string', action='store',
                      help="Allow output of multiple models")
d2c6181d2   Simon Glass   x86: Add a script...
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
      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)
cfcf8ea2d   Simon Glass   x86: Enhance the ...
278
279
280
281
282
283
284
285
      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)')
d2c6181d2   Simon Glass   x86: Add a script...
286
287
288
289
290
291
292
293
294
295
  
      if cmd == 'list':
          List(date, microcodes, options.model)
      elif cmd == 'license':
          print '
  '.join(license_text)
      elif cmd == 'create':
          if not options.model:
              parser.error('You must specify a model to create')
          model = options.model.lower()
cfcf8ea2d   Simon Glass   x86: Enhance the ...
296
297
298
299
300
301
          if options.model == 'all':
              options.multiple = True
              mcode_list = microcodes.values()
              tried = []
          else:
              mcode_list, tried = FindMicrocode(microcodes, model)
d2c6181d2   Simon Glass   x86: Add a script...
302
303
304
          if not mcode_list:
              parser.error("Unknown model '%s' (%s) - try 'list' to list" %
                          (model, ', '.join(tried)))
cfcf8ea2d   Simon Glass   x86: Enhance the ...
305
          if not options.multiple and len(mcode_list) > 1:
d2c6181d2   Simon Glass   x86: Add a script...
306
307
308
309
              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])))
cfcf8ea2d   Simon Glass   x86: Enhance the ...
310
          CreateFile(date, license_text, mcode_list, options.outfile)
d2c6181d2   Simon Glass   x86: Add a script...
311
312
313
314
315
      else:
          parser.error("Unknown command '%s'" % cmd)
  
  if __name__ == "__main__":
      MicrocodeTool()