blob: 5738a50826924d18dde0dab252f180db155aa179 [file] [log] [blame]
Tammo Spalink9a96b8a2012-04-03 11:10:41 +08001#!/usr/bin/python
Tammo Spalink01e11722012-07-24 10:17:54 -07002# pylint: disable=E1101
Tammo Spalink9a96b8a2012-04-03 11:10:41 +08003#
4# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
5# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7
8"""Google Factory Tool.
9
10This tool is indended to be used on factory assembly lines. It
11provides all of the Google required test functionality and must be run
12on each device as part of the assembly process.
13"""
14
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080015import logging
16import os
Jon Salz65266432012-07-30 19:02:49 +080017import pipes
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080018import re
19import sys
Hung-Te Lin6bd16472012-06-20 16:26:47 +080020import time
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080021
Andy Cheng2582d292012-12-04 17:38:28 +080022from tempfile import gettempdir
Tammo Spalink86a61c62012-05-25 15:10:35 +080023
Tammo Spalinka40293e2012-07-04 14:58:56 +080024import factory_common # pylint: disable=W0611
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080025
Jon Salz0f8a6842012-09-25 11:28:22 +080026from cros.factory.common import Error, SetupLogging, Shell
Tammo Spalink394e4492012-08-01 10:20:30 -070027from cros.factory.common import YamlWrite
Tammo Spalink01e11722012-07-24 10:17:54 -070028from cros.factory.gooftool import crosfw
Andy Chengc531e2f2012-10-15 19:09:17 +080029from cros.factory.gooftool import Gooftool
Tammo Spalink01e11722012-07-24 10:17:54 -070030from cros.factory.gooftool import report_upload
Andy Cheng8ece7382012-08-22 16:25:42 +080031from cros.factory.gooftool.probe import Probe, PROBEABLE_COMPONENT_CLASSES
Jon Salz0f8a6842012-09-25 11:28:22 +080032from cros.factory.gooftool.probe import ReadRoVpd, ReadRwVpd
Jon Salz05aba9d2012-10-05 17:25:38 +080033from cros.factory.gooftool.vpd_data import KNOWN_VPD_FIELD_DATA, FilterVPD
Tammo Spalinka40293e2012-07-04 14:58:56 +080034from cros.factory.hacked_argparse import CmdArg, Command, ParseCmdline
35from cros.factory.hacked_argparse import verbosity_cmd_arg
Tammo Spalink01e11722012-07-24 10:17:54 -070036from cros.factory.hwdb import hwid_tool
Jon Salz83591782012-06-26 11:09:58 +080037from cros.factory.event_log import EventLog, EVENT_LOG_DIR
38from cros.factory.event_log import TimedUuid
cychiang7fe09372012-07-04 14:31:23 +080039from cros.factory.test.factory import FACTORY_LOG_PATH
Jon Salzff88c022012-11-03 12:19:58 +080040from cros.factory.utils.process_utils import Spawn
Tammo Spalink86a61c62012-05-25 15:10:35 +080041
Tammo Spalink5c699832012-07-03 17:50:39 +080042
Tammo Spalink86a61c62012-05-25 15:10:35 +080043# Use a global event log, so that only a single log is created when
44# gooftool is called programmatically.
45_event_log = EventLog('gooftool')
46
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080047
Tammo Spalink5c699832012-07-03 17:50:39 +080048# TODO(tammo): Replace calls to sys.exit with raise Exit, and maybe
49# treat that specially (as a smoot exit, as opposed to the more
50# verbose output for generic Error).
51
52
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080053@Command('write_hwid',
54 CmdArg('hwid', metavar='HWID', help='HWID string'))
Andy Chengc92e6f92012-11-20 16:55:53 +080055def WriteHWID(options):
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080056 """Write specified HWID value into the system BB."""
Andy Cheng7a76cb82012-11-19 18:08:19 +080057
Tammo Spalink95c43732012-07-25 15:57:14 -070058 logging.info('writing hwid string %r', options.hwid)
Andy Chengc92e6f92012-11-20 16:55:53 +080059 Gooftool().WriteHWID(options.hwid)
Tammo Spalink86a61c62012-05-25 15:10:35 +080060 _event_log.Log('write_hwid', hwid=options.hwid)
Tammo Spalink95c43732012-07-25 15:57:14 -070061 print 'Wrote HWID: %r' % options.hwid
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080062
63
Tammo Spalink8fab5312012-05-28 18:33:30 +080064_hwdb_path_cmd_arg = CmdArg(
65 '--hwdb_path', metavar='PATH',
66 default=hwid_tool.DEFAULT_HWID_DATA_PATH,
67 help='Path to the HWID database.')
68
Tammo Spalink95c43732012-07-25 15:57:14 -070069_hwid_status_list_cmd_arg = CmdArg(
70 '--status', nargs='*', default=['supported'],
71 help='allow only HWIDs with these status values')
72
Jon Salzce124fb2012-10-02 17:42:03 +080073_probe_results_cmd_arg = CmdArg(
74 '--probe_results', metavar='RESULTS.yaml',
75 help=('Output from "gooftool probe" (used instead of '
76 'probing this system).'))
77
78_hwid_cmd_arg = CmdArg(
79 '--hwid', metavar='HWID',
80 help=('HWID to verify (instead of the currently set HWID of '
81 'this system)'))
82
Tammo Spalink95c43732012-07-25 15:57:14 -070083
84@Command('best_match_hwids',
Tammo Spalink8fab5312012-05-28 18:33:30 +080085 _hwdb_path_cmd_arg,
86 CmdArg('-b', '--board', metavar='BOARD',
Tammo Spalink95c43732012-07-25 15:57:14 -070087 help='optional BOARD name, needed only if data is present '
88 'for more than one'),
Tammo Spalink8fab5312012-05-28 18:33:30 +080089 CmdArg('--bom', metavar='BOM', help='BOM name'),
90 CmdArg('--variant', metavar='VARIANT', help='VARIANT code'),
Tammo Spalink5c699832012-07-03 17:50:39 +080091 CmdArg('--optimistic', action='store_true',
92 help='do not probe; assume singletons match'),
Tammo Spalink95c43732012-07-25 15:57:14 -070093 CmdArg('--comps', nargs='*', default=[],
94 help='list of canonical component names'),
95 CmdArg('--missing', nargs='*', default=[],
96 help='list component classes to be assumed missing'),
Tammo Spalink5c699832012-07-03 17:50:39 +080097 CmdArg('--status', nargs='*', default=['supported'],
Tammo Spalink95c43732012-07-25 15:57:14 -070098 help='consider only HWIDs within this list of status values'))
99def BestMatchHwids(options):
Tammo Spalink8fab5312012-05-28 18:33:30 +0800100 """Determine a list of possible HWIDs using provided args and probeing.
101
102 VOLATILE can always be determined by probing. To get a unique
103 result, VARIANT must be specified for all cases where the matching
104 BOM has more than one associated variant code, otherwise all HWID
105 variants will be returned. Both VARIANT and BOM information can
Tammo Spalink95c43732012-07-25 15:57:14 -0700106 alternatively be specified using the --stdin_comps argument, which
107 allows specifying a list of canonical names (one per line) on stdin,
108 one per line. Based on what is known from BOM and stdin_comps,
109 determine a list of components to probe for, and use those probe
110 results to resolve a list of matching HWIDs. If no boms,
111 components, or variant codes are specified, then a list of all HWIDs
Andy Cheng8ece7382012-08-22 16:25:42 +0800112 that match probeable components will be returned.
Tammo Spalink8fab5312012-05-28 18:33:30 +0800113
114 Returns (on stdout): A list of HWIDs that match the available probe
115 results and argument contraints, one per line.
Tammo Spalink70b48a52012-08-08 16:54:51 -0700116
117 Example:
118
119 // Three ways to specify a keyboard (assuming it is a variant component)
120 gooftool best_match_hwids --missing keyboard
121 gooftool best_match_hwids --variant A or
122 gooftool best_match_hwids --comps us_kbd
Tammo Spalink8fab5312012-05-28 18:33:30 +0800123 """
Andy Cheng7a76cb82012-11-19 18:08:19 +0800124
Tammo Spalink5c699832012-07-03 17:50:39 +0800125 map(hwid_tool.Validate.Status, options.status)
Tammo Spalink95c43732012-07-25 15:57:14 -0700126 hw_db = hwid_tool.HardwareDb(options.hwdb_path)
Tammo Spalink5c699832012-07-03 17:50:39 +0800127 comp_db = hw_db.comp_db
Tammo Spalink3a7e9022012-06-27 14:08:40 +0800128 device = hw_db.GetDevice(options.board)
Tammo Spalink5c699832012-07-03 17:50:39 +0800129 component_spec = hwid_tool.ComponentSpec.New()
Tammo Spalink8fab5312012-05-28 18:33:30 +0800130 if options.bom:
Tammo Spalink5c699832012-07-03 17:50:39 +0800131 device.BomExists(options.bom)
Tammo Spalink01e11722012-07-24 10:17:54 -0700132 component_spec = hwid_tool.CombineComponentSpecs(
Tammo Spalink5c699832012-07-03 17:50:39 +0800133 component_spec, device.boms[options.bom].primary)
Tammo Spalink8fab5312012-05-28 18:33:30 +0800134 if options.variant:
Tammo Spalink5c699832012-07-03 17:50:39 +0800135 device.VariantExists(options.variant)
136 variant_spec = device.variants[options.variant]
Tammo Spalink01e11722012-07-24 10:17:54 -0700137 if hwid_tool.ComponentSpecsConflict(component_spec, variant_spec):
Tammo Spalink95c43732012-07-25 15:57:14 -0700138 sys.exit('ERROR: multiple specifications for these components:\n%s'
Tammo Spalink394e4492012-08-01 10:20:30 -0700139 % YamlWrite(sorted(
140 hwid_tool.ComponentSpecClasses(component_spec) &
141 hwid_tool.ComponentSpecClasses(variant_spec))))
Tammo Spalink01e11722012-07-24 10:17:54 -0700142 component_spec = hwid_tool.CombineComponentSpecs(
143 component_spec, variant_spec)
Tammo Spalink95c43732012-07-25 15:57:14 -0700144 if options.comps or options.missing:
145 map(comp_db.CompExists, options.comps)
146 map(comp_db.CompClassExists, options.missing)
147 extra_comp_spec = comp_db.CreateComponentSpec(
148 components=options.comps,
149 missing=options.missing)
150 print 'cmdline asserted components:\n%s' % extra_comp_spec.Encode()
151 if hwid_tool.ComponentSpecsConflict(component_spec, extra_comp_spec):
152 sys.exit('ERROR: multiple specifications for these components:\n%s'
Tammo Spalink394e4492012-08-01 10:20:30 -0700153 % YamlWrite(sorted(
154 hwid_tool.ComponentSpecClasses(component_spec) &
155 hwid_tool.ComponentSpecClasses(extra_comp_spec))))
Tammo Spalink95c43732012-07-25 15:57:14 -0700156 component_spec = hwid_tool.CombineComponentSpecs(
157 component_spec, extra_comp_spec)
Tammo Spalink01e11722012-07-24 10:17:54 -0700158 spec_classes = hwid_tool.ComponentSpecClasses(component_spec)
Tammo Spalink95c43732012-07-25 15:57:14 -0700159 missing_classes = set(comp_db.all_comp_classes) - spec_classes
Tammo Spalink5c699832012-07-03 17:50:39 +0800160 if missing_classes and not options.optimistic:
Andy Cheng8ece7382012-08-22 16:25:42 +0800161 non_probeable_missing = missing_classes - PROBEABLE_COMPONENT_CLASSES
162 if non_probeable_missing:
Tammo Spalink95c43732012-07-25 15:57:14 -0700163 sys.exit('FAILURE: these classes are necessary, were not specified '
Tammo Spalink70b48a52012-08-08 16:54:51 -0700164 'as inputs, and cannot be probed for:\n%s'
165 'This problem can often be addressed by specifying all of '
166 'the missing components on the command line (see the command '
Andy Cheng8ece7382012-08-22 16:25:42 +0800167 'help).' % YamlWrite(list(non_probeable_missing)))
Tammo Spalink95c43732012-07-25 15:57:14 -0700168 print 'probing for missing classes:'
169 print YamlWrite(list(missing_classes))
170 probe_results = Probe(target_comp_classes=list(missing_classes),
171 probe_volatile=False, probe_initial_config=False)
172 cooked_components = comp_db.MatchComponentProbeValues(
173 probe_results.found_probe_value_map)
174 if cooked_components.unmatched:
175 sys.exit('ERROR: some probed components are unrecognized:\n%s'
176 % YamlWrite(cooked_components.unmatched))
177 probed_comp_spec = comp_db.CreateComponentSpec(
178 components=cooked_components.matched,
179 missing=probe_results.missing_component_classes)
180 component_spec = hwid_tool.CombineComponentSpecs(
181 component_spec, probed_comp_spec)
182 print YamlWrite({'component data used for matching': {
183 'missing component classes': component_spec.classes_missing,
184 'found components': component_spec.components}})
185 component_data = hwid_tool.ComponentData(
186 extant_components=hwid_tool.ComponentSpecCompClassMap(
187 component_spec).keys(),
188 classes_missing=component_spec.classes_missing)
189 match_tree = device.BuildMatchTree(component_data)
190 if not match_tree:
191 sys.exit('FAILURE: NO matching BOMs found')
192 print 'potential BOMs/VARIANTs:'
193 potential_variants = set()
194 potential_volatiles = set()
195 for bom_name, variant_tree in match_tree.items():
196 print ' BOM: %-8s VARIANTS: %s' % (
197 bom_name, ', '.join(sorted(variant_tree)))
198 for variant_code in variant_tree:
199 potential_variants.add(variant_code)
200 for volatile_code in device.volatiles:
201 status = device.GetHwidStatus(bom_name, variant_code, volatile_code)
202 if status in options.status:
203 potential_volatiles.add(volatile_code)
204 print ''
205 if len(potential_variants) == 0:
206 sys.exit('FAILURE: no matching VARIANTs found')
207 if len(potential_volatiles) == 0:
208 sys.exit('FAILURE: no VOLATILEs found for potential matching BOMs/VARIANTS '
209 '(with specified status)')
Tammo Spalink394e4492012-08-01 10:20:30 -0700210 if (options.optimistic and
211 len(match_tree) == 1 and
212 len(potential_variants) == 1 and
213 len(potential_volatiles) == 1):
214 print ('MATCHING HWID: %s' % device.FmtHwid(match_tree.keys().pop(),
215 potential_variants.pop(),
216 potential_volatiles.pop()))
Tammo Spalink5c699832012-07-03 17:50:39 +0800217 return
Tammo Spalink95c43732012-07-25 15:57:14 -0700218 print ('probing VOLATILEs to resolve potential matches: %s\n' %
219 ', '.join(sorted(potential_volatiles)))
220 vol_probe_results = Probe(
221 target_comp_classes=[],
222 probe_volatile=True,
223 probe_initial_config=False)
224 cooked_volatiles = device.MatchVolatileValues(
225 vol_probe_results.found_volatile_values)
226 match_tree = device.BuildMatchTree(
227 component_data, cooked_volatiles.matched_tags)
228 matched_hwids = device.GetMatchTreeHwids(match_tree)
229 if matched_hwids:
230 for hwid in matched_hwids:
Ricky Liang02f3bd72012-09-21 14:33:01 +0800231 if matched_hwids[hwid] in options.status:
232 print 'MATCHING HWID: %s' % hwid
Tammo Spalink95c43732012-07-25 15:57:14 -0700233 return
234 print 'exact HWID matching failed, but the following BOMs match: %s' % (
235 ', '.join(sorted(match_tree)))
236 if options.optimistic and len(match_tree) == 1:
237 bom_name = set(match_tree).pop()
Tammo Spalink5c699832012-07-03 17:50:39 +0800238 bom = device.boms[bom_name]
Tammo Spalink95c43732012-07-25 15:57:14 -0700239 variant_matches = match_tree[bom_name]
Tammo Spalink5c699832012-07-03 17:50:39 +0800240 if len(variant_matches) == 1:
241 var_code = set(variant_matches).pop()
242 elif len(bom.variants) == 1:
243 var_code = set(bom.variants).pop()
244 else:
Tammo Spalink95c43732012-07-25 15:57:14 -0700245 sys.exit('FAILURE: NO matching HWIDs found; optimistic matching failed '
246 'because there were too many variants to choose from for BOM %r'
247 % bom_name)
248 hwids = [device.FmtHwid(bom_name, var_code, vol_code)
249 for vol_code in device.volatiles
250 if device.GetHwidStatus(bom_name, var_code, vol_code)
251 in options.status]
252 for hwid in hwids:
253 print 'MATCHING HWID: %s' % hwid
Tammo Spalink5c699832012-07-03 17:50:39 +0800254 return
255 else:
Tammo Spalink95c43732012-07-25 15:57:14 -0700256 print ('optimistic matching not attempted because either it was '
257 'not requested, or because the number of BOMs was <> 1\n')
258 sys.exit('FAILURE: NO matching HWIDs found')
Tammo Spalink8fab5312012-05-28 18:33:30 +0800259
260
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800261@Command('probe',
262 CmdArg('--comps', nargs='*',
263 help='List of keys from the component_db registry.'),
264 CmdArg('--no_vol', action='store_true',
265 help='Do not probe volatile data.'),
266 CmdArg('--no_ic', action='store_true',
Jon Salz0f8a6842012-09-25 11:28:22 +0800267 help='Do not probe initial_config data.'),
268 CmdArg('--include_vpd', action='store_true',
269 help='Include VPD data in volatiles.'))
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800270def RunProbe(options):
271 """Print yaml-formatted breakdown of probed device properties."""
Andy Chengc92e6f92012-11-20 16:55:53 +0800272 print Gooftool().Probe(target_comp_classes=options.comps,
273 probe_volatile=not options.no_vol,
274 probe_initial_config=not options.no_ic,
275 probe_vpd=options.include_vpd).Encode()
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800276
Tammo Spalink214caf42012-05-28 10:45:00 +0800277@Command('verify_components',
278 _hwdb_path_cmd_arg,
Tammo Spalink5c699832012-07-03 17:50:39 +0800279 CmdArg('target_comps', nargs='*'))
Tammo Spalink214caf42012-05-28 10:45:00 +0800280def VerifyComponents(options):
Andy Cheng8ece7382012-08-22 16:25:42 +0800281 """Verify that probeable components all match entries in the component_db.
Tammo Spalink214caf42012-05-28 10:45:00 +0800282
Tammo Spalink5c699832012-07-03 17:50:39 +0800283 Probe for each component class in the target_comps and verify
Tammo Spalink214caf42012-05-28 10:45:00 +0800284 that a corresponding match exists in the component_db -- make sure
285 that these components are present, that they have been approved, but
286 do not check against any specific BOM/HWID configurations.
287 """
Andy Cheng7a76cb82012-11-19 18:08:19 +0800288
Tammo Spalink5c699832012-07-03 17:50:39 +0800289 comp_db = hwid_tool.HardwareDb(options.hwdb_path).comp_db
Andy Chengc531e2f2012-10-15 19:09:17 +0800290 try:
291 result = Gooftool(component_db=comp_db).VerifyComponents(
292 options.target_comps)
293 except ValueError, e:
294 sys.exit(e)
295
296 # group by matches and errors
Tammo Spalink214caf42012-05-28 10:45:00 +0800297 matches = []
Andy Chengc531e2f2012-10-15 19:09:17 +0800298 errors = []
299 for result_list in result.values():
300 for component_name, _, error in result_list:
301 if component_name:
302 matches.append(component_name)
Tammo Spalink214caf42012-05-28 10:45:00 +0800303 else:
Andy Chengc531e2f2012-10-15 19:09:17 +0800304 errors.append(error)
305
Andy Cheng228a8c92012-08-27 10:53:57 +0800306 if matches:
307 print 'found probeable components:\n %s' % '\n '.join(matches)
Tammo Spalink214caf42012-05-28 10:45:00 +0800308 if errors:
Andy Cheng228a8c92012-08-27 10:53:57 +0800309 print '\nerrors:\n %s' % '\n '.join(errors)
310 sys.exit('\ncomponent verification FAILURE')
Tammo Spalink214caf42012-05-28 10:45:00 +0800311 else:
Andy Cheng228a8c92012-08-27 10:53:57 +0800312 print "\ncomponent verification SUCCESS"
Tammo Spalink214caf42012-05-28 10:45:00 +0800313
314
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800315@Command('verify_hwid',
Tammo Spalink95c43732012-07-25 15:57:14 -0700316 _hwid_status_list_cmd_arg,
Jon Salz0f8a6842012-09-25 11:28:22 +0800317 _hwdb_path_cmd_arg,
Jon Salzce124fb2012-10-02 17:42:03 +0800318 _probe_results_cmd_arg,
319 _hwid_cmd_arg)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800320def VerifyHwid(options):
321 """Verify system HWID properties match probed device properties.
322
323 First probe components, volatile and initial_config parameters for
324 the DUT. Then use the available device data to produce a list of
325 candidate HWIDs. Then verify the HWID from the DUT is present in
326 that list. Then verify that the DUT initial config values match
327 those specified for its HWID. Finally, verify that VPD contains all
328 the necessary fields as specified by the board data, and when
329 possible verify that values are legitimate.
330 """
Andy Cheng7a76cb82012-11-19 18:08:19 +0800331
Ricky Liangf7857c12012-09-17 13:34:41 +0800332 def VerifyVpd(ro_vpd_keys, rw_vpd_keys):
Tammo Spalink5c699832012-07-03 17:50:39 +0800333 for key in ro_vpd_keys:
334 if key not in ro_vpd:
Ricky Liangf7857c12012-09-17 13:34:41 +0800335 sys.exit('Missing required RO VPD field: %s' % key)
Tammo Spalink01e11722012-07-24 10:17:54 -0700336 known_valid_values = KNOWN_VPD_FIELD_DATA.get(key, None)
Tammo Spalink5c699832012-07-03 17:50:39 +0800337 value = ro_vpd[key]
Ricky Liangf7857c12012-09-17 13:34:41 +0800338 if (known_valid_values is not None) and (value not in known_valid_values):
339 sys.exit('Invalid RO VPD entry : key %r, value %r' % (key, value))
Ricky Liangf7857c12012-09-17 13:34:41 +0800340 for key in rw_vpd_keys:
341 if key not in rw_vpd:
342 sys.exit('Missing required RW VPD field: %s' % key)
343 known_valid_values = KNOWN_VPD_FIELD_DATA.get(key, None)
344 value = rw_vpd[key]
345 if (known_valid_values is not None) and (value not in known_valid_values):
346 sys.exit('Invalid RW VPD entry : key %r, value %r' % (key, value))
Jon Salz05aba9d2012-10-05 17:25:38 +0800347 _event_log.Log('vpd', ro_vpd=FilterVPD(ro_vpd), rw_vpd=FilterVPD(rw_vpd))
Tammo Spalink5c699832012-07-03 17:50:39 +0800348 map(hwid_tool.Validate.Status, options.status)
Jon Salz0f8a6842012-09-25 11:28:22 +0800349
Jon Salz81350812012-10-11 16:13:22 +0800350 if not options.hwid or not options.probe_results:
351 main_fw_file = crosfw.LoadMainFirmware().GetFileName()
Jon Salz0f8a6842012-09-25 11:28:22 +0800352
353 if options.hwid:
354 hwid_str = options.hwid
355 else:
356 gbb_result = Shell('gbb_utility -g --hwid %s' % main_fw_file).stdout
357 hwid_str = re.findall(r'hardware_id:(.*)', gbb_result)[0].strip()
Tammo Spalink95c43732012-07-25 15:57:14 -0700358 hwid = hwid_tool.ParseHwid(hwid_str)
Tammo Spalink5c699832012-07-03 17:50:39 +0800359 hw_db = hwid_tool.HardwareDb(options.hwdb_path)
Jon Salz0f8a6842012-09-25 11:28:22 +0800360 print 'Verifying HWID: %r\n' % hwid.hwid
Tammo Spalink95c43732012-07-25 15:57:14 -0700361 device = hw_db.GetDevice(hwid.board)
362 hwid_status = device.GetHwidStatus(hwid.bom, hwid.variant, hwid.volatile)
363 if hwid_status not in options.status:
Tammo Spalink5c699832012-07-03 17:50:39 +0800364 sys.exit('HWID status must be one of [%s], found %r' %
Hung-Te Lin4f5beb12012-08-20 14:48:06 +0800365 (', '.join(options.status), hwid_status))
Jon Salz0f8a6842012-09-25 11:28:22 +0800366 if options.probe_results:
367 # Pull in probe results (including VPD data) from the given file
368 # rather than probing the current system.
369 probe_results = hwid_tool.ProbeResults.Decode(
370 open(options.probe_results).read())
371 ro_vpd = {}
372 rw_vpd = {}
373 for k, v in probe_results.found_volatile_values.items():
374 match = re.match('^vpd\.(ro|rw)\.(\w+)$', k)
375 if match:
376 del probe_results.found_volatile_values[k]
377 (ro_vpd if match.group(1) == 'ro' else rw_vpd)[match.group(2)] = v
378 else:
379 probe_results = Probe()
380 ro_vpd = ReadRoVpd(main_fw_file)
381 rw_vpd = ReadRwVpd(main_fw_file)
Tammo Spalink95c43732012-07-25 15:57:14 -0700382 cooked_components = hw_db.comp_db.MatchComponentProbeValues(
383 probe_results.found_probe_value_map)
384 cooked_volatiles = device.MatchVolatileValues(
385 probe_results.found_volatile_values)
386 cooked_initial_configs = device.MatchInitialConfigValues(
387 probe_results.initial_configs)
388 component_data = hwid_tool.ComponentData(
389 extant_components=cooked_components.matched,
390 classes_missing=probe_results.missing_component_classes)
391 match_tree = device.BuildMatchTree(
392 component_data, cooked_volatiles.matched_tags)
393 matched_hwids = device.GetMatchTreeHwids(match_tree)
394 print 'HWID status: %s\n' % hwid_status
395 print 'probed system components:'
396 print YamlWrite(cooked_components.__dict__)
397 print 'missing component classes:'
398 print YamlWrite(probe_results.missing_component_classes)
399 print 'probed volatiles:'
400 print YamlWrite(cooked_volatiles.__dict__)
401 print 'probed initial_configs:'
402 print YamlWrite(cooked_initial_configs)
403 print 'hwid match tree:'
404 print YamlWrite(match_tree)
Tammo Spalink86a61c62012-05-25 15:10:35 +0800405 _event_log.Log(
Tammo Spalink5c699832012-07-03 17:50:39 +0800406 'probe',
Tammo Spalink95c43732012-07-25 15:57:14 -0700407 found_components=cooked_components.__dict__,
408 missing_component_classes=probe_results.missing_component_classes,
409 volatiles=cooked_volatiles.__dict__,
410 initial_configs=cooked_initial_configs)
411 if hwid.hwid not in matched_hwids:
Tammo Spalink5c699832012-07-03 17:50:39 +0800412 err_msg = 'HWID verification FAILED.\n'
Tammo Spalink95c43732012-07-25 15:57:14 -0700413 if cooked_components.unmatched:
Tammo Spalink5c699832012-07-03 17:50:39 +0800414 sys.exit(err_msg + 'some components could not be indentified:\n%s' %
Tammo Spalink95c43732012-07-25 15:57:14 -0700415 YamlWrite(cooked_components.unmatched))
416 if not match_tree:
Tammo Spalink5c699832012-07-03 17:50:39 +0800417 sys.exit(err_msg + 'no matching boms were found for components:\n%s' %
Tammo Spalink95c43732012-07-25 15:57:14 -0700418 component_data.Encode())
419 if hwid.bom not in match_tree:
Tammo Spalink5c699832012-07-03 17:50:39 +0800420 sys.exit(err_msg + 'matching boms [%s] do not include target bom %r' %
Tammo Spalink95c43732012-07-25 15:57:14 -0700421 (', '.join(sorted(match_tree)), hwid.bom))
422 err_msg += 'target bom %r matches components' % hwid.bom
423 if hwid.bom not in device.IntersectBomsAndInitialConfigs(
424 cooked_initial_configs):
Tammo Spalink5c699832012-07-03 17:50:39 +0800425 sys.exit(err_msg + ', but failed initial config verification')
Tammo Spalink95c43732012-07-25 15:57:14 -0700426 matched_variants = match_tree.get(hwid.bom, {})
427 if hwid.variant not in matched_variants:
Tammo Spalink5c699832012-07-03 17:50:39 +0800428 sys.exit(err_msg + ', but target variant_code %r did not match' %
Tammo Spalink95c43732012-07-25 15:57:14 -0700429 hwid.variant)
430 matched_volatiles = matched_variants.get(hwid.variant, {})
431 if hwid.volatile not in matched_volatiles:
Tammo Spalink5c699832012-07-03 17:50:39 +0800432 sys.exit(err_msg + ', but target volatile_code %r did not match' %
Tammo Spalink95c43732012-07-25 15:57:14 -0700433 hwid.volatile)
434 found_status = matched_volatiles.get(hwid.volatile, None)
Tammo Spalink5c699832012-07-03 17:50:39 +0800435 sys.exit(err_msg + ', but hwid status %r was unacceptable' % found_status)
Ricky Liangf7857c12012-09-17 13:34:41 +0800436 VerifyVpd(device.vpd_ro_fields, device.vpd_rw_fields)
Tammo Spalink5c699832012-07-03 17:50:39 +0800437 _event_log.Log('verified_hwid', hwid=hwid)
Tammo Spalink95c43732012-07-25 15:57:14 -0700438 print 'Verification SUCCESS!'
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800439
440
441@Command('verify_keys')
Tammo Spalink01e11722012-07-24 10:17:54 -0700442def VerifyKeys(options): # pylint: disable=W0613
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800443 """Verify keys in firmware and SSD match."""
Andy Cheng7a76cb82012-11-19 18:08:19 +0800444
445 return Gooftool().VerifyKeys()
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800446
447
448@Command('set_fw_bitmap_locale')
Tammo Spalink01e11722012-07-24 10:17:54 -0700449def SetFirmwareBitmapLocale(options): # pylint: disable=W0613
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800450 """Use VPD locale value to set firmware bitmap default language."""
Andy Cheng7a76cb82012-11-19 18:08:19 +0800451
Andy Cheng2582d292012-12-04 17:38:28 +0800452 (index, locale) = Gooftool().SetFirmwareBitmapLocale()
453 logging.info('Firmware bitmap initial locale set to %d (%s).',
454 index, locale)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800455
456
457@Command('verify_system_time')
Tammo Spalink01e11722012-07-24 10:17:54 -0700458def VerifySystemTime(options): # pylint: disable=W0613
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800459 """Verify system time is later than release filesystem creation time."""
Andy Cheng7a76cb82012-11-19 18:08:19 +0800460
461 return Gooftool().VerifySystemTime()
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800462
463
464@Command('verify_rootfs')
Tammo Spalink01e11722012-07-24 10:17:54 -0700465def VerifyRootFs(options): # pylint: disable=W0613
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800466 """Verify rootfs on SSD is valid by checking hash."""
Andy Cheng7a76cb82012-11-19 18:08:19 +0800467
468 return Gooftool().VerifyRootFs()
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800469
470
471@Command('verify_switch_wp')
Andy Chengc92e6f92012-11-20 16:55:53 +0800472def VerifyWPSwitch(options): # pylint: disable=W0613
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800473 """Verify hardware write protection switch is enabled."""
Andy Cheng7a76cb82012-11-19 18:08:19 +0800474
Andy Chengc92e6f92012-11-20 16:55:53 +0800475 Gooftool().VerifyWPSwitch()
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800476
477
478@Command('verify_switch_dev')
Tammo Spalink01e11722012-07-24 10:17:54 -0700479def VerifyDevSwitch(options): # pylint: disable=W0613
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800480 """Verify developer switch is disabled."""
Andy Cheng7a76cb82012-11-19 18:08:19 +0800481
Andy Chengaf30a1b2012-12-03 16:50:09 +0800482 if Gooftool().CheckDevSwitchForDisabling():
Hung-Te Lind7d34722012-07-26 16:48:35 +0800483 logging.warn('VerifyDevSwitch: No physical switch.')
484 _event_log.Log('switch_dev', type='virtual switch')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800485
486
487@Command('write_protect')
Tammo Spalink01e11722012-07-24 10:17:54 -0700488def EnableFwWp(options): # pylint: disable=W0613
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800489 """Enable then verify firmware write protection."""
490
Hung-Te Linb21c6682012-08-01 13:53:57 +0800491 def CalculateLegacyRange(fw_type, length, section_data,
492 section_name):
Hung-Te Lin7ea39e82012-07-31 18:39:33 +0800493 ro_size = length / 2
494 ro_a = int(section_data[0] / ro_size)
495 ro_b = int((section_data[0] + section_data[1] - 1) / ro_size)
496 if ro_a != ro_b:
497 raise Error("%s firmware section %s has illegal size" %
Hung-Te Linb21c6682012-08-01 13:53:57 +0800498 (fw_type, section_name))
Hung-Te Lin7ea39e82012-07-31 18:39:33 +0800499 ro_offset = ro_a * ro_size
500 return (ro_offset, ro_size)
501
502 def WriteProtect(fw_file_path, fw_type, legacy_section):
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800503 """Calculate protection size, then invoke flashrom.
504
505 Our supported chips only allow write protecting half their total
506 size, so we parition the flash chipset space accordingly.
507 """
Andy Cheng7a76cb82012-11-19 18:08:19 +0800508
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800509 raw_image = open(fw_file_path, 'rb').read()
Hung-Te Lin7ea39e82012-07-31 18:39:33 +0800510 wp_section = 'WP_RO'
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800511 image = crosfw.FirmwareImage(raw_image)
Hung-Te Lin7ea39e82012-07-31 18:39:33 +0800512 if image.has_section(wp_section):
513 section_data = image.get_section_area(wp_section)
514 ro_offset = section_data[0]
515 ro_size = section_data[1]
516 elif image.has_section(legacy_section):
517 section_data = image.get_section_area(legacy_section)
518 (ro_offset, ro_size) = CalculateLegacyRange(
Hung-Te Linb21c6682012-08-01 13:53:57 +0800519 fw_type, len(raw_image), section_data, legacy_section)
Hung-Te Lin7ea39e82012-07-31 18:39:33 +0800520 else:
521 raise Error('could not find %s firmware section %s or %s' %
522 (fw_type, wp_section, legacy_section))
523
524 logging.debug('write protecting %s [off=%x size=%x]', fw_type,
525 ro_offset, ro_size)
526 crosfw.Flashrom(fw_type).EnableWriteProtection(ro_offset, ro_size)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800527
528 WriteProtect(crosfw.LoadMainFirmware().GetFileName(), 'main', 'RO_SECTION')
Tammo Spalink86a61c62012-05-25 15:10:35 +0800529 _event_log.Log('wp', fw='main')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800530 ec_fw_file = crosfw.LoadEcFirmware().GetFileName()
531 if ec_fw_file is not None:
532 WriteProtect(ec_fw_file, 'ec', 'EC_RO')
Tammo Spalink86a61c62012-05-25 15:10:35 +0800533 _event_log.Log('wp', fw='ec')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800534 else:
535 logging.warning('EC not write protected (seems there is no EC flash).')
536
537
538@Command('clear_gbb_flags')
Andy Chengc92e6f92012-11-20 16:55:53 +0800539def ClearGBBFlags(options): # pylint: disable=W0613
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800540 """Zero out the GBB flags, in preparation for transition to release state.
541
542 No GBB flags are set in release/shipping state, but they are useful
543 for factory/development. See "gbb_utility --flags" for details.
544 """
Andy Cheng7a76cb82012-11-19 18:08:19 +0800545
Andy Chengc92e6f92012-11-20 16:55:53 +0800546 Gooftool().ClearGBBFlags()
Tammo Spalink86a61c62012-05-25 15:10:35 +0800547 _event_log.Log('clear_gbb_flags')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800548
549
550@Command('prepare_wipe',
551 CmdArg('--fast', action='store_true',
552 help='use non-secure but faster wipe method.'))
553def PrepareWipe(options):
554 """Prepare system for transition to release state in next reboot."""
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800555
Andy Cheng7a76cb82012-11-19 18:08:19 +0800556 Gooftool().PrepareWipe(options.fast)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800557
558@Command('verify',
Hung-Te Lin6d827542012-07-19 11:50:41 +0800559 CmdArg('--no_write_protect', action='store_true',
560 help='Do not check write protection switch state.'),
Tammo Spalink95c43732012-07-25 15:57:14 -0700561 _hwid_status_list_cmd_arg,
Jon Salzce124fb2012-10-02 17:42:03 +0800562 _hwdb_path_cmd_arg,
563 _probe_results_cmd_arg,
564 _hwid_cmd_arg)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800565def Verify(options):
566 """Verifies if whole factory process is ready for finalization.
567
568 This routine performs all the necessary checks to make sure the
569 device is ready to be finalized, but does not modify state. These
570 checks include dev switch, firmware write protection switch, hwid,
571 system time, keys, and root file system.
572 """
Andy Cheng7a76cb82012-11-19 18:08:19 +0800573
Hung-Te Lin6d827542012-07-19 11:50:41 +0800574 if not options.no_write_protect:
Andy Chengc92e6f92012-11-20 16:55:53 +0800575 VerifyWPSwitch({})
Hung-Te Lin6d827542012-07-19 11:50:41 +0800576 VerifyDevSwitch({})
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800577 VerifyHwid(options)
578 VerifySystemTime({})
579 VerifyKeys({})
580 VerifyRootFs({})
581
582
Tammo Spalink86a61c62012-05-25 15:10:35 +0800583@Command('log_system_details')
Tammo Spalink01e11722012-07-24 10:17:54 -0700584def LogSystemDetails(options): # pylint: disable=W0613
Tammo Spalink86a61c62012-05-25 15:10:35 +0800585 """Write miscellaneous system details to the event log."""
Andy Cheng7a76cb82012-11-19 18:08:19 +0800586
Andy Cheng5b0e9882012-12-10 12:43:18 +0800587 _event_log.Log('system_details', **Gooftool().GetSystemDetails())
Tammo Spalink86a61c62012-05-25 15:10:35 +0800588
589
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800590_upload_method_cmd_arg = CmdArg(
591 '--upload_method', metavar='METHOD:PARAM',
592 help=('How to perform the upload. METHOD should be one of '
cychiang3b15bd52012-09-08 13:58:18 +0800593 '{ftp, shopfloor, ftps, cpfe}.'))
Jon Salz65266432012-07-30 19:02:49 +0800594_add_file_cmd_arg = CmdArg(
595 '--add_file', metavar='FILE', action='append',
596 help='Extra file to include in report (must be an absolute path)')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800597
598@Command('upload_report',
Jon Salz65266432012-07-30 19:02:49 +0800599 _upload_method_cmd_arg,
600 _add_file_cmd_arg)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800601def UploadReport(options):
602 """Create and a report containing key device details."""
Andy Cheng7a76cb82012-11-19 18:08:19 +0800603
Hung-Te Lin6bd16472012-06-20 16:26:47 +0800604 def NormalizeAsFileName(token):
605 return re.sub(r'\W+', '', token).strip()
Tammo Spalink86a61c62012-05-25 15:10:35 +0800606 ro_vpd = ReadRoVpd(crosfw.LoadMainFirmware().GetFileName())
607 device_sn = ro_vpd.get('serial_number', None)
608 if device_sn is None:
609 logging.warning('RO_VPD missing device serial number')
610 device_sn = 'MISSING_SN_' + TimedUuid()
Vic Yang85199e72013-01-28 14:33:11 +0800611 target_name = '%s_%s.tar.xz' % (time.strftime('%Y%m%dT%H%M%SZ',
612 time.gmtime()),
613 NormalizeAsFileName(device_sn))
Tammo Spalink86a61c62012-05-25 15:10:35 +0800614 target_path = os.path.join(gettempdir(), target_name)
615 # Intentionally ignoring dotfiles in EVENT_LOG_DIR.
Vic Yang85199e72013-01-28 14:33:11 +0800616 tar_cmd = 'cd %s ; tar cJf %s *' % (EVENT_LOG_DIR, target_path)
Tammo Spalink86a61c62012-05-25 15:10:35 +0800617 tar_cmd += ' --add-file %s' % FACTORY_LOG_PATH
Jon Salz65266432012-07-30 19:02:49 +0800618 if options.add_file:
619 for f in options.add_file:
620 # Require absolute paths since the tar command may change the
621 # directory.
622 if not f.startswith('/'):
623 raise Error('Not an absolute path: %s' % f)
624 if not os.path.exists(f):
625 raise Error('File does not exist: %s' % f)
626 tar_cmd += ' --add-file %s' % pipes.quote(f)
Tammo Spalink86a61c62012-05-25 15:10:35 +0800627 cmd_result = Shell(tar_cmd)
Jon Salzff88c022012-11-03 12:19:58 +0800628
629 if ((cmd_result.status == 1) and
630 all((x == '' or
631 'file changed as we read it' in x or
632 "Removing leading `/' from member names" in x)
633 for x in cmd_result.stderr.split('\n'))):
634 # That's OK. Make sure it's valid though.
Vic Yang85199e72013-01-28 14:33:11 +0800635 Spawn(['tar', 'tfJ', target_path], check_call=True, log=True,
Jon Salzff88c022012-11-03 12:19:58 +0800636 ignore_stdout=True)
637 elif not cmd_result.success:
Tammo Spalink86a61c62012-05-25 15:10:35 +0800638 raise Error('unable to tar event logs, cmd %r failed, stderr: %r' %
639 (tar_cmd, cmd_result.stderr))
Jon Salzff88c022012-11-03 12:19:58 +0800640
Tammo Spalink86a61c62012-05-25 15:10:35 +0800641 if options.upload_method is None or options.upload_method == 'none':
642 logging.warning('REPORT UPLOAD SKIPPED (report left at %s)', target_path)
643 return
644 method, param = options.upload_method.split(':', 1)
645 if method == 'shopfloor':
646 report_upload.ShopFloorUpload(target_path, param)
647 elif method == 'ftp':
Jay Kim360c1dd2012-06-25 10:58:11 -0700648 report_upload.FtpUpload(target_path, 'ftp:' + param)
Tammo Spalink86a61c62012-05-25 15:10:35 +0800649 elif method == 'ftps':
650 report_upload.CurlUrlUpload(target_path, '--ftp-ssl-reqd ftp:%s' % param)
651 elif method == 'cpfe':
652 report_upload.CpfeUpload(target_path, param)
653 else:
654 raise Error('unknown report upload method %r', method)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800655
656
657@Command('finalize',
Hung-Te Lin6d827542012-07-19 11:50:41 +0800658 CmdArg('--no_write_protect', action='store_true',
659 help='Do not enable firmware write protection.'),
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800660 CmdArg('--fast', action='store_true',
661 help='use non-secure but faster wipe method.'),
662 _hwdb_path_cmd_arg,
Tammo Spalink95c43732012-07-25 15:57:14 -0700663 _hwid_status_list_cmd_arg,
Jon Salz65266432012-07-30 19:02:49 +0800664 _upload_method_cmd_arg,
Jon Salzce124fb2012-10-02 17:42:03 +0800665 _add_file_cmd_arg,
666 _probe_results_cmd_arg,
667 _hwid_cmd_arg)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800668def Finalize(options):
669 """Verify system readiness and trigger transition into release state.
670
Hung-Te Lin6d827542012-07-19 11:50:41 +0800671 This routine first verifies system state (see verify command), modifies
672 firmware bitmaps to match locale, and then clears all of the factory-friendly
673 flags from the GBB. If everything is fine, it enables firmware write
674 protection (cannot rollback after this stage), uploads system logs & reports,
675 and sets the necessary boot flags to cause wipe of the factory image on the
676 next boot.
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800677 """
Andy Cheng7a76cb82012-11-19 18:08:19 +0800678
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800679 Verify(options)
680 SetFirmwareBitmapLocale({})
Andy Chengc92e6f92012-11-20 16:55:53 +0800681 ClearGBBFlags({})
Hung-Te Lin6d827542012-07-19 11:50:41 +0800682 if options.no_write_protect:
683 logging.warn('WARNING: Firmware Write Protection is SKIPPED.')
684 _event_log.Log('wp', fw='both', status='skipped')
685 else:
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800686 EnableFwWp({})
Jon Salza0f58e02012-05-29 19:33:39 +0800687 LogSystemDetails(options)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800688 UploadReport(options)
689 PrepareWipe(options)
690
691
692def Main():
693 """Run sub-command specified by the command line args."""
Andy Cheng7a76cb82012-11-19 18:08:19 +0800694
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800695 options = ParseCmdline(
696 'Perform Google required factory tests.',
697 CmdArg('-l', '--log', metavar='PATH',
698 help='Write logs to this file.'),
Jon Salza4bea382012-10-29 13:00:34 +0800699 CmdArg('--suppress-event-logs', action='store_true',
700 help='Suppress event logging.'),
Tammo Spalink8fab5312012-05-28 18:33:30 +0800701 verbosity_cmd_arg)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800702 SetupLogging(options.verbosity, options.log)
Jon Salza4bea382012-10-29 13:00:34 +0800703 _event_log.suppress = options.suppress_event_logs
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800704 logging.debug('gooftool options: %s', repr(options))
705 try:
706 logging.debug('GOOFTOOL command %r', options.command_name)
707 options.command(options)
708 logging.info('GOOFTOOL command %r SUCCESS', options.command_name)
709 except Error, e:
710 logging.exception(e)
711 sys.exit('GOOFTOOL command %r ERROR: %s' % (options.command_name, e))
712 except Exception, e:
713 logging.exception(e)
714 sys.exit('UNCAUGHT RUNTIME EXCEPTION %s' % e)
715
716
717if __name__ == '__main__':
718 Main()