blob: df3150bd58718be4b02fd71d2876ff222c91af81 [file] [log] [blame] [edit]
# Copyright 2012 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Uses HWID v3 to generate, encode, and verify the device's HWID.
Description
-----------
This test generates and verifies HWID of device under testing.
Test Procedure
--------------
This test does not require operator interaction.
When ``generate`` is ``True``, this test will do the following:
1. If ``enable_factory_server`` is ``True``, it downloads latest HWID database
from Google Factory Server.
2. Collect materials (including probed results, device data, and optionally
the vpd data) from DUT for generating the HWID string. This step is
equivalent to executing ``hwid collect-material`` in shell.
3. Generate HWID by command ``hwid generate --probed-results-file
<probed-results> --material-file <hwid-material-file> --json-output``.
4. Verify generated HWID by ``hwid verify --material-file <hwid-material-file>
--phase <phase>``.
5. Write HWID to GBB by ``hwid write <generated-hwid>``.
If ``generate`` is ``False``, then instead of running ``hwid generate`` in step
3, it will just use ``hwid read`` to read saved HWID from the device. And step
5 will be skipped.
If ``vpd_data_file`` is set to a string of ``<path>``, the vpd-related
arguments for ``hwid`` tool will be ``--vpd-data-file <path>``; otherwise if
``run_vpd`` is ``True``, the vpd-related arguments for ``hwid`` tool will be
``--run-vpd``. Note that ``run_vpd=True`` has no effect if ``vpd_data_file``
is set.
Dependency
----------
Various of system utilities like ``vpd`` and ``flashrom`` will be invoked to
grab materials from DUT.
Examples
--------
To generate and verify HWID, add this to your test list::
{
"pytest_name": "hwid",
"label": "Write HWID"
}
If you are doing RMA, to allow ``deprecated`` components, you need to enable RMA
mode::
{
"pytest_name": "hwid",
"label": "Write HWID",
"args": {
"rma_mode": true
}
}
New HWID with 'configless' format is still under testing. To enable this
feature, set argument like this::
{
"pytest_name": "hwid",
"label": "Write HWID",
"args": {
"enable_configless_fields": true
}
}
To override the default project name, use ``project`` arguments::
{
"pytest_name": "hwid",
"label": "Write HWID",
"args": {
"project": "nirwen"
}
}
"""
import json
import logging
import yaml
from cros.factory.device import device_utils
from cros.factory.test import device_data
from cros.factory.test.i18n import _
from cros.factory.test.rules import phase
from cros.factory.test import session
from cros.factory.test import test_case
from cros.factory.test.utils import deploy_utils
from cros.factory.test.utils import update_utils
from cros.factory.testlog import testlog
from cros.factory.utils.arg_utils import Arg
from cros.factory.utils import file_utils
class HWIDV3Test(test_case.TestCase):
"""A test for generating and verifying HWID v3."""
related_components = (test_case.TestCategory.HARDWARE_ID, )
ARGS = [
Arg('generate', bool,
'Generate and write the HWID (if False, only verify it).',
default=True),
Arg('enable_factory_server', bool,
'Update hwid data from factory server.', default=True),
Arg('run_vpd', bool,
'Run the `vpd` commandline tool to get the vpd data.', default=False),
Arg('vpd_data_file', str, 'Read the specified file to get the vpd data.',
default=None),
Arg('rma_mode', bool,
'Enable rma_mode, do not check for deprecated components.',
default=False),
Arg('verify_checksum', bool, 'Enable database checksum verification.',
default=True),
Arg('enable_configless_fields', bool, 'Include the configless fields',
default=False),
Arg('enable_component_status_check_on_pvt', bool,
'Enable component status check if the phase is PVT.', default=True),
Arg('include_brand_code', bool, 'Include RLZ brand code', default=True),
Arg('project', str, 'Project name of the HWID.', default=None),
]
def setUp(self):
self._dut = device_utils.CreateDUTInterface()
self.factory_tools = deploy_utils.CreateFactoryTools(self._dut)
self.tmpdir = self._dut.temp.mktemp(is_dir=True, prefix='hwid')
def tearDown(self):
self._dut.Call(['rm', '-rf', self.tmpdir])
def AppendProjectArg(self, cmd):
"""Append the project name to the command list if the name is not None."""
# yapf: disable
if self.args.project: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
# yapf: disable
cmd += ['--project', self.args.project] # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
def BuildCollectMaterialCommand(self, hwid_material_file: str):
"""Build the command for `hwid collect-material`.
Args:
hwid_material_file: The path of hwid_material_file on DUT.
Returns:
A list of arguments in the command.
"""
# pass device info to DUT
device_info_file = self._dut.path.join(self.tmpdir, 'device_info')
device_info = device_data.GetAllDeviceData()
with file_utils.UnopenedTemporaryFile() as f:
with open(f, 'w', encoding='utf8') as fp:
yaml.safe_dump(device_info, fp)
self._dut.SendFile(f, device_info_file)
collect_material_cmd = ['hwid', 'collect-material']
collect_material_cmd.extend(['--device-info-file', device_info_file])
# yapf: disable
if self.args.vpd_data_file: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
# yapf: disable
collect_material_cmd.extend(['--vpd-data-file', self.args.vpd_data_file]) # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
# yapf: disable
if self.args.run_vpd: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
collect_material_cmd.append('--run-vpd')
collect_material_cmd.extend(['--output-file', hwid_material_file])
return collect_material_cmd
def runTest(self):
testlog.LogParam(name='phase', value=str(phase.GetPhase()))
# yapf: disable
phase.AssertStartingAtPhase(phase.EVT, self.args.verify_checksum, # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
'HWID checksum must be verified')
phase.AssertStartingAtPhase(
# yapf: disable
phase.PVT, self.args.project is None, # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
'Should not use `project` option in this phase')
# yapf: disable
if self.args.enable_factory_server: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
update_utils.UpdateHWIDDatabase(self._dut)
# yapf: disable
self.ui.SetState(_('Collecting DUT materials...')) # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
hwid_material_file = self._dut.path.join(self.tmpdir, 'hwid_material_file')
collect_material_cmd = self.BuildCollectMaterialCommand(hwid_material_file)
self.factory_tools.Call(collect_material_cmd, log=True)
hwid_material = self._dut.ReadFile(hwid_material_file)
testlog.LogParam(name='hwid_material', value=hwid_material)
testlog.UpdateParam(name='hwid_material',
description='materials to generate HWID string')
# yapf: disable
if self.args.generate: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
# yapf: disable
self.ui.SetState(_('Generating HWID (v3)...')) # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
generate_cmd = [
'hwid', 'generate', '--material-file', hwid_material_file,
'--json-output'
]
# yapf: disable
if self.args.rma_mode: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
generate_cmd += ['--rma-mode']
# yapf: disable
if not self.args.verify_checksum: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
generate_cmd += ['--no-verify-checksum']
# yapf: disable
if self.args.enable_configless_fields: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
generate_cmd += ['--with-configless-fields']
# yapf: disable
if not self.args.include_brand_code: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
generate_cmd += ['--no-brand-code']
self.AppendProjectArg(generate_cmd)
output = self.factory_tools.CallOutput(generate_cmd)
self.assertIsNotNone(output, 'HWID generate failed.')
hwid = json.loads(output)
encoded_string = hwid['encoded_string']
session.console.info('Generated HWID: %s', encoded_string)
# try to decode HWID
decode_cmd = ['hwid', 'decode']
self.AppendProjectArg(decode_cmd)
decode_cmd += [encoded_string]
decoded_hwid = self.factory_tools.CallOutput(decode_cmd)
self.assertIsNotNone(decoded_hwid, 'HWID decode failed.')
logging.info('HWID Database checksum: %s', hwid['database_checksum'])
testlog.LogParam(name='generated_hwid', value=encoded_string)
testlog.LogParam(name='database_checksum',
value=hwid['database_checksum'])
testlog.LogParam(name='decoded_hwid', value=decoded_hwid)
device_data.UpdateDeviceData({'hwid': encoded_string})
else:
read_cmd = ['hwid', 'read']
self.AppendProjectArg(read_cmd)
encoded_string = self.factory_tools.CheckOutput(read_cmd).strip()
# yapf: disable
self.ui.SetState( # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
_('Verifying HWID (v3): {encoded_string}...',
encoded_string=(encoded_string or _('(unchanged)'))))
verify_cmd = [
'hwid', 'verify', '--material-file', hwid_material_file, '--phase',
str(phase.GetPhase())
]
# yapf: disable
if self.args.rma_mode: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
verify_cmd += ['--rma-mode']
# yapf: disable
if not self.args.verify_checksum: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
verify_cmd += ['--no-verify-checksum']
# yapf: disable
if not self.args.enable_component_status_check_on_pvt: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
verify_cmd += ['--no-pvt-component-status-check']
self.AppendProjectArg(verify_cmd)
verify_cmd += [encoded_string]
output = self.factory_tools.CheckOutput(verify_cmd)
self.assertTrue('Verification passed.' in output)
testlog.LogParam(name='verified_hwid', value=encoded_string)
# yapf: disable
if self.args.generate: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
# yapf: disable
self.ui.SetState( # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
_('Setting HWID (v3): {encoded_string}...',
encoded_string=encoded_string))
write_cmd = ['hwid', 'write']
self.AppendProjectArg(write_cmd)
write_cmd += [encoded_string]
self.factory_tools.CheckCall(write_cmd)