summaryrefslogtreecommitdiff
path: root/util/flash_cr50.py
diff options
context:
space:
mode:
authorJack Rosenthal <jrosenth@chromium.org>2021-11-04 12:11:58 -0600
committerCommit Bot <commit-bot@chromium.org>2021-11-05 04:22:34 +0000
commit252457d4b21f46889eebad61d4c0a65331919cec (patch)
tree01856c4d31d710b20e85a74c8d7b5836e35c3b98 /util/flash_cr50.py
parent08f5a1e6fc2c9467230444ac9b582dcf4d9f0068 (diff)
downloadchrome-ec-stabilize-14526.73.B-ish.tar.gz
In the interest of making long-term branch maintenance incur as little technical debt on us as possible, we should not maintain any files on the branch we are not actually using. This has the added effect of making it extremely clear when merging CLs from the main branch when changes have the possibility to affect us. The follow-on CL adds a convenience script to actually pull updates from the main branch and generate a CL for the update. BUG=b:204206272 BRANCH=ish TEST=make BOARD=arcada_ish && make BOARD=drallion_ish Signed-off-by: Jack Rosenthal <jrosenth@chromium.org> Change-Id: I17e4694c38219b5a0823e0a3e55a28d1348f4b18 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3262038 Reviewed-by: Jett Rink <jettrink@chromium.org> Reviewed-by: Tom Hughes <tomhughes@chromium.org>
Diffstat (limited to 'util/flash_cr50.py')
-rwxr-xr-xutil/flash_cr50.py771
1 files changed, 0 insertions, 771 deletions
diff --git a/util/flash_cr50.py b/util/flash_cr50.py
deleted file mode 100755
index 760a788627..0000000000
--- a/util/flash_cr50.py
+++ /dev/null
@@ -1,771 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-# Copyright 2020 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.
-"""Flash Cr50 using gsctool or cr50-rescue.
-
-gsctool example:
-util/flash_cr50.py --image cr50.bin.prod
-util/flash_cr50.py --release prod
-
-cr50-rescue example:
-util/flash_cr50.py --image cr50.bin.prod -c cr50-rescue -p 9999
-util/flash_cr50.py --release prod -c cr50-rescue -p 9999
-"""
-
-import argparse
-import logging
-import os
-import pprint
-import re
-import select
-import shutil
-import subprocess
-import sys
-import tempfile
-import threading
-import time
-
-from chromite.lib import cros_build_lib
-
-CR50_FIRMWARE_BASE = '/opt/google/cr50/firmware/cr50.bin.'
-RELEASE_PATHS = {
- 'prepvt': CR50_FIRMWARE_BASE + 'prepvt',
- 'prod': CR50_FIRMWARE_BASE + 'prod',
-}
-# Dictionary mapping a setup to controls used to verify that the setup is
-# correct. The keys are strings and the values are lists of strings.
-REQUIRED_CONTROLS = {
- 'cr50_uart': [
- r'raw_cr50_uart_pty:\S+',
- r'cr50_ec3po_interp_connect:\S+',
- ],
- 'cr50_reset_odl': [
- r'cr50_reset_odl:\S+',
- ],
- 'ec_uart': [
- r'ec_board:\S+',
- ],
- 'flex': [
- r'servo_type:.*servo_.[^4]',
- ],
- 'type-c_servo_v4': [
- r'servo_v4_type:type-c',
- r'servo_v4_role:\S+',
- ],
-}
-# Supported methods to resetting cr50.
-SUPPORTED_RESETS = (
- 'battery_cutoff',
- 'cr50_reset_odl',
- 'manual_reset',
-)
-
-
-class Error(Exception):
- """Exception class for flash_cr50 utility."""
-
-
-def run_command(cmd, check_error=True):
- """Run the given command.
-
- Args:
- cmd: The command to run as a list of arguments.
- check_error: Raise an error if the command fails.
-
- Returns:
- (exit_status, The command output)
-
- Raises:
- The command error if the command fails and check_error is True.
- """
- try:
- result = cros_build_lib.run(cmd,
- check=check_error,
- print_cmd=True,
- capture_output=True,
- encoding='utf-8',
- stderr=subprocess.STDOUT,
- debug_level=logging.DEBUG,
- log_output=True)
- except cros_build_lib.RunCommandError as cmd_error:
- if check_error:
- raise
- # OSErrors are handled differently. They're raised even if check is
- # False. Return the errno and message for OS errors.
- return cmd_error.exception.errno, cmd_error.msg
- return result.returncode, result.stdout.strip()
-
-
-class Cr50Image(object):
- """Class to handle cr50 image conversions."""
-
- SUFFIX_LEN = 6
- RW_NAME_BASE = 'cr50.rw.'
-
- def __init__(self, image, artifacts_dir):
- """Create an image object that can be used by cr50 updaters."""
- self._remove_dir = False
- if not os.path.exists(image):
- raise Error('Could not find image: %s' % image)
- if not artifacts_dir:
- self._remove_dir = tempfile.mkdtemp()
- artifacts_dir = self._remove_dir
- if not os.path.isdir(artifacts_dir):
- raise Error('Directory does not exist: %s' % artifacts_dir)
- self._original_image = image
- self._artifacts_dir = artifacts_dir
- self._generate_file_names()
-
- def __del__(self):
- """Remove temporary files."""
- if self._remove_dir:
- shutil.rmtree(self._remove_dir)
-
- def _generate_file_names(self):
- """Create some filenames to use for image conversion artifacts."""
- self._tmp_rw_bin = os.path.join(self._artifacts_dir,
- self.RW_NAME_BASE + '.bin')
- self._tmp_rw_hex = os.path.join(self._artifacts_dir,
- self.RW_NAME_BASE + '.hex')
- self._tmp_cr50_bin = os.path.join(self._artifacts_dir,
- self.RW_NAME_BASE + '.orig.bin')
-
- def extract_rw_a_hex(self):
- """Extract RW_A.hex from the original image."""
- run_command(['cp', self.get_bin(), self._tmp_cr50_bin])
- run_command(['dd', 'if=' + self._tmp_cr50_bin, 'of=' + self._tmp_rw_bin,
- 'skip=16384', 'count=233472', 'bs=1'])
- run_command(['objcopy', '-I', 'binary', '-O', 'ihex',
- '--change-addresses', '0x44000', self._tmp_rw_bin,
- self._tmp_rw_hex])
-
- def get_rw_hex(self):
- """cr50-rescue uses the .hex file."""
- if not os.path.exists(self._tmp_rw_hex):
- self.extract_rw_a_hex()
- return self._tmp_rw_hex
-
- def get_bin(self):
- """Get the filename of the update image."""
- return self._original_image
-
- def get_original_basename(self):
- """Get the basename of the original image."""
- return os.path.basename(self._original_image)
-
-
-class Servo(object):
- """Class to interact with servo."""
-
- # Wait 3 seconds for device to settle after running the dut control command.
- SHORT_WAIT = 3
-
- def __init__(self, port):
- """Initialize servo class.
-
- Args:
- port: The servo port for the device being updated.
- """
- self._port = port
-
- def dut_control(self, cmd, check_error=True, wait=False):
- """Run dut control commands
-
- Args:
- cmd: the command to run
- check_error: Raise RunCommandError if the command returns a non-zero
- exit status.
- wait: If True, wait SHORT_WAIT seconds after running the command
-
- Returns:
- (exit_status, output string) - The exit_status will be non-zero if
- the command failed and check_error is True.
-
- Raises:
- RunCommandError if the command fails and check_error is False
- """
- dut_control_cmd = ['dut-control', cmd, '-p', self._port]
- exit_status, output = run_command(dut_control_cmd, check_error)
- if wait:
- time.sleep(self.SHORT_WAIT)
- return exit_status, output.split(':', 1)[-1]
-
- def get_raw_cr50_pty(self):
- """Return raw_cr50_pty. Disable ec3po, so the raw pty can be used."""
- # Disconnect EC3PO, raw_cr50_uart_pty will control the cr50
- # output and input.
- self.dut_control('cr50_ec3po_interp_connect:off', wait=True)
- return self.dut_control('raw_cr50_uart_pty')[1]
-
- def get_cr50_version(self):
- """Return the current cr50 version string."""
- # Make sure ec3po is enabled, so getting cr50_version works.
- self.dut_control('cr50_ec3po_interp_connect:on', wait=True)
- return self.dut_control('cr50_version')[1]
-
-
-class Cr50Reset(object):
- """Class to enter and exit cr50 reset."""
-
- # A list of requirements for the setup. The requirement strings must match
- # something in the REQUIRED_CONTROLS dictionary.
- REQUIRED_SETUP = ()
-
- def __init__(self, servo, name):
- """Make sure the setup supports the given reset_type.
-
- Args:
- servo: The Servo object for the device.
- name: The reset type.
- """
- self._servo = servo
- self._reset_name = name
- self.verify_setup()
- self._original_watchdog_state = self.ccd_watchdog_enabled()
- self._servo_type = self._servo.dut_control('servo_type')[1]
-
- def verify_setup(self):
- """Verify the setup has all required controls to flash cr50.
-
- Raises:
- Error if something is wrong with the setup.
- """
- # If this failed before and didn't cleanup correctly, the device may be
- # cutoff. Try to set the servo_v4_role to recover the device before
- # checking the device state.
- self._servo.dut_control('servo_v4_role:src', check_error=False)
-
- logging.info('Requirements for %s: %s', self._reset_name,
- pprint.pformat(self.REQUIRED_SETUP))
-
- # Get the specific control requirements for the necessary categories.
- required_controls = []
- for category in self.REQUIRED_SETUP:
- required_controls.extend(REQUIRED_CONTROLS[category])
-
- logging.debug('Required controls for %r:\n%s', self._reset_name,
- pprint.pformat(required_controls))
- setup_issues = []
- # Check the setup has all required controls in the correct state.
- for required_control in required_controls:
- control, exp_response = required_control.split(':')
- returncode, output = self._servo.dut_control(control, False)
- logging.debug('%s: got %s expect %s', control, output, exp_response)
- match = re.search(exp_response, output)
- if returncode:
- setup_issues.append('%s: %s' % (control, output))
- elif not match:
- setup_issues.append('%s: need %s found %s' %
- (control, exp_response, output))
- else:
- logging.debug('matched control: %s:%s', control, match.string)
- # Save controls, so they can be restored during cleanup.
- setattr(self, '_' + control, output)
-
- if setup_issues:
- raise Error('Cannot run update using %s. Setup issues: %s' %
- (self._reset_name, setup_issues))
- logging.info('Device Setup: ok')
- logging.info('Reset Method: %s', self._reset_name)
-
- def cleanup(self):
- """Try to get the device out of reset and restore all controls."""
- logging.info('Cleaning up')
- self.restore_control('cr50_ec3po_interp_connect')
-
- # Toggle the servo v4 role if possible to try and get the device out of
- # cutoff.
- self._servo.dut_control('servo_v4_role:snk', check_error=False)
- self._servo.dut_control('servo_v4_role:src', check_error=False)
- self.restore_control('servo_v4_role')
-
- # Restore the ccd watchdog.
- self.enable_ccd_watchdog(self._original_watchdog_state)
-
- def restore_control(self, control):
- """Restore the control setting, if it has been saved.
-
- Args:
- control: The name of the servo control to restore.
- """
- setting = getattr(self, control, None)
- if setting is None:
- return
- self._servo.dut_control('%s:%s' % (control, setting))
-
- def ccd_watchdog_enabled(self):
- """Return True if servod is monitoring ccd"""
- if 'ccd_cr50' not in self._servo_type:
- return False
- watchdog_state = self._servo.dut_control('watchdog')[1]
- logging.debug(watchdog_state)
- return not re.search('ccd:.*disconnect ok', watchdog_state)
-
- def enable_ccd_watchdog(self, enable):
- """Control the CCD watchdog.
-
- Servo will die if it's watching CCD and cr50 is held in reset. Disable
- the CCD watchdog, so it's ok for CCD to disconnect.
-
- This function does nothing if ccd_cr50 isn't in the servo type.
-
- Args:
- enable: If True, enable the CCD watchdog. Otherwise disable it.
- """
- if 'ccd_cr50' not in self._servo_type:
- logging.debug('Servo is not watching ccd device.')
- return
-
- if enable:
- self._servo.dut_control('watchdog_add:ccd')
- else:
- self._servo.dut_control('watchdog_remove:ccd')
-
- if self.ccd_watchdog_enabled() != enable:
- raise Error('Could not %sable ccd watchdog' %
- ('en' if enable else 'dis'))
-
- def enter_reset(self):
- """Disable the CCD watchdog then run the reset cr50 function."""
- logging.info('Using %r to enter reset', self._reset_name)
- # Disable the CCD watchdog before putting servo into reset otherwise
- # servo will die in the middle of flashing cr50.
- self.enable_ccd_watchdog(False)
- try:
- self.run_reset()
- except Exception as e:
- logging.warning('%s enter reset failed: %s', self._reset_name, e)
- raise
-
- def exit_reset(self):
- """Exit cr50 reset."""
- logging.info('Recovering from %s', self._reset_name)
- try:
- self.recover_from_reset()
- except Exception as e:
- logging.warning('%s exit reset failed: %s', self._reset_name, e)
- raise
-
- def run_reset(self):
- """Start the cr50 reset process.
-
- Cr50 doesn't have to enter reset in this function. It just needs to do
- whatever setup is necessary for the exit reset function.
- """
- raise NotImplementedError()
-
- def recover_from_reset(self):
- """Recover from Cr50 reset.
-
- Cr50 has to hard or power-on reset during this function for rescue to
- work. Uart is disabled on deep sleep recovery, so deep sleep is not a
- valid reset.
- """
- raise NotImplementedError()
-
-
-class Cr50ResetODLReset(Cr50Reset):
- """Class for using the servo cr50_reset_odl to reset cr50."""
-
- REQUIRED_SETUP = (
- # Rescue is done through Cr50 uart. It requires a flex cable not ccd.
- 'flex',
- # cr50_reset_odl is used to hold cr50 in reset. This control only exists
- # if it actually resets cr50.
- 'cr50_reset_odl',
- # Cr50 rescue is done through cr50 uart.
- 'cr50_uart',
- )
-
- def cleanup(self):
- """Use the Cr50 reset signal to hold Cr50 in reset."""
- try:
- self.restore_control('cr50_reset_odl')
- finally:
- super(Cr50ResetODLReset, self).cleanup()
-
- def run_reset(self):
- """Use cr50_reset_odl to hold Cr50 in reset."""
- logging.info('cr50_reset_odl:on')
- self._servo.dut_control('cr50_reset_odl:on')
-
- def recover_from_reset(self):
- """Release the reset signal."""
- logging.info('cr50_reset_odl:off')
- self._servo.dut_control('cr50_reset_odl:off')
-
-
-class BatteryCutoffReset(Cr50Reset):
- """Class for using a battery cutoff through EC commands to reset cr50."""
-
- REQUIRED_SETUP = (
- # Rescue is done through Cr50 uart. It requires a flex cable not ccd.
- 'flex',
- # We need type c servo v4 to recover from battery_cutoff.
- 'type-c_servo_v4',
- # Cr50 rescue is done through cr50 uart.
- 'cr50_uart',
- # EC console needs to be read-write to issue cutoff command.
- 'ec_uart',
- )
-
- def run_reset(self):
- """Use EC commands to cutoff the battery."""
- self._servo.dut_control('servo_v4_role:snk')
-
- if self._servo.dut_control('ec_board', check_error=False)[0]:
- logging.warning('EC is unresponsive. Cutoff may not work.')
-
- self._servo.dut_control('ec_uart_cmd:cutoff', check_error=False,
- wait=True)
- self._servo.dut_control('ec_uart_cmd:reboot', check_error=False,
- wait=True)
-
- if not self._servo.dut_control('ec_board', check_error=False)[0]:
- raise Error('EC still responsive after cutoff')
- logging.info('Device is cutoff')
-
- def recover_from_reset(self):
- """Connect power using servo v4 to recover from cutoff."""
- logging.info('"Connecting" adapter')
- self._servo.dut_control('servo_v4_role:src', wait=True)
-
-
-class ManualReset(Cr50Reset):
- """Class for using a manual reset to reset Cr50."""
-
- REQUIRED_SETUP = (
- # Rescue is done through Cr50 uart. It requires a flex cable not ccd.
- 'flex',
- # Cr50 rescue is done through cr50 uart.
- 'cr50_uart',
- )
-
- PROMPT_WAIT = 5
- USER_RESET_TIMEOUT = 60
-
- def run_reset(self):
- """Nothing to do. User will reset cr50."""
-
- def recover_from_reset(self):
- """Wait for the user to reset cr50."""
- end_time = time.time() + self.USER_RESET_TIMEOUT
- while time.time() < end_time:
- logging.info('Press enter after you reset cr50')
- user_input = select.select([sys.stdin], [], [], self.PROMPT_WAIT)[0]
- if user_input:
- logging.info('User reset done')
- return
- logging.warning('User input timeout: assuming cr50 reset')
-
-
-class FlashCr50(object):
- """Class for updating cr50."""
-
- NAME = 'FlashCr50'
- PACKAGE = ''
- DEFAULT_UPDATER = ''
-
- def __init__(self, cmd):
- """Verify the update command exists.
-
- Args:
- cmd: The updater command.
-
- Raises:
- Error if no valid updater command was found.
- """
- updater = self.get_updater(cmd)
- if not updater:
- emerge_msg = (('Try emerging ' + self.PACKAGE) if self.PACKAGE
- else '')
- raise Error('Could not find %s command.%s' % (self, emerge_msg))
- self._updater = updater
-
- def get_updater(self, cmd):
- """Find a valid updater command.
-
- Args:
- cmd: the updater command.
-
- Returns:
- A command string or None if none of the commands ran successfully.
- The command string will be the one supplied or the DEFAULT_UPDATER
- command.
- """
- if not self.updater_works(cmd):
- return cmd
-
- use_default = (self.DEFAULT_UPDATER and
- not self.updater_works(self.DEFAULT_UPDATER))
- if use_default:
- logging.debug('%r failed using %r to update.', cmd,
- self.DEFAULT_UPDATER)
- return self.DEFAULT_UPDATER
- return None
-
- @staticmethod
- def updater_works(cmd):
- """Verify the updater command.
-
- Returns:
- non-zero status if the command failed.
- """
- logging.debug('Testing update command %r.', cmd)
- exit_status, output = run_command([cmd, '-h'], check_error=False)
- if 'Usage' in output:
- return 0
- if exit_status:
- logging.debug('Could not run %r (%s): %s', cmd, exit_status, output)
- return exit_status
-
- def update(self, image):
- """Try to update cr50 to the given image."""
- raise NotImplementedError()
-
- def __str__(self):
- """Use the updater name for the tostring."""
- return self.NAME
-
-
-class GsctoolUpdater(FlashCr50):
- """Class to flash cr50 using gsctool."""
-
- NAME = 'gsctool'
- PACKAGE = 'ec-utils'
- DEFAULT_UPDATER = '/usr/sbin/gsctool'
-
- # Common failures exit with this status. Use STANDARD_ERRORS to map the
- # exit status to reasons for the failure.
- STANDARD_ERROR_REGEX = r'Error: status (\S+)'
- STANDARD_ERRORS = {
- '0x8': 'Rejected image with old header.',
- '0x9': 'Update too soon.',
- '0xc': 'Board id mismatch',
- }
-
- def __init__(self, cmd, serial=None):
- """Generate the gsctool command.
-
- Args:
- cmd: gsctool updater command.
- serial: The serial number of the CCD device being updated.
- """
- super(GsctoolUpdater, self).__init__(cmd)
- self._gsctool_cmd = [self._updater]
- if serial:
- self._gsctool_cmd.extend(['-n', serial])
-
- def update(self, image):
- """Use gsctool to update cr50.
-
- Args:
- image: Cr50Image object.
- """
- update_cmd = self._gsctool_cmd[:]
- update_cmd.append(image.get_bin())
- exit_status, output = run_command(update_cmd, check_error=False)
- if not exit_status or (exit_status == 1 and 'image updated' in output):
- logging.info('update ok')
- return
- if exit_status == 3:
- match = re.search(self.STANDARD_ERROR_REGEX, output)
- if match:
- update_error = match.group(1)
- logging.info('Update error %s', update_error)
- raise Error(self.STANDARD_ERRORS[update_error])
- raise Error('gsctool update error: %s' % output.splitlines()[-1])
-
-
-class Cr50RescueUpdater(FlashCr50):
- """Class to flash cr50 through servo micro uart."""
-
- NAME = 'cr50-rescue'
- PACKAGE = 'cr50-utils'
- DEFAULT_UPDATER = '/usr/bin/cr50-rescue'
-
- WAIT_FOR_UPDATE = 120
- RESCUE_RESET_DELAY = 5
-
- def __init__(self, cmd, port, reset_type):
- """Initialize cr50-rescue updater.
-
- cr50-rescue can only be done through servo, because it needs access to
- a lot of dut-controls and cr50 uart through servo micro. During rescue
- Cr50 has to do a hard reset, so the user should supply a valid reset
- method for the setup that's being used.
-
- Args:
- cmd: The cr50-rescue command.
- port: The servo port of the device being updated.
- reset_type: A string (one of SUPPORTED_RESETS) that describes how
- to reset Cr50 during cr50-rescue.
- """
- super(Cr50RescueUpdater, self).__init__(cmd)
- self._servo = Servo(port)
- self._rescue_thread = None
- self._rescue_process = None
- self._cr50_reset = self.get_cr50_reset(reset_type)
-
- def get_cr50_reset(self, reset_type):
- """Get the cr50 reset object for the given reset_type.
-
- Args:
- reset_type: a string describing how cr50 will be reset. It must be
- in SUPPORTED_RESETS.
-
- Returns:
- The Cr50Reset object for the given reset_type.
- """
- assert reset_type in SUPPORTED_RESETS, '%s is unsupported.' % reset_type
- if reset_type == 'battery_cutoff':
- return BatteryCutoffReset(self._servo, reset_type)
- elif reset_type == 'cr50_reset_odl':
- return Cr50ResetODLReset(self._servo, reset_type)
- return ManualReset(self._servo, reset_type)
-
- def update(self, image):
- """Use cr50-rescue to update cr50 then cleanup.
-
- Args:
- image: Cr50Image object.
- """
- update_file = image.get_rw_hex()
- try:
- self.run_update(update_file)
- finally:
- self.restore_state()
-
- def start_rescue_process(self, update_file):
- """Run cr50-rescue in a process, so it can be killed it if it hangs."""
- pty = self._servo.get_raw_cr50_pty()
-
- rescue_cmd = [self._updater, '-v', '-i', update_file, '-d', pty]
- logging.info('Starting cr50-rescue: %s',
- cros_build_lib.CmdToStr(rescue_cmd))
-
- self._rescue_process = subprocess.Popen(rescue_cmd)
- self._rescue_process.communicate()
- logging.info('Rescue Finished')
-
- def start_rescue_thread(self, update_file):
- """Start cr50-rescue."""
- self._rescue_thread = threading.Thread(target=self.start_rescue_process,
- args=[update_file])
- self._rescue_thread.start()
-
- def run_update(self, update_file):
- """Run the Update"""
- # Enter reset before starting rescue, so any extra cr50 messages won't
- # interfere with cr50-rescue.
- self._cr50_reset.enter_reset()
-
- self.start_rescue_thread(update_file)
-
- time.sleep(self.RESCUE_RESET_DELAY)
- # Resume from cr50 reset.
- self._cr50_reset.exit_reset()
-
- self._rescue_thread.join(self.WAIT_FOR_UPDATE)
-
- logging.info('cr50_version:%s', self._servo.get_cr50_version())
-
- def restore_state(self):
- """Try to get the device out of reset and restore all controls"""
- try:
- self._cr50_reset.cleanup()
- finally:
- self.cleanup_rescue_thread()
-
- def cleanup_rescue_thread(self):
- """Cleanup the rescue process and handle any errors."""
- if not self._rescue_thread:
- return
- if self._rescue_thread.is_alive():
- logging.info('Killing cr50-rescue process')
- self._rescue_process.terminate()
- self._rescue_thread.join()
-
- self._rescue_thread = None
- if self._rescue_process.returncode:
- logging.info('cr50-rescue failed.')
- logging.info('stderr: %s', self._rescue_process.stderr)
- logging.info('stdout: %s', self._rescue_process.stdout)
- logging.info('returncode: %s', self._rescue_process.returncode)
- raise Error('cr50-rescue failed (%d)' %
- self._rescue_process.returncode)
-
-
-def parse_args(argv):
- """Parse commandline arguments.
-
- Args:
- argv: command line args
-
- Returns:
- options: an argparse.Namespace.
- """
- usage = ('%s -i $IMAGE [ -c cr50-rescue -p $SERVO_PORT [ -r '
- '$RESET_METHOD]]' % os.path.basename(argv[0]))
- parser = argparse.ArgumentParser(usage=usage, description=__doc__)
- parser.add_argument('-d', '--debug', action='store_true', default=False,
- help='enable debug messages.')
- parser.add_argument('-i', '--image', type=str,
- help='Path to cr50 binary image.')
- parser.add_argument('-R', '--release', type=str,
- choices=RELEASE_PATHS.keys(),
- help='Type of cr50 release. Use instead of the image '
- 'arg.')
- parser.add_argument('-c', '--updater-cmd', type=str, default='gsctool',
- help='Tool to update cr50. Either gsctool or '
- 'cr50-rescue')
- parser.add_argument('-s', '--serial', type=str, default='',
- help='serial number to pass to gsctool.')
- parser.add_argument('-p', '--port', type=str, default='',
- help='port servod is listening on (required for '
- 'rescue).')
- parser.add_argument('-r', '--reset-type', default='battery_cutoff',
- choices=SUPPORTED_RESETS,
- type=str, help='The method for cr50 reset.')
- parser.add_argument('-a', '--artifacts-dir', default=None, type=str,
- help='Location to store artifacts')
- opts = parser.parse_args(argv[1:])
- if 'cr50-rescue' in opts.updater_cmd and not opts.port:
- raise parser.error('Servo port is required for cr50 rescue')
- return opts
-
-
-def get_updater(opts):
- """Get the updater object."""
- if 'cr50-rescue' in opts.updater_cmd:
- return Cr50RescueUpdater(opts.updater_cmd, opts.port, opts.reset_type)
- if 'gsctool' in opts.updater_cmd:
- return GsctoolUpdater(opts.updater_cmd, opts.serial)
- raise Error('Unsupported update command %r' % opts.updater_cmd)
-
-
-def main(argv):
- """Update cr50 using gsctool or cr50-rescue."""
- opts = parse_args(argv)
-
- loglevel = logging.INFO
- log_format = '%(asctime)s - %(levelname)7s'
- if opts.debug:
- loglevel = logging.DEBUG
- log_format += ' - %(lineno)3d:%(funcName)-15s'
- log_format += ' - %(message)s'
- logging.basicConfig(level=loglevel, format=log_format)
-
- image = Cr50Image(RELEASE_PATHS.get(opts.release, opts.image),
- opts.artifacts_dir)
- flash_cr50 = get_updater(opts)
-
- logging.info('Using %s to update to %s', flash_cr50,
- image.get_original_basename())
- flash_cr50.update(image)
-
-
-if __name__ == '__main__':
- sys.exit(main(sys.argv))