Home | History | Annotate | Download | only in platform_FlashErasers
      1 # Copyright 2018 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 os
      7 import tempfile
      8 
      9 from autotest_lib.client.common_lib import error
     10 from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
     11 
     12 
     13 class platform_FlashErasers(FirmwareTest):
     14     """
     15     Test that various erase functions work correctly by calling flashrom to
     16     erase/write blocks of different sizes.
     17     """
     18     version = 1
     19 
     20     def run_cmd(self, command, checkfor=''):
     21         """
     22         Log and execute command and return the output.
     23 
     24         @param command: Command to execute on device.
     25         @param checkfor: If not emmpty, fail test if checkfor not in output.
     26         @returns the output of command as a list of strings.
     27         """
     28         command = command + ' 2>&1'
     29         logging.info('Execute %s', command)
     30         output = self.faft_client.system.run_shell_command_get_output(command)
     31         logging.info('Output >>> %s <<<', output)
     32         if checkfor and checkfor not in '\n'.join(output):
     33             raise error.TestFail('Expect %s in output of %s' %
     34                                  (checkfor, '\n'.join(output)))
     35         return output
     36 
     37     def _get_section(self, bios, section):
     38         """Return start address and size of an fmap section.
     39 
     40         @param bios: string, bios file name to retrieve fmap from
     41         @param section: string, section name to look for
     42 
     43         @return tuple of ints for start and size of the section.
     44         """
     45         # Store temp results in the attributes dictionary; when the expected
     46         # section name is found in the 'area_name:' field, the offset and size
     47         # of the section would be stored in this dictionary.
     48         attributes = {}
     49         for line in [x.strip() for x in self.run_cmd('dump_fmap %s' % bios)]:
     50             if not line:
     51                 continue
     52             tokens = line.split()
     53             if tokens[0] == 'area_name:' and tokens[1] == section:
     54                 return (int(attributes['area_offset:'], 16),
     55                         int(attributes['area_size:'], 16))
     56 
     57             if len(tokens) > 1:
     58                 attributes[tokens[0]] = tokens[1]
     59 
     60         raise error.TestFail('Could not find section %s in the fmap' % section)
     61 
     62     def _create_test_blob(self, blob_name, blob_size):
     63         """On the DUT create a blob containing 0xff bytes of the requested size.
     64 
     65         @param blob_name: string, name of the DUT file to save the blob in
     66         @param blob_size: integer, size of the blob to prepare
     67         """
     68         test_blob_data = ('%c' % 0xff) * blob_size
     69 
     70         # Save it into a local file.
     71         handle, local_file = tempfile.mkstemp()
     72 
     73         try:
     74             os.write(handle, test_blob_data)
     75             os.close(handle)
     76 
     77             # Copy the local file to the DUT.
     78             self._client.send_file(local_file, blob_name)
     79 
     80         finally:
     81             # Delete local file.
     82             os.remove(local_file)
     83 
     84     def run_once(self, dev_mode=True):
     85         """Main method implementing test logic."""
     86 
     87         # Find out which AP firmware section is active to determine which AP
     88         # firmware section could be overwritten.
     89         active_fw = self.run_cmd('crossystem mainfw_act')[0]
     90         if active_fw == 'A':
     91             section = 'RW_SECTION_B'
     92         elif active_fw == 'B':
     93             section = 'RW_SECTION_A'
     94         else:
     95             raise error.TestFail('Unexpected active fw %s' % active_fw)
     96 
     97         dut_work_path = self.faft_client.updater.get_work_path()
     98         # Sizes to try to erase.
     99         test_sizes = (4096, 4096 * 2, 4096 * 4, 4096 * 8, 4096 * 16)
    100 
    101         # Image read from the AP firmware flash chip.
    102         bios_image = os.path.join(dut_work_path, 'bios_image')
    103         self.run_cmd('flashrom -r %s' % bios_image)
    104 
    105         # A blob of all ones to paste into the image.
    106         test_blob = os.path.join(dut_work_path, 'test_blob')
    107         self._create_test_blob(test_blob, max(test_sizes))
    108 
    109         # The file to store the 'corrupted' image with all ones pasted.
    110         junk_image = os.path.join(dut_work_path, 'junk_image')
    111 
    112         # Find in fmap the AP firmware section which can be overwritten.
    113         start, size = self._get_section(bios_image, section)
    114         logging.info('Active firmware %s, alternative at %#x:%#x', active_fw,
    115                      start, start + size -1)
    116 
    117         # Command to paste the all ones blob into the corrupted image.
    118         dd_template = 'dd if="%s" of="%s" bs=1 conv=notrunc seek="%d"' % (
    119             test_blob, junk_image, start)
    120 
    121         for test_size in test_sizes:
    122 
    123             logging.info('Verifying section of size %d', test_size)
    124 
    125             self.run_cmd('cp %s %s' % (bios_image, junk_image))
    126 
    127             # Set section in the 'junk' image to 'all erased'
    128             dd_cmd = dd_template + ' count="%d"' % test_size
    129             self.run_cmd(dd_cmd)
    130 
    131             # Now program the corrupted image, this would involve erasing the
    132             # section of test_size bytes.
    133             self.run_cmd('flashrom -w %s --diff %s --fast-verify' %
    134                          (junk_image, bios_image))
    135 
    136             # Now restore the image.
    137             self.run_cmd('flashrom -w %s --diff %s' % (bios_image, junk_image))
    138