Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 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 | |
| 5 | import re |
| 6 | import logging |
| 7 | |
| 8 | from autotest_lib.client.common_lib import error |
| 9 | from autotest_lib.server.cros import vboot_constants as vboot |
| 10 | |
Tom Wai-Hong Tam | 144202a | 2013-07-18 13:59:29 +0800 | [diff] [blame] | 11 | |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 12 | class FAFTCheckers(object): |
| 13 | """Class that contains FAFT checkers.""" |
| 14 | version = 1 |
| 15 | |
Tom Wai-Hong Tam | 0cc9a4f | 2015-05-02 05:12:39 +0800 | [diff] [blame] | 16 | def __init__(self, faft_framework): |
| 17 | self.faft_framework = faft_framework |
| 18 | self.faft_client = faft_framework.faft_client |
| 19 | self.faft_config = faft_framework.faft_config |
Shelley Chen | 3edea98 | 2014-12-30 14:54:21 -0800 | [diff] [blame] | 20 | self.fw_vboot2 = self.faft_client.system.get_fw_vboot2() |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 21 | |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 22 | def _parse_crossystem_output(self, lines): |
| 23 | """Parse the crossystem output into a dict. |
| 24 | |
Tom Wai-Hong Tam | 144202a | 2013-07-18 13:59:29 +0800 | [diff] [blame] | 25 | @param lines: The list of crossystem output strings. |
| 26 | @return: A dict which contains the crossystem keys/values. |
| 27 | @raise TestError: If wrong format in crossystem output. |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 28 | |
| 29 | >>> seq = FAFTSequence() |
| 30 | >>> seq._parse_crossystem_output([ \ |
| 31 | "arch = x86 # Platform architecture", \ |
| 32 | "cros_debug = 1 # OS should allow debug", \ |
| 33 | ]) |
| 34 | {'cros_debug': '1', 'arch': 'x86'} |
| 35 | >>> seq._parse_crossystem_output([ \ |
| 36 | "arch=x86", \ |
| 37 | ]) |
| 38 | Traceback (most recent call last): |
| 39 | ... |
| 40 | TestError: Failed to parse crossystem output: arch=x86 |
| 41 | >>> seq._parse_crossystem_output([ \ |
| 42 | "arch = x86 # Platform architecture", \ |
| 43 | "arch = arm # Platform architecture", \ |
| 44 | ]) |
| 45 | Traceback (most recent call last): |
| 46 | ... |
| 47 | TestError: Duplicated crossystem key: arch |
| 48 | """ |
| 49 | pattern = "^([^ =]*) *= *(.*[^ ]) *# [^#]*$" |
| 50 | parsed_list = {} |
| 51 | for line in lines: |
| 52 | matched = re.match(pattern, line.strip()) |
| 53 | if not matched: |
| 54 | raise error.TestError("Failed to parse crossystem output: %s" |
| 55 | % line) |
| 56 | (name, value) = (matched.group(1), matched.group(2)) |
| 57 | if name in parsed_list: |
| 58 | raise error.TestError("Duplicated crossystem key: %s" % name) |
| 59 | parsed_list[name] = value |
| 60 | return parsed_list |
| 61 | |
Joel Kitching | b664408 | 2018-10-03 16:34:09 +0800 | [diff] [blame^] | 62 | def crossystem_checker(self, expected_dict, optional=None, |
| 63 | suppress_logging=False): |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 64 | """Check the crossystem values matched. |
| 65 | |
| 66 | Given an expect_dict which describes the expected crossystem values, |
| 67 | this function check the current crossystem values are matched or not. |
| 68 | |
Tom Wai-Hong Tam | 144202a | 2013-07-18 13:59:29 +0800 | [diff] [blame] | 69 | @param expected_dict: A dict which contains the expected values. |
Joel Kitching | b664408 | 2018-10-03 16:34:09 +0800 | [diff] [blame^] | 70 | @param optional: A list of expected_dict keys which are optional. If |
| 71 | crossystem does not report these keys (i.e. they don't |
| 72 | exist on the system), they will not trigger a failure. |
Tom Wai-Hong Tam | 69a7d65 | 2015-05-09 01:38:30 +0800 | [diff] [blame] | 73 | @param suppress_logging: True to suppress any logging messages. |
Tom Wai-Hong Tam | 144202a | 2013-07-18 13:59:29 +0800 | [diff] [blame] | 74 | @return: True if the crossystem value matched; otherwise, False. |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 75 | """ |
Joel Kitching | b664408 | 2018-10-03 16:34:09 +0800 | [diff] [blame^] | 76 | if optional == None: |
| 77 | optional = [] |
Tom Wai-Hong Tam | 2902237 | 2012-12-12 16:06:43 +0800 | [diff] [blame] | 78 | succeed = True |
Chun-ting Chang | d43aa9b | 2012-11-16 10:12:05 +0800 | [diff] [blame] | 79 | lines = self.faft_client.system.run_shell_command_get_output( |
| 80 | 'crossystem') |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 81 | got_dict = self._parse_crossystem_output(lines) |
| 82 | for key in expected_dict: |
| 83 | if key not in got_dict: |
Joel Kitching | b664408 | 2018-10-03 16:34:09 +0800 | [diff] [blame^] | 84 | if key in optional: |
| 85 | logging.warn('Skipping optional key %r ' |
| 86 | 'not in crossystem result', key) |
| 87 | else: |
| 88 | logging.warn('Expected key %r not in crossystem result', key) |
| 89 | succeed = False |
Tom Wai-Hong Tam | 2902237 | 2012-12-12 16:06:43 +0800 | [diff] [blame] | 90 | continue |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 91 | if isinstance(expected_dict[key], str): |
| 92 | if got_dict[key] != expected_dict[key]: |
Tom Wai-Hong Tam | 69a7d65 | 2015-05-09 01:38:30 +0800 | [diff] [blame] | 93 | message = ('Expected %r value %r but got %r' % ( |
| 94 | key, expected_dict[key], got_dict[key])) |
Tom Wai-Hong Tam | 2902237 | 2012-12-12 16:06:43 +0800 | [diff] [blame] | 95 | succeed = False |
Yusuf Mohsinally | b557fec | 2013-11-11 13:24:45 -0800 | [diff] [blame] | 96 | else: |
Tom Wai-Hong Tam | 69a7d65 | 2015-05-09 01:38:30 +0800 | [diff] [blame] | 97 | message = ('Expected %r value %r == real value %r' % ( |
| 98 | key, expected_dict[key], got_dict[key])) |
Yusuf Mohsinally | b557fec | 2013-11-11 13:24:45 -0800 | [diff] [blame] | 99 | |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 100 | elif isinstance(expected_dict[key], tuple): |
| 101 | # Expected value is a tuple of possible actual values. |
| 102 | if got_dict[key] not in expected_dict[key]: |
Tom Wai-Hong Tam | 69a7d65 | 2015-05-09 01:38:30 +0800 | [diff] [blame] | 103 | message = ('Expected %r values %r but got %r' % ( |
| 104 | key, expected_dict[key], got_dict[key])) |
Tom Wai-Hong Tam | 2902237 | 2012-12-12 16:06:43 +0800 | [diff] [blame] | 105 | succeed = False |
Yusuf Mohsinally | b557fec | 2013-11-11 13:24:45 -0800 | [diff] [blame] | 106 | else: |
Tom Wai-Hong Tam | 69a7d65 | 2015-05-09 01:38:30 +0800 | [diff] [blame] | 107 | message = ('Expected %r values %r == real value %r' % ( |
| 108 | key, expected_dict[key], got_dict[key])) |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 109 | else: |
Tom Wai-Hong Tam | 69a7d65 | 2015-05-09 01:38:30 +0800 | [diff] [blame] | 110 | logging.warn('The expected value of %r is neither a str nor a ' |
| 111 | 'dict: %r', key, expected_dict[key]) |
Tom Wai-Hong Tam | 2902237 | 2012-12-12 16:06:43 +0800 | [diff] [blame] | 112 | succeed = False |
Tom Wai-Hong Tam | 69a7d65 | 2015-05-09 01:38:30 +0800 | [diff] [blame] | 113 | continue |
| 114 | if not suppress_logging: |
| 115 | logging.info(message) |
Tom Wai-Hong Tam | 2902237 | 2012-12-12 16:06:43 +0800 | [diff] [blame] | 116 | return succeed |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 117 | |
Tom Wai-Hong Tam | 0cc9a4f | 2015-05-02 05:12:39 +0800 | [diff] [blame] | 118 | def mode_checker(self, mode): |
| 119 | """Check the current system in the given mode. |
| 120 | |
| 121 | @param mode: A string of mode, one of 'normal', 'dev', or 'rec'. |
| 122 | @return: True if the system in the given mode; otherwise, False. |
| 123 | """ |
Tom Wai-Hong Tam | 2ae4244 | 2015-05-21 02:00:00 +0800 | [diff] [blame] | 124 | is_devsw = (self.faft_config.mode_switcher_type == |
| 125 | 'physical_button_switcher') |
Tom Wai-Hong Tam | 0cc9a4f | 2015-05-02 05:12:39 +0800 | [diff] [blame] | 126 | if mode == 'normal': |
Tom Wai-Hong Tam | 2ae4244 | 2015-05-21 02:00:00 +0800 | [diff] [blame] | 127 | if is_devsw: |
| 128 | return self.crossystem_checker( |
| 129 | {'devsw_cur': '0'}, |
| 130 | suppress_logging=True) |
| 131 | else: |
Tom Wai-Hong Tam | 0cc9a4f | 2015-05-02 05:12:39 +0800 | [diff] [blame] | 132 | return self.crossystem_checker( |
| 133 | {'devsw_boot': '0', |
Tom Wai-Hong Tam | 69a7d65 | 2015-05-09 01:38:30 +0800 | [diff] [blame] | 134 | 'mainfw_type': 'normal'}, |
| 135 | suppress_logging=True) |
Tom Wai-Hong Tam | 0cc9a4f | 2015-05-02 05:12:39 +0800 | [diff] [blame] | 136 | elif mode == 'dev': |
Tom Wai-Hong Tam | 2ae4244 | 2015-05-21 02:00:00 +0800 | [diff] [blame] | 137 | if is_devsw: |
| 138 | return self.crossystem_checker( |
| 139 | {'devsw_cur': '1'}, |
| 140 | suppress_logging=True) |
| 141 | else: |
Tom Wai-Hong Tam | 0cc9a4f | 2015-05-02 05:12:39 +0800 | [diff] [blame] | 142 | return self.crossystem_checker( |
| 143 | {'devsw_boot': '1', |
Tom Wai-Hong Tam | 69a7d65 | 2015-05-09 01:38:30 +0800 | [diff] [blame] | 144 | 'mainfw_type': 'developer'}, |
| 145 | suppress_logging=True) |
Tom Wai-Hong Tam | 0cc9a4f | 2015-05-02 05:12:39 +0800 | [diff] [blame] | 146 | elif mode == 'rec': |
| 147 | return self.crossystem_checker( |
Tom Wai-Hong Tam | 69a7d65 | 2015-05-09 01:38:30 +0800 | [diff] [blame] | 148 | {'mainfw_type': 'recovery'}, |
| 149 | suppress_logging=True) |
Tom Wai-Hong Tam | 0cc9a4f | 2015-05-02 05:12:39 +0800 | [diff] [blame] | 150 | else: |
| 151 | raise NotImplementedError('The given mode %s not supported' % mode) |
| 152 | |
Shelley Chen | 3edea98 | 2014-12-30 14:54:21 -0800 | [diff] [blame] | 153 | def fw_tries_checker(self, |
| 154 | expected_mainfw_act, |
| 155 | expected_fw_tried=True, |
| 156 | expected_try_count=0): |
| 157 | """Check the current FW booted and try_count |
| 158 | |
| 159 | Mainly for dealing with the vboot1-specific flags fwb_tries and |
| 160 | tried_fwb fields in crossystem. In vboot2, fwb_tries is meaningless and |
| 161 | is ignored while tried_fwb is translated into fw_try_count. |
| 162 | |
| 163 | @param expected_mainfw_act: A string of expected firmware, 'A', 'B', or |
| 164 | None if don't care. |
| 165 | @param expected_fw_tried: True if tried expected FW at last boot. |
| 166 | This means that mainfw_act=A,tried_fwb=0 or |
| 167 | mainfw_act=B,tried_fwb=1. Set to False if want to |
| 168 | check the opposite case for the mainfw_act. This |
| 169 | check is only performed in vboot1 as tried_fwb is |
| 170 | never set in vboot2. |
| 171 | @param expected_try_count: Number of times to try a FW slot. |
| 172 | |
| 173 | @return: True if the correct boot firmware fields matched. Otherwise, |
| 174 | False. |
| 175 | """ |
| 176 | crossystem_dict = {'mainfw_act': expected_mainfw_act.upper()} |
| 177 | |
| 178 | if not self.fw_vboot2: |
| 179 | if expected_mainfw_act == 'B': |
| 180 | tried_fwb_val = True |
| 181 | else: |
| 182 | tried_fwb_val = False |
| 183 | if not expected_fw_tried: |
| 184 | tried_fwb_val = not tried_fwb_val |
| 185 | crossystem_dict['tried_fwb'] = '1' if tried_fwb_val else '0' |
| 186 | |
| 187 | crossystem_dict['fwb_tries'] = str(expected_try_count) |
| 188 | else: |
| 189 | crossystem_dict['fw_try_count'] = str(expected_try_count) |
| 190 | return self.crossystem_checker(crossystem_dict) |
| 191 | |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 192 | def vdat_flags_checker(self, mask, value): |
| 193 | """Check the flags from VbSharedData matched. |
| 194 | |
| 195 | This function checks the masked flags from VbSharedData using crossystem |
| 196 | are matched the given value. |
| 197 | |
Tom Wai-Hong Tam | 144202a | 2013-07-18 13:59:29 +0800 | [diff] [blame] | 198 | @param mask: A bitmask of flags to be matched. |
| 199 | @param value: An expected value. |
| 200 | @return: True if the flags matched; otherwise, False. |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 201 | """ |
Chun-ting Chang | d43aa9b | 2012-11-16 10:12:05 +0800 | [diff] [blame] | 202 | lines = self.faft_client.system.run_shell_command_get_output( |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 203 | 'crossystem vdat_flags') |
| 204 | vdat_flags = int(lines[0], 16) |
| 205 | if vdat_flags & mask != value: |
Vic Yang | 772df8a | 2012-10-31 10:10:49 +0800 | [diff] [blame] | 206 | logging.info("Expected vdat_flags 0x%x mask 0x%x but got 0x%x", |
| 207 | value, mask, vdat_flags) |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 208 | return False |
| 209 | return True |
| 210 | |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 211 | def ro_normal_checker(self, expected_fw=None, twostop=False): |
| 212 | """Check the current boot uses RO boot. |
| 213 | |
Tom Wai-Hong Tam | 144202a | 2013-07-18 13:59:29 +0800 | [diff] [blame] | 214 | @param expected_fw: A string of expected firmware, 'A', 'B', or |
| 215 | None if don't care. |
| 216 | @param twostop: True to expect a TwoStop boot; False to expect a RO |
| 217 | boot. |
| 218 | @return: True if the currect boot firmware matched and used RO boot; |
| 219 | otherwise, False. |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 220 | """ |
| 221 | crossystem_dict = {'tried_fwb': '0'} |
| 222 | if expected_fw: |
| 223 | crossystem_dict['mainfw_act'] = expected_fw.upper() |
Tom Wai-Hong Tam | 2902237 | 2012-12-12 16:06:43 +0800 | [diff] [blame] | 224 | succeed = True |
| 225 | if not self.vdat_flags_checker(vboot.VDAT_FLAG_LF_USE_RO_NORMAL, |
| 226 | 0 if twostop else vboot.VDAT_FLAG_LF_USE_RO_NORMAL): |
| 227 | succeed = False |
| 228 | if not self.crossystem_checker(crossystem_dict): |
| 229 | succeed = False |
Tom Wai-Hong Tam | 0cc9a4f | 2015-05-02 05:12:39 +0800 | [diff] [blame] | 230 | if self.faft_framework.check_ec_capability(suppress_warning=True): |
Tom Wai-Hong Tam | fe3dd97 | 2013-01-16 10:19:58 +0800 | [diff] [blame] | 231 | expected_ec = ('RW' if twostop else 'RO') |
| 232 | if not self.ec_act_copy_checker(expected_ec): |
| 233 | succeed = False |
Tom Wai-Hong Tam | 2902237 | 2012-12-12 16:06:43 +0800 | [diff] [blame] | 234 | return succeed |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 235 | |
Lenine Ajagappane | 2f79447 | 2018-02-09 16:45:22 +0530 | [diff] [blame] | 236 | def dev_boot_usb_checker(self, dev_boot_usb=True, kernel_key_hash=False): |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 237 | """Check the current boot is from a developer USB (Ctrl-U trigger). |
| 238 | |
Tom Wai-Hong Tam | 144202a | 2013-07-18 13:59:29 +0800 | [diff] [blame] | 239 | @param dev_boot_usb: True to expect an USB boot; |
| 240 | False to expect an internal device boot. |
Lenine Ajagappane | 2f79447 | 2018-02-09 16:45:22 +0530 | [diff] [blame] | 241 | @param kernel_key_hash: True to expect an USB boot with kernkey_vfy |
| 242 | value as 'hash'; |
| 243 | False to expect kernkey_vfy value as 'sig'. |
| 244 | @return: True if the current boot device matched; otherwise, False. |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 245 | """ |
Lenine Ajagappane | 2f79447 | 2018-02-09 16:45:22 +0530 | [diff] [blame] | 246 | assert (dev_boot_usb or not kernel_key_hash), ("Invalid condition " |
| 247 | "dev_boot_usb_checker(%s, %s). kernel_key_hash should not be " |
| 248 | "True in internal disk boot.") % (dev_boot_usb, kernel_key_hash) |
| 249 | # kernkey_vfy value will be 'sig', when device booted in internal |
| 250 | # disk or booted in USB image signed with SSD key(Ctrl-U trigger). |
| 251 | expected_kernkey_vfy = 'sig' |
| 252 | if kernel_key_hash: |
| 253 | expected_kernkey_vfy = 'hash' |
| 254 | return (self.crossystem_checker({'mainfw_type': 'developer', |
| 255 | 'kernkey_vfy': |
| 256 | expected_kernkey_vfy}) and |
| 257 | self.faft_client.system.is_removable_device_boot() == |
| 258 | dev_boot_usb) |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 259 | |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 260 | def root_part_checker(self, expected_part): |
| 261 | """Check the partition number of the root device matched. |
| 262 | |
Tom Wai-Hong Tam | 144202a | 2013-07-18 13:59:29 +0800 | [diff] [blame] | 263 | @param expected_part: A string containing the number of the expected |
| 264 | root partition. |
| 265 | @return: True if the currect root partition number matched; |
| 266 | otherwise, False. |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 267 | """ |
Chun-ting Chang | d43aa9b | 2012-11-16 10:12:05 +0800 | [diff] [blame] | 268 | part = self.faft_client.system.get_root_part()[-1] |
Tom Wai-Hong Tam | 0cc9a4f | 2015-05-02 05:12:39 +0800 | [diff] [blame] | 269 | if self.faft_framework.ROOTFS_MAP[expected_part] != part: |
Vic Yang | 772df8a | 2012-10-31 10:10:49 +0800 | [diff] [blame] | 270 | logging.info("Expected root part %s but got %s", |
Tom Wai-Hong Tam | 0cc9a4f | 2015-05-02 05:12:39 +0800 | [diff] [blame] | 271 | self.faft_framework.ROOTFS_MAP[expected_part], part) |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 272 | return False |
| 273 | return True |
| 274 | |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 275 | def ec_act_copy_checker(self, expected_copy): |
| 276 | """Check the EC running firmware copy matches. |
| 277 | |
Tom Wai-Hong Tam | 144202a | 2013-07-18 13:59:29 +0800 | [diff] [blame] | 278 | @param expected_copy: A string containing 'RO', 'A', or 'B' indicating |
| 279 | the expected copy of EC running firmware. |
| 280 | @return: True if the current EC running copy matches; otherwise, False. |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 281 | """ |
Tom Wai-Hong Tam | c7bdb75 | 2015-08-26 05:30:39 +0800 | [diff] [blame] | 282 | if self.faft_client.system.has_host(): |
| 283 | cmd = 'fwtool ec version' |
| 284 | else: |
| 285 | cmd = 'ectool version' |
| 286 | lines = self.faft_client.system.run_shell_command_get_output(cmd) |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 287 | pattern = re.compile("Firmware copy: (.*)") |
| 288 | for line in lines: |
| 289 | matched = pattern.match(line) |
Tom Wai-Hong Tam | fe3dd97 | 2013-01-16 10:19:58 +0800 | [diff] [blame] | 290 | if matched: |
| 291 | if matched.group(1) == expected_copy: |
| 292 | return True |
| 293 | else: |
| 294 | logging.info("Expected EC in %s but now in %s", |
| 295 | expected_copy, matched.group(1)) |
| 296 | return False |
Tom Wai-Hong Tam | c7bdb75 | 2015-08-26 05:30:39 +0800 | [diff] [blame] | 297 | logging.info("Wrong output format of '%s':\n%s", cmd, '\n'.join(lines)) |
Vic Yang | f93f702 | 2012-10-31 09:40:36 +0800 | [diff] [blame] | 298 | return False |