• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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