1 # Copyright 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 5 import logging 6 7 from autotest_lib.client.bin import test, utils 8 from autotest_lib.client.common_lib import error 9 10 from autotest_lib.client.cros import sys_power 11 12 13 class kernel_CheckArmErrata(test.test): 14 """ 15 Test for the presence of ARM errata fixes. 16 17 See control file for more details. 18 """ 19 version = 1 20 SECS_TO_SUSPEND = 15 21 22 @staticmethod 23 def _parse_cpu_info(cpuinfo_str): 24 """ 25 Parse the contents of /proc/cpuinfo 26 27 This will return a dict of dicts with info about all the CPUs. 28 29 :param cpuinfo_str: The contents of /proc/cpuinfo as a string. 30 :return: A dictionary of dictionaries; top key is processor ID and 31 secondary key is info from cpuinfo about that processor. 32 33 >>> cpuinfo = kernel_CheckArmErrata._parse_cpu_info( 34 ... '''processor : 0 35 ... model name : ARMv7 Processor rev 1 (v7l) 36 ... Features : swp half thumb fastmult vfp edsp thumbee neon ... 37 ... CPU implementer : 0x41 38 ... CPU architecture: 7 39 ... CPU variant : 0x0 40 ... CPU part : 0xc0d 41 ... CPU revision : 1 42 ... 43 ... processor : 1 44 ... model name : ARMv7 Processor rev 1 (v7l) 45 ... Features : swp half thumb fastmult vfp edsp thumbee neon ... 46 ... CPU implementer : 0x41 47 ... CPU architecture: 7 48 ... CPU variant : 0x0 49 ... CPU part : 0xc0d 50 ... CPU revision : 1 51 ... 52 ... Hardware : Rockchip (Device Tree) 53 ... Revision : 0000 54 ... Serial : 0000000000000000''') 55 >>> cpuinfo == { 56 ... 0: {"CPU architecture": 7, 57 ... "CPU implementer": 65, 58 ... "CPU part": 3085, 59 ... "CPU revision": 1, 60 ... "CPU variant": 0, 61 ... "Features": "swp half thumb fastmult vfp edsp thumbee neon ...", 62 ... "model name": "ARMv7 Processor rev 1 (v7l)", 63 ... "processor": 0}, 64 ... 1: {"CPU architecture": 7, 65 ... "CPU implementer": 65, 66 ... "CPU part": 3085, 67 ... "CPU revision": 1, 68 ... "CPU variant": 0, 69 ... "Features": "swp half thumb fastmult vfp edsp thumbee neon ...", 70 ... "model name": "ARMv7 Processor rev 1 (v7l)", 71 ... "processor": 1} 72 ... } 73 True 74 """ 75 cpuinfo = {} 76 processor = None 77 for info_line in cpuinfo_str.splitlines(): 78 # Processors are separated by blank lines 79 if not info_line: 80 processor = None 81 continue 82 83 key, _, val = info_line.partition(':') 84 key = key.strip() 85 val = val.strip() 86 87 # Try to convert to int... 88 try: 89 val = int(val, 0) 90 except ValueError: 91 pass 92 93 # Handle processor special. 94 if key == "processor": 95 processor = int(val) 96 cpuinfo[processor] = { "processor": processor } 97 continue 98 99 # Skip over any info we have no processor for 100 if processor is None: 101 continue 102 103 cpuinfo[processor][key] = val 104 105 return cpuinfo 106 107 @staticmethod 108 def _get_regid_to_val(cpu_id): 109 """ 110 Parse the contents of /sys/kernel/debug/arm_coprocessor_debug 111 112 This will read /sys/kernel/debug/arm_coprocessor_debug and create 113 a dictionary mapping register IDs, which look like: 114 "(p15, 0, c15, c0, 1)" 115 ...to values (as integers). 116 117 :return: A dictionary mapping string register IDs to int value. 118 """ 119 try: 120 arm_debug_info = utils.run( 121 "taskset -c %d cat /sys/kernel/debug/arm_coprocessor_debug" % 122 cpu_id).stdout.strip() 123 except error.CmdError: 124 arm_debug_info = "" 125 126 regid_to_val = {} 127 for line in arm_debug_info.splitlines(): 128 # Each line is supposed to show the CPU number; confirm it's right 129 if not line.startswith("CPU %d: " % cpu_id): 130 raise error.TestError( 131 "arm_coprocessor_debug error: CPU %d != %s" % 132 (cpu_id, line.split(":")[0])) 133 134 _, _, regid, val = line.split(":") 135 regid_to_val[regid.strip()] = int(val, 0) 136 137 return regid_to_val 138 139 def _check_one_cortex_a12(self, cpuinfo): 140 """ 141 Check the errata for a Cortex-A12. 142 143 :param cpuinfo: The CPU info for one CPU. See _parse_cpu_info for 144 the format. 145 146 >>> _testobj._get_regid_to_val = lambda cpu_id: {} 147 >>> try: 148 ... _testobj._check_one_cortex_a12({ 149 ... "processor": 2, 150 ... "model name": "ARMv7 Processor rev 1 (v7l)", 151 ... "CPU implementer": ord("A"), 152 ... "CPU part": 0xc0d, 153 ... "CPU variant": 0, 154 ... "CPU revision": 1}) 155 ... except Exception: 156 ... import traceback 157 ... print traceback.format_exc(), 158 Traceback (most recent call last): 159 ... 160 TestError: Kernel didn't provide register vals 161 162 >>> _testobj._get_regid_to_val = lambda cpu_id: \ 163 {"(p15, 0, c15, c0, 1)": 0, "(p15, 0, c15, c0, 2)": 0} 164 >>> try: 165 ... _testobj._check_one_cortex_a12({ 166 ... "processor": 2, 167 ... "model name": "ARMv7 Processor rev 1 (v7l)", 168 ... "CPU implementer": ord("A"), 169 ... "CPU part": 0xc0d, 170 ... "CPU variant": 0, 171 ... "CPU revision": 1}) 172 ... except Exception: 173 ... import traceback 174 ... print traceback.format_exc(), 175 Traceback (most recent call last): 176 ... 177 TestError: Missing bit 12 for erratum 818325 / 852422: 0x00000000 178 179 >>> _testobj._get_regid_to_val = lambda cpu_id: \ 180 { "(p15, 0, c15, c0, 1)": (1 << 12) | (1 << 24), \ 181 "(p15, 0, c15, c0, 2)": (1 << 1)} 182 >>> _info_io.seek(0); _info_io.truncate() 183 >>> _testobj._check_one_cortex_a12({ 184 ... "processor": 2, 185 ... "model name": "ARMv7 Processor rev 1 (v7l)", 186 ... "CPU implementer": ord("A"), 187 ... "CPU part": 0xc0d, 188 ... "CPU variant": 0, 189 ... "CPU revision": 1}) 190 >>> "good" in _info_io.getvalue() 191 True 192 193 >>> _testobj._check_one_cortex_a12({ 194 ... "processor": 2, 195 ... "model name": "ARMv7 Processor rev 1 (v7l)", 196 ... "CPU implementer": ord("A"), 197 ... "CPU part": 0xc0d, 198 ... "CPU variant": 0, 199 ... "CPU revision": 2}) 200 Traceback (most recent call last): 201 ... 202 TestError: Unexpected A12 revision: r0p2 203 """ 204 cpu_id = cpuinfo["processor"] 205 variant = cpuinfo.get("CPU variant", -1) 206 revision = cpuinfo.get("CPU revision", -1) 207 208 # Handy to express this the way ARM does 209 rev_str = "r%dp%d" % (variant, revision) 210 211 # We don't ever expect an A12 newer than r0p1. If we ever see one 212 # then maybe the errata was fixed. 213 if rev_str not in ("r0p0", "r0p1"): 214 raise error.TestError("Unexpected A12 revision: %s" % rev_str) 215 216 regid_to_val = self._get_regid_to_val(cpu_id) 217 218 # Getting this means we're missing the change to expose debug 219 # registers via arm_coprocessor_debug 220 if not regid_to_val: 221 raise error.TestError("Kernel didn't provide register vals") 222 223 # Erratum 818325 applies to old A12s and erratum 852422 to newer. 224 # Fix is to set bit 12 in diag register. Confirm that's done. 225 diag_reg = regid_to_val.get("(p15, 0, c15, c0, 1)") 226 if diag_reg is None: 227 raise error.TestError("Kernel didn't provide diag register") 228 elif not (diag_reg & (1 << 12)): 229 raise error.TestError( 230 "Missing bit 12 for erratum 818325 / 852422: %#010x" % 231 diag_reg) 232 logging.info("CPU %d: erratum 818325 / 852422 good", cpu_id) 233 234 # Erratum 821420 applies to all A12s. Make sure bit 1 of the 235 # internal feature register is set. 236 int_feat_reg = regid_to_val.get("(p15, 0, c15, c0, 2)") 237 if int_feat_reg is None: 238 raise error.TestError("Kernel didn't provide internal feature reg") 239 elif not (int_feat_reg & (1 << 1)): 240 raise error.TestError( 241 "Missing bit 1 for erratum 821420: %#010x" % int_feat_reg) 242 logging.info("CPU %d: erratum 821420 good", cpu_id) 243 244 # Erratum 825619 applies to all A12s. Need bit 24 in diag reg. 245 diag_reg = regid_to_val.get("(p15, 0, c15, c0, 1)") 246 if diag_reg is None: 247 raise error.TestError("Kernel didn't provide diag register") 248 elif not (diag_reg & (1 << 24)): 249 raise error.TestError( 250 "Missing bit 24 for erratum 825619: %#010x" % diag_reg) 251 logging.info("CPU %d: erratum 825619 good", cpu_id) 252 253 def _check_one_cortex_a17(self, cpuinfo): 254 """ 255 Check the errata for a Cortex-A17. 256 257 :param cpuinfo: The CPU info for one CPU. See _parse_cpu_info for 258 the format. 259 260 >>> _testobj._get_regid_to_val = lambda cpu_id: {} 261 >>> try: 262 ... _testobj._check_one_cortex_a17({ 263 ... "processor": 2, 264 ... "model name": "ARMv7 Processor rev 1 (v7l)", 265 ... "CPU implementer": ord("A"), 266 ... "CPU part": 0xc0e, 267 ... "CPU variant": 1, 268 ... "CPU revision": 1}) 269 ... except Exception: 270 ... import traceback 271 ... print traceback.format_exc(), 272 Traceback (most recent call last): 273 ... 274 TestError: Kernel didn't provide register vals 275 276 >>> _testobj._get_regid_to_val = lambda cpu_id: \ 277 {"(p15, 0, c15, c0, 1)": 0} 278 >>> try: 279 ... _testobj._check_one_cortex_a17({ 280 ... "processor": 2, 281 ... "model name": "ARMv7 Processor rev 1 (v7l)", 282 ... "CPU implementer": ord("A"), 283 ... "CPU part": 0xc0e, 284 ... "CPU variant": 1, 285 ... "CPU revision": 1}) 286 ... except Exception: 287 ... import traceback 288 ... print traceback.format_exc(), 289 Traceback (most recent call last): 290 ... 291 TestError: Missing bit 24 for erratum 852421: 0x00000000 292 293 >>> _testobj._get_regid_to_val = lambda cpu_id: \ 294 {"(p15, 0, c15, c0, 1)": (1 << 12) | (1 << 24)} 295 >>> _info_io.seek(0); _info_io.truncate() 296 >>> _testobj._check_one_cortex_a17({ 297 ... "processor": 2, 298 ... "model name": "ARMv7 Processor rev 1 (v7l)", 299 ... "CPU implementer": ord("A"), 300 ... "CPU part": 0xc0e, 301 ... "CPU variant": 1, 302 ... "CPU revision": 2}) 303 >>> "good" in _info_io.getvalue() 304 True 305 306 >>> _info_io.seek(0); _info_io.truncate() 307 >>> _testobj._check_one_cortex_a17({ 308 ... "processor": 2, 309 ... "model name": "ARMv7 Processor rev 1 (v7l)", 310 ... "CPU implementer": ord("A"), 311 ... "CPU part": 0xc0e, 312 ... "CPU variant": 2, 313 ... "CPU revision": 0}) 314 >>> print _info_io.getvalue() 315 CPU 2: new A17 (r2p0) = no errata 316 """ 317 cpu_id = cpuinfo["processor"] 318 variant = cpuinfo.get("CPU variant", -1) 319 revision = cpuinfo.get("CPU revision", -1) 320 321 # Handy to express this the way ARM does 322 rev_str = "r%dp%d" % (variant, revision) 323 324 regid_to_val = self._get_regid_to_val(cpu_id) 325 326 # Erratum 852421 and 852423 apply to "r1p0", "r1p1", "r1p2" 327 if rev_str in ("r1p0", "r1p1", "r1p2"): 328 # Getting this means we're missing the change to expose debug 329 # registers via arm_coprocessor_debug 330 if not regid_to_val: 331 raise error.TestError("Kernel didn't provide register vals") 332 333 diag_reg = regid_to_val.get("(p15, 0, c15, c0, 1)") 334 if diag_reg is None: 335 raise error.TestError("Kernel didn't provide diag register") 336 elif not (diag_reg & (1 << 24)): 337 raise error.TestError( 338 "Missing bit 24 for erratum 852421: %#010x" % diag_reg) 339 logging.info("CPU %d: erratum 852421 good",cpu_id) 340 341 diag_reg = regid_to_val.get("(p15, 0, c15, c0, 1)") 342 if diag_reg is None: 343 raise error.TestError("Kernel didn't provide diag register") 344 elif not (diag_reg & (1 << 12)): 345 raise error.TestError( 346 "Missing bit 12 for erratum 852423: %#010x" % diag_reg) 347 logging.info("CPU %d: erratum 852423 good",cpu_id) 348 else: 349 logging.info("CPU %d: new A17 (%s) = no errata", cpu_id, rev_str) 350 351 def _check_one_armv7(self, cpuinfo): 352 """ 353 Check the errata for one ARMv7 CPU. 354 355 :param cpuinfo: The CPU info for one CPU. See _parse_cpu_info for 356 the format. 357 358 >>> _info_io.seek(0); _info_io.truncate() 359 >>> _testobj._check_one_cpu({ 360 ... "processor": 2, 361 ... "model name": "ARMv7 Processor rev 1 (v7l)", 362 ... "CPU implementer": ord("B"), 363 ... "CPU part": 0xc99, 364 ... "CPU variant": 7, 365 ... "CPU revision": 9}) 366 >>> print _info_io.getvalue() 367 CPU 2: No errata tested: imp=0x42, part=0xc99 368 """ 369 # Get things so we don't worry about key errors below 370 cpu_id = cpuinfo["processor"] 371 implementer = cpuinfo.get("CPU implementer", "?") 372 part = cpuinfo.get("CPU part", 0xfff) 373 374 if implementer == ord("A") and part == 0xc0d: 375 self._check_one_cortex_a12(cpuinfo) 376 elif implementer == ord("A") and part == 0xc0e: 377 self._check_one_cortex_a17(cpuinfo) 378 else: 379 logging.info("CPU %d: No errata tested: imp=%#04x, part=%#05x", 380 cpu_id, implementer, part) 381 382 def _check_one_cpu(self, cpuinfo): 383 """ 384 Check the errata for one CPU. 385 386 :param cpuinfo: The CPU info for one CPU. See _parse_cpu_info for 387 the format. 388 389 >>> _info_io.seek(0); _info_io.truncate() 390 >>> _testobj._check_one_cpu({ 391 ... "processor": 0, 392 ... "model name": "LEGv7 Processor"}) 393 >>> print _info_io.getvalue() 394 CPU 0: Not an ARM, skipping: LEGv7 Processor 395 396 >>> _info_io.seek(0); _info_io.truncate() 397 >>> _testobj._check_one_cpu({ 398 ... "processor": 1, 399 ... "model name": "ARMv99 Processor"}) 400 >>> print _info_io.getvalue() 401 CPU 1: No errata tested; model: ARMv99 Processor 402 """ 403 if cpuinfo["model name"].startswith("ARMv7"): 404 self._check_one_armv7(cpuinfo) 405 elif cpuinfo["model name"].startswith("ARM"): 406 logging.info("CPU %d: No errata tested; model: %s", 407 cpuinfo["processor"], cpuinfo["model name"]) 408 else: 409 logging.info("CPU %d: Not an ARM, skipping: %s", 410 cpuinfo["processor"], cpuinfo["model name"]) 411 412 def run_once(self, doctest=False): 413 """ 414 Run the test. 415 416 :param doctest: If true we will just run our doctests. We'll set these 417 globals to help our tests: 418 - _testobj: An instance of this object. 419 - _info_io: A StringIO that's stuffed into logging.info 420 so we can see what was written there. 421 ... 422 """ 423 if doctest: 424 import doctest, inspect, StringIO 425 global _testobj, _info_io 426 427 # Keep a backup of _get_regid_to_val() since tests will clobber. 428 old_get_regid_to_val = self._get_regid_to_val 429 430 # Mock out logging.info to help tests. 431 _info_io = StringIO.StringIO() 432 old_logging_info = logging.info 433 logging.info = lambda fmt, *args: _info_io.write(fmt % args) 434 435 # Stash an object in a global to help tests 436 _testobj = self 437 try: 438 failure_count, test_count = doctest.testmod( 439 inspect.getmodule(self), optionflags=doctest.ELLIPSIS) 440 finally: 441 logging.info = old_logging_info 442 443 # Technically don't need to clean this up, but let's be nice. 444 self._get_regid_to_val = old_get_regid_to_val 445 446 logging.info("Doctest ran %d tests, had %d failures", 447 test_count, failure_count) 448 return 449 450 cpuinfo = self._parse_cpu_info(utils.read_file('/proc/cpuinfo')) 451 452 for cpu_id in sorted(cpuinfo.keys()): 453 self._check_one_cpu(cpuinfo[cpu_id]) 454 455 sys_power.do_suspend(self.SECS_TO_SUSPEND) 456 457 for cpu_id in sorted(cpuinfo.keys()): 458 self._check_one_cpu(cpuinfo[cpu_id]) 459