| 1 | # Copyright (c) 2015, Intel Corporation |
| 2 | # All rights reserved. |
| 3 | # |
| 4 | # SPDX-License-Identifier: BSD-3-Clause |
| 5 | # |
| 6 | # Redistribution and use in source and binary forms, with or without |
| 7 | # modification, are permitted provided that the following conditions are met: |
| 8 | # |
| 9 | # * Redistributions of source code must retain the above copyright notice, |
| 10 | # this list of conditions and the following disclaimer. |
| 11 | # * Redistributions in binary form must reproduce the above copyright notice, |
| 12 | # this list of conditions and the following disclaimer in the documentation |
| 13 | # and/or other materials provided with the distribution. |
| 14 | # * Neither the name of Intel Corporation nor the names of its contributors |
| 15 | # may be used to endorse or promote products derived from this software |
| 16 | # without specific prior written permission. |
| 17 | # |
| 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR |
| 22 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| 25 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 | |
| 29 | # This script runs only from the biosbits VM. |
| 30 | |
| 31 | """Tests for ACPI""" |
| 32 | |
| 33 | import acpi |
| 34 | import bits |
| 35 | import bits.mwait |
| 36 | import struct |
| 37 | import testutil |
| 38 | import testsuite |
| 39 | import time |
| 40 | |
| 41 | def register_tests(): |
| 42 | testsuite.add_test("ACPI _MAT (Multiple APIC Table Entry) under Processor objects", test_mat, submenu="ACPI Tests") |
| 43 | # testsuite.add_test("ACPI _PSS (Pstate) table conformance tests", test_pss, submenu="ACPI Tests") |
| 44 | # testsuite.add_test("ACPI _PSS (Pstate) runtime tests", test_pstates, submenu="ACPI Tests") |
| 45 | testsuite.add_test("ACPI DSDT (Differentiated System Description Table)", test_dsdt, submenu="ACPI Tests") |
| 46 | testsuite.add_test("ACPI FACP (Fixed ACPI Description Table)", test_facp, submenu="ACPI Tests") |
| 47 | testsuite.add_test("ACPI HPET (High Precision Event Timer Table)", test_hpet, submenu="ACPI Tests") |
| 48 | testsuite.add_test("ACPI MADT (Multiple APIC Description Table)", test_apic, submenu="ACPI Tests") |
| 49 | testsuite.add_test("ACPI MPST (Memory Power State Table)", test_mpst, submenu="ACPI Tests") |
| 50 | testsuite.add_test("ACPI RSDP (Root System Description Pointer Structure)", test_rsdp, submenu="ACPI Tests") |
| 51 | testsuite.add_test("ACPI XSDT (Extended System Description Table)", test_xsdt, submenu="ACPI Tests") |
| 52 | |
| 53 | def test_mat(): |
| 54 | cpupaths = acpi.get_cpupaths() |
| 55 | apic = acpi.parse_apic() |
| 56 | procid_apicid = apic.procid_apicid |
| 57 | uid_x2apicid = apic.uid_x2apicid |
| 58 | for cpupath in cpupaths: |
| 59 | # Find the ProcId defined by the processor object |
| 60 | processor = acpi.evaluate(cpupath) |
| 61 | # Find the UID defined by the processor object's _UID method |
| 62 | uid = acpi.evaluate(cpupath + "._UID") |
| 63 | mat_buffer = acpi.evaluate(cpupath + "._MAT") |
| 64 | if mat_buffer is None: |
| 65 | continue |
| 66 | # Process each _MAT subtable |
| 67 | mat = acpi._MAT(mat_buffer) |
| 68 | for index, subtable in enumerate(mat): |
| 69 | if subtable.subtype == acpi.MADT_TYPE_LOCAL_APIC: |
| 70 | if subtable.flags.bits.enabled: |
| 71 | testsuite.test("{} Processor declaration ProcId = _MAT ProcId".format(cpupath), processor.ProcId == subtable.proc_id) |
| 72 | testsuite.print_detail("{} ProcId ({:#02x}) != _MAT ProcId ({:#02x})".format(cpupath, processor.ProcId, subtable.proc_id)) |
| 73 | testsuite.print_detail("Processor Declaration: {}".format(processor)) |
| 74 | testsuite.print_detail("_MAT entry[{}]: {}".format(index, subtable)) |
| 75 | if testsuite.test("{} with local APIC in _MAT has local APIC in MADT".format(cpupath), processor.ProcId in procid_apicid): |
| 76 | testsuite.test("{} ApicId derived using Processor declaration ProcId = _MAT ApicId".format(cpupath), procid_apicid[processor.ProcId] == subtable.apic_id) |
| 77 | testsuite.print_detail("{} ApicId derived from MADT ({:#02x}) != _MAT ApicId ({:#02x})".format(cpupath, procid_apicid[processor.ProcId], subtable.apic_id)) |
| 78 | testsuite.print_detail("Processor Declaration: {}".format(processor)) |
| 79 | testsuite.print_detail("_MAT entry[{}]: {}".format(index, subtable)) |
| 80 | if subtable.subtype == acpi.MADT_TYPE_LOCAL_X2APIC: |
| 81 | if subtable.flags.bits.enabled: |
| 82 | if testsuite.test("{} with x2Apic in _MAT has _UID".format(cpupath), uid is not None): |
| 83 | testsuite.test("{}._UID = _MAT UID".format(cpupath), uid == subtable.uid) |
| 84 | testsuite.print_detail("{}._UID ({:#x}) != _MAT UID ({:#x})".format(cpupath, uid, subtable.uid)) |
| 85 | testsuite.print_detail("_MAT entry[{}]: {}".format(index, subtable)) |
| 86 | if testsuite.test("{} with _MAT x2Apic has x2Apic in MADT".format(cpupath), subtable.uid in uid_x2apicid): |
| 87 | testsuite.test("{} x2ApicId derived from MADT using UID = _MAT x2ApicId".format(cpupath), uid_x2apicid[subtable.uid] == subtable.x2apicid) |
| 88 | testsuite.print_detail("{} x2ApicId derived from MADT ({:#02x}) != _MAT x2ApicId ({:#02x})".format(cpupath, uid_x2apicid[subtable.uid], subtable.x2apicid)) |
| 89 | testsuite.print_detail("_MAT entry[{}]: {}".format(index, subtable)) |
| 90 | |
| 91 | def test_pss(): |
| 92 | uniques = acpi.parse_cpu_method("_PSS") |
| 93 | # We special-case None here to avoid a double-failure for CPUs without a _PSS |
| 94 | testsuite.test("_PSS must be identical for all CPUs", len(uniques) <= 1 or (len(uniques) == 2 and None in uniques)) |
| 95 | for pss, cpupaths in uniques.iteritems(): |
| 96 | if not testsuite.test("_PSS must exist", pss is not None): |
| 97 | testsuite.print_detail(acpi.factor_commonprefix(cpupaths)) |
| 98 | testsuite.print_detail('No _PSS exists') |
| 99 | continue |
| 100 | |
| 101 | if not testsuite.test("_PSS must not be empty", pss.pstates): |
| 102 | testsuite.print_detail(acpi.factor_commonprefix(cpupaths)) |
| 103 | testsuite.print_detail('_PSS is empty') |
| 104 | continue |
| 105 | |
| 106 | testsuite.print_detail(acpi.factor_commonprefix(cpupaths)) |
| 107 | for index, pstate in enumerate(pss.pstates): |
| 108 | testsuite.print_detail("P[{}]: {}".format(index, pstate)) |
| 109 | |
| 110 | testsuite.test("_PSS must contain at most 16 Pstates", len(pss.pstates) <= 16) |
| 111 | testsuite.test("_PSS must have no duplicate Pstates", len(pss.pstates) == len(set(pss.pstates))) |
| 112 | |
| 113 | frequencies = [p.core_frequency for p in pss.pstates] |
| 114 | testsuite.test("_PSS must list Pstates in descending order of frequency", frequencies == sorted(frequencies, reverse=True)) |
| 115 | |
| 116 | testsuite.test("_PSS must have Pstates with no duplicate frequencies", len(frequencies) == len(set(frequencies))) |
| 117 | |
| 118 | dissipations = [p.power for p in pss.pstates] |
| 119 | testsuite.test("_PSS must list Pstates in descending order of power dissipation", dissipations == sorted(dissipations, reverse=True)) |
| 120 | |
| 121 | def test_pstates(): |
| 122 | """Execute and verify frequency for each Pstate in the _PSS""" |
| 123 | IA32_PERF_CTL = 0x199 |
| 124 | with bits.mwait.use_hint(), bits.preserve_msr(IA32_PERF_CTL): |
| 125 | cpupath_procid = acpi.find_procid() |
| 126 | cpupath_uid = acpi.find_uid() |
| 127 | apic = acpi.parse_apic() |
| 128 | procid_apicid = apic.procid_apicid |
| 129 | uid_x2apicid = apic.uid_x2apicid |
| 130 | def cpupath_apicid(cpupath): |
| 131 | if procid_apicid is not None: |
| 132 | procid = cpupath_procid.get(cpupath, None) |
| 133 | if procid is not None: |
| 134 | apicid = procid_apicid.get(procid, None) |
| 135 | if apicid is not None: |
| 136 | return apicid |
| 137 | if uid_x2apicid is not None: |
| 138 | uid = cpupath_uid.get(cpupath, None) |
| 139 | if uid is not None: |
| 140 | apicid = uid_x2apicid.get(uid, None) |
| 141 | if apicid is not None: |
| 142 | return apicid |
| 143 | return bits.cpus()[0] |
| 144 | |
| 145 | bclk = testutil.adjust_to_nearest(bits.bclk(), 100.0/12) * 1000000 |
| 146 | |
| 147 | uniques = acpi.parse_cpu_method("_PSS") |
| 148 | for pss, cpupaths in uniques.iteritems(): |
| 149 | if not testsuite.test("_PSS must exist", pss is not None): |
| 150 | testsuite.print_detail(acpi.factor_commonprefix(cpupaths)) |
| 151 | testsuite.print_detail('No _PSS exists') |
| 152 | continue |
| 153 | |
| 154 | for n, pstate in enumerate(pss.pstates): |
| 155 | for cpupath in cpupaths: |
| 156 | apicid = cpupath_apicid(cpupath) |
| 157 | if apicid is None: |
| 158 | print 'Failed to find apicid for cpupath {}'.format(cpupath) |
| 159 | continue |
| 160 | bits.wrmsr(apicid, IA32_PERF_CTL, pstate.control) |
| 161 | |
| 162 | # Detecting Turbo frequency requires at least 2 pstates |
| 163 | # since turbo frequency = max non-turbo frequency + 1 |
| 164 | turbo = False |
| 165 | if len(pss.pstates) >= 2: |
| 166 | turbo = (n == 0 and pstate.core_frequency == (pss.pstates[1].core_frequency + 1)) |
| 167 | if turbo: |
| 168 | # Needs to busywait, not sleep |
| 169 | start = time.time() |
| 170 | while (time.time() - start < 2): |
| 171 | pass |
| 172 | |
| 173 | for duration in (0.1, 1.0): |
| 174 | frequency_data = bits.cpu_frequency(duration) |
| 175 | # Abort the test if no cpu frequency is not available |
| 176 | if frequency_data is None: |
| 177 | continue |
| 178 | aperf = frequency_data[1] |
| 179 | aperf = testutil.adjust_to_nearest(aperf, bclk/2) |
| 180 | aperf = int(aperf / 1000000) |
| 181 | if turbo: |
| 182 | if aperf >= pstate.core_frequency: |
| 183 | break |
| 184 | else: |
| 185 | if aperf == pstate.core_frequency: |
| 186 | break |
| 187 | |
| 188 | if turbo: |
| 189 | testsuite.test("P{}: Turbo measured frequency {} >= expected {} MHz".format(n, aperf, pstate.core_frequency), aperf >= pstate.core_frequency) |
| 190 | else: |
| 191 | testsuite.test("P{}: measured frequency {} MHz == expected {} MHz".format(n, aperf, pstate.core_frequency), aperf == pstate.core_frequency) |
| 192 | |
| 193 | def test_psd_thread_scope(): |
| 194 | uniques = acpi.parse_cpu_method("_PSD") |
| 195 | if not testsuite.test("_PSD (P-State Dependency) must exist for each processor", None not in uniques): |
| 196 | testsuite.print_detail(acpi.factor_commonprefix(uniques[None])) |
| 197 | testsuite.print_detail('No _PSD exists') |
| 198 | return |
| 199 | unique_num_dependencies = {} |
| 200 | unique_num_entries = {} |
| 201 | unique_revision = {} |
| 202 | unique_domain = {} |
| 203 | unique_coordination_type = {} |
| 204 | unique_num_processors = {} |
| 205 | for value, cpupaths in uniques.iteritems(): |
| 206 | unique_num_dependencies.setdefault(len(value.dependencies), []).extend(cpupaths) |
| 207 | unique_num_entries.setdefault(value.dependencies[0].num_entries, []).extend(cpupaths) |
| 208 | unique_revision.setdefault(value.dependencies[0].revision, []).extend(cpupaths) |
| 209 | unique_domain.setdefault(value.dependencies[0].domain, []).extend(cpupaths) |
| 210 | unique_coordination_type.setdefault(value.dependencies[0].coordination_type, []).extend(cpupaths) |
| 211 | unique_num_processors.setdefault(value.dependencies[0].num_processors, []).extend(cpupaths) |
| 212 | def detail(d, fmt): |
| 213 | for value, cpupaths in sorted(d.iteritems(), key=(lambda (k,v): v)): |
| 214 | testsuite.print_detail(acpi.factor_commonprefix(cpupaths)) |
| 215 | testsuite.print_detail(fmt.format(value)) |
| 216 | |
| 217 | testsuite.test('Dependency count for each processor must be 1', unique_num_dependencies.keys() == [1]) |
| 218 | detail(unique_num_dependencies, 'Dependency count for each processor = {} (Expected 1)') |
| 219 | testsuite.test('_PSD.num_entries must be 5', unique_num_entries.keys() == [5]) |
| 220 | detail(unique_num_entries, 'num_entries = {} (Expected 5)') |
| 221 | testsuite.test('_PSD.revision must be 0', unique_revision.keys() == [0]) |
| 222 | detail(unique_revision, 'revision = {}') |
| 223 | testsuite.test('_PSD.coordination_type must be 0xFE (HW_ALL)', unique_coordination_type.keys() == [0xfe]) |
| 224 | detail(unique_coordination_type, 'coordination_type = {:#x} (Expected 0xFE HW_ALL)') |
| 225 | testsuite.test('_PSD.domain must be unique (thread-scoped) for each processor', len(unique_domain) == len(acpi.get_cpupaths())) |
| 226 | detail(unique_domain, 'domain = {:#x} (Expected a unique value for each processor)') |
| 227 | testsuite.test('_PSD.num_processors must be 1', unique_num_processors.keys() == [1]) |
| 228 | detail(unique_num_processors, 'num_processors = {} (Expected 1)') |
| 229 | |
| 230 | def test_table_checksum(data): |
| 231 | csum = sum(ord(c) for c in data) % 0x100 |
| 232 | testsuite.test('ACPI table cumulative checksum must equal 0', csum == 0) |
| 233 | testsuite.print_detail("Cumulative checksum = {} (Expected 0)".format(csum)) |
| 234 | |
| 235 | def test_apic(): |
| 236 | data = acpi.get_table("APIC") |
| 237 | if data is None: |
| 238 | return |
| 239 | test_table_checksum(data) |
| 240 | apic = acpi.parse_apic() |
| 241 | |
| 242 | def test_dsdt(): |
| 243 | data = acpi.get_table("DSDT") |
| 244 | if data is None: |
| 245 | return |
| 246 | test_table_checksum(data) |
| 247 | |
| 248 | def test_facp(): |
| 249 | data = acpi.get_table("FACP") |
| 250 | if data is None: |
| 251 | return |
| 252 | test_table_checksum(data) |
| 253 | facp = acpi.parse_facp() |
| 254 | |
| 255 | def test_hpet(): |
| 256 | data = acpi.get_table("HPET") |
| 257 | if data is None: |
| 258 | return |
| 259 | test_table_checksum(data) |
| 260 | hpet = acpi.parse_hpet() |
| 261 | |
| 262 | def test_mpst(): |
| 263 | data = acpi.get_table("MPST") |
| 264 | if data is None: |
| 265 | return |
| 266 | test_table_checksum(data) |
| 267 | mpst = acpi.MPST(data) |
| 268 | |
| 269 | def test_rsdp(): |
| 270 | data = acpi.get_table("RSD PTR ") |
| 271 | if data is None: |
| 272 | return |
| 273 | |
| 274 | # Checksum the first 20 bytes per ACPI 1.0 |
| 275 | csum = sum(ord(c) for c in data[:20]) % 0x100 |
| 276 | testsuite.test('ACPI 1.0 table first 20 bytes cumulative checksum must equal 0', csum == 0) |
| 277 | testsuite.print_detail("Cumulative checksum = {} (Expected 0)".format(csum)) |
| 278 | |
| 279 | test_table_checksum(data) |
| 280 | rsdp = acpi.parse_rsdp() |
| 281 | |
| 282 | def test_xsdt(): |
| 283 | data = acpi.get_table("XSDT") |
| 284 | if data is None: |
| 285 | return |
| 286 | test_table_checksum(data) |
| 287 | xsdt = acpi.parse_xsdt() |