xref: /aosp_15_r20/external/autotest/server/site_tests/firmware_FWupdateWP/firmware_FWupdateWP.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# Copyright 2015 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
6
7from autotest_lib.client.common_lib import error
8from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
9
10
11class firmware_FWupdateWP(FirmwareTest):
12    """RO+RW firmware update using chromeos-firmware, with WP=1 and with WP=0.
13    It modifies the FWIDs of the current firmware before flashing, and restores
14    the firmware after the test.
15    """
16
17    # Region to use for flashrom wp-region commands
18    WP_REGION = 'WP_RO'
19    MODE = 'recovery'
20
21    def initialize(self, host, cmdline_args):
22
23        self.flashed = False
24
25        super(firmware_FWupdateWP, self).initialize(host, cmdline_args)
26
27        self._old_bios_wp = self.faft_client.bios.get_write_protect_status()
28
29        stripped_bios = self.faft_client.bios.strip_modified_fwids()
30        if stripped_bios:
31            logging.warning(
32                    "Fixed the previously modified BIOS FWID(s): %s",
33                    stripped_bios)
34
35        if self.faft_config.chrome_ec:
36            stripped_ec = self.faft_client.ec.strip_modified_fwids()
37            if stripped_ec:
38                logging.warning(
39                        "Fixed the previously modified EC FWID(s): %s",
40                        stripped_ec)
41
42        self.backup_firmware()
43
44        self.set_ap_write_protect_and_reboot(False)
45        self.faft_client.bios.set_write_protect_region(self.WP_REGION, True)
46        self.set_ap_write_protect_and_reboot(True)
47
48    def get_installed_versions(self):
49        """Get the installed versions of BIOS and EC firmware.
50
51        @return: A nested dict keyed by target ('bios' or 'ec') and then section
52        @rtype: dict
53        """
54        vers = dict()
55        vers['bios'] = self.faft_client.updater.get_device_fwids('bios')
56        if self.faft_config.chrome_ec:
57            vers['ec'] = self.faft_client.updater.get_device_fwids('ec')
58        return vers
59
60    def run_case(self, append, write_protected, before_fwids, image_fwids):
61        """Run chromeos-firmwareupdate with given sub-case
62
63        @param append: additional piece to add to shellball name
64        @param write_protected: is the flash write protected (--wp)?
65        @param before_fwids: fwids before flashing ('bios' and 'ec' as keys)
66        @param image_fwids: fwids in image ('bios' and 'ec' as keys)
67        @return: a list of failure messages for the case
68        """
69
70        cmd_desc = ('chromeos-firmwareupdate --mode=%s [wp=%s]'
71                    % (self.MODE, write_protected))
72
73        # Unlock the protection of the wp-enable and wp-range registers
74        self.set_ap_write_protect_and_reboot(False)
75
76        if write_protected:
77            self.faft_client.bios.set_write_protect_region(self.WP_REGION, True)
78            self.set_ap_write_protect_and_reboot(True)
79        else:
80            self.faft_client.bios.set_write_protect_region(
81                    self.WP_REGION, False)
82
83        expected_written = {}
84
85        if write_protected:
86            bios_written = ['a', 'b']
87            ec_written = []  # EC write is all-or-nothing
88
89        else:
90            bios_written = ['ro', 'a', 'b']
91            ec_written = ['ro', 'rw']
92
93        expected_written['bios'] = bios_written
94
95        if self.faft_config.chrome_ec and ec_written:
96            expected_written['ec'] = ec_written
97
98        # bios: [a, b], ec: [ro, rw]
99        written_desc = repr(expected_written).replace("'", "")[1:-1]
100        logging.debug('Before(%s): %s', append, before_fwids)
101        logging.debug('Image(%s):  %s', append, image_fwids)
102        logging.info("Run %s (should write %s)", cmd_desc, written_desc)
103
104        # make sure we restore firmware after the test, if it tried to flash.
105        self.flashed = True
106
107        errors = []
108        options = ['--quirks=ec_partial_recovery=0']
109        result = self.run_chromeos_firmwareupdate(
110                self.MODE, append, options, ignore_status=True)
111
112        if result.exit_status == 255:
113            logging.info("DUT network dropped during update.")
114        elif result.exit_status != 0:
115            if (image_fwids == before_fwids and
116                    'Good. It seems nothing was changed.' in result.stdout):
117                logging.info("DUT already matched the image; updater aborted.")
118            else:
119                errors.append('...updater: unexpectedly failed (rc=%s)' %
120                              result.exit_status)
121
122        after_fwids = self.get_installed_versions()
123        logging.debug('After(%s):  %s', append, after_fwids)
124
125        errors += self.check_fwids_written(
126                before_fwids, image_fwids, after_fwids, expected_written)
127
128        if errors:
129            logging.debug('%s', '\n'.join(errors))
130            return ["%s (should write %s)\n%s"
131                    % (cmd_desc, written_desc, '\n'.join(errors))]
132        else:
133            return []
134
135    def run_once(self, host):
136        """Run chromeos-firmwareupdate with recovery or factory mode.
137
138        @param host: host to run on
139        """
140        append = 'new'
141        have_ec = bool(self.faft_config.chrome_ec)
142
143        self.faft_client.updater.extract_shellball()
144
145        before_fwids = self.get_installed_versions()
146
147        # Modify the stock image
148        logging.info(
149                "Using the currently running firmware, with modified fwids")
150        self.setup_firmwareupdate_shellball()
151        self.faft_client.updater.reload_images()
152        self.modify_shellball(append, modify_ro=True, modify_ec=have_ec)
153        modded_fwids = self.identify_shellball(include_ec=have_ec)
154
155        fail_msg = "Section contents didn't show the expected changes."
156
157        errors = []
158        # no args specified, so check both wp=1 and wp=0
159        errors += self.run_case(append, 1, before_fwids, modded_fwids)
160        errors += self.run_case(append, 0, before_fwids, modded_fwids)
161
162        if errors:
163            raise error.TestFail("%s\n%s" % (fail_msg, '\n'.join(errors)))
164
165    def cleanup(self):
166        """
167        Restore firmware from the backup taken before flashing.
168        No EC reboot is needed, because the test doesn't actually reboot the EC
169        with the "new" firmware.
170        """
171        self.set_ap_write_protect_and_reboot(False)
172        self.faft_client.bios.set_write_protect_range(0, 0, False)
173
174        if self.flashed:
175            logging.info("Restoring firmware")
176            self.restore_firmware()
177
178        # Restore the old write-protection value at the end of the test.
179        self.faft_client.bios.set_write_protect_range(
180                self._old_bios_wp['start'],
181                self._old_bios_wp['length'],
182                self._old_bios_wp['enabled'])
183
184        super(firmware_FWupdateWP, self).cleanup()
185