blob: e1ceb286b2d295665ed4d62bf2a13c00b27b32b7 [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 Spalink9a96b8a2012-04-03 11:10:41 +080023import bmpblk
24import crosfw
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080025import probe
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080026import report_upload
27import vpd_data
28
Tammo Spalink8fab5312012-05-28 18:33:30 +080029from common import Error, ParseKeyValueData, SetupLogging, Shell
30from common import YamlRead, YamlWrite
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080031from hacked_argparse import CmdArg, Command, ParseCmdline, verbosity_cmd_arg
Tammo Spalink3a7e9022012-06-27 14:08:40 +080032from hwid_tool import HardwareDb
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080033
cychiang7fe09372012-07-04 14:31:23 +080034import factory_common
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
39# Use a global event log, so that only a single log is created when
40# gooftool is called programmatically.
41_event_log = EventLog('gooftool')
42
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080043
44def GetPrimaryDevicePath(partition=None):
45 def IsFixed(dev):
46 sysfs_path = '/sys/block/%s/removable' % dev
47 return (os.path.exists(sysfs_path) and
48 open(sysfs_path).read().strip() == '0')
49 alpha_re = re.compile(r'^/dev/([a-zA-Z]+)[0-9]+$')
50 alnum_re = re.compile(r'^/dev/([a-zA-Z]+[0-9]+)p[0-9]+$')
cychiangde1dee22012-05-22 09:42:09 +080051 matched_alnum = False
52 dev_set = set()
53 for path in Shell('cgpt find -t rootfs').stdout.strip().split():
54 for dev in alpha_re.findall(path):
55 if IsFixed(dev):
56 dev_set.add(dev)
57 matched_alnum = False
58 for dev in alnum_re.findall(path):
59 if IsFixed(dev):
60 dev_set.add(dev)
61 matched_alnum = True
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080062 if len(dev_set) != 1:
63 raise Error('zero or multiple primary devs: %s' % dev_set)
64 dev_path = os.path.join('/dev', dev_set.pop())
65 if partition is None:
66 return dev_path
cychiangde1dee22012-05-22 09:42:09 +080067 fmt_str = '%sp%d' if matched_alnum else '%s%d'
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080068 return fmt_str % (dev_path, partition)
69
70
71def GetReleaseRootPartitionPath():
72 return GetPrimaryDevicePath(5)
73
74
75def GetReleaseKernelPartitionPath():
76 return GetPrimaryDevicePath(4)
77
78
79def FindScript(script_name):
Jon Salzd24269c2012-05-29 17:22:59 +080080 script_path = os.path.join(os.path.dirname(os.path.dirname(
81 os.path.realpath(__file__))), 'sh', script_name)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080082 if not os.path.exists(script_path):
83 raise Error('Needed script %s does not exist.' % script_path)
84 return script_path
85
86
Tammo Spalink86a61c62012-05-25 15:10:35 +080087def ReadVpd(fw_image_file, kind):
88 raw_vpd_data = Shell('vpd -i %s -l -f %s' % (kind, fw_image_file)).stdout
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080089 return ParseKeyValueData('"(.*)"="(.*)"$', raw_vpd_data)
90
91
Tammo Spalink86a61c62012-05-25 15:10:35 +080092def ReadRoVpd(fw_image_file):
93 return ReadVpd(fw_image_file, 'RO_VPD')
94
95
96def ReadRwVpd(fw_image_file):
97 return ReadVpd(fw_image_file, 'RW_VPD')
98
99
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800100@Command('write_hwid',
101 CmdArg('hwid', metavar='HWID', help='HWID string'))
102def WriteHwid(options):
103 """Write specified HWID value into the system BB."""
104 logging.debug('writing hwid string %r', options.hwid)
105 main_fw = crosfw.LoadMainFirmware()
106 Shell('gbb_utility --set --hwid="%s" "%s"' %
107 (options.hwid, main_fw.GetFileName()))
108 main_fw.Write(sections=['GBB'])
Tammo Spalink86a61c62012-05-25 15:10:35 +0800109 _event_log.Log('write_hwid', hwid=options.hwid)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800110
111
Tammo Spalink8fab5312012-05-28 18:33:30 +0800112_hwdb_path_cmd_arg = CmdArg(
113 '--hwdb_path', metavar='PATH',
114 default=hwid_tool.DEFAULT_HWID_DATA_PATH,
115 help='Path to the HWID database.')
116
117
118@Command('probe_hwids',
119 _hwdb_path_cmd_arg,
120 CmdArg('-b', '--board', metavar='BOARD',
121 help='BOARD name', required=True),
122 CmdArg('--bom', metavar='BOM', help='BOM name'),
123 CmdArg('--variant', metavar='VARIANT', help='VARIANT code'),
124 CmdArg('--comp_map', action='store_true'),
125 CmdArg('--status', nargs='*',
126 help='consider only HWIDs with this status'))
127def ProbeHwid(options):
128 """Determine a list of possible HWIDs using provided args and probeing.
129
130 VOLATILE can always be determined by probing. To get a unique
131 result, VARIANT must be specified for all cases where the matching
132 BOM has more than one associated variant code, otherwise all HWID
133 variants will be returned. Both VARIANT and BOM information can
134 alternatively be specified using the --comp_map argument, which
135 allows specifying a list of
136
137 component-class: canonical-component-name
138
139 pairs on stdin, one per line (yaml format). Based on what is known
140 from BOM and comp_map, determine a list of components to probe for,
141 and use those probe results to resolve a list of matching HWIDs. If
142 no boms, components, or variant codes are specified, then a list of
143 all HWIDs that match probable components will be returned.
144
145 Returns (on stdout): A list of HWIDs that match the available probe
146 results and argument contraints, one per line.
147 """
Tammo Spalink3a7e9022012-06-27 14:08:40 +0800148 hw_db = HardwareDb(options.data_path)
149 device = hw_db.GetDevice(options.board)
Tammo Spalink8fab5312012-05-28 18:33:30 +0800150 component_map = {}
151 if options.bom:
152 bom_details = device.hwid_map.get(options.bom, None)
153 if bom_details is None:
154 sys.exit('ERROR: unkown bom %r for board %r' %
155 (options.bom, options.board))
156 component_map.update(bom_details.component_map)
157 comp_db_class_map = hwid_tool.CalcCompDbClassMap(hwdb.comp_db)
158 if options.variant:
159 variant_details = device.variant_map.get(options.variant, None)
160 if options.variant is None:
161 sys.exit('ERROR: unknown variant code %r for board %r' %
162 (options.variant, options.board))
163 for comp_name in variant_details:
164 comp_class = comp_db_class_map[comp_name]
165 if comp_class in component_map:
166 sys.exit('ERROR: multiple specifications for %r components'
167 ' (both VARIANT and BOM)' % comp_class)
168 component_map[comp_class] = comp_name
169 if options.comp_map:
170 input_map = YamlRead(sys.stdin.read())
171 logging.info('stdin component map: %r', input_map)
172 for key, value in input_map.items():
173 if key not in hwdb.comp_db.registry:
174 sys.exit('ERROR: unknown component class %r (from stdin)' % key)
175 if value not in comp_db_class_map:
176 sys.exit('ERROR: unkown component name %r (from stdin)' % value)
177 if key in component_map:
178 sys.exit('ERROR: multiple specifications for %r components'
179 ' (stdin and BOM/VARIANT)' % key)
180 component_map[key] = value
181 missing_classes = list(set(hwdb.comp_db.registry) - set(component_map))
182 if missing_classes:
183 logging.info('probing for %s', ', '.join(missing_classes))
184 probe_results = probe.Probe(target_comp_classes=missing_classes,
185 probe_volatile=True, probe_initial_config=False)
186 cooked_results = hwid_tool.CookProbeResults(
187 hwdb, probe_results, options.board)
188 cooked_results.matched_components.update(component_map)
189 status_set = set(options.status) if options.status else set(['supported'])
190 hwid_set = hwid_tool.MatchHwids(hwdb, cooked_results, options.board,
191 status_set)
192 if not hwid_set:
193 sys.exit('NO matching HWIDs found')
194 print '\n'.join(hwid_set)
195
196
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800197@Command('probe',
198 CmdArg('--comps', nargs='*',
199 help='List of keys from the component_db registry.'),
200 CmdArg('--no_vol', action='store_true',
201 help='Do not probe volatile data.'),
202 CmdArg('--no_ic', action='store_true',
203 help='Do not probe initial_config data.'))
204def RunProbe(options):
205 """Print yaml-formatted breakdown of probed device properties."""
206 probe_results = probe.Probe(target_comp_classes=options.comps,
207 probe_volatile=not options.no_vol,
208 probe_initial_config=not options.no_ic)
Tammo Spalink3a7e9022012-06-27 14:08:40 +0800209 print probe_results.Encode()
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800210
211
Tammo Spalink214caf42012-05-28 10:45:00 +0800212@Command('verify_components',
213 _hwdb_path_cmd_arg,
214 CmdArg('comp_white_list', nargs='*'))
215def VerifyComponents(options):
216 """Verify that probable components all match entries in the component_db.
217
218 Probe for each component class in the comp_white_list and verify
219 that a corresponding match exists in the component_db -- make sure
220 that these components are present, that they have been approved, but
221 do not check against any specific BOM/HWID configurations.
222 """
223 hwdb = hwid_tool.ReadDatastore(options.hwdb_path)
224 if not options.comp_white_list:
225 sys.exit('ERROR: no component white list specified; possible choices:\n %s'
226 % '\n '.join(sorted(hwdb.comp_db.registry)))
227 for comp_class in options.comp_white_list:
228 if comp_class not in hwdb.comp_db.registry:
229 sys.exit('ERROR: specified white list component class %r does not exist'
230 ' in the component DB.' % comp_class)
231 probe_results = probe.Probe(target_comp_classes=options.comp_white_list,
232 probe_volatile=False, probe_initial_config=False)
233 probe_val_map = hwid_tool.CalcCompDbProbeValMap(hwdb.comp_db)
234 errors = []
235 matches = []
236 for comp_class in sorted(options.comp_white_list):
237 probe_val = probe_results.found_components.get(comp_class, None)
238 if probe_val is not None:
239 comp_name = probe_val_map.get(probe_val, None)
240 if comp_name is not None:
241 matches.append(comp_name)
242 else:
243 errors.append('unsupported %r component found with probe result'
244 ' %r (no matching name in the component DB)' %
245 (comp_class, probe_val))
246 else:
247 errors.append('missing %r component' % comp_class)
248 if errors:
249 print '\n'.join(errors)
250 sys.exit('component verification FAILURE')
251 else:
252 print 'component verification SUCCESS'
253 print 'found components:\n %s' % '\n '.join(matches)
254
255
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800256@Command('verify_hwid',
257 _hwdb_path_cmd_arg)
258def VerifyHwid(options):
259 """Verify system HWID properties match probed device properties.
260
261 First probe components, volatile and initial_config parameters for
262 the DUT. Then use the available device data to produce a list of
263 candidate HWIDs. Then verify the HWID from the DUT is present in
264 that list. Then verify that the DUT initial config values match
265 those specified for its HWID. Finally, verify that VPD contains all
266 the necessary fields as specified by the board data, and when
267 possible verify that values are legitimate.
268 """
269 hwdb = hwid_tool.ReadDatastore(options.hwdb_path)
270 main_fw_file = crosfw.LoadMainFirmware().GetFileName()
271 gbb_result = Shell('gbb_utility -g --hwid %s' % main_fw_file).stdout
272 hwid = re.findall(r'hardware_id:(.*)', gbb_result)[0].strip()
273 hwid_properties = hwid_tool.LookupHwidProperties(hwdb, hwid)
274 logging.info('Verifying system HWID: %r', hwid_properties.hwid)
275 logging.debug('expected system properties:\n%s',
276 YamlWrite(hwid_properties.__dict__))
277 probe_results = probe.Probe()
278 cooked_results = hwid_tool.CookProbeResults(
279 hwdb, probe_results, hwid_properties.board)
280 logging.debug('found system properties:\n%s',
281 YamlWrite(cooked_results.__dict__))
Jon Salz51932222012-06-01 12:54:15 +0800282 _event_log.Log('probe',
283 results=cooked_results.__dict__)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800284 match_errors = []
Tammo Spalink8fab5312012-05-28 18:33:30 +0800285 # TODO(tammo): Refactor to use hwid_tool.MatchHwids() ; this will
286 # make error reporting harder... Or maybe just factor out the
287 # shared matching logic, and add error reporting to that, which the
288 # MatchHwids routine could ignore.
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800289 for comp_class, expected_name in hwid_properties.component_map.items():
290 if expected_name == 'ANY':
291 continue
292 if expected_name == cooked_results.matched_components.get(comp_class, None):
293 continue
294 if comp_class in probe_results.missing_components:
295 match_errors.append(' %s component mismatch, expected %s, found nothing'
296 % (comp_class, expected_name))
297 else:
298 probe_value = probe_results.found_components.get(comp_class, None)
299 match_errors.append(' %s component mismatch, expected %s, found %r' %
300 (comp_class, expected_name, probe_value))
301 if match_errors:
302 raise Error('HWID verification FAILED.\n%s' % '\n'.join(match_errors))
303 if hwid_properties.volatile not in cooked_results.matched_volatile_tags:
304 msg = (' HWID specified volatile %s, but found match only for %s' %
305 (hwid_properties.volatile,
306 ', '.join(cooked_results.matched_volatile_tags)))
307 raise Error('HWID verification FAILED.\n%s' % msg)
308 if (hwid_properties.initial_config is not None and
309 hwid_properties.initial_config not in
310 cooked_results.matched_initial_config_tags):
311 msg = (' HWID specified initial_config %s, but only found match for [%s]' %
312 (hwid_properties.initial_config,
313 ', '.join(cooked_results.matched_initial_config_tags)))
314 raise Error('HWID verification FAILED.\n%s' % msg)
315 # TODO(tammo): Verify HWID status is supported (or deprecated for RMA).
316 ro_vpd = ReadRoVpd(main_fw_file)
317 for field in hwid_properties.vpd_ro_field_list:
318 if field not in ro_vpd:
319 raise Error('Missing required VPD field: %s' % field)
320 known_valid_values = vpd_data.KNOWN_VPD_FIELD_DATA.get(field, None)
321 value = ro_vpd[field]
322 if known_valid_values is not None and value not in known_valid_values:
323 raise Error('Invalid VPD entry : field %r, value %r' % (field, value))
Tammo Spalink86a61c62012-05-25 15:10:35 +0800324 rw_vpd = ReadRwVpd(main_fw_file)
325 _event_log.Log(
326 'verify_hwid',
327 matched_components=cooked_results.matched_components,
328 initial_configs=cooked_results.matched_initial_config_tags,
329 volatiles=cooked_results.matched_volatile_tags,
330 ro_vpd=ro_vpd,
331 rw_vpd=rw_vpd)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800332
333
334@Command('verify_keys')
335def VerifyKeys(options):
336 """Verify keys in firmware and SSD match."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800337 script = FindScript('verify_keys.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800338 kernel_device = GetReleaseKernelPartitionPath()
339 main_fw_file = crosfw.LoadMainFirmware().GetFileName()
340 result = Shell('%s %s %s' % (script, kernel_device, main_fw_file))
341 if not result.success:
342 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
343
344
345@Command('set_fw_bitmap_locale')
346def SetFirmwareBitmapLocale(options):
347 """Use VPD locale value to set firmware bitmap default language."""
348 image_file = crosfw.LoadMainFirmware().GetFileName()
349 locale = ReadRoVpd(image_file).get('initial_locale', None)
350 if locale is None:
351 raise Error, 'Missing initial_locale VPD.'
352 bitmap_locales = []
353 with NamedTemporaryFile() as f:
354 Shell('gbb_utility -g --bmpfv=%s %s' % (f.name, image_file))
355 bmpblk_data = bmpblk.unpack_bmpblock(f.read())
356 bitmap_locales = bmpblk_data.get('locales', bitmap_locales)
357 # Some locale values are just a language code and others are a
358 # hyphen-separated language code and country code pair. We care
359 # only about the language code part.
360 language_code = locale.partition('-')[0]
361 if language_code not in bitmap_locales:
362 raise Error, ('Firmware bitmaps do not contain support for the specified '
363 'initial locale language %r' % language_code)
364 else:
365 locale_index = bitmap_locales.index(language_code)
366 logging.info('Firmware bitmap initial locale set to %d (%s).',
367 locale_index, bitmap_locales[locale_index])
368 Shell('crossystem loc_idx=%d' % locale_index)
369
370
371@Command('verify_system_time')
372def VerifySystemTime(options):
373 """Verify system time is later than release filesystem creation time."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800374 script = FindScript('verify_system_time.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800375 rootfs_device = GetReleaseRootPartitionPath()
376 result = Shell('%s %s' % (script, rootfs_device))
377 if not result.success:
378 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
379
380
381@Command('verify_rootfs')
382def VerifyRootFs(options):
383 """Verify rootfs on SSD is valid by checking hash."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800384 script = FindScript('verify_rootfs.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800385 rootfs_device = GetReleaseRootPartitionPath()
386 result = Shell('%s %s' % (script, rootfs_device))
387 if not result.success:
388 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
389
390
391@Command('verify_switch_wp')
392def VerifyWpSwitch(options):
393 """Verify hardware write protection switch is enabled."""
394 if Shell('crossystem wpsw_cur').stdout.strip() != '1':
Hung-Te Lin6d827542012-07-19 11:50:41 +0800395 raise Error, 'write protection switch is disabled'
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800396
397
398@Command('verify_switch_dev')
399def VerifyDevSwitch(options):
400 """Verify developer switch is disabled."""
Hung-Te Lin8f643e32012-05-23 18:40:47 +0800401 result = Shell('crossystem devsw_cur')
402 if result.success:
403 if result.stdout.strip() != '0':
404 raise Error, 'developer mode is enabled'
405 else:
406 return
Hung-Te Lind1bec9a2012-07-09 16:45:03 +0800407 # devsw_cur is not available -- probably a device using keyboard-based
Hung-Te Lin6d827542012-07-19 11:50:41 +0800408 # developer/recovery mode. That will be handled in prepare_wipe.sh by
409 # setting "crossystem disable_dev_request=1" -- although we can't verify that
410 # until next reboot, because the real values are stored in TPM.
411 logging.warn('VerifyDevSwitch: No physical switch.')
412 _event_log.Log('switch_dev', type='virtual switch')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800413
414
415@Command('write_protect')
416def EnableFwWp(options):
417 """Enable then verify firmware write protection."""
418
419 def WriteProtect(fw_file_path, fw_type, section):
420 """Calculate protection size, then invoke flashrom.
421
422 Our supported chips only allow write protecting half their total
423 size, so we parition the flash chipset space accordingly.
424 """
425 raw_image = open(fw_file_path, 'rb').read()
426 image = crosfw.FirmwareImage(raw_image)
427 if not image.has_section(section):
428 raise Error('could not find %s firmware section %s' % (fw_type, section))
429 section_data = image.get_section_area(section)
430 protectable_size = len(raw_image) / 2
431 ro_a = int(section_data[0] / protectable_size)
432 ro_b = int((section_data[0] + section_data[1] - 1) / protectable_size)
433 if ro_a != ro_b:
434 raise Error("%s firmware section %s has illegal size" %
435 (fw_type, section))
436 ro_offset = ro_a * protectable_size
437 logging.debug('write protecting %s', fw_type)
438 crosfw.Flashrom(fw_type).EnableWriteProtection(ro_offset, protectable_size)
439
440 WriteProtect(crosfw.LoadMainFirmware().GetFileName(), 'main', 'RO_SECTION')
Tammo Spalink86a61c62012-05-25 15:10:35 +0800441 _event_log.Log('wp', fw='main')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800442 ec_fw_file = crosfw.LoadEcFirmware().GetFileName()
443 if ec_fw_file is not None:
Hung-Te Lin6d827542012-07-19 11:50:41 +0800444 # TODO(hungte) Support WP_RO if that section exist.
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800445 WriteProtect(ec_fw_file, 'ec', 'EC_RO')
Tammo Spalink86a61c62012-05-25 15:10:35 +0800446 _event_log.Log('wp', fw='ec')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800447 else:
448 logging.warning('EC not write protected (seems there is no EC flash).')
449
450
451@Command('clear_gbb_flags')
452def ClearGbbFlags(options):
453 """Zero out the GBB flags, in preparation for transition to release state.
454
455 No GBB flags are set in release/shipping state, but they are useful
456 for factory/development. See "gbb_utility --flags" for details.
457 """
Tammo Spalink461ddce2012-05-10 19:28:55 +0800458 script = FindScript('clear_gbb_flags.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800459 result = Shell(script)
460 if not result.success:
461 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
Tammo Spalink86a61c62012-05-25 15:10:35 +0800462 _event_log.Log('clear_gbb_flags')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800463
464
465@Command('prepare_wipe',
466 CmdArg('--fast', action='store_true',
467 help='use non-secure but faster wipe method.'))
468def PrepareWipe(options):
469 """Prepare system for transition to release state in next reboot."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800470 script = FindScript('prepare_wipe.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800471 tag = 'fast' if options.fast else ''
472 rootfs_device = GetReleaseRootPartitionPath()
473 result = Shell('FACTORY_WIPE_TAGS=%s %s %s' % (tag, script, rootfs_device))
474 if not result.success:
475 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
476
477
478@Command('verify',
Hung-Te Lin6d827542012-07-19 11:50:41 +0800479 CmdArg('--no_write_protect', action='store_true',
480 help='Do not check write protection switch state.'),
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800481 _hwdb_path_cmd_arg)
482def Verify(options):
483 """Verifies if whole factory process is ready for finalization.
484
485 This routine performs all the necessary checks to make sure the
486 device is ready to be finalized, but does not modify state. These
487 checks include dev switch, firmware write protection switch, hwid,
488 system time, keys, and root file system.
489 """
Hung-Te Lin6d827542012-07-19 11:50:41 +0800490 if not options.no_write_protect:
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800491 VerifyWpSwitch({})
Hung-Te Lin6d827542012-07-19 11:50:41 +0800492 VerifyDevSwitch({})
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800493 VerifyHwid(options)
494 VerifySystemTime({})
495 VerifyKeys({})
496 VerifyRootFs({})
497
498
Tammo Spalink86a61c62012-05-25 15:10:35 +0800499@Command('log_system_details')
500def LogSystemDetails(options):
501 """Write miscellaneous system details to the event log."""
502 raw_cs_data = Shell('crossystem').stdout.strip().splitlines()
503 # The crossytem output contains many lines like:
504 # 'key = value # description'
505 # Use regexps to pull out the key-value pairs and build a dict.
506 cs_data = dict((k, v.strip()) for k, v in
507 map(lambda x: re.findall(r'\A(\S+)\s+=\s+(.*)#.*\Z', x)[0],
508 raw_cs_data))
509 _event_log.Log(
510 'system_details',
511 platform_name=Shell('mosys platform name').stdout.strip(),
512 crossystem=cs_data,
513 modem_status=Shell('modem status').stdout.splitlines(),
514 ec_wp_status=Shell(
515 'flashrom -p internal:bus=lpc --get-size 2>/dev/null && '
516 'flashrom -p internal:bus=lpc --wp-status || '
517 'echo "EC is not available."').stdout,
518 bios_wp_status = Shell(
519 'flashrom -p internal:bus=spi --wp-status').stdout)
520
521
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800522_upload_method_cmd_arg = CmdArg(
523 '--upload_method', metavar='METHOD:PARAM',
524 help=('How to perform the upload. METHOD should be one of '
525 '{ftp, shopfloor, curl, cpfe, custom}.'))
526
527
528@Command('upload_report',
529 _upload_method_cmd_arg)
530def UploadReport(options):
531 """Create and a report containing key device details."""
Hung-Te Lin6bd16472012-06-20 16:26:47 +0800532 def NormalizeAsFileName(token):
533 return re.sub(r'\W+', '', token).strip()
Tammo Spalink86a61c62012-05-25 15:10:35 +0800534 ro_vpd = ReadRoVpd(crosfw.LoadMainFirmware().GetFileName())
535 device_sn = ro_vpd.get('serial_number', None)
536 if device_sn is None:
537 logging.warning('RO_VPD missing device serial number')
538 device_sn = 'MISSING_SN_' + TimedUuid()
Hung-Te Lin6bd16472012-06-20 16:26:47 +0800539 target_name = '%s_%s.tbz2' % (time.strftime('%Y%m%dT%H%M%SZ', time.gmtime()),
540 NormalizeAsFileName(device_sn))
Tammo Spalink86a61c62012-05-25 15:10:35 +0800541 target_path = os.path.join(gettempdir(), target_name)
542 # Intentionally ignoring dotfiles in EVENT_LOG_DIR.
543 tar_cmd = 'cd %s ; tar cjf %s *' % (EVENT_LOG_DIR, target_path)
544 tar_cmd += ' --add-file %s' % FACTORY_LOG_PATH
545 cmd_result = Shell(tar_cmd)
546 if not cmd_result.success:
547 raise Error('unable to tar event logs, cmd %r failed, stderr: %r' %
548 (tar_cmd, cmd_result.stderr))
549 if options.upload_method is None or options.upload_method == 'none':
550 logging.warning('REPORT UPLOAD SKIPPED (report left at %s)', target_path)
551 return
552 method, param = options.upload_method.split(':', 1)
553 if method == 'shopfloor':
554 report_upload.ShopFloorUpload(target_path, param)
555 elif method == 'ftp':
Jay Kim360c1dd2012-06-25 10:58:11 -0700556 report_upload.FtpUpload(target_path, 'ftp:' + param)
Tammo Spalink86a61c62012-05-25 15:10:35 +0800557 elif method == 'ftps':
558 report_upload.CurlUrlUpload(target_path, '--ftp-ssl-reqd ftp:%s' % param)
559 elif method == 'cpfe':
560 report_upload.CpfeUpload(target_path, param)
561 else:
562 raise Error('unknown report upload method %r', method)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800563
564
565@Command('finalize',
Hung-Te Lin6d827542012-07-19 11:50:41 +0800566 CmdArg('--no_write_protect', action='store_true',
567 help='Do not enable firmware write protection.'),
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800568 CmdArg('--fast', action='store_true',
569 help='use non-secure but faster wipe method.'),
570 _hwdb_path_cmd_arg,
571 _upload_method_cmd_arg)
572def Finalize(options):
573 """Verify system readiness and trigger transition into release state.
574
Hung-Te Lin6d827542012-07-19 11:50:41 +0800575 This routine first verifies system state (see verify command), modifies
576 firmware bitmaps to match locale, and then clears all of the factory-friendly
577 flags from the GBB. If everything is fine, it enables firmware write
578 protection (cannot rollback after this stage), uploads system logs & reports,
579 and sets the necessary boot flags to cause wipe of the factory image on the
580 next boot.
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800581 """
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800582 Verify(options)
583 SetFirmwareBitmapLocale({})
Hung-Te Lin6d827542012-07-19 11:50:41 +0800584 ClearGbbFlags({})
585 if options.no_write_protect:
586 logging.warn('WARNING: Firmware Write Protection is SKIPPED.')
587 _event_log.Log('wp', fw='both', status='skipped')
588 else:
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800589 EnableFwWp({})
Jon Salza0f58e02012-05-29 19:33:39 +0800590 LogSystemDetails(options)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800591 UploadReport(options)
592 PrepareWipe(options)
593
594
595def Main():
596 """Run sub-command specified by the command line args."""
597 options = ParseCmdline(
598 'Perform Google required factory tests.',
599 CmdArg('-l', '--log', metavar='PATH',
600 help='Write logs to this file.'),
Tammo Spalink8fab5312012-05-28 18:33:30 +0800601 verbosity_cmd_arg)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800602 SetupLogging(options.verbosity, options.log)
603 logging.debug('gooftool options: %s', repr(options))
604 try:
605 logging.debug('GOOFTOOL command %r', options.command_name)
606 options.command(options)
607 logging.info('GOOFTOOL command %r SUCCESS', options.command_name)
608 except Error, e:
609 logging.exception(e)
610 sys.exit('GOOFTOOL command %r ERROR: %s' % (options.command_name, e))
611 except Exception, e:
612 logging.exception(e)
613 sys.exit('UNCAUGHT RUNTIME EXCEPTION %s' % e)
614
615
616if __name__ == '__main__':
617 Main()