blob: 8e54914c23e0e0a540d40d44dd98792fbfdbdf37 [file] [log] [blame]
Richard Barnette90ad4262016-11-17 17:29:24 -08001# Copyright 2016 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 re
7
8import common
9from autotest_lib.client.common_lib import hosts
10from autotest_lib.server import afe_utils
11
12
13class FirmwareVersionVerifier(hosts.Verifier):
14 """
15 Check for a firmware update, and apply it if appropriate.
16
17 This verifier checks to ensure that either the firmware on the DUT
18 is up-to-date, or that the target firmware can be installed from the
19 currently running build.
20
21 Failure occurs when all of the following apply:
22 1. The DUT is not part of a FAFT pool. (DUTs used for FAFT testing
23 instead use `FirmwareRepair`, below.)
24 2. The DUT has an assigned stable firmware version.
25 3. The DUT is not running the assigned stable firmware.
26 4. The firmware supplied in the running OS build is not the
27 assigned stable firmware.
28
29 If the DUT needs an upgrade and the currently running OS build
30 supplies the necessary firmware, use `chromeos-firmwareupdate` to
31 install the new firmware. Failure to install will cause the
32 verifier to fail.
33
34 This verifier nominally breaks the rule that "verifiers must succeed
35 quickly", since it can invoke `reboot()` during the success code
36 path. We're doing it anyway for two reasons:
37 * The time between updates will typically be measured in months,
38 so the amortized cost is low.
39 * The reason we distinguish repair from verify is to allow
40 rescheduling work immediately while the expensive repair happens
41 out-of-band. But a firmware update will likely hit all DUTs at
42 once, so it's pointless to pass the buck to repair.
43
44 N.B. This verifier is a trigger for all repair actions that install
45 the stable repair image. If the firmware is out-of-date, but the
46 stable repair image does *not* contain the proper firmware version,
47 _the target DUT will fail repair, and will be unable to fix itself_.
48 """
49
50 @staticmethod
51 def _get_rw_firmware(host):
52 result = host.run('crossystem fwid', ignore_status=True)
53 if result.exit_status == 0:
54 return result.stdout
55 else:
56 return None
57
58 @staticmethod
59 def _get_available_firmware(host):
60 result = host.run('chromeos-firmwareupdate -V',
61 ignore_status=True)
62 if result.exit_status == 0:
63 version = re.search(r'BIOS version:\s*(?P<version>.*)',
64 result.stdout)
65 if version is not None:
66 return version.group('version')
67 return None
68
69 @staticmethod
70 def _check_hardware_match(version_a, version_b):
71 """
72 Check that two firmware versions identify the same hardware.
73
74 Firmware version strings look like this:
75 Google_Gnawty.5216.239.34
76 The part before the numbers identifies the hardware for which
77 the firmware was built. This function checks that the hardware
78 identified by `version_a` and `version_b` is the same.
79
80 This is a sanity check to protect us from installing the wrong
81 firmware on a DUT when a board label has somehow gone astray.
82
83 @param version_a First firmware version for the comparison.
84 @param version_b Second firmware version for the comparison.
85 """
86 hardware_a = version_a.split('.')[0]
87 hardware_b = version_b.split('.')[0]
88 if hardware_a != hardware_b:
89 message = 'Hardware/Firmware mismatch updating %s to %s'
90 raise hosts.AutoservVerifyError(
91 message % (version_a, version_b))
92
93 def verify(self, host):
94 # Test 1 - The DUT is not part of a FAFT pool.
95 if host._is_firmware_repair_supported():
96 return
97 # Test 2 - The DUT has an assigned stable firmware version.
98 stable_firmware = afe_utils.get_stable_firmware_version(
99 host._get_board_from_afe())
100 if stable_firmware is None:
101 # This DUT doesn't have a firmware update target
102 return
103
104 # For tests 3 and 4: If the output from `crossystem` or
105 # `chromeos-firmwareupdate` isn't what we expect, we log an
106 # error, but don't fail: We don't want DUTs unable to test a
107 # build merely because of a bug or change in either of those
108 # commands.
109
110 # Test 3 - The DUT is not running the target stable firmware.
111 current_firmware = self._get_rw_firmware(host)
112 if current_firmware is None:
113 logging.error('DUT firmware version can\'t be determined.')
114 return
115 if current_firmware == stable_firmware:
116 return
117 # Test 4 - The firmware supplied in the running OS build is not
118 # the assigned stable firmware.
119 available_firmware = self._get_available_firmware(host)
120 if available_firmware is None:
121 logging.error('Supplied firmware version in OS can\'t be '
122 'determined.')
123 return
124 if available_firmware != stable_firmware:
125 raise hosts.AutoservVerifyError(
126 'DUT firmware requires update from %s to %s' %
127 (current_firmware, stable_firmware))
128 # Time to update the firmware.
129 logging.info('Updating firmware from %s to %s',
130 current_firmware, stable_firmware)
131 self._check_hardware_match(current_firmware, stable_firmware)
132 try:
133 host.run('chromeos-firmwareupdate --mode=autoupdate')
134 host.reboot()
135 except Exception as e:
136 message = ('chromeos-firmwareupdate failed: from '
137 '%s to %s')
138 logging.exception(message, current_firmware, stable_firmware)
139 raise hosts.AutoservVerifyError(
140 message % (current_firmware, stable_firmware))
141
142 @property
143 def description(self):
144 return 'The firmware on this DUT is up-to-date'
145
146
147class FirmwareRepair(hosts.RepairAction):
148 """
149 Reinstall the firmware image using servo.
150
151 This repair function attempts to use servo to install the DUT's
152 designated "stable firmware version".
153
154 This repair method only applies to DUTs used for FAFT.
155 """
156
157 def repair(self, host):
158 if not host._is_firmware_repair_supported():
159 raise hosts.AutoservRepairError(
160 'Firmware repair is not applicable to host %s.' %
161 host.hostname)
162 if not host.servo:
163 raise hosts.AutoservRepairError(
164 '%s has no servo support.' % host.hostname)
165 host.firmware_install()
166
167 @property
168 def description(self):
169 return 'Re-install the stable firmware via servo'