1*05caa062SThomas Huth# Copyright (c) 2015, Intel Corporation
2*05caa062SThomas Huth# All rights reserved.
3*05caa062SThomas Huth#
4*05caa062SThomas Huth# SPDX-License-Identifier: BSD-3-Clause
5*05caa062SThomas Huth
6*05caa062SThomas Huth# Redistribution and use in source and binary forms, with or without
7*05caa062SThomas Huth# modification, are permitted provided that the following conditions are met:
8*05caa062SThomas Huth#
9*05caa062SThomas Huth#     * Redistributions of source code must retain the above copyright notice,
10*05caa062SThomas Huth#       this list of conditions and the following disclaimer.
11*05caa062SThomas Huth#     * Redistributions in binary form must reproduce the above copyright notice,
12*05caa062SThomas Huth#       this list of conditions and the following disclaimer in the documentation
13*05caa062SThomas Huth#       and/or other materials provided with the distribution.
14*05caa062SThomas Huth#     * Neither the name of Intel Corporation nor the names of its contributors
15*05caa062SThomas Huth#       may be used to endorse or promote products derived from this software
16*05caa062SThomas Huth#       without specific prior written permission.
17*05caa062SThomas Huth#
18*05caa062SThomas Huth# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19*05caa062SThomas Huth# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20*05caa062SThomas Huth# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21*05caa062SThomas Huth# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22*05caa062SThomas Huth# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23*05caa062SThomas Huth# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24*05caa062SThomas Huth# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25*05caa062SThomas Huth# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*05caa062SThomas Huth# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27*05caa062SThomas Huth# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*05caa062SThomas Huth
29*05caa062SThomas Huth# This script runs only from the biosbits VM.
30*05caa062SThomas Huth
31*05caa062SThomas Huth"""SMI latency test."""
32*05caa062SThomas Huth
33*05caa062SThomas Huthimport bits
34*05caa062SThomas Huthfrom collections import namedtuple
35*05caa062SThomas Huthimport testsuite
36*05caa062SThomas Huthimport time
37*05caa062SThomas Huthimport usb
38*05caa062SThomas Huth
39*05caa062SThomas Huthdef register_tests():
40*05caa062SThomas Huth     pass
41*05caa062SThomas Huth#    testsuite.add_test("SMI latency test", smi_latency);
42*05caa062SThomas Huth#    testsuite.add_test("SMI latency test with USB disabled via BIOS handoff", test_with_usb_disabled, runall=False);
43*05caa062SThomas Huth
44*05caa062SThomas Huthdef smi_latency():
45*05caa062SThomas Huth    MSR_SMI_COUNT = 0x34
46*05caa062SThomas Huth
47*05caa062SThomas Huth    print "Warning: touching the keyboard can affect the results of this test."
48*05caa062SThomas Huth
49*05caa062SThomas Huth    tsc_per_sec = bits.tsc_per_sec()
50*05caa062SThomas Huth    tsc_per_usec = tsc_per_sec / (1000 * 1000)
51*05caa062SThomas Huth    bins = [long(tsc_per_usec * 10**i) for i in range(9)]
52*05caa062SThomas Huth    bin_descs = [
53*05caa062SThomas Huth        "0     < t <=   1us",
54*05caa062SThomas Huth        "1us   < t <=  10us",
55*05caa062SThomas Huth        "10us  < t <= 100us",
56*05caa062SThomas Huth        "100us < t <=   1ms",
57*05caa062SThomas Huth        "1ms   < t <=  10ms",
58*05caa062SThomas Huth        "10ms  < t <= 100ms",
59*05caa062SThomas Huth        "100ms < t <=   1s ",
60*05caa062SThomas Huth        "1s    < t <=  10s ",
61*05caa062SThomas Huth        "10s   < t <= 100s ",
62*05caa062SThomas Huth        "100s  < t         ",
63*05caa062SThomas Huth    ]
64*05caa062SThomas Huth
65*05caa062SThomas Huth    print "Starting test. Wait here, I will be back in 15 seconds."
66*05caa062SThomas Huth    (max_latency, smi_count_delta, bins) = bits.smi_latency(long(15 * tsc_per_sec), bins)
67*05caa062SThomas Huth    BinType = namedtuple('BinType', ("max", "total", "count", "times"))
68*05caa062SThomas Huth    bins = [BinType(*b) for b in bins]
69*05caa062SThomas Huth
70*05caa062SThomas Huth    testsuite.test("SMI latency < 150us to minimize risk of OS timeouts", max_latency / tsc_per_usec <= 150)
71*05caa062SThomas Huth    if not testsuite.show_detail():
72*05caa062SThomas Huth        return
73*05caa062SThomas Huth
74*05caa062SThomas Huth    for bin, desc in zip(bins, bin_descs):
75*05caa062SThomas Huth        if bin.count == 0:
76*05caa062SThomas Huth            continue
77*05caa062SThomas Huth        testsuite.print_detail("{}; average = {}; count = {}".format(desc, bits.format_tsc(bin.total/bin.count), bin.count))
78*05caa062SThomas Huth        deltas = (bits.format_tsc(t2 - t1) for t1,t2 in zip(bin.times, bin.times[1:]))
79*05caa062SThomas Huth        testsuite.print_detail(" Times between first few observations: {}".format(" ".join("{:>6}".format(delta) for delta in deltas)))
80*05caa062SThomas Huth
81*05caa062SThomas Huth    if smi_count_delta is not None:
82*05caa062SThomas Huth        testsuite.print_detail("{} SMI detected using MSR_SMI_COUNT (MSR {:#x})".format(smi_count_delta, MSR_SMI_COUNT))
83*05caa062SThomas Huth
84*05caa062SThomas Huth    testsuite.print_detail("Summary of impact: observed maximum latency = {}".format(bits.format_tsc(max_latency)))
85*05caa062SThomas Huth
86*05caa062SThomas Huthdef test_with_usb_disabled():
87*05caa062SThomas Huth    if usb.handoff_to_os():
88*05caa062SThomas Huth        smi_latency()
89*05caa062SThomas Huth
90*05caa062SThomas Huthdef average_io_smi(port, value, count):
91*05caa062SThomas Huth    def f():
92*05caa062SThomas Huth        tsc_start = bits.rdtsc()
93*05caa062SThomas Huth        bits.outb(port, value)
94*05caa062SThomas Huth        return bits.rdtsc() - tsc_start
95*05caa062SThomas Huth    counts = [f() for i in range(count)]
96*05caa062SThomas Huth    return sum(counts)/len(counts)
97*05caa062SThomas Huth
98*05caa062SThomas Huthdef time_io_smi(port=0xb2, value=0, count=1000):
99*05caa062SThomas Huth    count_for_estimate = 10
100*05caa062SThomas Huth    start = time.time()
101*05caa062SThomas Huth    average_io_smi(port, value, count_for_estimate)
102*05caa062SThomas Huth    avg10 = time.time() - start
103*05caa062SThomas Huth    estimate = avg10 * count/count_for_estimate
104*05caa062SThomas Huth    if estimate > 1:
105*05caa062SThomas Huth        print "Running test, estimated time: {}s".format(int(estimate))
106*05caa062SThomas Huth    average = average_io_smi(port, value, count)
107*05caa062SThomas Huth    print "Average of {} SMIs (via outb, port={:#x}, value={:#x}): {}".format(count, port, value, bits.format_tsc(average))
108