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 allow_gbb_force=True) 70 71 def cleanup(self): 72 """Cleanup the test""" 73 try: 74 if self.is_firmware_saved(): 75 self.restore_firmware() 76 finally: 77 super(firmware_SysfsVPD, self).cleanup() 78 79 def run_once(self, dev_mode=False): 80 """Runs a single iteration of the test.""" 81 # Log the initial VPD sections so we can manually restore VPD from 82 # test logs if necessary. 83 logging.info("Logging initial RO+RW VPD data") 84 self.host.run("vpd -i RO_VPD -l") 85 self.host.run("vpd -i RW_VPD -l") 86 87 # Generate a random string and write it to RO section of VPD 88 vpd_ro_test_val = random_string(length=self._TEST_VAL_LENGTH, 89 chars=self._TEST_VAL_CHARS) 90 logging.info("Writting RO test data to VPD (key = %s, val = %s)", 91 self.VPD_RO_TEST_KEY, vpd_ro_test_val) 92 self.host.run("vpd -i RO_VPD -s %s=%s" % 93 (self.VPD_RO_TEST_KEY, vpd_ro_test_val)) 94 95 # Generate a random string and write it to RW section of VPD 96 vpd_rw_test_val = random_string(length=self._TEST_VAL_LENGTH, 97 chars=self._TEST_VAL_CHARS) 98 logging.info("Writting RW test data to VPD (key = %s, val = %s)", 99 self.VPD_RW_TEST_KEY, vpd_rw_test_val) 100 self.host.run("vpd -i RW_VPD -s %s=%s" % 101 (self.VPD_RW_TEST_KEY, vpd_rw_test_val)) 102 103 # Reboot DUT to load new VPD data in sysfs 104 logging.info('Rebooting DUT') 105 self.host.reset_via_servo() 106 107 # Verify RO test string can be read through sysfs and matches test value 108 logging.info('Verifying RO VPD test data in sysfs') 109 try: 110 path = "/sys/firmware/vpd/ro/%s" % self.VPD_RO_TEST_KEY 111 result = self.host.run("cat %s" % path) 112 value = result.stdout.strip() 113 except error.AutoservRunError: 114 raise error.TestFail("Failed to read back RO VPD data") 115 if value != vpd_ro_test_val: 116 raise error.TestFail( 117 "Mismatched RO VPD data, read %s (expected %s)" % 118 (value, vpd_ro_test_val)) 119 120 # Verify RW test string can be read through sysfs and matches test value 121 logging.info('Verifying RW VPD test data in sysfs') 122 try: 123 path = "/sys/firmware/vpd/rw/%s" % self.VPD_RW_TEST_KEY 124 result = self.host.run("cat %s" % path) 125 value = result.stdout.strip() 126 except error.AutoservRunError: 127 raise error.TestFail("Failed to read back RW VPD data") 128 if value != vpd_rw_test_val: 129 raise error.TestFail( 130 "Mismatched RW VPD data, read %s (expected %s)" % 131 (value, vpd_rw_test_val)) 132 133 # Remove the test keys from VPD 134 logging.info("Deleting test data from VPD") 135 self.host.run("vpd -i RO_VPD -d %s" % self.VPD_RO_TEST_KEY) 136 self.host.run("vpd -i RW_VPD -d %s" % self.VPD_RW_TEST_KEY) 137