blob: f9f3420f346355b68c6cb925700ee56b08593392 [file] [log] [blame]
Tammo Spalink9a96b8a2012-04-03 11:10:41 +08001#!/usr/bin/python
2#
3# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Google Factory Tool.
8
9This tool is indended to be used on factory assembly lines. It
10provides all of the Google required test functionality and must be run
11on each device as part of the assembly process.
12"""
13
14
15import logging
16import os
17import re
18import sys
Hung-Te Lin6bd16472012-06-20 16:26:47 +080019import time
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080020
Tammo Spalink8fab5312012-05-28 18:33:30 +080021from tempfile import gettempdir, NamedTemporaryFile
Tammo Spalink86a61c62012-05-25 15:10:35 +080022
Tammo Spalinka40293e2012-07-04 14:58:56 +080023import factory_common # pylint: disable=W0611
24import cros.factory.gooftool.bmpblk
25import cros.factory.gooftool.crosfw
26import cros.factory.gooftool.probe
27import cros.factory.gooftool.report_upload
28import cros.factory.gooftool.vpd_data
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080029
Tammo Spalinka40293e2012-07-04 14:58:56 +080030from cros.factory.common import Error, ParseKeyValueData, SetupLogging, Shell
31from cros.factory.common import YamlRead, YamlWrite
32from cros.factory.hacked_argparse import CmdArg, Command, ParseCmdline
33from cros.factory.hacked_argparse import verbosity_cmd_arg
34from cros.factory.hwdb.hwid_tool import HardwareDb
Jon Salz83591782012-06-26 11:09:58 +080035from cros.factory.event_log import EventLog, EVENT_LOG_DIR
36from cros.factory.event_log import TimedUuid
cychiang7fe09372012-07-04 14:31:23 +080037from cros.factory.test.factory import FACTORY_LOG_PATH
Tammo Spalink86a61c62012-05-25 15:10:35 +080038
Tammo Spalink5c699832012-07-03 17:50:39 +080039
Tammo Spalink86a61c62012-05-25 15:10:35 +080040# Use a global event log, so that only a single log is created when
41# gooftool is called programmatically.
42_event_log = EventLog('gooftool')
43
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080044
45def GetPrimaryDevicePath(partition=None):
46 def IsFixed(dev):
47 sysfs_path = '/sys/block/%s/removable' % dev
48 return (os.path.exists(sysfs_path) and
49 open(sysfs_path).read().strip() == '0')
50 alpha_re = re.compile(r'^/dev/([a-zA-Z]+)[0-9]+$')
51 alnum_re = re.compile(r'^/dev/([a-zA-Z]+[0-9]+)p[0-9]+$')
cychiangde1dee22012-05-22 09:42:09 +080052 matched_alnum = False
53 dev_set = set()
54 for path in Shell('cgpt find -t rootfs').stdout.strip().split():
55 for dev in alpha_re.findall(path):
56 if IsFixed(dev):
57 dev_set.add(dev)
58 matched_alnum = False
59 for dev in alnum_re.findall(path):
60 if IsFixed(dev):
61 dev_set.add(dev)
62 matched_alnum = True
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080063 if len(dev_set) != 1:
64 raise Error('zero or multiple primary devs: %s' % dev_set)
65 dev_path = os.path.join('/dev', dev_set.pop())
66 if partition is None:
67 return dev_path
cychiangde1dee22012-05-22 09:42:09 +080068 fmt_str = '%sp%d' if matched_alnum else '%s%d'
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080069 return fmt_str % (dev_path, partition)
70
71
72def GetReleaseRootPartitionPath():
73 return GetPrimaryDevicePath(5)
74
75
76def GetReleaseKernelPartitionPath():
77 return GetPrimaryDevicePath(4)
78
79
80def FindScript(script_name):
Jon Salzd24269c2012-05-29 17:22:59 +080081 script_path = os.path.join(os.path.dirname(os.path.dirname(
82 os.path.realpath(__file__))), 'sh', script_name)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080083 if not os.path.exists(script_path):
84 raise Error('Needed script %s does not exist.' % script_path)
85 return script_path
86
87
Tammo Spalink86a61c62012-05-25 15:10:35 +080088def ReadVpd(fw_image_file, kind):
89 raw_vpd_data = Shell('vpd -i %s -l -f %s' % (kind, fw_image_file)).stdout
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080090 return ParseKeyValueData('"(.*)"="(.*)"$', raw_vpd_data)
91
92
Tammo Spalink86a61c62012-05-25 15:10:35 +080093def ReadRoVpd(fw_image_file):
94 return ReadVpd(fw_image_file, 'RO_VPD')
95
96
97def ReadRwVpd(fw_image_file):
98 return ReadVpd(fw_image_file, 'RW_VPD')
99
100
Tammo Spalink5c699832012-07-03 17:50:39 +0800101# TODO(tammo): Replace calls to sys.exit with raise Exit, and maybe
102# treat that specially (as a smoot exit, as opposed to the more
103# verbose output for generic Error).
104
105
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800106@Command('write_hwid',
107 CmdArg('hwid', metavar='HWID', help='HWID string'))
108def WriteHwid(options):
109 """Write specified HWID value into the system BB."""
110 logging.debug('writing hwid string %r', options.hwid)
111 main_fw = crosfw.LoadMainFirmware()
112 Shell('gbb_utility --set --hwid="%s" "%s"' %
113 (options.hwid, main_fw.GetFileName()))
114 main_fw.Write(sections=['GBB'])
Tammo Spalink86a61c62012-05-25 15:10:35 +0800115 _event_log.Log('write_hwid', hwid=options.hwid)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800116
117
Tammo Spalink8fab5312012-05-28 18:33:30 +0800118_hwdb_path_cmd_arg = CmdArg(
119 '--hwdb_path', metavar='PATH',
120 default=hwid_tool.DEFAULT_HWID_DATA_PATH,
121 help='Path to the HWID database.')
122
123
124@Command('probe_hwids',
125 _hwdb_path_cmd_arg,
126 CmdArg('-b', '--board', metavar='BOARD',
127 help='BOARD name', required=True),
128 CmdArg('--bom', metavar='BOM', help='BOM name'),
129 CmdArg('--variant', metavar='VARIANT', help='VARIANT code'),
Tammo Spalink5c699832012-07-03 17:50:39 +0800130 CmdArg('--optimistic', action='store_true',
131 help='do not probe; assume singletons match'),
132 CmdArg('--stdin_comp_map', action='store_true'),
133 CmdArg('--status', nargs='*', default=['supported'],
Tammo Spalink8fab5312012-05-28 18:33:30 +0800134 help='consider only HWIDs with this status'))
Tammo Spalink5c699832012-07-03 17:50:39 +0800135# TODO(tammo): Add a 'hw-only probe' option that does not probe firmware.
Tammo Spalink8fab5312012-05-28 18:33:30 +0800136def ProbeHwid(options):
137 """Determine a list of possible HWIDs using provided args and probeing.
138
139 VOLATILE can always be determined by probing. To get a unique
140 result, VARIANT must be specified for all cases where the matching
141 BOM has more than one associated variant code, otherwise all HWID
142 variants will be returned. Both VARIANT and BOM information can
Tammo Spalink5c699832012-07-03 17:50:39 +0800143 alternatively be specified using the --stdin_comp_map argument,
144 which allows specifying a list of
Tammo Spalink8fab5312012-05-28 18:33:30 +0800145
146 component-class: canonical-component-name
147
148 pairs on stdin, one per line (yaml format). Based on what is known
Tammo Spalink5c699832012-07-03 17:50:39 +0800149 from BOM and stdin_comp_map, determine a list of components to probe
150 for, and use those probe results to resolve a list of matching
151 HWIDs. If no boms, components, or variant codes are specified, then
152 a list of all HWIDs that match probable components will be returned.
Tammo Spalink8fab5312012-05-28 18:33:30 +0800153
154 Returns (on stdout): A list of HWIDs that match the available probe
155 results and argument contraints, one per line.
156 """
Tammo Spalink5c699832012-07-03 17:50:39 +0800157 map(hwid_tool.Validate.Status, options.status)
Tammo Spalink3a7e9022012-06-27 14:08:40 +0800158 hw_db = HardwareDb(options.data_path)
Tammo Spalink5c699832012-07-03 17:50:39 +0800159 comp_db = hw_db.comp_db
Tammo Spalink3a7e9022012-06-27 14:08:40 +0800160 device = hw_db.GetDevice(options.board)
Tammo Spalink5c699832012-07-03 17:50:39 +0800161 component_spec = hwid_tool.ComponentSpec.New()
Tammo Spalink8fab5312012-05-28 18:33:30 +0800162 if options.bom:
Tammo Spalink5c699832012-07-03 17:50:39 +0800163 device.BomExists(options.bom)
164 component_spec = CombineComponentSpecs(
165 component_spec, device.boms[options.bom].primary)
Tammo Spalink8fab5312012-05-28 18:33:30 +0800166 if options.variant:
Tammo Spalink5c699832012-07-03 17:50:39 +0800167 device.VariantExists(options.variant)
168 variant_spec = device.variants[options.variant]
169 if ComponentSpecsConflict(component_spec, variant_spec):
170 sys.exit('ERROR: multiple specifications for %r components'
171 ' (both VARIANT and BOM)' % comp_class)
172 component_spec = CombineComponentSpecs(component_spec, variant_spec)
173 if options.stdin_comp_map:
Tammo Spalink8fab5312012-05-28 18:33:30 +0800174 input_map = YamlRead(sys.stdin.read())
175 logging.info('stdin component map: %r', input_map)
Tammo Spalink5c699832012-07-03 17:50:39 +0800176 spec_classes = ComponentSpecClasses(component_spec)
Tammo Spalink8fab5312012-05-28 18:33:30 +0800177 for key, value in input_map.items():
Tammo Spalink5c699832012-07-03 17:50:39 +0800178 if key not in comp_db.all_comp_vlasses:
Tammo Spalink8fab5312012-05-28 18:33:30 +0800179 sys.exit('ERROR: unknown component class %r (from stdin)' % key)
Tammo Spalink5c699832012-07-03 17:50:39 +0800180 if value not in comp_db.all_comp_names:
Tammo Spalink8fab5312012-05-28 18:33:30 +0800181 sys.exit('ERROR: unkown component name %r (from stdin)' % value)
Tammo Spalink5c699832012-07-03 17:50:39 +0800182 if key in spec_classes:
Tammo Spalink8fab5312012-05-28 18:33:30 +0800183 sys.exit('ERROR: multiple specifications for %r components'
184 ' (stdin and BOM/VARIANT)' % key)
Tammo Spalink5c699832012-07-03 17:50:39 +0800185 component_spec.components.update(input_map)
186 logging.info('component spec used for matching:\n%s' % component_spec.Encode)
187 spec_classes = ComponentSpecClasses(component_spec)
188 missing_classes = list(set(comp_db.all_comp_classes) - spec_classes)
189 if missing_classes and not options.optimistic:
190 logging.info('probing for missing classes %s', ', '.join(missing_classes))
191 probe_results = probe.Probe(target_comp_classes=missing_classes,
192 probe_volatile=True, probe_initial_config=False)
193 else:
194 probe_results = hwid_tool.ProbeResults(
195 found_components=component_spec.components,
196 missing_component_classes=component_spec.classes_missing,
197 volatiles={}, initial_configs={})
198 cooked_results = hwid_tool.CookedProbeResults(comp_db, device, probe_results)
199 if not cooked_results.match_tree:
200 sys.exit('NO matching BOMs found')
201 if cooked_results.matched_hwids:
202 print '\n'.join(cooked_results.matched_hwids)
203 return
204 logging.info('exact HWID matching failed, but the following BOMs match: %s' %
205 ', '.join(sorted(cooked_results.match_tree)))
206 if options.optimistic and len(cooked_results.match_tree) == 1:
207 bom_name = set(cooked_results.match_tree).pop()
208 bom = device.boms[bom_name]
209 variant_matches = cooked_results.match_tree[bom_name]
210 if len(variant_matches) == 1:
211 var_code = set(variant_matches).pop()
212 elif len(bom.variants) == 1:
213 var_code = set(bom.variants).pop()
214 else:
215 sys.exit('NO matching HWIDs found; optimistic matching failed because '
216 'there were too many variants to choose from for BOM %r' %
217 bom_name)
218 print '\n'.join(
219 device.FmtHwid(bom_name, var_code, vol_code)
220 for vol_code in device.volatiles
221 if device.GetHwidStatus(bom_name, var_code, vol_code)
222 in options.status)
223 return
224 else:
225 logging.info('optimistic matching not attempted because either it was '
226 'not requested, or because the number of BOMs was <> 1')
227 print 'NO matching HWIDs found')
Tammo Spalink8fab5312012-05-28 18:33:30 +0800228
229
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800230@Command('probe',
231 CmdArg('--comps', nargs='*',
232 help='List of keys from the component_db registry.'),
233 CmdArg('--no_vol', action='store_true',
234 help='Do not probe volatile data.'),
235 CmdArg('--no_ic', action='store_true',
236 help='Do not probe initial_config data.'))
237def RunProbe(options):
238 """Print yaml-formatted breakdown of probed device properties."""
239 probe_results = probe.Probe(target_comp_classes=options.comps,
240 probe_volatile=not options.no_vol,
241 probe_initial_config=not options.no_ic)
Tammo Spalink3a7e9022012-06-27 14:08:40 +0800242 print probe_results.Encode()
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800243
244
Tammo Spalink214caf42012-05-28 10:45:00 +0800245@Command('verify_components',
246 _hwdb_path_cmd_arg,
Tammo Spalink5c699832012-07-03 17:50:39 +0800247 CmdArg('target_comps', nargs='*'))
Tammo Spalink214caf42012-05-28 10:45:00 +0800248def VerifyComponents(options):
249 """Verify that probable components all match entries in the component_db.
250
Tammo Spalink5c699832012-07-03 17:50:39 +0800251 Probe for each component class in the target_comps and verify
Tammo Spalink214caf42012-05-28 10:45:00 +0800252 that a corresponding match exists in the component_db -- make sure
253 that these components are present, that they have been approved, but
254 do not check against any specific BOM/HWID configurations.
255 """
Tammo Spalink5c699832012-07-03 17:50:39 +0800256 comp_db = hwid_tool.HardwareDb(options.hwdb_path).comp_db
257 if not options.target_comps:
258 sys.exit('ERROR: no target component classes specified; possible choices:\n'
259 + '\n '.join(sorted(comp_db.components)))
260 for comp_class in options.target_comps:
261 if comp_class not in comp_db.components:
262 sys.exit('ERROR: specified component class %r does not exist'
Tammo Spalink214caf42012-05-28 10:45:00 +0800263 ' in the component DB.' % comp_class)
Tammo Spalink5c699832012-07-03 17:50:39 +0800264 probe_results = probe.Probe(target_comp_classes=options.target_comps,
Tammo Spalink214caf42012-05-28 10:45:00 +0800265 probe_volatile=False, probe_initial_config=False)
Tammo Spalink214caf42012-05-28 10:45:00 +0800266 errors = []
267 matches = []
Tammo Spalink5c699832012-07-03 17:50:39 +0800268 for comp_class in sorted(options.target_comps):
Tammo Spalink214caf42012-05-28 10:45:00 +0800269 probe_val = probe_results.found_components.get(comp_class, None)
270 if probe_val is not None:
Tammo Spalink5c699832012-07-03 17:50:39 +0800271 comp_name = comp_db.result_name_map.get(probe_val, None)
Tammo Spalink214caf42012-05-28 10:45:00 +0800272 if comp_name is not None:
273 matches.append(comp_name)
274 else:
275 errors.append('unsupported %r component found with probe result'
276 ' %r (no matching name in the component DB)' %
277 (comp_class, probe_val))
278 else:
279 errors.append('missing %r component' % comp_class)
280 if errors:
281 print '\n'.join(errors)
282 sys.exit('component verification FAILURE')
283 else:
284 print 'component verification SUCCESS'
285 print 'found components:\n %s' % '\n '.join(matches)
286
287
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800288@Command('verify_hwid',
Tammo Spalink5c699832012-07-03 17:50:39 +0800289 CmdArg('--status', nargs='*', default=['supported'],
290 help='allow only HWIDs with these status values'),
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800291 _hwdb_path_cmd_arg)
292def VerifyHwid(options):
293 """Verify system HWID properties match probed device properties.
294
295 First probe components, volatile and initial_config parameters for
296 the DUT. Then use the available device data to produce a list of
297 candidate HWIDs. Then verify the HWID from the DUT is present in
298 that list. Then verify that the DUT initial config values match
299 those specified for its HWID. Finally, verify that VPD contains all
300 the necessary fields as specified by the board data, and when
301 possible verify that values are legitimate.
302 """
Tammo Spalink5c699832012-07-03 17:50:39 +0800303 def VerifyVpd(ro_vpd_keys):
304 ro_vpd = ReadRoVpd(main_fw_file)
305 for key in ro_vpd_keys:
306 if key not in ro_vpd:
307 sys.exit('Missing required VPD field: %s' % key)
308 known_valid_values = vpd_data.KNOWN_VPD_KEY_DATA.get(key, None)
309 value = ro_vpd[key]
310 if known_valid_values is not None and value not in known_valid_values:
311 sys.exit('Invalid VPD entry : key %r, value %r' % (key, value))
312 rw_vpd = ReadRwVpd(main_fw_file)
313 _event_log.Log('vpd', ro_vpd=ro_vpd, rw_vpd=rw_vpd)
314 map(hwid_tool.Validate.Status, options.status)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800315 main_fw_file = crosfw.LoadMainFirmware().GetFileName()
316 gbb_result = Shell('gbb_utility -g --hwid %s' % main_fw_file).stdout
317 hwid = re.findall(r'hardware_id:(.*)', gbb_result)[0].strip()
Tammo Spalink5c699832012-07-03 17:50:39 +0800318 hw_db = hwid_tool.HardwareDb(options.hwdb_path)
319 hwid_data = hw_db.GetHwidData(hwid)
320 logging.info('Verifying system HWID: %r', hwid_data.hwid)
321 if hwid_data.status not in options.status:
322 sys.exit('HWID status must be one of [%s], found %r' %
323 (', '.join(options.status, hwid_data.status)))
324 logging.debug('expected system properties:\n%s', hwid_data.Encode())
325 device = hw_db.GetDevice(hwid_data.board_name)
326 cooked_probe_results = hwid_tool.CookedProbeResults(
327 hw_db.comp_db, device, probe.Probe())
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800328 logging.debug('found system properties:\n%s',
329 YamlWrite(cooked_results.__dict__))
Tammo Spalink86a61c62012-05-25 15:10:35 +0800330 _event_log.Log(
Tammo Spalink5c699832012-07-03 17:50:39 +0800331 'probe',
332 found_components=cooked_results.found_components,
333 missing_component_classes=cooked_results.missing_component_classes,
334 volatiles=cooked_results.volatiles,
335 initial_configs=cooked_results.initial_configs)
336 if hwid not in cooked_probe_results.matched_hwids:
337 err_msg = 'HWID verification FAILED.\n'
338 if cooked_probe_results.unmatched_components:
339 sys.exit(err_msg + 'some components could not be indentified:\n%s' %
340 YamlWrite(cooked_probe_results.unmatched_components))
341 if not cooked_probe_results.match_tree:
342 sys.exit(err_msg + 'no matching boms were found for components:\n%s' %
343 cooked_probe_results.component_data.Encode())
344 if hwid.bom_name not in cooked_probe_results.match_tree:
345 sys.exit(err_msg + 'matching boms [%s] do not include target bom %r' %
346 (', '.join(sorted(cooked_probe_results.match_tree)),
347 hwid_data.bom))
348 err_msg += 'target bom %r matches components' % hwid_data.bom_name
349 if hwid.bom_name not in device.matched_ic_boms:
350 sys.exit(err_msg + ', but failed initial config verification')
351 match_tree = cooked_probe_results.match_tree
352 matched_variants = match_tree.get(hwid_data.bom_name, {})
353 if hwid.variant_code not in matched_variants:
354 sys.exit(err_msg + ', but target variant_code %r did not match' %
355 hwid_data.variant_code)
356 matched_volatiles = matched_variants.get(hwid_data.variant_code, {})
357 if hwid.volatile_code not in matched_volatiles:
358 sys.exit(err_msg + ', but target volatile_code %r did not match' %
359 hwid_data.volatile_code)
360 found_status = matched_volatiles.get(hwid_data.volatile_code, None)
361 sys.exit(err_msg + ', but hwid status %r was unacceptable' % found_status)
362 VerifyVpd(hwid_data.ro_vpds)
363 _event_log.Log('verified_hwid', hwid=hwid)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800364
365
366@Command('verify_keys')
367def VerifyKeys(options):
368 """Verify keys in firmware and SSD match."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800369 script = FindScript('verify_keys.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800370 kernel_device = GetReleaseKernelPartitionPath()
371 main_fw_file = crosfw.LoadMainFirmware().GetFileName()
372 result = Shell('%s %s %s' % (script, kernel_device, main_fw_file))
373 if not result.success:
374 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
375
376
377@Command('set_fw_bitmap_locale')
378def SetFirmwareBitmapLocale(options):
379 """Use VPD locale value to set firmware bitmap default language."""
380 image_file = crosfw.LoadMainFirmware().GetFileName()
381 locale = ReadRoVpd(image_file).get('initial_locale', None)
382 if locale is None:
383 raise Error, 'Missing initial_locale VPD.'
384 bitmap_locales = []
385 with NamedTemporaryFile() as f:
386 Shell('gbb_utility -g --bmpfv=%s %s' % (f.name, image_file))
387 bmpblk_data = bmpblk.unpack_bmpblock(f.read())
388 bitmap_locales = bmpblk_data.get('locales', bitmap_locales)
389 # Some locale values are just a language code and others are a
390 # hyphen-separated language code and country code pair. We care
391 # only about the language code part.
392 language_code = locale.partition('-')[0]
393 if language_code not in bitmap_locales:
394 raise Error, ('Firmware bitmaps do not contain support for the specified '
395 'initial locale language %r' % language_code)
396 else:
397 locale_index = bitmap_locales.index(language_code)
398 logging.info('Firmware bitmap initial locale set to %d (%s).',
399 locale_index, bitmap_locales[locale_index])
400 Shell('crossystem loc_idx=%d' % locale_index)
401
402
403@Command('verify_system_time')
404def VerifySystemTime(options):
405 """Verify system time is later than release filesystem creation time."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800406 script = FindScript('verify_system_time.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800407 rootfs_device = GetReleaseRootPartitionPath()
408 result = Shell('%s %s' % (script, rootfs_device))
409 if not result.success:
410 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
411
412
413@Command('verify_rootfs')
414def VerifyRootFs(options):
415 """Verify rootfs on SSD is valid by checking hash."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800416 script = FindScript('verify_rootfs.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800417 rootfs_device = GetReleaseRootPartitionPath()
418 result = Shell('%s %s' % (script, rootfs_device))
419 if not result.success:
420 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
421
422
423@Command('verify_switch_wp')
424def VerifyWpSwitch(options):
425 """Verify hardware write protection switch is enabled."""
426 if Shell('crossystem wpsw_cur').stdout.strip() != '1':
Hung-Te Lin6d827542012-07-19 11:50:41 +0800427 raise Error, 'write protection switch is disabled'
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800428
429
430@Command('verify_switch_dev')
431def VerifyDevSwitch(options):
432 """Verify developer switch is disabled."""
Hung-Te Lin8f643e32012-05-23 18:40:47 +0800433 result = Shell('crossystem devsw_cur')
434 if result.success:
435 if result.stdout.strip() != '0':
436 raise Error, 'developer mode is enabled'
437 else:
438 return
Hung-Te Lind1bec9a2012-07-09 16:45:03 +0800439 # devsw_cur is not available -- probably a device using keyboard-based
Hung-Te Lin6d827542012-07-19 11:50:41 +0800440 # developer/recovery mode. That will be handled in prepare_wipe.sh by
441 # setting "crossystem disable_dev_request=1" -- although we can't verify that
442 # until next reboot, because the real values are stored in TPM.
443 logging.warn('VerifyDevSwitch: No physical switch.')
444 _event_log.Log('switch_dev', type='virtual switch')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800445
446
447@Command('write_protect')
448def EnableFwWp(options):
449 """Enable then verify firmware write protection."""
450
451 def WriteProtect(fw_file_path, fw_type, section):
452 """Calculate protection size, then invoke flashrom.
453
454 Our supported chips only allow write protecting half their total
455 size, so we parition the flash chipset space accordingly.
456 """
457 raw_image = open(fw_file_path, 'rb').read()
458 image = crosfw.FirmwareImage(raw_image)
459 if not image.has_section(section):
460 raise Error('could not find %s firmware section %s' % (fw_type, section))
461 section_data = image.get_section_area(section)
462 protectable_size = len(raw_image) / 2
463 ro_a = int(section_data[0] / protectable_size)
464 ro_b = int((section_data[0] + section_data[1] - 1) / protectable_size)
465 if ro_a != ro_b:
466 raise Error("%s firmware section %s has illegal size" %
467 (fw_type, section))
468 ro_offset = ro_a * protectable_size
469 logging.debug('write protecting %s', fw_type)
470 crosfw.Flashrom(fw_type).EnableWriteProtection(ro_offset, protectable_size)
471
472 WriteProtect(crosfw.LoadMainFirmware().GetFileName(), 'main', 'RO_SECTION')
Tammo Spalink86a61c62012-05-25 15:10:35 +0800473 _event_log.Log('wp', fw='main')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800474 ec_fw_file = crosfw.LoadEcFirmware().GetFileName()
475 if ec_fw_file is not None:
Hung-Te Lin6d827542012-07-19 11:50:41 +0800476 # TODO(hungte) Support WP_RO if that section exist.
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800477 WriteProtect(ec_fw_file, 'ec', 'EC_RO')
Tammo Spalink86a61c62012-05-25 15:10:35 +0800478 _event_log.Log('wp', fw='ec')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800479 else:
480 logging.warning('EC not write protected (seems there is no EC flash).')
481
482
483@Command('clear_gbb_flags')
484def ClearGbbFlags(options):
485 """Zero out the GBB flags, in preparation for transition to release state.
486
487 No GBB flags are set in release/shipping state, but they are useful
488 for factory/development. See "gbb_utility --flags" for details.
489 """
Tammo Spalink461ddce2012-05-10 19:28:55 +0800490 script = FindScript('clear_gbb_flags.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800491 result = Shell(script)
492 if not result.success:
493 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
Tammo Spalink86a61c62012-05-25 15:10:35 +0800494 _event_log.Log('clear_gbb_flags')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800495
496
497@Command('prepare_wipe',
498 CmdArg('--fast', action='store_true',
499 help='use non-secure but faster wipe method.'))
500def PrepareWipe(options):
501 """Prepare system for transition to release state in next reboot."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800502 script = FindScript('prepare_wipe.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800503 tag = 'fast' if options.fast else ''
504 rootfs_device = GetReleaseRootPartitionPath()
505 result = Shell('FACTORY_WIPE_TAGS=%s %s %s' % (tag, script, rootfs_device))
506 if not result.success:
507 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
508
509
510@Command('verify',
Hung-Te Lin6d827542012-07-19 11:50:41 +0800511 CmdArg('--no_write_protect', action='store_true',
512 help='Do not check write protection switch state.'),
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800513 _hwdb_path_cmd_arg)
514def Verify(options):
515 """Verifies if whole factory process is ready for finalization.
516
517 This routine performs all the necessary checks to make sure the
518 device is ready to be finalized, but does not modify state. These
519 checks include dev switch, firmware write protection switch, hwid,
520 system time, keys, and root file system.
521 """
Hung-Te Lin6d827542012-07-19 11:50:41 +0800522 if not options.no_write_protect:
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800523 VerifyWpSwitch({})
Hung-Te Lin6d827542012-07-19 11:50:41 +0800524 VerifyDevSwitch({})
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800525 VerifyHwid(options)
526 VerifySystemTime({})
527 VerifyKeys({})
528 VerifyRootFs({})
529
530
Tammo Spalink86a61c62012-05-25 15:10:35 +0800531@Command('log_system_details')
532def LogSystemDetails(options):
533 """Write miscellaneous system details to the event log."""
534 raw_cs_data = Shell('crossystem').stdout.strip().splitlines()
535 # The crossytem output contains many lines like:
536 # 'key = value # description'
537 # Use regexps to pull out the key-value pairs and build a dict.
538 cs_data = dict((k, v.strip()) for k, v in
539 map(lambda x: re.findall(r'\A(\S+)\s+=\s+(.*)#.*\Z', x)[0],
540 raw_cs_data))
541 _event_log.Log(
542 'system_details',
543 platform_name=Shell('mosys platform name').stdout.strip(),
544 crossystem=cs_data,
545 modem_status=Shell('modem status').stdout.splitlines(),
546 ec_wp_status=Shell(
547 'flashrom -p internal:bus=lpc --get-size 2>/dev/null && '
548 'flashrom -p internal:bus=lpc --wp-status || '
549 'echo "EC is not available."').stdout,
550 bios_wp_status = Shell(
551 'flashrom -p internal:bus=spi --wp-status').stdout)
552
553
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800554_upload_method_cmd_arg = CmdArg(
555 '--upload_method', metavar='METHOD:PARAM',
556 help=('How to perform the upload. METHOD should be one of '
557 '{ftp, shopfloor, curl, cpfe, custom}.'))
558
559
560@Command('upload_report',
561 _upload_method_cmd_arg)
562def UploadReport(options):
563 """Create and a report containing key device details."""
Hung-Te Lin6bd16472012-06-20 16:26:47 +0800564 def NormalizeAsFileName(token):
565 return re.sub(r'\W+', '', token).strip()
Tammo Spalink86a61c62012-05-25 15:10:35 +0800566 ro_vpd = ReadRoVpd(crosfw.LoadMainFirmware().GetFileName())
567 device_sn = ro_vpd.get('serial_number', None)
568 if device_sn is None:
569 logging.warning('RO_VPD missing device serial number')
570 device_sn = 'MISSING_SN_' + TimedUuid()
Hung-Te Lin6bd16472012-06-20 16:26:47 +0800571 target_name = '%s_%s.tbz2' % (time.strftime('%Y%m%dT%H%M%SZ', time.gmtime()),
572 NormalizeAsFileName(device_sn))
Tammo Spalink86a61c62012-05-25 15:10:35 +0800573 target_path = os.path.join(gettempdir(), target_name)
574 # Intentionally ignoring dotfiles in EVENT_LOG_DIR.
575 tar_cmd = 'cd %s ; tar cjf %s *' % (EVENT_LOG_DIR, target_path)
576 tar_cmd += ' --add-file %s' % FACTORY_LOG_PATH
577 cmd_result = Shell(tar_cmd)
578 if not cmd_result.success:
579 raise Error('unable to tar event logs, cmd %r failed, stderr: %r' %
580 (tar_cmd, cmd_result.stderr))
581 if options.upload_method is None or options.upload_method == 'none':
582 logging.warning('REPORT UPLOAD SKIPPED (report left at %s)', target_path)
583 return
584 method, param = options.upload_method.split(':', 1)
585 if method == 'shopfloor':
586 report_upload.ShopFloorUpload(target_path, param)
587 elif method == 'ftp':
Jay Kim360c1dd2012-06-25 10:58:11 -0700588 report_upload.FtpUpload(target_path, 'ftp:' + param)
Tammo Spalink86a61c62012-05-25 15:10:35 +0800589 elif method == 'ftps':
590 report_upload.CurlUrlUpload(target_path, '--ftp-ssl-reqd ftp:%s' % param)
591 elif method == 'cpfe':
592 report_upload.CpfeUpload(target_path, param)
593 else:
594 raise Error('unknown report upload method %r', method)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800595
596
597@Command('finalize',
Hung-Te Lin6d827542012-07-19 11:50:41 +0800598 CmdArg('--no_write_protect', action='store_true',
599 help='Do not enable firmware write protection.'),
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800600 CmdArg('--fast', action='store_true',
601 help='use non-secure but faster wipe method.'),
602 _hwdb_path_cmd_arg,
603 _upload_method_cmd_arg)
604def Finalize(options):
605 """Verify system readiness and trigger transition into release state.
606
Hung-Te Lin6d827542012-07-19 11:50:41 +0800607 This routine first verifies system state (see verify command), modifies
608 firmware bitmaps to match locale, and then clears all of the factory-friendly
609 flags from the GBB. If everything is fine, it enables firmware write
610 protection (cannot rollback after this stage), uploads system logs & reports,
611 and sets the necessary boot flags to cause wipe of the factory image on the
612 next boot.
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800613 """
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800614 Verify(options)
615 SetFirmwareBitmapLocale({})
Hung-Te Lin6d827542012-07-19 11:50:41 +0800616 ClearGbbFlags({})
617 if options.no_write_protect:
618 logging.warn('WARNING: Firmware Write Protection is SKIPPED.')
619 _event_log.Log('wp', fw='both', status='skipped')
620 else:
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800621 EnableFwWp({})
Jon Salza0f58e02012-05-29 19:33:39 +0800622 LogSystemDetails(options)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800623 UploadReport(options)
624 PrepareWipe(options)
625
626
627def Main():
628 """Run sub-command specified by the command line args."""
629 options = ParseCmdline(
630 'Perform Google required factory tests.',
631 CmdArg('-l', '--log', metavar='PATH',
632 help='Write logs to this file.'),
Tammo Spalink8fab5312012-05-28 18:33:30 +0800633 verbosity_cmd_arg)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800634 SetupLogging(options.verbosity, options.log)
635 logging.debug('gooftool options: %s', repr(options))
636 try:
637 logging.debug('GOOFTOOL command %r', options.command_name)
638 options.command(options)
639 logging.info('GOOFTOOL command %r SUCCESS', options.command_name)
640 except Error, e:
641 logging.exception(e)
642 sys.exit('GOOFTOOL command %r ERROR: %s' % (options.command_name, e))
643 except Exception, e:
644 logging.exception(e)
645 sys.exit('UNCAUGHT RUNTIME EXCEPTION %s' % e)
646
647
648if __name__ == '__main__':
649 Main()