676 lines
23 KiB
Python
676 lines
23 KiB
Python
# Copyright 2017 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.
|
|
|
|
import argparse
|
|
import logging
|
|
import re
|
|
|
|
from autotest_lib.client.common_lib import error
|
|
|
|
|
|
RO = 'ro'
|
|
RW = 'rw'
|
|
BID = 'bid'
|
|
CR50_PROD = '/opt/google/cr50/firmware/cr50.bin.prod'
|
|
CR50_PREPVT = '/opt/google/cr50/firmware/cr50.bin.prepvt'
|
|
CR50_STATE = '/var/cache/cr50*'
|
|
CR50_VERSION = '/var/cache/cr50-version'
|
|
GET_CR50_VERSION = 'cat %s' % CR50_VERSION
|
|
GET_CR50_MESSAGES ='grep "cr50-.*\[" /var/log/messages'
|
|
UPDATE_FAILURE = 'unexpected cr50-update exit code'
|
|
DUMMY_VER = '-1.-1.-1'
|
|
# This dictionary is used to search the gsctool output for the version strings.
|
|
# There are two gsctool commands that will return versions: 'fwver' and
|
|
# 'binvers'.
|
|
#
|
|
# 'fwver' is used to get the running RO and RW versions from cr50
|
|
# 'binvers' gets the version strings for each RO and RW region in the given
|
|
# file
|
|
#
|
|
# The value in the dictionary is the regular expression that can be used to
|
|
# find the version strings for each region.
|
|
#
|
|
# --fwver
|
|
# example output:
|
|
# open_device 18d1:5014
|
|
# found interface 3 endpoint 4, chunk_len 64
|
|
# READY
|
|
# -------
|
|
# start
|
|
# target running protocol version 6
|
|
# keyids: RO 0xaa66150f, RW 0xde88588d
|
|
# offsets: backup RO at 0x40000, backup RW at 0x44000
|
|
# Current versions:
|
|
# RO 0.0.10
|
|
# RW 0.0.21
|
|
# match groupdict:
|
|
# {
|
|
# 'ro': '0.0.10',
|
|
# 'rw': '0.0.21'
|
|
# }
|
|
#
|
|
# --binvers
|
|
# example output:
|
|
# read 524288(0x80000) bytes from /tmp/cr50.bin
|
|
# RO_A:0.0.10 RW_A:0.0.21[00000000:00000000:00000000]
|
|
# RO_B:0.0.10 RW_B:0.0.21[00000000:00000000:00000000]
|
|
# match groupdict:
|
|
# {
|
|
# 'rw_b': '0.0.21',
|
|
# 'rw_a': '0.0.21',
|
|
# 'ro_b': '0.0.10',
|
|
# 'ro_a': '0.0.10',
|
|
# 'bid_a': '00000000:00000000:00000000',
|
|
# 'bid_b': '00000000:00000000:00000000'
|
|
# }
|
|
VERSION_RE = {
|
|
'--fwver' : '\nRO (?P<ro>\S+).*\nRW (?P<rw>\S+)',
|
|
'--binvers' : 'RO_A:(?P<ro_a>[\d\.]+).*' \
|
|
'RW_A:(?P<rw_a>[\d\.]+)(\[(?P<bid_a>[\d\:A-z]+)\])?.*' \
|
|
'RO_B:(?P<ro_b>\S+).*' \
|
|
'RW_B:(?P<rw_b>[\d\.]+)(\[(?P<bid_b>[\d\:A-z]+)\])?.*',
|
|
}
|
|
UPDATE_TIMEOUT = 60
|
|
UPDATE_OK = 1
|
|
|
|
MP_BID_FLAGS = 0x7f80
|
|
ERASED_BID_INT = 0xffffffff
|
|
ERASED_BID_STR = hex(ERASED_BID_INT)
|
|
# With an erased bid, the flags and board id will both be erased
|
|
ERASED_CHIP_BID = (ERASED_BID_INT, ERASED_BID_INT, ERASED_BID_INT)
|
|
# Any image with this board id will run on any device
|
|
EMPTY_IMAGE_BID = '00000000:00000000:00000000'
|
|
EMPTY_IMAGE_BID_CHARACTERS = set(EMPTY_IMAGE_BID)
|
|
SYMBOLIC_BID_LENGTH = 4
|
|
|
|
gsctool = argparse.ArgumentParser()
|
|
gsctool.add_argument('-a', '--any', dest='universal', action='store_true')
|
|
# use /dev/tpm0 to send the command
|
|
gsctool.add_argument('-s', '--systemdev', dest='systemdev', action='store_true')
|
|
gsctool.add_argument('-o', '--ccd_open', dest='ccd_open', action='store_true')
|
|
# Any command used for something other than updating. These commands should
|
|
# never timeout because they do not force cr50 to reboot. They should all just
|
|
# return information about cr50 and should only have a nonzero exit status if
|
|
# something went wrong.
|
|
gsctool.add_argument('-b', '--binvers', '-f', '--fwver', '-g', '--getbootmode',
|
|
'-i', '--board_id', '-r', '--rma_auth', '-F', '--factory',
|
|
'-m', '--tpm_mode', '-L', '--flog',
|
|
dest='info_cmd', action='store_true')
|
|
# upstart and post_reset will post resets instead of rebooting immediately
|
|
gsctool.add_argument('-u', '--upstart', '-p', '--post_reset', dest='post_reset',
|
|
action='store_true')
|
|
gsctool.add_argument('extras', nargs=argparse.REMAINDER)
|
|
|
|
|
|
def AssertVersionsAreEqual(name_a, ver_a, name_b, ver_b):
|
|
"""Raise an error ver_a isn't the same as ver_b
|
|
|
|
Args:
|
|
name_a: the name of section a
|
|
ver_a: the version string for section a
|
|
name_b: the name of section b
|
|
ver_b: the version string for section b
|
|
|
|
Raises:
|
|
AssertionError if ver_a is not equal to ver_b
|
|
"""
|
|
assert ver_a == ver_b, ('Versions do not match: %s %s %s %s' %
|
|
(name_a, ver_a, name_b, ver_b))
|
|
|
|
|
|
def GetNewestVersion(ver_a, ver_b):
|
|
"""Compare the versions. Return the newest one. If they are the same return
|
|
None."""
|
|
a = [int(x) for x in ver_a.split('.')]
|
|
b = [int(x) for x in ver_b.split('.')]
|
|
|
|
if a > b:
|
|
return ver_a
|
|
if b > a:
|
|
return ver_b
|
|
return None
|
|
|
|
|
|
def GetVersion(versions, name):
|
|
"""Return the version string from the dictionary.
|
|
|
|
Get the version for each key in the versions dictionary that contains the
|
|
substring name. Make sure all of the versions match and return the version
|
|
string. Raise an error if the versions don't match.
|
|
|
|
Args:
|
|
version: dictionary with the partition names as keys and the
|
|
partition version strings as values.
|
|
name: the string used to find the relevant items in versions.
|
|
|
|
Returns:
|
|
the version from versions or "-1.-1.-1" if an invalid RO was detected.
|
|
"""
|
|
ver = None
|
|
key = None
|
|
for k, v in versions.iteritems():
|
|
if name in k:
|
|
if v == DUMMY_VER:
|
|
logging.info('Detected invalid %s %s', name, v)
|
|
return v
|
|
elif ver:
|
|
AssertVersionsAreEqual(key, ver, k, v)
|
|
else:
|
|
ver = v
|
|
key = k
|
|
return ver
|
|
|
|
|
|
def FindVersion(output, arg):
|
|
"""Find the ro and rw versions.
|
|
|
|
Args:
|
|
output: The string to search
|
|
arg: string representing the gsctool option, either '--binvers' or
|
|
'--fwver'
|
|
|
|
Returns:
|
|
a tuple of the ro and rw versions
|
|
"""
|
|
versions = re.search(VERSION_RE[arg], output)
|
|
if not versions:
|
|
raise Exception('Unable to determine version from: %s' % output)
|
|
|
|
versions = versions.groupdict()
|
|
ro = GetVersion(versions, RO)
|
|
rw = GetVersion(versions, RW)
|
|
# --binver is the only gsctool command that may have bid keys in its
|
|
# versions dictionary. If no bid keys exist, bid will be None.
|
|
bid = GetVersion(versions, BID)
|
|
# Use GetBoardIdInfoString to standardize all board ids to the non
|
|
# symbolic form.
|
|
return ro, rw, GetBoardIdInfoString(bid, symbolic=False)
|
|
|
|
|
|
def GetSavedVersion(client):
|
|
"""Return the saved version from /var/cache/cr50-version
|
|
|
|
Some boards dont have cr50.bin.prepvt. They may still have prepvt flags.
|
|
It is possible that cr50-update wont successfully run in this case.
|
|
Return None if the file doesn't exist.
|
|
|
|
Returns:
|
|
the version saved in cr50-version or None if cr50-version doesn't exist
|
|
"""
|
|
if not client.path_exists(CR50_VERSION):
|
|
return None
|
|
|
|
result = client.run(GET_CR50_VERSION).stdout.strip()
|
|
return FindVersion(result, '--fwver')
|
|
|
|
|
|
def StopTrunksd(client):
|
|
"""Stop trunksd on the client"""
|
|
if 'running' in client.run('status trunksd').stdout:
|
|
client.run('stop trunksd')
|
|
|
|
|
|
def GSCTool(client, args, ignore_status=False):
|
|
"""Run gsctool with the given args.
|
|
|
|
Args:
|
|
client: the object to run commands on
|
|
args: a list of strings that contiain the gsctool args
|
|
|
|
Returns:
|
|
the result of gsctool
|
|
"""
|
|
options = gsctool.parse_args(args)
|
|
|
|
if options.systemdev:
|
|
StopTrunksd(client)
|
|
|
|
# If we are updating the cr50 image, gsctool will return a non-zero exit
|
|
# status so we should ignore it.
|
|
ignore_status = not options.info_cmd or ignore_status
|
|
# immediate reboots are only honored if the command is sent using /dev/tpm0
|
|
expect_reboot = ((options.systemdev or options.universal) and
|
|
not options.post_reset and not options.info_cmd)
|
|
|
|
result = client.run('gsctool %s' % ' '.join(args),
|
|
ignore_status=ignore_status,
|
|
ignore_timeout=expect_reboot,
|
|
timeout=UPDATE_TIMEOUT)
|
|
|
|
# After a posted reboot, the gsctool exit code should equal 1.
|
|
if (result and result.exit_status and result.exit_status != UPDATE_OK and
|
|
not ignore_status):
|
|
logging.debug(result)
|
|
raise error.TestFail('Unexpected gsctool exit code after %s %d' %
|
|
(' '.join(args), result.exit_status))
|
|
return result
|
|
|
|
|
|
def GetVersionFromUpdater(client, args):
|
|
"""Return the version from gsctool"""
|
|
result = GSCTool(client, args).stdout.strip()
|
|
return FindVersion(result, args[0])
|
|
|
|
|
|
def GetFwVersion(client):
|
|
"""Get the running version using 'gsctool --fwver'"""
|
|
return GetVersionFromUpdater(client, ['--fwver', '-a'])
|
|
|
|
|
|
def GetBinVersion(client, image=CR50_PROD):
|
|
"""Get the image version using 'gsctool --binvers image'"""
|
|
return GetVersionFromUpdater(client, ['--binvers', image])
|
|
|
|
|
|
def GetVersionString(ver):
|
|
"""Combine the RO and RW tuple into a understandable string"""
|
|
return 'RO %s RW %s%s' % (ver[0], ver[1],
|
|
' BID %s' % ver[2] if ver[2] else '')
|
|
|
|
|
|
def GetRunningVersion(client):
|
|
"""Get the running Cr50 version.
|
|
|
|
The version from gsctool and /var/cache/cr50-version should be the
|
|
same. Get both versions and make sure they match.
|
|
|
|
Args:
|
|
client: the object to run commands on
|
|
|
|
Returns:
|
|
running_ver: a tuple with the ro and rw version strings
|
|
|
|
Raises:
|
|
TestFail
|
|
- If the version in /var/cache/cr50-version is not the same as the
|
|
version from 'gsctool --fwver'
|
|
"""
|
|
running_ver = GetFwVersion(client)
|
|
saved_ver = GetSavedVersion(client)
|
|
|
|
if saved_ver:
|
|
AssertVersionsAreEqual('Running', GetVersionString(running_ver),
|
|
'Saved', GetVersionString(saved_ver))
|
|
return running_ver
|
|
|
|
|
|
def GetActiveCr50ImagePath(client):
|
|
"""Get the path the device uses to update cr50
|
|
|
|
Extract the active cr50 path from the cr50-update messages. This path is
|
|
determined by cr50-get-name based on the board id flag value.
|
|
|
|
Args:
|
|
client: the object to run commands on
|
|
|
|
Raises:
|
|
TestFail
|
|
- If cr50-update uses more than one path or if the path we find
|
|
is not a known cr50 update path.
|
|
"""
|
|
ClearUpdateStateAndReboot(client)
|
|
messages = client.run(GET_CR50_MESSAGES).stdout.strip()
|
|
paths = set(re.findall('/opt/google/cr50/firmware/cr50.bin[\S]+', messages))
|
|
if not paths:
|
|
raise error.TestFail('Could not determine cr50-update path')
|
|
path = paths.pop()
|
|
if len(paths) > 1 or (path != CR50_PROD and path != CR50_PREPVT):
|
|
raise error.TestFail('cannot determine cr50 path')
|
|
return path
|
|
|
|
|
|
def CheckForFailures(client, last_message):
|
|
"""Check for any unexpected cr50-update exit codes.
|
|
|
|
This only checks the cr50 update messages that have happened since
|
|
last_message. If a unexpected exit code is detected it will raise an error>
|
|
|
|
Args:
|
|
client: the object to run commands on
|
|
last_message: the last cr50 message from the last update run
|
|
|
|
Returns:
|
|
the last cr50 message in /var/log/messages
|
|
|
|
Raises:
|
|
TestFail
|
|
- If there is a unexpected cr50-update exit code after last_message
|
|
in /var/log/messages
|
|
"""
|
|
messages = client.run(GET_CR50_MESSAGES).stdout.strip()
|
|
if last_message:
|
|
messages = messages.rsplit(last_message, 1)[-1].split('\n')
|
|
failures = []
|
|
for message in messages:
|
|
if UPDATE_FAILURE in message:
|
|
failures.append(message)
|
|
if len(failures):
|
|
logging.info(messages)
|
|
raise error.TestFail('Detected unexpected exit code during update: '
|
|
'%s' % failures)
|
|
return messages[-1]
|
|
|
|
|
|
def VerifyUpdate(client, ver='', last_message=''):
|
|
"""Verify that the saved update state is correct and there were no
|
|
unexpected cr50-update exit codes since the last update.
|
|
|
|
Args:
|
|
client: the object to run commands on
|
|
ver: the expected version tuple (ro ver, rw ver)
|
|
last_message: the last cr50 message from the last update run
|
|
|
|
Returns:
|
|
new_ver: a tuple containing the running ro and rw versions
|
|
last_message: The last cr50 update message in /var/log/messages
|
|
"""
|
|
# Check that there were no unexpected reboots from cr50-result
|
|
last_message = CheckForFailures(client, last_message)
|
|
logging.debug('last cr50 message %s', last_message)
|
|
|
|
new_ver = GetRunningVersion(client)
|
|
if ver != '':
|
|
if DUMMY_VER != ver[0]:
|
|
AssertVersionsAreEqual('Old RO', ver[0], 'Updated RO', new_ver[0])
|
|
AssertVersionsAreEqual('Old RW', ver[1], 'Updated RW', new_ver[1])
|
|
return new_ver, last_message
|
|
|
|
|
|
def GetDevicePath(ext):
|
|
"""Return the device path for the .prod or .prepvt image."""
|
|
if ext == 'prod':
|
|
return CR50_PROD
|
|
elif ext == 'prepvt':
|
|
return CR50_PREPVT
|
|
raise error.TestError('Unsupported cr50 image type %r' % ext)
|
|
|
|
|
|
def ClearUpdateStateAndReboot(client):
|
|
"""Removes the cr50 status files in /var/cache and reboots the AP"""
|
|
# If any /var/cache/cr50* files exist, remove them.
|
|
result = client.run('ls %s' % CR50_STATE, ignore_status=True)
|
|
if not result.exit_status:
|
|
client.run('rm %s' % ' '.join(result.stdout.split()))
|
|
elif result.exit_status != 2:
|
|
# Exit status 2 means the file didn't exist. If the command fails for
|
|
# some other reason, raise an error.
|
|
logging.debug(result)
|
|
raise error.TestFail(result.stderr)
|
|
client.reboot()
|
|
|
|
|
|
def InstallImage(client, src, dest=CR50_PROD):
|
|
"""Copy the image at src to dest on the dut
|
|
|
|
Args:
|
|
client: the object to run commands on
|
|
src: the image location of the server
|
|
dest: the desired location on the dut
|
|
|
|
Returns:
|
|
The filename where the image was copied to on the dut, a tuple
|
|
containing the RO and RW version of the file
|
|
"""
|
|
# Send the file to the DUT
|
|
client.send_file(src, dest)
|
|
|
|
ver = GetBinVersion(client, dest)
|
|
client.run('sync')
|
|
return dest, ver
|
|
|
|
|
|
def GetBoardIdInfoTuple(board_id_str):
|
|
"""Convert the string into board id args.
|
|
|
|
Split the board id string board_id:(mask|board_id_inv):flags to a tuple of
|
|
its parts. Each element will be converted to an integer.
|
|
|
|
Returns:
|
|
board id int, mask|board_id_inv, and flags or None if its a universal
|
|
image.
|
|
"""
|
|
# In tests None is used for universal board ids. Some old images don't
|
|
# support getting board id, so we use None. Convert 0:0:0 to None.
|
|
if not board_id_str or set(board_id_str) == EMPTY_IMAGE_BID_CHARACTERS:
|
|
return None
|
|
|
|
board_id, param2, flags = board_id_str.split(':')
|
|
return GetIntBoardId(board_id), int(param2, 16), int(flags, 16)
|
|
|
|
|
|
def GetBoardIdInfoString(board_id_info, symbolic=False):
|
|
"""Convert the board id list or str into a symbolic or non symbolic str.
|
|
|
|
This can be used to convert the board id info list into a symbolic or non
|
|
symbolic board id string. It can also be used to convert a the board id
|
|
string into a board id string with a symbolic or non symbolic board id
|
|
|
|
Args:
|
|
board_id_info: A string of the form board_id:(mask|board_id_inv):flags
|
|
or a list with the board_id, (mask|board_id_inv), flags
|
|
|
|
Returns:
|
|
(board_id|symbolic_board_id):(mask|board_id_inv):flags. Will return
|
|
None if if the given board id info is empty or is not valid
|
|
"""
|
|
# Convert board_id_info to a tuple if it's a string.
|
|
if isinstance(board_id_info, str):
|
|
board_id_info = GetBoardIdInfoTuple(board_id_info)
|
|
|
|
if not board_id_info:
|
|
return None
|
|
|
|
board_id, param2, flags = board_id_info
|
|
# Get the hex string for board id
|
|
board_id = '%08x' % GetIntBoardId(board_id)
|
|
|
|
# Convert the board id hex to a symbolic board id
|
|
if symbolic:
|
|
board_id = GetSymbolicBoardId(board_id)
|
|
|
|
# Return the board_id_str:8_digit_hex_mask: 8_digit_hex_flags
|
|
return '%s:%08x:%08x' % (board_id, param2, flags)
|
|
|
|
|
|
def GetSymbolicBoardId(board_id):
|
|
"""Convert an integer board id to a symbolic string
|
|
|
|
Args:
|
|
board_id: the board id to convert to the symbolic board id
|
|
|
|
Returns:
|
|
the 4 character symbolic board id
|
|
"""
|
|
symbolic_board_id = ''
|
|
board_id = GetIntBoardId(board_id)
|
|
|
|
# Convert the int to a symbolic board id
|
|
for i in range(SYMBOLIC_BID_LENGTH):
|
|
symbolic_board_id += chr((board_id >> (i * 8)) & 0xff)
|
|
symbolic_board_id = symbolic_board_id[::-1]
|
|
|
|
# Verify the created board id is 4 characters
|
|
if len(symbolic_board_id) != SYMBOLIC_BID_LENGTH:
|
|
raise error.TestFail('Created invalid symbolic board id %s' %
|
|
symbolic_board_id)
|
|
return symbolic_board_id
|
|
|
|
|
|
def ConvertSymbolicBoardId(symbolic_board_id):
|
|
"""Convert the symbolic board id str to an int
|
|
|
|
Args:
|
|
symbolic_board_id: a ASCII string. It can be up to 4 characters
|
|
|
|
Returns:
|
|
the symbolic board id string converted to an int
|
|
"""
|
|
board_id = 0
|
|
for c in symbolic_board_id:
|
|
board_id = ord(c) | (board_id << 8)
|
|
return board_id
|
|
|
|
|
|
def GetIntBoardId(board_id):
|
|
""""Return the gsctool interpretation of board_id
|
|
|
|
Args:
|
|
board_id: a int or string value of the board id
|
|
|
|
Returns:
|
|
a int representation of the board id
|
|
"""
|
|
if type(board_id) == int:
|
|
return board_id
|
|
|
|
if len(board_id) <= SYMBOLIC_BID_LENGTH:
|
|
return ConvertSymbolicBoardId(board_id)
|
|
|
|
return int(board_id, 16)
|
|
|
|
|
|
def GetExpectedFlags(flags):
|
|
"""If flags are not specified, gsctool will set them to 0xff00
|
|
|
|
Args:
|
|
flags: The int value or None
|
|
|
|
Returns:
|
|
the original flags or 0xff00 if flags is None
|
|
"""
|
|
return flags if flags != None else 0xff00
|
|
|
|
|
|
def RMAOpen(client, cmd='', ignore_status=False):
|
|
"""Run gsctool RMA commands"""
|
|
return GSCTool(client, ['-a', '-r', cmd], ignore_status)
|
|
|
|
|
|
def GetChipBoardId(client):
|
|
"""Return the board id and flags
|
|
|
|
Args:
|
|
client: the object to run commands on
|
|
|
|
Returns:
|
|
a tuple with the int values of board id, board id inv, flags
|
|
|
|
Raises:
|
|
TestFail if the second board id response field is not ~board_id
|
|
"""
|
|
result = GSCTool(client, ['-a', '-i']).stdout.strip()
|
|
board_id_info = result.split('Board ID space: ')[-1].strip().split(':')
|
|
board_id, board_id_inv, flags = [int(val, 16) for val in board_id_info]
|
|
logging.info('BOARD_ID: %x:%x:%x', board_id, board_id_inv, flags)
|
|
|
|
if board_id == board_id_inv == ERASED_BID_INT:
|
|
if flags == ERASED_BID_INT:
|
|
logging.info('board id is erased')
|
|
else:
|
|
logging.info('board id type is erased')
|
|
elif board_id & board_id_inv:
|
|
raise error.TestFail('board_id_inv should be ~board_id got %x %x' %
|
|
(board_id, board_id_inv))
|
|
return board_id, board_id_inv, flags
|
|
|
|
|
|
def GetChipBIDFromImageBID(image_bid, brand):
|
|
"""Calculate a chip bid that will work with the image bid.
|
|
|
|
Returns:
|
|
A tuple of integers (bid type, ~bid type, bid flags)
|
|
"""
|
|
image_bid_tuple = GetBoardIdInfoTuple(image_bid)
|
|
# GetBoardIdInfoTuple returns None if the image isn't board id locked.
|
|
# Generate a Tuple of all 0s the rest of the function can use.
|
|
if not image_bid_tuple:
|
|
image_bid_tuple = (0, 0, 0)
|
|
|
|
image_bid, image_mask, image_flags = image_bid_tuple
|
|
if image_mask:
|
|
new_brand = GetSymbolicBoardId(image_bid)
|
|
else:
|
|
new_brand = brand
|
|
new_flags = image_flags or MP_BID_FLAGS
|
|
bid_type = GetIntBoardId(new_brand)
|
|
# If the board id type is erased, type_inv should also be unset.
|
|
if bid_type == ERASED_BID_INT:
|
|
return (ERASED_BID_INT, ERASED_BID_INT, new_flags)
|
|
return bid_type, 0xffffffff & ~bid_type, new_flags
|
|
|
|
|
|
def CheckChipBoardId(client, board_id, flags, board_id_inv=None):
|
|
"""Compare the given board_id and flags to the running board_id and flags
|
|
|
|
Interpret board_id and flags how gsctool would interpret them, then compare
|
|
those interpreted values to the running board_id and flags.
|
|
|
|
Args:
|
|
client: the object to run commands on
|
|
board_id: a hex str, symbolic str, or int value for board_id
|
|
board_id_inv: a hex str or int value of board_id_inv. Ignore
|
|
board_id_inv if None. board_id_inv is ~board_id unless
|
|
the board id is erased. In case both should be 0xffffffff.
|
|
flags: the int value of flags or None
|
|
|
|
Raises:
|
|
TestFail if the new board id info does not match
|
|
"""
|
|
# Read back the board id and flags
|
|
new_board_id, new_board_id_inv, new_flags = GetChipBoardId(client)
|
|
|
|
expected_board_id = GetIntBoardId(board_id)
|
|
expected_flags = GetExpectedFlags(flags)
|
|
|
|
if board_id_inv == None:
|
|
new_board_id_inv_str = ''
|
|
expected_board_id_inv_str = ''
|
|
else:
|
|
new_board_id_inv_str = '%08x:' % new_board_id_inv
|
|
expected_board_id_inv = GetIntBoardId(board_id_inv)
|
|
expected_board_id_inv_str = '%08x:' % expected_board_id_inv
|
|
|
|
expected_str = '%08x:%s%08x' % (expected_board_id,
|
|
expected_board_id_inv_str,
|
|
expected_flags)
|
|
new_str = '%08x:%s%08x' % (new_board_id, new_board_id_inv_str, new_flags)
|
|
|
|
if new_str != expected_str:
|
|
raise error.TestFail('Failed to set board id: expected %r got %r' %
|
|
(expected_str, new_str))
|
|
|
|
|
|
def SetChipBoardId(client, board_id, flags=None, pad=True):
|
|
"""Sets the board id and flags
|
|
|
|
Args:
|
|
client: the object to run commands on
|
|
board_id: a string of the symbolic board id or board id hex value. If
|
|
the string is less than 4 characters long it will be
|
|
considered a symbolic value
|
|
flags: a int flag value. If board_id is a symbolic value, then this will
|
|
be ignored.
|
|
pad: pad any int board id, so the string is not 4 characters long.
|
|
|
|
Raises:
|
|
TestFail if we were unable to set the flags to the correct value
|
|
"""
|
|
if isinstance(board_id, int):
|
|
# gsctool will interpret any 4 character string as a RLZ code. If pad is
|
|
# true, pad the board id with 0s to make sure the board id isn't 4
|
|
# characters long.
|
|
board_id_arg = ('0x%08x' % board_id) if pad else hex(board_id)
|
|
else:
|
|
board_id_arg = board_id
|
|
if flags != None:
|
|
board_id_arg += ':' + hex(flags)
|
|
# Set the board id using the given board id and flags
|
|
result = GSCTool(client, ['-a', '-i', board_id_arg]).stdout.strip()
|
|
|
|
CheckChipBoardId(client, board_id, flags)
|
|
|
|
def DumpFlog(client):
|
|
"""Retrieve contents of the flash log"""
|
|
return GSCTool(client, ['-a', '-L']).stdout.strip()
|