xref: /aosp_15_r20/external/autotest/server/site_tests/firmware_SysfsVPD/firmware_SysfsVPD.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
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