1# Copyright (c) 2016 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 6 7from autotest_lib.client.bin import utils 8from autotest_lib.client.common_lib import error 9from autotest_lib.client.common_lib.utils import crc8 10from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 11 12 13class firmware_TPMNotCorruptedDevMode(FirmwareTest): 14 """ 15 Checks the kernel anti-rollback info stored in the TPM NVRAM, and then boots 16 to USB and checks the firmware version and kernel version via crossystem for 17 corruption. 18 19 This test requires a USB disk plugged-in, which contains a ChromeOS test 20 image (built by "build_image test"). 21 """ 22 version = 1 23 NEEDS_SERVO_USB = True 24 25 # The "what" of KERNEL_ANTIROLLBACK_SPACE_BYTES, KERNEL_NV_INDEX, and 26 # TPM_NVRAM_EXPECTED_VALUES can be understood from 27 # platform/vboot_reference/firmware/lib/include/rollback_index.h. 28 KERNEL_ANTIROLLBACK_SPACE_BYTES = 0xd 29 KERNEL_ANTIROLLBACK_SPACE_BYTES_v10 = 0x28 30 KERNEL_NV_INDEX = 0x1008 31 # TODO(kmshelton): This test intends to check whether the kernel version 32 # stored in the TPM's NVRAM is corrupted during a transition from normal 33 # mode to dev mode (according to it's objective in an internal tool named 34 # test tracker). Figure out why a specific kernel version is checked for, 35 # and take a look at not requiring a specific kernel version. Examine 36 # transitioning to crossystem for the normal mode read of the kernel 37 # version (e.g. look at things like whether a successful exit code from 38 # a crossystem reading of the kernel version can establish whether the 39 # kernel version starts in a valid state). 40 TPM_NVRAM_EXPECTED_VALUES = set([ 41 '1 4c 57 52 47 1 0 1 0 0 0 0 0', '2 4c 57 52 47 1 0 1 0 0 0 0 55', 42 '2 4c 57 52 47 0 0 0 0 0 0 0 e8' 43 ]) 44 45 # TPM kernel data format version 1.0 46 # byte[0]: version. bit[7:4]=Major, bit[3:0]=Minor. 47 # byte[1]: byte size. 0x28, 40(0x28) bytes 48 # byte[2]: crc8 49 # byte[3]: flag 50 # byte[7-4]: kernel version. 0x00010001 51 # byte[39:8]: ECFW hash in sha256. 52 TPM_NVRAM_V10_HEADER = '10 28 ' 53 TPM_NVRAM_V10_CRC_OFFSET = 2 54 55 def initialize(self, host, cmdline_args, ec_wp=None): 56 """Initialize the test""" 57 dict_args = utils.args_to_dict(cmdline_args) 58 super(firmware_TPMNotCorruptedDevMode, self).initialize( 59 host, cmdline_args, ec_wp=ec_wp) 60 61 self.switcher.setup_mode('dev') 62 # Use the USB key for Ctrl-U dev boot, not recovery. 63 self.setup_usbkey(usbkey=True, host=False, used_for_recovery=False) 64 65 self.original_dev_boot_usb = self.faft_client.system.get_dev_boot_usb() 66 logging.info('Original dev_boot_usb value: %s', 67 str(self.original_dev_boot_usb)) 68 69 def cleanup(self): 70 """Cleanup the test""" 71 try: 72 self.ensure_dev_internal_boot(self.original_dev_boot_usb) 73 except Exception as e: 74 logging.error("Caught exception: %s", str(e)) 75 super(firmware_TPMNotCorruptedDevMode, self).cleanup() 76 77 def ensure_usb_device_boot(self): 78 """Ensure USB device boot and if not reboot into USB.""" 79 if not self.faft_client.system.is_removable_device_boot(): 80 logging.info('Reboot into USB...') 81 self.faft_client.system.set_dev_boot_usb(1) 82 self.switcher.simple_reboot() 83 self.switcher.bypass_dev_boot_usb() 84 self.switcher.wait_for_client() 85 86 self.check_state((self.checkers.dev_boot_usb_checker, (True, True), 87 'Device not booted from USB image properly.')) 88 89 def read_tmpc(self): 90 """First checks if internal device boot and if not reboots into it. 91 Then stops the TPM daemon, reads the anti-rollback kernel version data 92 and compares it to expected values. 93 """ 94 self.ensure_dev_internal_boot(self.original_dev_boot_usb) 95 logging.info('Reading kernel anti-rollback data from the TPM.') 96 self.faft_client.tpm.stop_daemon() 97 kernel_rollback_space = ( 98 self.faft_client.system.run_shell_command_get_output( 99 'tpmc read %s %s' % 100 (hex(self.KERNEL_NV_INDEX), 101 hex(self.KERNEL_ANTIROLLBACK_SPACE_BYTES)))) 102 self.faft_client.tpm.restart_daemon() 103 104 logging.info('===== TPMC OUTPUT: %s =====', kernel_rollback_space) 105 if not self.check_tpmc(kernel_rollback_space): 106 raise error.TestFail( 107 'Kernel anti-rollback info looks unexpected. Actual: %s ' 108 'Expected one of: %s or r%r' % 109 (kernel_rollback_space, self.TPM_NVRAM_EXPECTED_VALUES, 110 self.TPM_NVRAM_V10_REGEXP)) 111 112 def read_kern_fw_ver(self): 113 """First ensures that we are booted on a USB device. Then checks the 114 firmware and kernel version reported by crossystem. 115 """ 116 self.ensure_usb_device_boot() 117 logging.info('Checking kernel and fw version via crossystem.') 118 119 if self.checkers.crossystem_checker({ 120 'tpm_fwver': '0xffffffff', 121 'tpm_kernver': '0xffffffff', 122 }): 123 raise error.TestFail( 124 'Invalid kernel and firmware versions stored in the TPM.') 125 126 def check_tpmc_v10(self): 127 self.faft_client.tpm.stop_daemon() 128 try: 129 kernel_data = ( 130 self.faft_client.system.run_shell_command_get_output( 131 'tpmc read %s %s' % 132 (hex(self.KERNEL_NV_INDEX), 133 hex(self.KERNEL_ANTIROLLBACK_SPACE_BYTES_v10))) 134 )[0].split() 135 finally: 136 self.faft_client.tpm.restart_daemon() 137 138 # Convert string list to int list 139 kernel_data_i = [ int(i, 16) for i in kernel_data ] 140 141 # For all the bytes from offset 3, calculate CRC8. 142 crc8_calc = crc8(kernel_data_i[(self.TPM_NVRAM_V10_CRC_OFFSET + 1):]) 143 144 if kernel_data_i[self.TPM_NVRAM_V10_CRC_OFFSET] != crc8_calc: 145 logging.error('Kernel Data CRC(%s) is not correct: should be %s.', 146 hex(kernel_data_i[self.TPM_NVRAM_V10_CRC_OFFSET]), 147 hex(crc8_calc)) 148 return False 149 150 logging.info('Kernel data v1.0 CRC is checked.') 151 return True 152 153 def check_tpmc(self, tpmc_output): 154 """Checks that the kernel anti-rollback data from the tpmc read command 155 is one of the expected values. 156 """ 157 if len(tpmc_output) != 1: 158 return False 159 160 # Check if TPM kernel data is in the format of version 1.0. 161 if tpmc_output[0].startswith(self.TPM_NVRAM_V10_HEADER): 162 return self.check_tpmc_v10() 163 164 return (tpmc_output[0] in self.TPM_NVRAM_EXPECTED_VALUES) 165 166 def run_once(self): 167 """Runs a single iteration of the test.""" 168 self.read_tmpc() 169 self.read_kern_fw_ver() 170