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 5import logging 6import os 7import tempfile 8 9from autotest_lib.client.common_lib import error 10from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 11 12 13class 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