• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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