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