blob: 6db08fb503b0ef644125a6fdb300e3b55050a871 [file] [log] [blame]
Vic Yangf93f7022012-10-31 09:40:36 +08001# 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 re
6import logging
7
8from autotest_lib.client.common_lib import error
9from autotest_lib.server.cros import vboot_constants as vboot
10
Tom Wai-Hong Tam144202a2013-07-18 13:59:29 +080011
Vic Yangf93f7022012-10-31 09:40:36 +080012class FAFTCheckers(object):
13 """Class that contains FAFT checkers."""
14 version = 1
15
Tom Wai-Hong Tam0cc9a4f2015-05-02 05:12:39 +080016 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 Chen3edea982014-12-30 14:54:21 -080020 self.fw_vboot2 = self.faft_client.system.get_fw_vboot2()
Vic Yangf93f7022012-10-31 09:40:36 +080021
Vic Yangf93f7022012-10-31 09:40:36 +080022 def _parse_crossystem_output(self, lines):
23 """Parse the crossystem output into a dict.
24
Tom Wai-Hong Tam144202a2013-07-18 13:59:29 +080025 @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 Yangf93f7022012-10-31 09:40:36 +080028
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 Kitchingb6644082018-10-03 16:34:09 +080062 def crossystem_checker(self, expected_dict, optional=None,
63 suppress_logging=False):
Vic Yangf93f7022012-10-31 09:40:36 +080064 """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 Tam144202a2013-07-18 13:59:29 +080069 @param expected_dict: A dict which contains the expected values.
Joel Kitchingb6644082018-10-03 16:34:09 +080070 @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 Tam69a7d652015-05-09 01:38:30 +080073 @param suppress_logging: True to suppress any logging messages.
Tom Wai-Hong Tam144202a2013-07-18 13:59:29 +080074 @return: True if the crossystem value matched; otherwise, False.
Vic Yangf93f7022012-10-31 09:40:36 +080075 """
Joel Kitchingb6644082018-10-03 16:34:09 +080076 if optional == None:
77 optional = []
Tom Wai-Hong Tam29022372012-12-12 16:06:43 +080078 succeed = True
Chun-ting Changd43aa9b2012-11-16 10:12:05 +080079 lines = self.faft_client.system.run_shell_command_get_output(
80 'crossystem')
Vic Yangf93f7022012-10-31 09:40:36 +080081 got_dict = self._parse_crossystem_output(lines)
82 for key in expected_dict:
83 if key not in got_dict:
Joel Kitchingb6644082018-10-03 16:34:09 +080084 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 Tam29022372012-12-12 16:06:43 +080090 continue
Vic Yangf93f7022012-10-31 09:40:36 +080091 if isinstance(expected_dict[key], str):
92 if got_dict[key] != expected_dict[key]:
Tom Wai-Hong Tam69a7d652015-05-09 01:38:30 +080093 message = ('Expected %r value %r but got %r' % (
94 key, expected_dict[key], got_dict[key]))
Tom Wai-Hong Tam29022372012-12-12 16:06:43 +080095 succeed = False
Yusuf Mohsinallyb557fec2013-11-11 13:24:45 -080096 else:
Tom Wai-Hong Tam69a7d652015-05-09 01:38:30 +080097 message = ('Expected %r value %r == real value %r' % (
98 key, expected_dict[key], got_dict[key]))
Yusuf Mohsinallyb557fec2013-11-11 13:24:45 -080099
Vic Yangf93f7022012-10-31 09:40:36 +0800100 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 Tam69a7d652015-05-09 01:38:30 +0800103 message = ('Expected %r values %r but got %r' % (
104 key, expected_dict[key], got_dict[key]))
Tom Wai-Hong Tam29022372012-12-12 16:06:43 +0800105 succeed = False
Yusuf Mohsinallyb557fec2013-11-11 13:24:45 -0800106 else:
Tom Wai-Hong Tam69a7d652015-05-09 01:38:30 +0800107 message = ('Expected %r values %r == real value %r' % (
108 key, expected_dict[key], got_dict[key]))
Vic Yangf93f7022012-10-31 09:40:36 +0800109 else:
Tom Wai-Hong Tam69a7d652015-05-09 01:38:30 +0800110 logging.warn('The expected value of %r is neither a str nor a '
111 'dict: %r', key, expected_dict[key])
Tom Wai-Hong Tam29022372012-12-12 16:06:43 +0800112 succeed = False
Tom Wai-Hong Tam69a7d652015-05-09 01:38:30 +0800113 continue
114 if not suppress_logging:
115 logging.info(message)
Tom Wai-Hong Tam29022372012-12-12 16:06:43 +0800116 return succeed
Vic Yangf93f7022012-10-31 09:40:36 +0800117
Tom Wai-Hong Tam0cc9a4f2015-05-02 05:12:39 +0800118 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 Tam2ae42442015-05-21 02:00:00 +0800124 is_devsw = (self.faft_config.mode_switcher_type ==
125 'physical_button_switcher')
Tom Wai-Hong Tam0cc9a4f2015-05-02 05:12:39 +0800126 if mode == 'normal':
Tom Wai-Hong Tam2ae42442015-05-21 02:00:00 +0800127 if is_devsw:
128 return self.crossystem_checker(
129 {'devsw_cur': '0'},
130 suppress_logging=True)
131 else:
Tom Wai-Hong Tam0cc9a4f2015-05-02 05:12:39 +0800132 return self.crossystem_checker(
133 {'devsw_boot': '0',
Tom Wai-Hong Tam69a7d652015-05-09 01:38:30 +0800134 'mainfw_type': 'normal'},
135 suppress_logging=True)
Tom Wai-Hong Tam0cc9a4f2015-05-02 05:12:39 +0800136 elif mode == 'dev':
Tom Wai-Hong Tam2ae42442015-05-21 02:00:00 +0800137 if is_devsw:
138 return self.crossystem_checker(
139 {'devsw_cur': '1'},
140 suppress_logging=True)
141 else:
Tom Wai-Hong Tam0cc9a4f2015-05-02 05:12:39 +0800142 return self.crossystem_checker(
143 {'devsw_boot': '1',
Tom Wai-Hong Tam69a7d652015-05-09 01:38:30 +0800144 'mainfw_type': 'developer'},
145 suppress_logging=True)
Tom Wai-Hong Tam0cc9a4f2015-05-02 05:12:39 +0800146 elif mode == 'rec':
147 return self.crossystem_checker(
Tom Wai-Hong Tam69a7d652015-05-09 01:38:30 +0800148 {'mainfw_type': 'recovery'},
149 suppress_logging=True)
Tom Wai-Hong Tam0cc9a4f2015-05-02 05:12:39 +0800150 else:
151 raise NotImplementedError('The given mode %s not supported' % mode)
152
Shelley Chen3edea982014-12-30 14:54:21 -0800153 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 Yangf93f7022012-10-31 09:40:36 +0800192 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 Tam144202a2013-07-18 13:59:29 +0800198 @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 Yangf93f7022012-10-31 09:40:36 +0800201 """
Chun-ting Changd43aa9b2012-11-16 10:12:05 +0800202 lines = self.faft_client.system.run_shell_command_get_output(
Vic Yangf93f7022012-10-31 09:40:36 +0800203 'crossystem vdat_flags')
204 vdat_flags = int(lines[0], 16)
205 if vdat_flags & mask != value:
Vic Yang772df8a2012-10-31 10:10:49 +0800206 logging.info("Expected vdat_flags 0x%x mask 0x%x but got 0x%x",
207 value, mask, vdat_flags)
Vic Yangf93f7022012-10-31 09:40:36 +0800208 return False
209 return True
210
Vic Yangf93f7022012-10-31 09:40:36 +0800211 def ro_normal_checker(self, expected_fw=None, twostop=False):
212 """Check the current boot uses RO boot.
213
Tom Wai-Hong Tam144202a2013-07-18 13:59:29 +0800214 @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 Yangf93f7022012-10-31 09:40:36 +0800220 """
221 crossystem_dict = {'tried_fwb': '0'}
222 if expected_fw:
223 crossystem_dict['mainfw_act'] = expected_fw.upper()
Tom Wai-Hong Tam29022372012-12-12 16:06:43 +0800224 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 Tam0cc9a4f2015-05-02 05:12:39 +0800230 if self.faft_framework.check_ec_capability(suppress_warning=True):
Tom Wai-Hong Tamfe3dd972013-01-16 10:19:58 +0800231 expected_ec = ('RW' if twostop else 'RO')
232 if not self.ec_act_copy_checker(expected_ec):
233 succeed = False
Tom Wai-Hong Tam29022372012-12-12 16:06:43 +0800234 return succeed
Vic Yangf93f7022012-10-31 09:40:36 +0800235
Lenine Ajagappane2f794472018-02-09 16:45:22 +0530236 def dev_boot_usb_checker(self, dev_boot_usb=True, kernel_key_hash=False):
Vic Yangf93f7022012-10-31 09:40:36 +0800237 """Check the current boot is from a developer USB (Ctrl-U trigger).
238
Tom Wai-Hong Tam144202a2013-07-18 13:59:29 +0800239 @param dev_boot_usb: True to expect an USB boot;
240 False to expect an internal device boot.
Lenine Ajagappane2f794472018-02-09 16:45:22 +0530241 @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 Yangf93f7022012-10-31 09:40:36 +0800245 """
Lenine Ajagappane2f794472018-02-09 16:45:22 +0530246 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 Yangf93f7022012-10-31 09:40:36 +0800259
Vic Yangf93f7022012-10-31 09:40:36 +0800260 def root_part_checker(self, expected_part):
261 """Check the partition number of the root device matched.
262
Tom Wai-Hong Tam144202a2013-07-18 13:59:29 +0800263 @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 Yangf93f7022012-10-31 09:40:36 +0800267 """
Chun-ting Changd43aa9b2012-11-16 10:12:05 +0800268 part = self.faft_client.system.get_root_part()[-1]
Tom Wai-Hong Tam0cc9a4f2015-05-02 05:12:39 +0800269 if self.faft_framework.ROOTFS_MAP[expected_part] != part:
Vic Yang772df8a2012-10-31 10:10:49 +0800270 logging.info("Expected root part %s but got %s",
Tom Wai-Hong Tam0cc9a4f2015-05-02 05:12:39 +0800271 self.faft_framework.ROOTFS_MAP[expected_part], part)
Vic Yangf93f7022012-10-31 09:40:36 +0800272 return False
273 return True
274
Vic Yangf93f7022012-10-31 09:40:36 +0800275 def ec_act_copy_checker(self, expected_copy):
276 """Check the EC running firmware copy matches.
277
Tom Wai-Hong Tam144202a2013-07-18 13:59:29 +0800278 @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 Yangf93f7022012-10-31 09:40:36 +0800281 """
Tom Wai-Hong Tamc7bdb752015-08-26 05:30:39 +0800282 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 Yangf93f7022012-10-31 09:40:36 +0800287 pattern = re.compile("Firmware copy: (.*)")
288 for line in lines:
289 matched = pattern.match(line)
Tom Wai-Hong Tamfe3dd972013-01-16 10:19:58 +0800290 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 Tamc7bdb752015-08-26 05:30:39 +0800297 logging.info("Wrong output format of '%s':\n%s", cmd, '\n'.join(lines))
Vic Yangf93f7022012-10-31 09:40:36 +0800298 return False