• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2012 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 re
8
9from autotest_lib.client.bin import test, utils
10from autotest_lib.client.common_lib import error
11
12class firmware_LockedME(test.test):
13    # Needed by autotest
14    version = 1
15
16    # Temporary file to read BIOS image into. We run in a tempdir anyway, so it
17    # doesn't need a path.
18    BIOS_FILE = 'bios.bin'
19
20    def flashrom(self, ignore_status=False, args=()):
21        """Run flashrom, expect it to work. Fail if it doesn't"""
22        extra = ['-p', 'host'] + list(args)
23        return utils.run('flashrom', ignore_status=ignore_status, args=extra)
24
25    def has_ME(self):
26        """See if we can detect an ME.
27        FREG* is printed only when HSFS_FDV is set, which means the descriptor
28        table is valid. If we're running a BIOS without a valid descriptor this
29        step will fail. Unfortunately, we don't know of a simple and reliable
30        way to identify systems that have ME hardware.
31        """
32        logging.info('See if we have an ME...')
33        r = self.flashrom(args=('-V',))
34        return r.stdout.find("FREG0") >= 0
35
36    def try_to_rewrite(self, sectname):
37        """If we can modify the ME section, restore it and raise an error."""
38        logging.info('Try to write section %s...', sectname)
39        size = os.stat(sectname).st_size
40        utils.run('dd', args=('if=/dev/urandom', 'of=newdata',
41                              'count=1', 'bs=%d' % (size)))
42        r = self.flashrom(args=('-w', self.BIOS_FILE,
43                                '-i' , '%s:newdata' % (sectname),
44                                '--fast-verify'),
45                          ignore_status=True)
46        if not r.exit_status:
47            logging.info('Oops, it worked! Put it back...')
48            self.flashrom(args=('-w', self.BIOS_FILE,
49                                '-i', '%s:%s' % (sectname, sectname),
50                                '--fast-verify'),
51                          ignore_status=True)
52            raise error.TestFail('%s is writable, ME is unlocked' % sectname)
53
54    def check_manufacturing_mode(self):
55        """Fail if manufacturing mode is not found or enbaled."""
56
57        # See if coreboot told us that the ME is still in Manufacturing Mode.
58        # It shouldn't be. We have to look only at the last thing it reports
59        # because it reports the values twice and the first one isn't always
60        # reliable.
61        logging.info('Check for Manufacturing Mode...')
62        last = None
63        with open('/sys/firmware/log') as infile:
64            for line in infile:
65                if re.search('ME: Manufacturing Mode', line):
66                    last = line
67        if last is not None and last.find("YES") >= 0:
68            raise error.TestFail("The ME is still in Manufacturing Mode")
69
70    def check_region_inaccessible(self, sectname):
71        """Test and ensure a region is not accessible by host CPU."""
72
73        # flashrom should have read the section as all 0xff's. If not,
74        # the ME is not locked.
75        logging.info('%s should be all 0xff...' % sectname)
76        with open(sectname, 'rb') as f:
77            for c in f.read():
78                if c != chr(0xff):
79                    err_string = "%s was readable by flashrom" % sectname
80                    raise error.TestFail(err_string)
81
82        # See if it is writable.
83        self.try_to_rewrite(sectname)
84
85    def run_once(self, expect_me_present=True):
86        """Fail unless the ME is locked.
87
88        @param expect_me_present: False means the system has no ME.
89        """
90
91        # See if the system even has an ME, and whether we expected that.
92        if self.has_ME():
93            if not expect_me_present:
94                raise error.TestFail('We expected no ME, but found one anyway')
95        else:
96            if expect_me_present:
97                raise error.TestNAError("No ME found. That's probably wrong.")
98            else:
99                logging.info('We expected no ME and we have no ME, so pass.')
100                return
101
102        # Make sure manufacturing mode is off.
103        self.check_manufacturing_mode()
104
105        # Read the image using flashrom.
106        self.flashrom(args=('-r', self.BIOS_FILE))
107
108        # Use 'IFWI' fmap region as a proxy for a device which doesn't
109        # have a dedicated ME region in the boot media.
110        r = utils.run('dump_fmap', args=('-p', self.BIOS_FILE))
111        is_IFWI_platform = r.stdout.find("IFWI") >= 0
112
113        # Get the bios image and extract the ME components
114        logging.info('Pull the ME components from the BIOS...')
115        dump_fmap_args = ['-x', self.BIOS_FILE, 'SI_DESC']
116        inaccessible_sections = []
117        if is_IFWI_platform:
118            inaccessible_sections.append('DEVICE_EXTENSION')
119        else:
120            inaccessible_sections.append('SI_ME')
121        dump_fmap_args.extend(inaccessible_sections)
122        utils.run('dump_fmap', args=tuple(dump_fmap_args))
123
124        # So far, so good, but we need to be certain. Rather than parse what
125        # flashrom tells us about the ME-related registers, we'll just try to
126        # change the ME components. We shouldn't be able to.
127        self.try_to_rewrite('SI_DESC')
128        for sectname in inaccessible_sections:
129            self.check_region_inaccessible(sectname)
130
131        # Okay, that's about all we can try. Looks like it's locked.
132