Home | History | Annotate | Download | only in brillo_BootLoader
      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 
      5 import logging
      6 import time
      7 
      8 import common
      9 from autotest_lib.client.common_lib import error
     10 from autotest_lib.server import test
     11 
     12 
     13 def _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 
     25 class 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