1# Copyright 2017 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import os 7import re 8 9from autotest_lib.client.common_lib import error 10 11 12RO = 'ro' 13RW = 'rw' 14CR50_FILE = '/opt/google/cr50/firmware/cr50.bin.prod' 15CR50_STATE = '/var/cache/cr50*' 16GET_CR50_VERSION = 'cat /var/cache/cr50-version' 17GET_CR50_MESSAGES ='grep "cr50-.*\[" /var/log/messages' 18UPDATE_FAILURE = 'unexpected cr50-update exit code' 19# This dictionary is used to search the usb_updater output for the version 20# strings. There are two usb_updater commands that will return versions: 21# 'fwver' and 'binver'. 22# 23# 'fwver' is used to get the running RO and RW versions from cr50 24# 'binver' gets the version strings for each RO and RW region in the given 25# file 26# 27# The value in the dictionary is the regular expression that can be used to 28# find the version strings for each region. 29VERSION_RE = { 30 "--fwver" : '\nRO (?P<ro>\S+).*\nRW (?P<rw>\S+)', 31 "--binver" : 'RO_A:(?P<ro_a>\S+).*RW_A:(?P<rw_a>\S+).*' \ 32 'RO_B:(?P<ro_b>\S+).*RW_B:(?P<rw_b>\S+)', 33} 34 35 36def GetVersion(versions, name): 37 """Return the version string from the dictionary. 38 39 Get the version for each key in the versions dictionary that contains the 40 substring name. Make sure all of the versions match and return the version 41 string. Raise an error if the versions don't match. 42 43 @param version: dictionary with the partition names as keys and the 44 partition version strings as values. 45 @param name: the string used to find the relevant items in versions. 46 """ 47 ver = None 48 for k, v in versions.iteritems(): 49 if name in k: 50 if ver and ver != v: 51 raise error.TestFail("Versions don't match %s %s" % (ver, v)) 52 else: 53 ver = v 54 return ver 55 56 57def FindVersion(output, arg): 58 """Find the ro and rw versions. 59 60 @param output: The string to search 61 @param arg: string representing the usb_updater option, either 62 '--binver' or '--fwver' 63 @param compare: raise an error if the ro or rw versions don't match 64 """ 65 versions = re.search(VERSION_RE[arg], output) 66 versions = versions.groupdict() 67 ro = GetVersion(versions, RO) 68 rw = GetVersion(versions, RW) 69 return ro, rw 70 71 72def GetSavedVersion(client): 73 """Return the saved version from /var/cache/cr50-version""" 74 result = client.run(GET_CR50_VERSION).stdout.strip() 75 return FindVersion(result, "--fwver") 76 77 78def GetVersionFromUpdater(client, args): 79 """Return the version from usb_updater""" 80 result = client.run("usb_updater %s" % ' '.join(args)).stdout.strip() 81 return FindVersion(result, args[0]) 82 83 84def GetFwVersion(client): 85 """Get the running version using 'usb_updater --fwver'""" 86 return GetVersionFromUpdater(client, ["--fwver"]) 87 88 89def GetBinVersion(client, image=CR50_FILE): 90 """Get the image version using 'usb_updater --binver image'""" 91 return GetVersionFromUpdater(client, ["--binver", image]) 92 93 94def CompareVersions(name_a, ver_a, rw_a, ver_b): 95 """Compare ver_a to ver_b. Raise an error if they aren't the same""" 96 if ver_a != ver_b: 97 raise error.TestFail("Versions do not match: %s RO %s RW %s %s RO %s " \ 98 "RW %s" % (name_a, ver_a[0], ver_a[1], name_b, 99 ver_b[0], ver_b[1])) 100 101 102def GetRunningVersion(client): 103 """Get the running Cr50 version. 104 105 The version from usb_updater and /var/cache/cr50-version should be the 106 same. Get both versions and make sure they match. 107 108 Returns: 109 running_ver: a tuple with the ro and rw version strings 110 Raises: 111 TestFail 112 - If the version in /var/cache/cr50-version is not the same as the 113 version from 'usb_updater --fwver' 114 """ 115 running_ver = GetFwVersion(client) 116 saved_ver = GetSavedVersion(client) 117 118 CompareVersions("Running", running_ver, "Saved", saved_ver) 119 return running_ver 120 121 122def CheckForFailures(client, last_message): 123 """Check for any unexpected cr50-update exit codes. 124 125 This only checks the cr50 update messages that have happened since 126 last_message. If a unexpected exit code is detected it will raise an error> 127 128 Args: 129 last_message: the last cr50 message from the last update run 130 131 Returns: 132 the last cr50 message in /var/log/messages 133 134 Raises: 135 TestFail 136 - If there is a unexpected cr50-update exit code after last_message 137 in /var/log/messages 138 """ 139 messages = client.run(GET_CR50_MESSAGES).stdout.strip() 140 if last_message: 141 messages = messages.rsplit(last_message, 1)[-1] 142 if UPDATE_FAILURE in messages: 143 logging.debug(messages) 144 raise error.TestFail("Detected unexpected exit code during update") 145 return messages.rsplit('\n', 1)[-1] 146 147 148def VerifyUpdate(client, ver=None, last_message=''): 149 """Verify that the saved update state is correct and there were no 150 unexpected cr50-update exit codes since the last update. 151 152 Returns: 153 new_ver: a tuple containing the running ro and rw versions 154 last_message: The last cr50 update message in /var/log/messages 155 """ 156 # Check that there were no unexpected reboots from cr50-result 157 last_message = CheckForFailures(client, last_message) 158 logging.debug("last cr50 message %s", last_message) 159 160 new_ver = GetRunningVersion(client) 161 if ver: 162 CompareVersions("Old", ver, "Updated", new_ver) 163 return new_ver, last_message 164 165 166def ClearUpdateStateAndReboot(client): 167 """Removes the cr50 status files in /var/cache and reboots the AP""" 168 client.run("rm %s" % CR50_STATE) 169 client.reboot() 170