1# Copyright (c) 2020 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 string 6import random 7import logging 8 9from autotest_lib.client.common_lib import error 10from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 11 12 13def random_string(length, chars): 14 """Generate a random string of characters. 15 16 @param length: the length of string to generate. 17 @param chars: the set of characters to use when generating the string. 18 19 @returns The generated string. 20 """ 21 return ''.join(random.SystemRandom().choice(chars) for _ in range(length)) 22 23 24class firmware_SysfsVPD(FirmwareTest): 25 """ 26 Servo based test for reading VPD data through sysfs. 27 28 This test writes random test strings to the RO and RW sections of VPD data 29 and verifies that they can be read through sysfs after a reboot. 30 """ 31 version = 1 32 33 # Length of test string to generate and write to VPD 34 _TEST_VAL_LENGTH = 8 35 36 # Character set to use when generating string to write to VPD 37 _TEST_VAL_CHARS = string.ascii_lowercase + string.digits 38 39 # Name of key to write to RO section of VPD 40 VPD_RO_TEST_KEY = "RO_TEST" 41 42 # Name of key to write to RW section of VPD 43 VPD_RW_TEST_KEY = "RW_TEST" 44 45 def initialize(self, host, cmdline_args, dev_mode=False): 46 """Initialize the test""" 47 super(firmware_SysfsVPD, self).initialize(host, cmdline_args) 48 49 fwver = self.faft_client.system.run_shell_command_get_output( 50 'crossystem fwid')[0] 51 try: 52 fwver_major = int(fwver.split('.')[1]) 53 except ValueError: 54 raise error.TestFail('Could not determine firmware version') 55 # Only run this test for 8846 or newer because previous firmware 56 # versions don't add the pointer to ACPI that Linux uses to look up 57 # cbmem which is then used to get the VPD. 58 # See b:156407743 for details. 59 self.disable_test = fwver_major < 8846 60 61 # Backup and mode switching is expensive so skip if we won't be 62 # doing anything anyway. 63 if self.disable_test: 64 raise error.TestNAError("Firmware too old for SysfsVPD") 65 66 self.host = host 67 self.backup_firmware() 68 self.switcher.setup_mode('dev' if dev_mode else 'normal') 69 70 def cleanup(self): 71 """Cleanup the test""" 72 try: 73 if self.is_firmware_saved(): 74 self.restore_firmware() 75 finally: 76 super(firmware_SysfsVPD, self).cleanup() 77 78 def run_once(self, dev_mode=False): 79 """Runs a single iteration of the test.""" 80 # Log the initial VPD sections so we can manually restore VPD from 81 # test logs if necessary. 82 logging.info("Logging initial RO+RW VPD data") 83 self.host.run("vpd -i RO_VPD -l") 84 self.host.run("vpd -i RW_VPD -l") 85 86 # Generate a random string and write it to RO section of VPD 87 vpd_ro_test_val = random_string(length=self._TEST_VAL_LENGTH, 88 chars=self._TEST_VAL_CHARS) 89 logging.info("Writting RO test data to VPD (key = %s, val = %s)", 90 self.VPD_RO_TEST_KEY, vpd_ro_test_val) 91 self.host.run("vpd -i RO_VPD -s %s=%s" % 92 (self.VPD_RO_TEST_KEY, vpd_ro_test_val)) 93 94 # Generate a random string and write it to RW section of VPD 95 vpd_rw_test_val = random_string(length=self._TEST_VAL_LENGTH, 96 chars=self._TEST_VAL_CHARS) 97 logging.info("Writting RW test data to VPD (key = %s, val = %s)", 98 self.VPD_RW_TEST_KEY, vpd_rw_test_val) 99 self.host.run("vpd -i RW_VPD -s %s=%s" % 100 (self.VPD_RW_TEST_KEY, vpd_rw_test_val)) 101 102 # Reboot DUT to load new VPD data in sysfs 103 logging.info('Rebooting DUT') 104 self.host.reset_via_servo() 105 106 # Verify RO test string can be read through sysfs and matches test value 107 logging.info('Verifying RO VPD test data in sysfs') 108 try: 109 path = "/sys/firmware/vpd/ro/%s" % self.VPD_RO_TEST_KEY 110 result = self.host.run("cat %s" % path) 111 value = result.stdout.strip() 112 except error.AutoservRunError: 113 raise error.TestFail("Failed to read back RO VPD data") 114 if value != vpd_ro_test_val: 115 raise error.TestFail( 116 "Mismatched RO VPD data, read %s (expected %s)" % 117 (value, vpd_ro_test_val)) 118 119 # Verify RW test string can be read through sysfs and matches test value 120 logging.info('Verifying RW VPD test data in sysfs') 121 try: 122 path = "/sys/firmware/vpd/rw/%s" % self.VPD_RW_TEST_KEY 123 result = self.host.run("cat %s" % path) 124 value = result.stdout.strip() 125 except error.AutoservRunError: 126 raise error.TestFail("Failed to read back RW VPD data") 127 if value != vpd_rw_test_val: 128 raise error.TestFail( 129 "Mismatched RW VPD data, read %s (expected %s)" % 130 (value, vpd_rw_test_val)) 131 132 # Remove the test keys from VPD 133 logging.info("Deleting test data from VPD") 134 self.host.run("vpd -i RO_VPD -d %s" % self.VPD_RO_TEST_KEY) 135 self.host.run("vpd -i RW_VPD -d %s" % self.VPD_RW_TEST_KEY) 136