• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
6import time
7
8import common
9from autotest_lib.client.common_lib import error
10from autotest_lib.server import test
11
12
13def _assert_equal(expected, actual):
14    """Compares objects.
15
16    @param expected: the expected value.
17    @param actual: the actual value.
18
19    @raises error.TestFail
20    """
21    if expected != actual:
22        raise error.TestFail('Expected: %r, actual: %r' % (expected, actual))
23
24
25class brillo_BootLoader(test.test):
26    """A/B tests for boot loader and boot_control HAL implementation."""
27    version = 1
28
29
30    def get_slots_and_suffix(self):
31        """Gets number of slots supported and slot suffixes used.
32
33        Prerequisite: The DUT is in ADB mode.
34        """
35        self.num_slots = int(self.dut.run_output('bootctl get-number-slots'))
36        logging.info('Number of slots: %d', self.num_slots)
37        self.suffix_a = self.dut.run_output('bootctl get-suffix 0')
38        self.suffix_b = self.dut.run_output('bootctl get-suffix 1')
39        logging.info('Slot 0 suffix: "%s"', self.suffix_a)
40        logging.info('Slot 1 suffix: "%s"', self.suffix_b)
41        _assert_equal(self.num_slots, 2)
42
43
44    def get_current_slot(self):
45        """Gets the current slot the DUT is running from.
46
47        Prerequisite: The DUT is in ADB mode.
48        """
49        return int(self.dut.run_output('bootctl get-current-slot'))
50
51
52    def assert_current_slot(self, slot_number):
53        """Checks that DUT is running from the given slot.
54
55        Prerequisite: The DUT is in ADB mode.
56
57        @slot_number: Zero-based index of slot to be running from.
58        """
59        _assert_equal(self.get_current_slot(), slot_number)
60
61
62    def set_active_slot(self, slot_number):
63        """Instructs the DUT to attempt booting from given slot.
64
65        Prerequisite: The DUT is in ADB mode.
66
67        @slot_number: Zero-based index of slot to make active.
68        """
69        logging.info('Setting slot %d active.', slot_number)
70        self.dut.run('bootctl set-active-boot-slot %d' % slot_number)
71
72
73    def ensure_running_slot(self, slot_number):
74        """Ensures that DUT is running from the given slot.
75
76        Prerequisite: The DUT is in ADB mode.
77
78        @slot_number: Zero-based index of slot to be running from.
79        """
80        logging.info('Ensuring device is running from slot %d.', slot_number)
81        if self.get_current_slot() != slot_number:
82            logging.info('Rebooting into slot %d', slot_number)
83            self.set_active_slot(slot_number)
84            self.dut.reboot()
85            self.assert_current_slot(slot_number)
86
87
88    def copy_a_to_b(self):
89        """Copies contents of slot A to slot B.
90
91        Prerequisite: The DUT is in ADB mode and booted from slot A.
92        """
93        self.assert_current_slot(0)
94        for i in ['boot', 'system']:
95            logging.info('Copying %s%s to %s%s.',
96                         i, self.suffix_a, i, self.suffix_b)
97            self.dut.run('dd if=/dev/block/by-name/%s%s '
98                         'of=/dev/block/by-name/%s%s bs=4096' %
99                         (i, self.suffix_a, i, self.suffix_b))
100
101
102    def check_bootctl_set_active(self):
103        """Checks that setActiveBootSlot in the boot_control HAL work.
104
105        Prerequisite: The DUT is in ADB mode with populated A and B slots.
106        """
107        logging.info('Check setActiveBootSlot() in boot_control HAL.')
108        self.set_active_slot(0)
109        self.dut.reboot()
110        self.assert_current_slot(0)
111        self.set_active_slot(1)
112        self.dut.reboot()
113        self.assert_current_slot(1)
114
115
116    def check_fastboot_set_active(self):
117        """Checks that 'fastboot set_active <SUFFIX>' work.
118
119        Prerequisite: The DUT is in ADB mode with populated A and B slots.
120        """
121        logging.info('Check set_active command in fastboot-compliant bootloader.')
122        self.dut.ensure_bootloader_mode()
123        # TODO(zeuthen): The "oem set_active <NUMBER>" is specific to
124        # edison. We need to get set_active support in fastboot - see
125        # b/25075082 - and then for vendors to implement "set_active
126        # <SLOT_SUFFIX>" e.g. use suffix instead of number.
127        self.dut.fastboot_run('oem set_active 0')
128        self.dut.fastboot_run('continue')
129        self.dut.adb_run('wait-for-device')
130        self.assert_current_slot(0)
131        self.dut.ensure_bootloader_mode()
132        self.dut.fastboot_run('oem set_active 1')
133        self.dut.fastboot_run('continue')
134        self.dut.adb_run('wait-for-device')
135        self.assert_current_slot(1)
136
137
138    def check_bootloader_fallback_on_invalid(self):
139        """Checks bootloader fallback if current slot is invalid.
140
141        Prerequisite: The DUT is in ADB mode with populated A and B slots.
142        """
143        logging.info('Checking bootloader fallback if current slot '
144                     'is invalid.')
145        # Make sure we're in slot B, then zero out boot_b (so slot B
146        # is invalid), reboot and check that the bootloader correctly
147        # fell back to A.
148        self.ensure_running_slot(1)
149        self.dut.run('dd if=/dev/zero of=/dev/block/by-name/boot%s '
150                     'count=8192 bs=4096' % self.suffix_b)
151        self.dut.reboot()
152        self.assert_current_slot(0)
153        # Restore boot_b for use in future test cases.
154        self.dut.run('dd if=/dev/block/by-name/boot%s '
155                     'of=/dev/block/by-name/boot%s bs=4096' %
156                     (self.suffix_a, self.suffix_b))
157
158
159    def check_bootloader_fallback_on_retries(self):
160        """Checks bootloader fallback if slot made active runs out of tries.
161
162        Prerequisite: The DUT is in ADB mode with populated A and B slots.
163
164        @raises error.TestFail
165        """
166        logging.info('Checking bootloader fallback if slot made active '
167                     'runs out of tries.')
168        self.ensure_running_slot(0)
169        self.set_active_slot(1)
170        self.dut.reboot()
171        num_retries = 1
172        while num_retries < 10 and self.get_current_slot() == 1:
173            logging.info('Still with B after %d retries' % num_retries)
174            num_retries += 1
175            self.dut.reboot()
176        if self.get_current_slot() != 0:
177            raise error.TestFail('Bootloader did not fall back after '
178                                 '%d retries without the slot being marked '
179                                 'as GOOD' % num_retries)
180        logging.info('Falled back to A after %d retries.', num_retries)
181
182
183    def check_bootloader_mark_successful(self):
184        """Checks bootloader stays with slot after it has been marked good.
185
186        Prerequisite: The DUT is in ADB mode with populated A and B slots.
187        """
188        logging.info('Checking bootloader is staying with a slot after it has '
189                     'been marked as GOOD for at least 10 reboots.')
190        self.ensure_running_slot(0)
191        self.dut.run('bootctl mark-boot-successful')
192        num_reboots = 0
193        while num_reboots < 10:
194            self.dut.reboot()
195            self.assert_current_slot(0)
196            num_reboots += 1
197            logging.info('Still with A after %d reboots' % num_reboots)
198
199
200    def run_once(self, dut=None):
201        """A/B tests for boot loader and boot_control HAL implementation.
202
203        Verifies that boot loader and boot_control HAL implementation
204        implements A/B correctly.
205
206        Prerequisite: The DUT is in ADB mode.
207
208        @param dut: host object representing the device under test.
209        """
210        self.dut = dut
211        self.get_slots_and_suffix()
212        self.ensure_running_slot(0)
213        self.copy_a_to_b()
214        self.check_bootctl_set_active()
215        self.check_fastboot_set_active()
216        self.check_bootloader_fallback_on_invalid()
217        self.check_bootloader_fallback_on_retries()
218        self.check_bootloader_mark_successful()
219