blob: deb79c1f42255b1597f8af3556733e444acc6ef6 [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
25import hwid_tool
26import probe
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080027import report_upload
28import vpd_data
29
Tammo Spalink8fab5312012-05-28 18:33:30 +080030from common import Error, ParseKeyValueData, SetupLogging, Shell
31from common import YamlRead, YamlWrite
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080032from hacked_argparse import CmdArg, Command, ParseCmdline, verbosity_cmd_arg
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080033
Tammo Spalink86a61c62012-05-25 15:10:35 +080034# TODO(tammo): Remove imp logic once the cros/factory code moves into this repo.
Tammo Spalink8fab5312012-05-28 18:33:30 +080035# NOTE: These imports also corrupt the python logging module...
Tammo Spalink86a61c62012-05-25 15:10:35 +080036import imp
37at_common = imp.find_module('common', ['/usr/local/autotest/client/bin'])
38imp.load_module('at_common', *at_common)
39from autotest_lib.client.cros.factory.event_log import EventLog, EVENT_LOG_DIR
Hung-Te Lin6bd16472012-06-20 16:26:47 +080040from autotest_lib.client.cros.factory.event_log import TimedUuid
Tammo Spalink86a61c62012-05-25 15:10:35 +080041from autotest_lib.client.cros.factory import FACTORY_LOG_PATH
42
43
44# Use a global event log, so that only a single log is created when
45# gooftool is called programmatically.
46_event_log = EventLog('gooftool')
47
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080048
49def GetPrimaryDevicePath(partition=None):
50 def IsFixed(dev):
51 sysfs_path = '/sys/block/%s/removable' % dev
52 return (os.path.exists(sysfs_path) and
53 open(sysfs_path).read().strip() == '0')
54 alpha_re = re.compile(r'^/dev/([a-zA-Z]+)[0-9]+$')
55 alnum_re = re.compile(r'^/dev/([a-zA-Z]+[0-9]+)p[0-9]+$')
cychiangde1dee22012-05-22 09:42:09 +080056 matched_alnum = False
57 dev_set = set()
58 for path in Shell('cgpt find -t rootfs').stdout.strip().split():
59 for dev in alpha_re.findall(path):
60 if IsFixed(dev):
61 dev_set.add(dev)
62 matched_alnum = False
63 for dev in alnum_re.findall(path):
64 if IsFixed(dev):
65 dev_set.add(dev)
66 matched_alnum = True
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080067 if len(dev_set) != 1:
68 raise Error('zero or multiple primary devs: %s' % dev_set)
69 dev_path = os.path.join('/dev', dev_set.pop())
70 if partition is None:
71 return dev_path
cychiangde1dee22012-05-22 09:42:09 +080072 fmt_str = '%sp%d' if matched_alnum else '%s%d'
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080073 return fmt_str % (dev_path, partition)
74
75
76def GetReleaseRootPartitionPath():
77 return GetPrimaryDevicePath(5)
78
79
80def GetReleaseKernelPartitionPath():
81 return GetPrimaryDevicePath(4)
82
83
84def FindScript(script_name):
Jon Salzd24269c2012-05-29 17:22:59 +080085 script_path = os.path.join(os.path.dirname(os.path.dirname(
86 os.path.realpath(__file__))), 'sh', script_name)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080087 if not os.path.exists(script_path):
88 raise Error('Needed script %s does not exist.' % script_path)
89 return script_path
90
91
Tammo Spalink86a61c62012-05-25 15:10:35 +080092def ReadVpd(fw_image_file, kind):
93 raw_vpd_data = Shell('vpd -i %s -l -f %s' % (kind, fw_image_file)).stdout
Tammo Spalink9a96b8a2012-04-03 11:10:41 +080094 return ParseKeyValueData('"(.*)"="(.*)"$', raw_vpd_data)
95
96
Tammo Spalink86a61c62012-05-25 15:10:35 +080097def ReadRoVpd(fw_image_file):
98 return ReadVpd(fw_image_file, 'RO_VPD')
99
100
101def ReadRwVpd(fw_image_file):
102 return ReadVpd(fw_image_file, 'RW_VPD')
103
104
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800105@Command('write_hwid',
106 CmdArg('hwid', metavar='HWID', help='HWID string'))
107def WriteHwid(options):
108 """Write specified HWID value into the system BB."""
109 logging.debug('writing hwid string %r', options.hwid)
110 main_fw = crosfw.LoadMainFirmware()
111 Shell('gbb_utility --set --hwid="%s" "%s"' %
112 (options.hwid, main_fw.GetFileName()))
113 main_fw.Write(sections=['GBB'])
Tammo Spalink86a61c62012-05-25 15:10:35 +0800114 _event_log.Log('write_hwid', hwid=options.hwid)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800115
116
Tammo Spalink8fab5312012-05-28 18:33:30 +0800117_hwdb_path_cmd_arg = CmdArg(
118 '--hwdb_path', metavar='PATH',
119 default=hwid_tool.DEFAULT_HWID_DATA_PATH,
120 help='Path to the HWID database.')
121
122
123@Command('probe_hwids',
124 _hwdb_path_cmd_arg,
125 CmdArg('-b', '--board', metavar='BOARD',
126 help='BOARD name', required=True),
127 CmdArg('--bom', metavar='BOM', help='BOM name'),
128 CmdArg('--variant', metavar='VARIANT', help='VARIANT code'),
129 CmdArg('--comp_map', action='store_true'),
130 CmdArg('--status', nargs='*',
131 help='consider only HWIDs with this status'))
132def ProbeHwid(options):
133 """Determine a list of possible HWIDs using provided args and probeing.
134
135 VOLATILE can always be determined by probing. To get a unique
136 result, VARIANT must be specified for all cases where the matching
137 BOM has more than one associated variant code, otherwise all HWID
138 variants will be returned. Both VARIANT and BOM information can
139 alternatively be specified using the --comp_map argument, which
140 allows specifying a list of
141
142 component-class: canonical-component-name
143
144 pairs on stdin, one per line (yaml format). Based on what is known
145 from BOM and comp_map, determine a list of components to probe for,
146 and use those probe results to resolve a list of matching HWIDs. If
147 no boms, components, or variant codes are specified, then a list of
148 all HWIDs that match probable components will be returned.
149
150 Returns (on stdout): A list of HWIDs that match the available probe
151 results and argument contraints, one per line.
152 """
153 hwdb = hwid_tool.ReadDatastore(options.hwdb_path)
154 if options.board not in hwdb.device_db:
155 sys.exit('ERROR: unknown board %r' % options.board)
156 device = hwdb.device_db[options.board]
157 component_map = {}
158 if options.bom:
159 bom_details = device.hwid_map.get(options.bom, None)
160 if bom_details is None:
161 sys.exit('ERROR: unkown bom %r for board %r' %
162 (options.bom, options.board))
163 component_map.update(bom_details.component_map)
164 comp_db_class_map = hwid_tool.CalcCompDbClassMap(hwdb.comp_db)
165 if options.variant:
166 variant_details = device.variant_map.get(options.variant, None)
167 if options.variant is None:
168 sys.exit('ERROR: unknown variant code %r for board %r' %
169 (options.variant, options.board))
170 for comp_name in variant_details:
171 comp_class = comp_db_class_map[comp_name]
172 if comp_class in component_map:
173 sys.exit('ERROR: multiple specifications for %r components'
174 ' (both VARIANT and BOM)' % comp_class)
175 component_map[comp_class] = comp_name
176 if options.comp_map:
177 input_map = YamlRead(sys.stdin.read())
178 logging.info('stdin component map: %r', input_map)
179 for key, value in input_map.items():
180 if key not in hwdb.comp_db.registry:
181 sys.exit('ERROR: unknown component class %r (from stdin)' % key)
182 if value not in comp_db_class_map:
183 sys.exit('ERROR: unkown component name %r (from stdin)' % value)
184 if key in component_map:
185 sys.exit('ERROR: multiple specifications for %r components'
186 ' (stdin and BOM/VARIANT)' % key)
187 component_map[key] = value
188 missing_classes = list(set(hwdb.comp_db.registry) - set(component_map))
189 if missing_classes:
190 logging.info('probing for %s', ', '.join(missing_classes))
191 probe_results = probe.Probe(target_comp_classes=missing_classes,
192 probe_volatile=True, probe_initial_config=False)
193 cooked_results = hwid_tool.CookProbeResults(
194 hwdb, probe_results, options.board)
195 cooked_results.matched_components.update(component_map)
196 status_set = set(options.status) if options.status else set(['supported'])
197 hwid_set = hwid_tool.MatchHwids(hwdb, cooked_results, options.board,
198 status_set)
199 if not hwid_set:
200 sys.exit('NO matching HWIDs found')
201 print '\n'.join(hwid_set)
202
203
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800204@Command('probe',
205 CmdArg('--comps', nargs='*',
206 help='List of keys from the component_db registry.'),
207 CmdArg('--no_vol', action='store_true',
208 help='Do not probe volatile data.'),
209 CmdArg('--no_ic', action='store_true',
210 help='Do not probe initial_config data.'))
211def RunProbe(options):
212 """Print yaml-formatted breakdown of probed device properties."""
213 probe_results = probe.Probe(target_comp_classes=options.comps,
214 probe_volatile=not options.no_vol,
215 probe_initial_config=not options.no_ic)
216 print YamlWrite(probe_results.__dict__)
217
218
Tammo Spalink214caf42012-05-28 10:45:00 +0800219@Command('verify_components',
220 _hwdb_path_cmd_arg,
221 CmdArg('comp_white_list', nargs='*'))
222def VerifyComponents(options):
223 """Verify that probable components all match entries in the component_db.
224
225 Probe for each component class in the comp_white_list and verify
226 that a corresponding match exists in the component_db -- make sure
227 that these components are present, that they have been approved, but
228 do not check against any specific BOM/HWID configurations.
229 """
230 hwdb = hwid_tool.ReadDatastore(options.hwdb_path)
231 if not options.comp_white_list:
232 sys.exit('ERROR: no component white list specified; possible choices:\n %s'
233 % '\n '.join(sorted(hwdb.comp_db.registry)))
234 for comp_class in options.comp_white_list:
235 if comp_class not in hwdb.comp_db.registry:
236 sys.exit('ERROR: specified white list component class %r does not exist'
237 ' in the component DB.' % comp_class)
238 probe_results = probe.Probe(target_comp_classes=options.comp_white_list,
239 probe_volatile=False, probe_initial_config=False)
240 probe_val_map = hwid_tool.CalcCompDbProbeValMap(hwdb.comp_db)
241 errors = []
242 matches = []
243 for comp_class in sorted(options.comp_white_list):
244 probe_val = probe_results.found_components.get(comp_class, None)
245 if probe_val is not None:
246 comp_name = probe_val_map.get(probe_val, None)
247 if comp_name is not None:
248 matches.append(comp_name)
249 else:
250 errors.append('unsupported %r component found with probe result'
251 ' %r (no matching name in the component DB)' %
252 (comp_class, probe_val))
253 else:
254 errors.append('missing %r component' % comp_class)
255 if errors:
256 print '\n'.join(errors)
257 sys.exit('component verification FAILURE')
258 else:
259 print 'component verification SUCCESS'
260 print 'found components:\n %s' % '\n '.join(matches)
261
262
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800263@Command('verify_hwid',
264 _hwdb_path_cmd_arg)
265def VerifyHwid(options):
266 """Verify system HWID properties match probed device properties.
267
268 First probe components, volatile and initial_config parameters for
269 the DUT. Then use the available device data to produce a list of
270 candidate HWIDs. Then verify the HWID from the DUT is present in
271 that list. Then verify that the DUT initial config values match
272 those specified for its HWID. Finally, verify that VPD contains all
273 the necessary fields as specified by the board data, and when
274 possible verify that values are legitimate.
275 """
276 hwdb = hwid_tool.ReadDatastore(options.hwdb_path)
277 main_fw_file = crosfw.LoadMainFirmware().GetFileName()
278 gbb_result = Shell('gbb_utility -g --hwid %s' % main_fw_file).stdout
279 hwid = re.findall(r'hardware_id:(.*)', gbb_result)[0].strip()
280 hwid_properties = hwid_tool.LookupHwidProperties(hwdb, hwid)
281 logging.info('Verifying system HWID: %r', hwid_properties.hwid)
282 logging.debug('expected system properties:\n%s',
283 YamlWrite(hwid_properties.__dict__))
284 probe_results = probe.Probe()
285 cooked_results = hwid_tool.CookProbeResults(
286 hwdb, probe_results, hwid_properties.board)
287 logging.debug('found system properties:\n%s',
288 YamlWrite(cooked_results.__dict__))
Jon Salz51932222012-06-01 12:54:15 +0800289 _event_log.Log('probe',
290 results=cooked_results.__dict__)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800291 match_errors = []
Tammo Spalink8fab5312012-05-28 18:33:30 +0800292 # TODO(tammo): Refactor to use hwid_tool.MatchHwids() ; this will
293 # make error reporting harder... Or maybe just factor out the
294 # shared matching logic, and add error reporting to that, which the
295 # MatchHwids routine could ignore.
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800296 for comp_class, expected_name in hwid_properties.component_map.items():
297 if expected_name == 'ANY':
298 continue
299 if expected_name == cooked_results.matched_components.get(comp_class, None):
300 continue
301 if comp_class in probe_results.missing_components:
302 match_errors.append(' %s component mismatch, expected %s, found nothing'
303 % (comp_class, expected_name))
304 else:
305 probe_value = probe_results.found_components.get(comp_class, None)
306 match_errors.append(' %s component mismatch, expected %s, found %r' %
307 (comp_class, expected_name, probe_value))
308 if match_errors:
309 raise Error('HWID verification FAILED.\n%s' % '\n'.join(match_errors))
310 if hwid_properties.volatile not in cooked_results.matched_volatile_tags:
311 msg = (' HWID specified volatile %s, but found match only for %s' %
312 (hwid_properties.volatile,
313 ', '.join(cooked_results.matched_volatile_tags)))
314 raise Error('HWID verification FAILED.\n%s' % msg)
315 if (hwid_properties.initial_config is not None and
316 hwid_properties.initial_config not in
317 cooked_results.matched_initial_config_tags):
318 msg = (' HWID specified initial_config %s, but only found match for [%s]' %
319 (hwid_properties.initial_config,
320 ', '.join(cooked_results.matched_initial_config_tags)))
321 raise Error('HWID verification FAILED.\n%s' % msg)
322 # TODO(tammo): Verify HWID status is supported (or deprecated for RMA).
323 ro_vpd = ReadRoVpd(main_fw_file)
324 for field in hwid_properties.vpd_ro_field_list:
325 if field not in ro_vpd:
326 raise Error('Missing required VPD field: %s' % field)
327 known_valid_values = vpd_data.KNOWN_VPD_FIELD_DATA.get(field, None)
328 value = ro_vpd[field]
329 if known_valid_values is not None and value not in known_valid_values:
330 raise Error('Invalid VPD entry : field %r, value %r' % (field, value))
Tammo Spalink86a61c62012-05-25 15:10:35 +0800331 rw_vpd = ReadRwVpd(main_fw_file)
332 _event_log.Log(
333 'verify_hwid',
334 matched_components=cooked_results.matched_components,
335 initial_configs=cooked_results.matched_initial_config_tags,
336 volatiles=cooked_results.matched_volatile_tags,
337 ro_vpd=ro_vpd,
338 rw_vpd=rw_vpd)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800339
340
341@Command('verify_keys')
342def VerifyKeys(options):
343 """Verify keys in firmware and SSD match."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800344 script = FindScript('verify_keys.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800345 kernel_device = GetReleaseKernelPartitionPath()
346 main_fw_file = crosfw.LoadMainFirmware().GetFileName()
347 result = Shell('%s %s %s' % (script, kernel_device, main_fw_file))
348 if not result.success:
349 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
350
351
352@Command('set_fw_bitmap_locale')
353def SetFirmwareBitmapLocale(options):
354 """Use VPD locale value to set firmware bitmap default language."""
355 image_file = crosfw.LoadMainFirmware().GetFileName()
356 locale = ReadRoVpd(image_file).get('initial_locale', None)
357 if locale is None:
358 raise Error, 'Missing initial_locale VPD.'
359 bitmap_locales = []
360 with NamedTemporaryFile() as f:
361 Shell('gbb_utility -g --bmpfv=%s %s' % (f.name, image_file))
362 bmpblk_data = bmpblk.unpack_bmpblock(f.read())
363 bitmap_locales = bmpblk_data.get('locales', bitmap_locales)
364 # Some locale values are just a language code and others are a
365 # hyphen-separated language code and country code pair. We care
366 # only about the language code part.
367 language_code = locale.partition('-')[0]
368 if language_code not in bitmap_locales:
369 raise Error, ('Firmware bitmaps do not contain support for the specified '
370 'initial locale language %r' % language_code)
371 else:
372 locale_index = bitmap_locales.index(language_code)
373 logging.info('Firmware bitmap initial locale set to %d (%s).',
374 locale_index, bitmap_locales[locale_index])
375 Shell('crossystem loc_idx=%d' % locale_index)
376
377
378@Command('verify_system_time')
379def VerifySystemTime(options):
380 """Verify system time is later than release filesystem creation time."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800381 script = FindScript('verify_system_time.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800382 rootfs_device = GetReleaseRootPartitionPath()
383 result = Shell('%s %s' % (script, rootfs_device))
384 if not result.success:
385 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
386
387
388@Command('verify_rootfs')
389def VerifyRootFs(options):
390 """Verify rootfs on SSD is valid by checking hash."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800391 script = FindScript('verify_rootfs.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800392 rootfs_device = GetReleaseRootPartitionPath()
393 result = Shell('%s %s' % (script, rootfs_device))
394 if not result.success:
395 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
396
397
398@Command('verify_switch_wp')
399def VerifyWpSwitch(options):
400 """Verify hardware write protection switch is enabled."""
401 if Shell('crossystem wpsw_cur').stdout.strip() != '1':
402 raise Error, 'write protection is disabled'
403
404
405@Command('verify_switch_dev')
406def VerifyDevSwitch(options):
407 """Verify developer switch is disabled."""
Hung-Te Lin8f643e32012-05-23 18:40:47 +0800408 result = Shell('crossystem devsw_cur')
409 if result.success:
410 if result.stdout.strip() != '0':
411 raise Error, 'developer mode is enabled'
412 else:
413 return
414 # Try ChromeOS-EC. This may hang 15 seconds if the EC does not respond.
415 logging.warn('VerifyDevSwitch: Trying ChromeOS-EC...')
416 if not Shell('ectool vboot 0').success:
417 raise Error, 'failed to turn off developer mode.'
418 # TODO(hungte) Verify if the switch is turned off properly, using "ectoo
419 # vboot" and parse the key-value pairs, when the names are determined.
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800420
421
422@Command('write_protect')
423def EnableFwWp(options):
424 """Enable then verify firmware write protection."""
425
426 def WriteProtect(fw_file_path, fw_type, section):
427 """Calculate protection size, then invoke flashrom.
428
429 Our supported chips only allow write protecting half their total
430 size, so we parition the flash chipset space accordingly.
431 """
432 raw_image = open(fw_file_path, 'rb').read()
433 image = crosfw.FirmwareImage(raw_image)
434 if not image.has_section(section):
435 raise Error('could not find %s firmware section %s' % (fw_type, section))
436 section_data = image.get_section_area(section)
437 protectable_size = len(raw_image) / 2
438 ro_a = int(section_data[0] / protectable_size)
439 ro_b = int((section_data[0] + section_data[1] - 1) / protectable_size)
440 if ro_a != ro_b:
441 raise Error("%s firmware section %s has illegal size" %
442 (fw_type, section))
443 ro_offset = ro_a * protectable_size
444 logging.debug('write protecting %s', fw_type)
445 crosfw.Flashrom(fw_type).EnableWriteProtection(ro_offset, protectable_size)
446
447 WriteProtect(crosfw.LoadMainFirmware().GetFileName(), 'main', 'RO_SECTION')
Tammo Spalink86a61c62012-05-25 15:10:35 +0800448 _event_log.Log('wp', fw='main')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800449 ec_fw_file = crosfw.LoadEcFirmware().GetFileName()
450 if ec_fw_file is not None:
451 WriteProtect(ec_fw_file, 'ec', 'EC_RO')
Tammo Spalink86a61c62012-05-25 15:10:35 +0800452 _event_log.Log('wp', fw='ec')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800453 else:
454 logging.warning('EC not write protected (seems there is no EC flash).')
455
456
457@Command('clear_gbb_flags')
458def ClearGbbFlags(options):
459 """Zero out the GBB flags, in preparation for transition to release state.
460
461 No GBB flags are set in release/shipping state, but they are useful
462 for factory/development. See "gbb_utility --flags" for details.
463 """
Tammo Spalink461ddce2012-05-10 19:28:55 +0800464 script = FindScript('clear_gbb_flags.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800465 result = Shell(script)
466 if not result.success:
467 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
Tammo Spalink86a61c62012-05-25 15:10:35 +0800468 _event_log.Log('clear_gbb_flags')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800469
470
471@Command('prepare_wipe',
472 CmdArg('--fast', action='store_true',
473 help='use non-secure but faster wipe method.'))
474def PrepareWipe(options):
475 """Prepare system for transition to release state in next reboot."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800476 script = FindScript('prepare_wipe.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800477 tag = 'fast' if options.fast else ''
478 rootfs_device = GetReleaseRootPartitionPath()
479 result = Shell('FACTORY_WIPE_TAGS=%s %s %s' % (tag, script, rootfs_device))
480 if not result.success:
481 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
482
483
484@Command('verify',
485 CmdArg('--dev', action='store_true',
486 help='Do not verify switch state (dev mode and fw wp).'),
487 _hwdb_path_cmd_arg)
488def Verify(options):
489 """Verifies if whole factory process is ready for finalization.
490
491 This routine performs all the necessary checks to make sure the
492 device is ready to be finalized, but does not modify state. These
493 checks include dev switch, firmware write protection switch, hwid,
494 system time, keys, and root file system.
495 """
496 if not options.dev:
497 VerifyDevSwitch({})
498 VerifyWpSwitch({})
499 VerifyHwid(options)
500 VerifySystemTime({})
501 VerifyKeys({})
502 VerifyRootFs({})
503
504
Tammo Spalink86a61c62012-05-25 15:10:35 +0800505@Command('log_system_details')
506def LogSystemDetails(options):
507 """Write miscellaneous system details to the event log."""
508 raw_cs_data = Shell('crossystem').stdout.strip().splitlines()
509 # The crossytem output contains many lines like:
510 # 'key = value # description'
511 # Use regexps to pull out the key-value pairs and build a dict.
512 cs_data = dict((k, v.strip()) for k, v in
513 map(lambda x: re.findall(r'\A(\S+)\s+=\s+(.*)#.*\Z', x)[0],
514 raw_cs_data))
515 _event_log.Log(
516 'system_details',
517 platform_name=Shell('mosys platform name').stdout.strip(),
518 crossystem=cs_data,
519 modem_status=Shell('modem status').stdout.splitlines(),
520 ec_wp_status=Shell(
521 'flashrom -p internal:bus=lpc --get-size 2>/dev/null && '
522 'flashrom -p internal:bus=lpc --wp-status || '
523 'echo "EC is not available."').stdout,
524 bios_wp_status = Shell(
525 'flashrom -p internal:bus=spi --wp-status').stdout)
526
527
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800528_upload_method_cmd_arg = CmdArg(
529 '--upload_method', metavar='METHOD:PARAM',
530 help=('How to perform the upload. METHOD should be one of '
531 '{ftp, shopfloor, curl, cpfe, custom}.'))
532
533
534@Command('upload_report',
535 _upload_method_cmd_arg)
536def UploadReport(options):
537 """Create and a report containing key device details."""
Hung-Te Lin6bd16472012-06-20 16:26:47 +0800538 def NormalizeAsFileName(token):
539 return re.sub(r'\W+', '', token).strip()
Tammo Spalink86a61c62012-05-25 15:10:35 +0800540 ro_vpd = ReadRoVpd(crosfw.LoadMainFirmware().GetFileName())
541 device_sn = ro_vpd.get('serial_number', None)
542 if device_sn is None:
543 logging.warning('RO_VPD missing device serial number')
544 device_sn = 'MISSING_SN_' + TimedUuid()
Hung-Te Lin6bd16472012-06-20 16:26:47 +0800545 target_name = '%s_%s.tbz2' % (time.strftime('%Y%m%dT%H%M%SZ', time.gmtime()),
546 NormalizeAsFileName(device_sn))
Tammo Spalink86a61c62012-05-25 15:10:35 +0800547 target_path = os.path.join(gettempdir(), target_name)
548 # Intentionally ignoring dotfiles in EVENT_LOG_DIR.
549 tar_cmd = 'cd %s ; tar cjf %s *' % (EVENT_LOG_DIR, target_path)
550 tar_cmd += ' --add-file %s' % FACTORY_LOG_PATH
551 cmd_result = Shell(tar_cmd)
552 if not cmd_result.success:
553 raise Error('unable to tar event logs, cmd %r failed, stderr: %r' %
554 (tar_cmd, cmd_result.stderr))
555 if options.upload_method is None or options.upload_method == 'none':
556 logging.warning('REPORT UPLOAD SKIPPED (report left at %s)', target_path)
557 return
558 method, param = options.upload_method.split(':', 1)
559 if method == 'shopfloor':
560 report_upload.ShopFloorUpload(target_path, param)
561 elif method == 'ftp':
562 report_upload.FtpUpload(target_path, 'ftp://' + param)
563 elif method == 'ftps':
564 report_upload.CurlUrlUpload(target_path, '--ftp-ssl-reqd ftp:%s' % param)
565 elif method == 'cpfe':
566 report_upload.CpfeUpload(target_path, param)
567 else:
568 raise Error('unknown report upload method %r', method)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800569
570
571@Command('finalize',
572 CmdArg('--dev', action='store_true',
573 help='Do not verify or alter write protection or dev mode.'),
574 CmdArg('--fast', action='store_true',
575 help='use non-secure but faster wipe method.'),
576 _hwdb_path_cmd_arg,
577 _upload_method_cmd_arg)
578def Finalize(options):
579 """Verify system readiness and trigger transition into release state.
580
581 This routine first verifies system state (see verify command), then
582 clears all of the testing flags from the GBB, then modifies firmware
583 bitmaps to match locale. Then it enables firmware write protection
584 and sets the necessary boot flags to cause wipe of the factory image
585 on the next boot.
586 """
587 ClearGbbFlags({})
588 Verify(options)
589 SetFirmwareBitmapLocale({})
590 if not options.dev:
591 EnableFwWp({})
Jon Salza0f58e02012-05-29 19:33:39 +0800592 LogSystemDetails(options)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800593 UploadReport(options)
594 PrepareWipe(options)
595
596
597def Main():
598 """Run sub-command specified by the command line args."""
599 options = ParseCmdline(
600 'Perform Google required factory tests.',
601 CmdArg('-l', '--log', metavar='PATH',
602 help='Write logs to this file.'),
Tammo Spalink8fab5312012-05-28 18:33:30 +0800603 verbosity_cmd_arg)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800604 SetupLogging(options.verbosity, options.log)
605 logging.debug('gooftool options: %s', repr(options))
606 try:
607 logging.debug('GOOFTOOL command %r', options.command_name)
608 options.command(options)
609 logging.info('GOOFTOOL command %r SUCCESS', options.command_name)
610 except Error, e:
611 logging.exception(e)
612 sys.exit('GOOFTOOL command %r ERROR: %s' % (options.command_name, e))
613 except Exception, e:
614 logging.exception(e)
615 sys.exit('UNCAUGHT RUNTIME EXCEPTION %s' % e)
616
617
618if __name__ == '__main__':
619 Main()