blob: ed4b4aa4a440da9a6ffe83aba7282a7358283db2 [file] [log] [blame]
#!/bin/env python
#
# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Command-line interface for HWID v3 utilities."""
import json
import logging
import os
import yaml
import factory_common # pylint: disable=W0611
from cros.factory.hwid.v3 import common
from cros.factory.hwid.v3 import rule
from cros.factory.hwid.v3 import database
from cros.factory.hwid.v3 import hwid_utils
from cros.factory.test import shopfloor
from cros.factory.test.rules import phase
from cros.factory.tools import build_board
from cros.factory.utils.argparse_utils import CmdArg
from cros.factory.utils.argparse_utils import Command
from cros.factory.utils.argparse_utils import ParseCmdline
from cros.factory.utils import sys_utils
from cros.factory.utils import process_utils
_COMMON_ARGS = [
CmdArg('-p', '--hwid-db-path', default=None,
help='path to the HWID database directory'),
CmdArg('-b', '--board', default=None,
help=('board name of the HWID database to load.\n'
'(required if not running on a DUT)')),
CmdArg('-v', '--verbose', default=False, action='store_true',
help='enable verbose output'),
CmdArg('--no-verify-checksum', default=False, action='store_true',
help='do not check database checksum'),
CmdArg('--phase', default=None,
help=('override phase for phase checking (defaults to the current '
'as returned by the "factory phase" command)')),
]
class Arg(object):
"""A simple class to store arguments passed to the add_argument method of
argparse module.
"""
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
@Command(
'generate',
CmdArg('--probed-results-file', default=None,
help=('a file with probed results.\n'
'(required if not running on a DUT)')),
CmdArg('--device-info-file', default=None,
help=('a file with device info.\n'
'(required if not running on a DUT.)\n'
'example content of this file:\n'
' component.antenna: ACN\n'
' component.has_cellular: True\n'
' component.keyboard: US_API\n')),
CmdArg('--rma-mode', default=False, action='store_true',
help='whether to enable RMA mode'),
CmdArg('--json-output', default=False, action='store_true',
help='whether to dump result in JSON format'))
def GenerateHWIDWrapper(options):
"""Generates HWID."""
probed_results = hwid_utils.GetProbedResults(options.probed_results_file)
# Select right device info (from file or shopfloor).
if options.device_info_file:
device_info = hwid_utils.GetDeviceInfo(options.device_info_file)
elif sys_utils.InChroot():
raise ValueError('Cannot get device info from shopfloor in chroot. '
'Please specify device info with an input file. If you '
'are running with command-line, use --device_info_file')
else:
device_info = shopfloor.GetDeviceData()
vpd = hwid_utils.GetVPD(probed_results)
verbose_output = {
'device_info': device_info,
'probed_results': probed_results,
'vpd': vpd
}
logging.debug(yaml.dump(verbose_output, default_flow_style=False))
hwid = hwid_utils.GenerateHWID(options.database, probed_results, device_info,
vpd, options.rma_mode)
if options.json_output:
print json.dumps({
'encoded_string': hwid.encoded_string,
'binary_string': hwid.binary_string,
'hwdb_checksum': hwid.database.checksum})
else:
print 'Encoded HWID string: %s' % hwid.encoded_string
print 'Binary HWID string: %s' % hwid.binary_string
@Command(
'decode',
CmdArg('hwid', nargs='?', default=None,
help='the HWID to decode.\n(required if not running on a DUT)'))
def DecodeHWIDWrapper(options):
"""Decodes HWID."""
encoded_string = options.hwid if options.hwid else hwid_utils.GetHWIDString()
decoded_hwid = hwid_utils.DecodeHWID(options.database, encoded_string)
print yaml.dump(hwid_utils.ParseDecodedHWID(decoded_hwid),
default_flow_style=False)
@Command(
'verify',
CmdArg('hwid', nargs='?', default=None,
help='the HWID to verify.\n(required if not running on a DUT)'),
CmdArg('--probed-results-file', default=None,
help=('a file with probed results.\n'
'(required if not running on a DUT)')),
CmdArg('--rma-mode', default=False, action='store_true',
help='whether to enable RMA mode.'))
def VerifyHWIDWrapper(options):
"""Verifies HWID."""
encoded_string = options.hwid if options.hwid else hwid_utils.GetHWIDString()
probed_results = hwid_utils.GetProbedResults(options.probed_results_file)
vpd = hwid_utils.GetVPD(probed_results)
hwid_utils.VerifyHWID(options.database, encoded_string, probed_results, vpd,
options.rma_mode, options.phase)
# No exception raised. Verification was successful.
print 'Verification passed.'
@Command(
'verify-components',
CmdArg('--probed-results-file', default=None,
help=('a file with probed results.\n'
'(required if not running on a DUT)')),
CmdArg('--json_output', action='store_true', default=False,
help='Output the returned value in json format.'),
CmdArg('-c', '--components', default=None,
help='the list of component classes to verify'),
CmdArg('--no-fast-fw-probe', dest='fast_fw_probe', action='store_false',
default=True, help='probe only firmware and EC version strings'))
def VerifyComponentsWrapper(options):
"""Verifies components."""
redirect_stdout = process_utils.DummyFile() if options.json_output else None
with process_utils.RedirectStandardStreams(stdout=redirect_stdout):
if not options.components:
probed_results = hwid_utils.GetProbedResults(
infile=options.probed_results_file,
fast_fw_probe=options.fast_fw_probe)
else:
options.components = [v.strip() for v in options.components.split(',')]
if set(['ro_ec_firmware', 'ro_main_firmware']) & set(options.components):
probe_volatile = True
else:
probe_volatile = False
probed_results = hwid_utils.GetProbedResults(
infile=options.probed_results_file,
target_comp_classes=options.components,
fast_fw_probe=options.fast_fw_probe,
probe_volatile=probe_volatile, probe_initial_config=False)
result = hwid_utils.VerifyComponents(options.database, probed_results,
options.components)
if options.json_output:
def _ConvertToDict(obj):
if isinstance(obj, (common.ProbedComponentResult, rule.Value)):
return _ConvertToDict(obj.__dict__)
if isinstance(obj, list):
return [_ConvertToDict(item) for item in obj]
if isinstance(obj, tuple):
return tuple([_ConvertToDict(item) for item in obj])
if isinstance(obj, dict):
return {key: _ConvertToDict(value) for key, value in obj.iteritems()}
return obj
new_result = _ConvertToDict(result)
print json.dumps(new_result)
else:
failed = []
waive_list = []
if options.fast_fw_probe:
waive_list = ['key_recovery', 'key_root', 'hash_gbb']
for comp_cls, comps in result.iteritems():
if comp_cls in waive_list:
continue
for comp_result in comps:
if comp_result.error:
failed.append('%s: %s' % (comp_cls, comp_result.error))
if failed:
print 'Verification failed for the following components:'
print '\n'.join(failed)
else:
print 'Verification passed.'
@Command(
'write',
CmdArg('hwid', help='the encoded HWID string to write'))
def WriteHWIDWrapper(options):
"""Writes HWID to firmware GBB."""
hwid_utils.WriteHWID(options.hwid)
print 'HWID %r written to firmware GBB.' % options.hwid
@Command('read')
def ReadHWIDWrapper(options): # pylint: disable=unused-argument
"""Reads HWID from firmware GBB."""
print hwid_utils.GetHWIDString()
@Command(
'list-components',
CmdArg('comp_class', nargs='*', default=None,
help='the component classes to look up'))
def ListComponentsWrapper(options):
"""Lists components of the given class."""
components_list = hwid_utils.ListComponents(options.database,
options.comp_class)
print yaml.safe_dump(components_list, default_flow_style=False)
@Command(
'enumerate-hwid',
CmdArg('-i', '--image_id', default=None,
help='the image ID to enumerate.'),
CmdArg('-s', '--status', default='supported',
choices=['supported', 'released', 'all'],
help='the status of components to enumerate'))
def EnumerateHWIDWrapper(options):
"""Enumerates possible HWIDs."""
# Enumerating may take a very long time so we want to verbosely make logs.
logging.debug('Enumerating all HWIDs...')
hwids = hwid_utils.EnumerateHWID(options.database, options.image_id,
options.status)
logging.debug('Printing %d sorted HWIDs...', len(hwids))
for k, v in sorted(hwids.iteritems()):
print '%s: %s' % (k, v)
@Command('verify-database')
def VerifyHWIDDatabase(options):
"""Verifies the given HWID database."""
# Do nothing here since all the verifications are done when loading the
# database with HWID library.
print 'Database %s verified' % options.board
def ParseOptions(args=None):
"""Parse arguments and generate necessary options."""
return ParseCmdline('HWID command-line utilities', *_COMMON_ARGS,
args_to_parse=args)
def InitializeDefaultOptions(options):
if not options.hwid_db_path:
options.hwid_db_path = common.DEFAULT_HWID_DATA_PATH
if not options.board:
options.board = common.ProbeBoard()
board = build_board.BuildBoard(options.board).base
board_variant = build_board.BuildBoard(options.board).variant
# Use the variant specific HWID db if one exists, else reuse the one
# from the base board.
if board_variant and os.path.exists(
os.path.join(options.hwid_db_path, board_variant.upper())):
board = board_variant
# Create the Database object here since it's common to all functions.
logging.debug('Loading database file %s/%s...', options.hwid_db_path,
board.upper())
options.database = database.Database.LoadFile(
os.path.join(options.hwid_db_path, board.upper()),
verify_checksum=(not options.no_verify_checksum))
phase.OverridePhase(options.phase)
def Main():
"""Parses options, sets up logging, and runs the given subcommand."""
options = ParseOptions()
if options.verbose:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.INFO)
InitializeDefaultOptions(options)
logging.debug('Perform command <%s>.. ', options.command_name)
options.command(options)
if __name__ == '__main__':
Main()