blob: 4d33725470cf47402ac098893d88ccf7df773f9e [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
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 """
148 hwdb = hwid_tool.ReadDatastore(options.hwdb_path)
149 if options.board not in hwdb.device_db:
150 sys.exit('ERROR: unknown board %r' % options.board)
151 device = hwdb.device_db[options.board]
152 component_map = {}
153 if options.bom:
154 bom_details = device.hwid_map.get(options.bom, None)
155 if bom_details is None:
156 sys.exit('ERROR: unkown bom %r for board %r' %
157 (options.bom, options.board))
158 component_map.update(bom_details.component_map)
159 comp_db_class_map = hwid_tool.CalcCompDbClassMap(hwdb.comp_db)
160 if options.variant:
161 variant_details = device.variant_map.get(options.variant, None)
162 if options.variant is None:
163 sys.exit('ERROR: unknown variant code %r for board %r' %
164 (options.variant, options.board))
165 for comp_name in variant_details:
166 comp_class = comp_db_class_map[comp_name]
167 if comp_class in component_map:
168 sys.exit('ERROR: multiple specifications for %r components'
169 ' (both VARIANT and BOM)' % comp_class)
170 component_map[comp_class] = comp_name
171 if options.comp_map:
172 input_map = YamlRead(sys.stdin.read())
173 logging.info('stdin component map: %r', input_map)
174 for key, value in input_map.items():
175 if key not in hwdb.comp_db.registry:
176 sys.exit('ERROR: unknown component class %r (from stdin)' % key)
177 if value not in comp_db_class_map:
178 sys.exit('ERROR: unkown component name %r (from stdin)' % value)
179 if key in component_map:
180 sys.exit('ERROR: multiple specifications for %r components'
181 ' (stdin and BOM/VARIANT)' % key)
182 component_map[key] = value
183 missing_classes = list(set(hwdb.comp_db.registry) - set(component_map))
184 if missing_classes:
185 logging.info('probing for %s', ', '.join(missing_classes))
186 probe_results = probe.Probe(target_comp_classes=missing_classes,
187 probe_volatile=True, probe_initial_config=False)
188 cooked_results = hwid_tool.CookProbeResults(
189 hwdb, probe_results, options.board)
190 cooked_results.matched_components.update(component_map)
191 status_set = set(options.status) if options.status else set(['supported'])
192 hwid_set = hwid_tool.MatchHwids(hwdb, cooked_results, options.board,
193 status_set)
194 if not hwid_set:
195 sys.exit('NO matching HWIDs found')
196 print '\n'.join(hwid_set)
197
198
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800199@Command('probe',
200 CmdArg('--comps', nargs='*',
201 help='List of keys from the component_db registry.'),
202 CmdArg('--no_vol', action='store_true',
203 help='Do not probe volatile data.'),
204 CmdArg('--no_ic', action='store_true',
205 help='Do not probe initial_config data.'))
206def RunProbe(options):
207 """Print yaml-formatted breakdown of probed device properties."""
208 probe_results = probe.Probe(target_comp_classes=options.comps,
209 probe_volatile=not options.no_vol,
210 probe_initial_config=not options.no_ic)
211 print YamlWrite(probe_results.__dict__)
212
213
Tammo Spalink214caf42012-05-28 10:45:00 +0800214@Command('verify_components',
215 _hwdb_path_cmd_arg,
216 CmdArg('comp_white_list', nargs='*'))
217def VerifyComponents(options):
218 """Verify that probable components all match entries in the component_db.
219
220 Probe for each component class in the comp_white_list and verify
221 that a corresponding match exists in the component_db -- make sure
222 that these components are present, that they have been approved, but
223 do not check against any specific BOM/HWID configurations.
224 """
225 hwdb = hwid_tool.ReadDatastore(options.hwdb_path)
226 if not options.comp_white_list:
227 sys.exit('ERROR: no component white list specified; possible choices:\n %s'
228 % '\n '.join(sorted(hwdb.comp_db.registry)))
229 for comp_class in options.comp_white_list:
230 if comp_class not in hwdb.comp_db.registry:
231 sys.exit('ERROR: specified white list component class %r does not exist'
232 ' in the component DB.' % comp_class)
233 probe_results = probe.Probe(target_comp_classes=options.comp_white_list,
234 probe_volatile=False, probe_initial_config=False)
235 probe_val_map = hwid_tool.CalcCompDbProbeValMap(hwdb.comp_db)
236 errors = []
237 matches = []
238 for comp_class in sorted(options.comp_white_list):
239 probe_val = probe_results.found_components.get(comp_class, None)
240 if probe_val is not None:
241 comp_name = probe_val_map.get(probe_val, None)
242 if comp_name is not None:
243 matches.append(comp_name)
244 else:
245 errors.append('unsupported %r component found with probe result'
246 ' %r (no matching name in the component DB)' %
247 (comp_class, probe_val))
248 else:
249 errors.append('missing %r component' % comp_class)
250 if errors:
251 print '\n'.join(errors)
252 sys.exit('component verification FAILURE')
253 else:
254 print 'component verification SUCCESS'
255 print 'found components:\n %s' % '\n '.join(matches)
256
257
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800258@Command('verify_hwid',
259 _hwdb_path_cmd_arg)
260def VerifyHwid(options):
261 """Verify system HWID properties match probed device properties.
262
263 First probe components, volatile and initial_config parameters for
264 the DUT. Then use the available device data to produce a list of
265 candidate HWIDs. Then verify the HWID from the DUT is present in
266 that list. Then verify that the DUT initial config values match
267 those specified for its HWID. Finally, verify that VPD contains all
268 the necessary fields as specified by the board data, and when
269 possible verify that values are legitimate.
270 """
271 hwdb = hwid_tool.ReadDatastore(options.hwdb_path)
272 main_fw_file = crosfw.LoadMainFirmware().GetFileName()
273 gbb_result = Shell('gbb_utility -g --hwid %s' % main_fw_file).stdout
274 hwid = re.findall(r'hardware_id:(.*)', gbb_result)[0].strip()
275 hwid_properties = hwid_tool.LookupHwidProperties(hwdb, hwid)
276 logging.info('Verifying system HWID: %r', hwid_properties.hwid)
277 logging.debug('expected system properties:\n%s',
278 YamlWrite(hwid_properties.__dict__))
279 probe_results = probe.Probe()
280 cooked_results = hwid_tool.CookProbeResults(
281 hwdb, probe_results, hwid_properties.board)
282 logging.debug('found system properties:\n%s',
283 YamlWrite(cooked_results.__dict__))
Jon Salz51932222012-06-01 12:54:15 +0800284 _event_log.Log('probe',
285 results=cooked_results.__dict__)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800286 match_errors = []
Tammo Spalink8fab5312012-05-28 18:33:30 +0800287 # TODO(tammo): Refactor to use hwid_tool.MatchHwids() ; this will
288 # make error reporting harder... Or maybe just factor out the
289 # shared matching logic, and add error reporting to that, which the
290 # MatchHwids routine could ignore.
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800291 for comp_class, expected_name in hwid_properties.component_map.items():
292 if expected_name == 'ANY':
293 continue
294 if expected_name == cooked_results.matched_components.get(comp_class, None):
295 continue
296 if comp_class in probe_results.missing_components:
297 match_errors.append(' %s component mismatch, expected %s, found nothing'
298 % (comp_class, expected_name))
299 else:
300 probe_value = probe_results.found_components.get(comp_class, None)
301 match_errors.append(' %s component mismatch, expected %s, found %r' %
302 (comp_class, expected_name, probe_value))
303 if match_errors:
304 raise Error('HWID verification FAILED.\n%s' % '\n'.join(match_errors))
305 if hwid_properties.volatile not in cooked_results.matched_volatile_tags:
306 msg = (' HWID specified volatile %s, but found match only for %s' %
307 (hwid_properties.volatile,
308 ', '.join(cooked_results.matched_volatile_tags)))
309 raise Error('HWID verification FAILED.\n%s' % msg)
310 if (hwid_properties.initial_config is not None and
311 hwid_properties.initial_config not in
312 cooked_results.matched_initial_config_tags):
313 msg = (' HWID specified initial_config %s, but only found match for [%s]' %
314 (hwid_properties.initial_config,
315 ', '.join(cooked_results.matched_initial_config_tags)))
316 raise Error('HWID verification FAILED.\n%s' % msg)
317 # TODO(tammo): Verify HWID status is supported (or deprecated for RMA).
318 ro_vpd = ReadRoVpd(main_fw_file)
319 for field in hwid_properties.vpd_ro_field_list:
320 if field not in ro_vpd:
321 raise Error('Missing required VPD field: %s' % field)
322 known_valid_values = vpd_data.KNOWN_VPD_FIELD_DATA.get(field, None)
323 value = ro_vpd[field]
324 if known_valid_values is not None and value not in known_valid_values:
325 raise Error('Invalid VPD entry : field %r, value %r' % (field, value))
Tammo Spalink86a61c62012-05-25 15:10:35 +0800326 rw_vpd = ReadRwVpd(main_fw_file)
327 _event_log.Log(
328 'verify_hwid',
329 matched_components=cooked_results.matched_components,
330 initial_configs=cooked_results.matched_initial_config_tags,
331 volatiles=cooked_results.matched_volatile_tags,
332 ro_vpd=ro_vpd,
333 rw_vpd=rw_vpd)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800334
335
336@Command('verify_keys')
337def VerifyKeys(options):
338 """Verify keys in firmware and SSD match."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800339 script = FindScript('verify_keys.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800340 kernel_device = GetReleaseKernelPartitionPath()
341 main_fw_file = crosfw.LoadMainFirmware().GetFileName()
342 result = Shell('%s %s %s' % (script, kernel_device, main_fw_file))
343 if not result.success:
344 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
345
346
347@Command('set_fw_bitmap_locale')
348def SetFirmwareBitmapLocale(options):
349 """Use VPD locale value to set firmware bitmap default language."""
350 image_file = crosfw.LoadMainFirmware().GetFileName()
351 locale = ReadRoVpd(image_file).get('initial_locale', None)
352 if locale is None:
353 raise Error, 'Missing initial_locale VPD.'
354 bitmap_locales = []
355 with NamedTemporaryFile() as f:
356 Shell('gbb_utility -g --bmpfv=%s %s' % (f.name, image_file))
357 bmpblk_data = bmpblk.unpack_bmpblock(f.read())
358 bitmap_locales = bmpblk_data.get('locales', bitmap_locales)
359 # Some locale values are just a language code and others are a
360 # hyphen-separated language code and country code pair. We care
361 # only about the language code part.
362 language_code = locale.partition('-')[0]
363 if language_code not in bitmap_locales:
364 raise Error, ('Firmware bitmaps do not contain support for the specified '
365 'initial locale language %r' % language_code)
366 else:
367 locale_index = bitmap_locales.index(language_code)
368 logging.info('Firmware bitmap initial locale set to %d (%s).',
369 locale_index, bitmap_locales[locale_index])
370 Shell('crossystem loc_idx=%d' % locale_index)
371
372
373@Command('verify_system_time')
374def VerifySystemTime(options):
375 """Verify system time is later than release filesystem creation time."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800376 script = FindScript('verify_system_time.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800377 rootfs_device = GetReleaseRootPartitionPath()
378 result = Shell('%s %s' % (script, rootfs_device))
379 if not result.success:
380 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
381
382
383@Command('verify_rootfs')
384def VerifyRootFs(options):
385 """Verify rootfs on SSD is valid by checking hash."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800386 script = FindScript('verify_rootfs.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800387 rootfs_device = GetReleaseRootPartitionPath()
388 result = Shell('%s %s' % (script, rootfs_device))
389 if not result.success:
390 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
391
392
393@Command('verify_switch_wp')
394def VerifyWpSwitch(options):
395 """Verify hardware write protection switch is enabled."""
396 if Shell('crossystem wpsw_cur').stdout.strip() != '1':
397 raise Error, 'write protection is disabled'
398
399
400@Command('verify_switch_dev')
401def VerifyDevSwitch(options):
402 """Verify developer switch is disabled."""
Hung-Te Lin8f643e32012-05-23 18:40:47 +0800403 result = Shell('crossystem devsw_cur')
404 if result.success:
405 if result.stdout.strip() != '0':
406 raise Error, 'developer mode is enabled'
407 else:
408 return
409 # Try ChromeOS-EC. This may hang 15 seconds if the EC does not respond.
410 logging.warn('VerifyDevSwitch: Trying ChromeOS-EC...')
411 if not Shell('ectool vboot 0').success:
412 raise Error, 'failed to turn off developer mode.'
413 # TODO(hungte) Verify if the switch is turned off properly, using "ectoo
414 # vboot" and parse the key-value pairs, when the names are determined.
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800415
416
417@Command('write_protect')
418def EnableFwWp(options):
419 """Enable then verify firmware write protection."""
420
421 def WriteProtect(fw_file_path, fw_type, section):
422 """Calculate protection size, then invoke flashrom.
423
424 Our supported chips only allow write protecting half their total
425 size, so we parition the flash chipset space accordingly.
426 """
427 raw_image = open(fw_file_path, 'rb').read()
428 image = crosfw.FirmwareImage(raw_image)
429 if not image.has_section(section):
430 raise Error('could not find %s firmware section %s' % (fw_type, section))
431 section_data = image.get_section_area(section)
432 protectable_size = len(raw_image) / 2
433 ro_a = int(section_data[0] / protectable_size)
434 ro_b = int((section_data[0] + section_data[1] - 1) / protectable_size)
435 if ro_a != ro_b:
436 raise Error("%s firmware section %s has illegal size" %
437 (fw_type, section))
438 ro_offset = ro_a * protectable_size
439 logging.debug('write protecting %s', fw_type)
440 crosfw.Flashrom(fw_type).EnableWriteProtection(ro_offset, protectable_size)
441
442 WriteProtect(crosfw.LoadMainFirmware().GetFileName(), 'main', 'RO_SECTION')
Tammo Spalink86a61c62012-05-25 15:10:35 +0800443 _event_log.Log('wp', fw='main')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800444 ec_fw_file = crosfw.LoadEcFirmware().GetFileName()
445 if ec_fw_file is not None:
446 WriteProtect(ec_fw_file, 'ec', 'EC_RO')
Tammo Spalink86a61c62012-05-25 15:10:35 +0800447 _event_log.Log('wp', fw='ec')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800448 else:
449 logging.warning('EC not write protected (seems there is no EC flash).')
450
451
452@Command('clear_gbb_flags')
453def ClearGbbFlags(options):
454 """Zero out the GBB flags, in preparation for transition to release state.
455
456 No GBB flags are set in release/shipping state, but they are useful
457 for factory/development. See "gbb_utility --flags" for details.
458 """
Tammo Spalink461ddce2012-05-10 19:28:55 +0800459 script = FindScript('clear_gbb_flags.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800460 result = Shell(script)
461 if not result.success:
462 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
Tammo Spalink86a61c62012-05-25 15:10:35 +0800463 _event_log.Log('clear_gbb_flags')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800464
465
466@Command('prepare_wipe',
467 CmdArg('--fast', action='store_true',
468 help='use non-secure but faster wipe method.'))
469def PrepareWipe(options):
470 """Prepare system for transition to release state in next reboot."""
Tammo Spalink461ddce2012-05-10 19:28:55 +0800471 script = FindScript('prepare_wipe.sh')
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800472 tag = 'fast' if options.fast else ''
473 rootfs_device = GetReleaseRootPartitionPath()
474 result = Shell('FACTORY_WIPE_TAGS=%s %s %s' % (tag, script, rootfs_device))
475 if not result.success:
476 raise Error, '%r failed, stderr: %r' % (script, result.stderr)
477
478
479@Command('verify',
480 CmdArg('--dev', action='store_true',
481 help='Do not verify switch state (dev mode and fw wp).'),
482 _hwdb_path_cmd_arg)
483def Verify(options):
484 """Verifies if whole factory process is ready for finalization.
485
486 This routine performs all the necessary checks to make sure the
487 device is ready to be finalized, but does not modify state. These
488 checks include dev switch, firmware write protection switch, hwid,
489 system time, keys, and root file system.
490 """
491 if not options.dev:
492 VerifyDevSwitch({})
493 VerifyWpSwitch({})
494 VerifyHwid(options)
495 VerifySystemTime({})
496 VerifyKeys({})
497 VerifyRootFs({})
498
499
Tammo Spalink86a61c62012-05-25 15:10:35 +0800500@Command('log_system_details')
501def LogSystemDetails(options):
502 """Write miscellaneous system details to the event log."""
503 raw_cs_data = Shell('crossystem').stdout.strip().splitlines()
504 # The crossytem output contains many lines like:
505 # 'key = value # description'
506 # Use regexps to pull out the key-value pairs and build a dict.
507 cs_data = dict((k, v.strip()) for k, v in
508 map(lambda x: re.findall(r'\A(\S+)\s+=\s+(.*)#.*\Z', x)[0],
509 raw_cs_data))
510 _event_log.Log(
511 'system_details',
512 platform_name=Shell('mosys platform name').stdout.strip(),
513 crossystem=cs_data,
514 modem_status=Shell('modem status').stdout.splitlines(),
515 ec_wp_status=Shell(
516 'flashrom -p internal:bus=lpc --get-size 2>/dev/null && '
517 'flashrom -p internal:bus=lpc --wp-status || '
518 'echo "EC is not available."').stdout,
519 bios_wp_status = Shell(
520 'flashrom -p internal:bus=spi --wp-status').stdout)
521
522
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800523_upload_method_cmd_arg = CmdArg(
524 '--upload_method', metavar='METHOD:PARAM',
525 help=('How to perform the upload. METHOD should be one of '
526 '{ftp, shopfloor, curl, cpfe, custom}.'))
527
528
529@Command('upload_report',
530 _upload_method_cmd_arg)
531def UploadReport(options):
532 """Create and a report containing key device details."""
Hung-Te Lin6bd16472012-06-20 16:26:47 +0800533 def NormalizeAsFileName(token):
534 return re.sub(r'\W+', '', token).strip()
Tammo Spalink86a61c62012-05-25 15:10:35 +0800535 ro_vpd = ReadRoVpd(crosfw.LoadMainFirmware().GetFileName())
536 device_sn = ro_vpd.get('serial_number', None)
537 if device_sn is None:
538 logging.warning('RO_VPD missing device serial number')
539 device_sn = 'MISSING_SN_' + TimedUuid()
Hung-Te Lin6bd16472012-06-20 16:26:47 +0800540 target_name = '%s_%s.tbz2' % (time.strftime('%Y%m%dT%H%M%SZ', time.gmtime()),
541 NormalizeAsFileName(device_sn))
Tammo Spalink86a61c62012-05-25 15:10:35 +0800542 target_path = os.path.join(gettempdir(), target_name)
543 # Intentionally ignoring dotfiles in EVENT_LOG_DIR.
544 tar_cmd = 'cd %s ; tar cjf %s *' % (EVENT_LOG_DIR, target_path)
545 tar_cmd += ' --add-file %s' % FACTORY_LOG_PATH
546 cmd_result = Shell(tar_cmd)
547 if not cmd_result.success:
548 raise Error('unable to tar event logs, cmd %r failed, stderr: %r' %
549 (tar_cmd, cmd_result.stderr))
550 if options.upload_method is None or options.upload_method == 'none':
551 logging.warning('REPORT UPLOAD SKIPPED (report left at %s)', target_path)
552 return
553 method, param = options.upload_method.split(':', 1)
554 if method == 'shopfloor':
555 report_upload.ShopFloorUpload(target_path, param)
556 elif method == 'ftp':
Jay Kim360c1dd2012-06-25 10:58:11 -0700557 report_upload.FtpUpload(target_path, 'ftp:' + param)
Tammo Spalink86a61c62012-05-25 15:10:35 +0800558 elif method == 'ftps':
559 report_upload.CurlUrlUpload(target_path, '--ftp-ssl-reqd ftp:%s' % param)
560 elif method == 'cpfe':
561 report_upload.CpfeUpload(target_path, param)
562 else:
563 raise Error('unknown report upload method %r', method)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800564
565
566@Command('finalize',
567 CmdArg('--dev', action='store_true',
568 help='Do not verify or alter write protection or dev mode.'),
569 CmdArg('--fast', action='store_true',
570 help='use non-secure but faster wipe method.'),
571 _hwdb_path_cmd_arg,
572 _upload_method_cmd_arg)
573def Finalize(options):
574 """Verify system readiness and trigger transition into release state.
575
576 This routine first verifies system state (see verify command), then
577 clears all of the testing flags from the GBB, then modifies firmware
578 bitmaps to match locale. Then it enables firmware write protection
579 and sets the necessary boot flags to cause wipe of the factory image
580 on the next boot.
581 """
582 ClearGbbFlags({})
583 Verify(options)
584 SetFirmwareBitmapLocale({})
585 if not options.dev:
586 EnableFwWp({})
Jon Salza0f58e02012-05-29 19:33:39 +0800587 LogSystemDetails(options)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800588 UploadReport(options)
589 PrepareWipe(options)
590
591
592def Main():
593 """Run sub-command specified by the command line args."""
594 options = ParseCmdline(
595 'Perform Google required factory tests.',
596 CmdArg('-l', '--log', metavar='PATH',
597 help='Write logs to this file.'),
Tammo Spalink8fab5312012-05-28 18:33:30 +0800598 verbosity_cmd_arg)
Tammo Spalink9a96b8a2012-04-03 11:10:41 +0800599 SetupLogging(options.verbosity, options.log)
600 logging.debug('gooftool options: %s', repr(options))
601 try:
602 logging.debug('GOOFTOOL command %r', options.command_name)
603 options.command(options)
604 logging.info('GOOFTOOL command %r SUCCESS', options.command_name)
605 except Error, e:
606 logging.exception(e)
607 sys.exit('GOOFTOOL command %r ERROR: %s' % (options.command_name, e))
608 except Exception, e:
609 logging.exception(e)
610 sys.exit('UNCAUGHT RUNTIME EXCEPTION %s' % e)
611
612
613if __name__ == '__main__':
614 Main()