xref: /aosp_15_r20/external/autotest/client/site_tests/firmware_CheckEOPState/firmware_CheckEOPState.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# Copyright (c) 2021 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 array
6import fcntl
7import os
8import struct
9import uuid
10
11from autotest_lib.client.bin import test, utils
12from autotest_lib.client.common_lib import error
13
14
15class firmware_CheckEOPState(test.test):
16    """Validates that the ME has been told by firmware that POST is done"""
17    # Needed by autotest
18    version = 1
19
20    def read_post_boot_state(self):
21        """Fail if the ME should be capable of reporting EOP but doesn't."""
22        HECI_MKHI = uuid.UUID('{8e6a6715-9abc-4043-88ef-9e39c6f63e0f}')
23        IOCTL_MEI_CONNECT_CLIENT = 0xc0104801  # _IOWR('H', 1, 16);
24
25        try:
26            mei_dev = os.open('/dev/mei0', os.O_RDWR)
27        except OSError:
28            raise error.TestFail('ME device not found, probably old kernel.')
29
30        # Connect to MKHI
31        buf = array.array('B', 16 * [0])
32        struct.pack_into('<16s', buf, 0, HECI_MKHI.bytes_le)
33        fcntl.ioctl(mei_dev, IOCTL_MEI_CONNECT_CLIENT, buf)
34        max_msg_length, protocol_version = struct.unpack_from('<IB', buf)
35
36        # Protocol 2 appears to be the minimum version that allows querying EOP
37        if protocol_version < 2:
38            os.close(mei_dev)
39            raise error.TestNAError(
40                    'ME protocol too old. Not checking for EOP.')
41
42        # query EOP State
43        group_id = 0xff
44        command = 0x1d
45        os.write(mei_dev, struct.pack('<BBBB', group_id, command, 0, 0))
46        inb = os.read(mei_dev, max_msg_length)
47        os.close(mei_dev)
48
49        if len(inb) != 8:
50            raise error.TestFail('Unknown response by ME.')
51
52        group_id_resp, command_plus_80, rsvd, result, eop_state = struct.unpack(
53                '<BBBBI', inb)
54
55        if (group_id_resp != group_id) or (command_plus_80 != command | 0x80):
56            raise error.TestFail('ME didn\'t respond to Query EOP State.')
57        if result == 0x8d:
58            raise error.TestFail('ME didn\'t understand Query EOP State.')
59        if result == 0x8e:
60            raise error.TestFail('ME reported failure on Query EOP State.')
61        if result != 0:
62            raise error.TestFail(
63                    'ME gave unknown response to Query EOP State.')
64
65        # if True, EOP has been issued by firmware and we're in Post-Boot State
66        eop_state = (eop_state & 0xff) == 0
67
68        return eop_state
69
70    def run_once(self):
71        """Fail unless ME returns Post-Boot State"""
72        cpu_family = utils.get_cpu_soc_family()
73        if cpu_family not in ('x86_64', 'i386'):
74            raise error.TestNAError(
75                    'This test is not applicable, '
76                    'because a non-Intel device has been detected. '
77                    'Such devices do not have an ME (Management Engine)')
78
79        if utils.is_intel_uarch_older_than('Tiger Lake'):
80            raise error.TestNAError('Skipping test on pre-TGL')
81        if utils.is_intel_uarch_older_than('Gracemont'):
82            raise error.TestNAError('Skipping test on production Atom designs')
83
84        self.read_post_boot_state()
85