1*a98a962fSPatrick Mooney /*
2*a98a962fSPatrick Mooney * This file and its contents are supplied under the terms of the
3*a98a962fSPatrick Mooney * Common Development and Distribution License ("CDDL"), version 1.0.
4*a98a962fSPatrick Mooney * You may only use this file in accordance with the terms of version
5*a98a962fSPatrick Mooney * 1.0 of the CDDL.
6*a98a962fSPatrick Mooney *
7*a98a962fSPatrick Mooney * A full copy of the text of the CDDL should have accompanied this
8*a98a962fSPatrick Mooney * source. A copy of the CDDL is also available via the Internet at
9*a98a962fSPatrick Mooney * http://www.illumos.org/license/CDDL.
10*a98a962fSPatrick Mooney */
11*a98a962fSPatrick Mooney
12*a98a962fSPatrick Mooney /*
13*a98a962fSPatrick Mooney * Copyright 2023 Oxide Computer Company
14*a98a962fSPatrick Mooney */
15*a98a962fSPatrick Mooney
16*a98a962fSPatrick Mooney #include "payload_common.h"
17*a98a962fSPatrick Mooney #include "payload_utils.h"
18*a98a962fSPatrick Mooney
19*a98a962fSPatrick Mooney /* Arbitrary 2MB limit to keep heap away from stack */
20*a98a962fSPatrick Mooney #define HEAP_LIMIT 0x200000
21*a98a962fSPatrick Mooney
22*a98a962fSPatrick Mooney #define MSR_APICBASE 0x01b
23*a98a962fSPatrick Mooney #define IOP_ICU1 0x20
24*a98a962fSPatrick Mooney #define ADDR_IOAPIC_BASE 0xfec00000
25*a98a962fSPatrick Mooney
26*a98a962fSPatrick Mooney typedef struct test_data {
27*a98a962fSPatrick Mooney uint32_t count;
28*a98a962fSPatrick Mooney uint64_t *data;
29*a98a962fSPatrick Mooney } test_data_t;
30*a98a962fSPatrick Mooney
31*a98a962fSPatrick Mooney static void
zero_data(const test_data_t * td)32*a98a962fSPatrick Mooney zero_data(const test_data_t *td)
33*a98a962fSPatrick Mooney {
34*a98a962fSPatrick Mooney for (uint32_t i = 0; i < td->count; i++) {
35*a98a962fSPatrick Mooney td->data[i] = 0;
36*a98a962fSPatrick Mooney }
37*a98a962fSPatrick Mooney }
38*a98a962fSPatrick Mooney
39*a98a962fSPatrick Mooney static void
output_data(const test_data_t * td,uint64_t tsc_start)40*a98a962fSPatrick Mooney output_data(const test_data_t *td, uint64_t tsc_start)
41*a98a962fSPatrick Mooney {
42*a98a962fSPatrick Mooney for (uint32_t i = td->count - 1; i > 0; i--) {
43*a98a962fSPatrick Mooney td->data[i] -= td->data[i - 1];
44*a98a962fSPatrick Mooney }
45*a98a962fSPatrick Mooney td->data[0] -= tsc_start;
46*a98a962fSPatrick Mooney
47*a98a962fSPatrick Mooney /*
48*a98a962fSPatrick Mooney * Output the low 32-bits of the data pointers, since that is adequate
49*a98a962fSPatrick Mooney * while the test resides wholly in lowmem.
50*a98a962fSPatrick Mooney */
51*a98a962fSPatrick Mooney outl(IOP_TEST_VALUE, (uint32_t)(uintptr_t)td->data);
52*a98a962fSPatrick Mooney }
53*a98a962fSPatrick Mooney
54*a98a962fSPatrick Mooney static uint32_t
mmio_read4(volatile uint32_t * ptr)55*a98a962fSPatrick Mooney mmio_read4(volatile uint32_t *ptr)
56*a98a962fSPatrick Mooney {
57*a98a962fSPatrick Mooney return (*ptr);
58*a98a962fSPatrick Mooney }
59*a98a962fSPatrick Mooney
60*a98a962fSPatrick Mooney /*
61*a98a962fSPatrick Mooney * For a relatively cheap exit, rdmsr(APICBASE) should be suitable, since its
62*a98a962fSPatrick Mooney * emulation is dead simple and LAPIC-related MSR operations are handled within
63*a98a962fSPatrick Mooney * the tight confines of the SVM/VMX vmrun loop.
64*a98a962fSPatrick Mooney */
65*a98a962fSPatrick Mooney static void
do_test_rdmsr(const test_data_t * td)66*a98a962fSPatrick Mooney do_test_rdmsr(const test_data_t *td)
67*a98a962fSPatrick Mooney {
68*a98a962fSPatrick Mooney zero_data(td);
69*a98a962fSPatrick Mooney
70*a98a962fSPatrick Mooney const uint64_t tsc_start = rdtsc();
71*a98a962fSPatrick Mooney for (uint32_t i = 0; i < td->count; i++) {
72*a98a962fSPatrick Mooney (void) rdmsr(MSR_APICBASE);
73*a98a962fSPatrick Mooney td->data[i] = rdtsc();
74*a98a962fSPatrick Mooney }
75*a98a962fSPatrick Mooney
76*a98a962fSPatrick Mooney output_data(td, tsc_start);
77*a98a962fSPatrick Mooney }
78*a98a962fSPatrick Mooney
79*a98a962fSPatrick Mooney /*
80*a98a962fSPatrick Mooney * For a moderately priced exit, an IO port read from the ATPIC should suffice.
81*a98a962fSPatrick Mooney * This will take us out of the SVM/VMX vmrun loop and into the instruction
82*a98a962fSPatrick Mooney * emulation, but the instruction fetch/decode should already be taken care of
83*a98a962fSPatrick Mooney * by the hardware, and no further memory (guest) accesses are required.
84*a98a962fSPatrick Mooney */
85*a98a962fSPatrick Mooney static void
do_test_inb(const test_data_t * td)86*a98a962fSPatrick Mooney do_test_inb(const test_data_t *td)
87*a98a962fSPatrick Mooney {
88*a98a962fSPatrick Mooney zero_data(td);
89*a98a962fSPatrick Mooney
90*a98a962fSPatrick Mooney const uint64_t tsc_start = rdtsc();
91*a98a962fSPatrick Mooney for (uint32_t i = 0; i < td->count; i++) {
92*a98a962fSPatrick Mooney (void) inb(IOP_ICU1);
93*a98a962fSPatrick Mooney td->data[i] = rdtsc();
94*a98a962fSPatrick Mooney }
95*a98a962fSPatrick Mooney
96*a98a962fSPatrick Mooney output_data(td, tsc_start);
97*a98a962fSPatrick Mooney }
98*a98a962fSPatrick Mooney
99*a98a962fSPatrick Mooney /*
100*a98a962fSPatrick Mooney * For a more expensive exit, read from the selector register in the IOAPIC.
101*a98a962fSPatrick Mooney * The device emulation is handled in-kernel, but the instruction will need to
102*a98a962fSPatrick Mooney * (potentially) fetched and decoded.
103*a98a962fSPatrick Mooney */
104*a98a962fSPatrick Mooney static void
do_test_mmio_cheap(const test_data_t * td)105*a98a962fSPatrick Mooney do_test_mmio_cheap(const test_data_t *td)
106*a98a962fSPatrick Mooney {
107*a98a962fSPatrick Mooney zero_data(td);
108*a98a962fSPatrick Mooney volatile uint32_t *ioapic_regsel = (void *)(uintptr_t)ADDR_IOAPIC_BASE;
109*a98a962fSPatrick Mooney
110*a98a962fSPatrick Mooney const uint64_t tsc_start = rdtsc();
111*a98a962fSPatrick Mooney for (uint32_t i = 0; i < td->count; i++) {
112*a98a962fSPatrick Mooney (void) mmio_read4(ioapic_regsel);
113*a98a962fSPatrick Mooney td->data[i] = rdtsc();
114*a98a962fSPatrick Mooney }
115*a98a962fSPatrick Mooney
116*a98a962fSPatrick Mooney output_data(td, tsc_start);
117*a98a962fSPatrick Mooney }
118*a98a962fSPatrick Mooney
119*a98a962fSPatrick Mooney void
start(void)120*a98a962fSPatrick Mooney start(void)
121*a98a962fSPatrick Mooney {
122*a98a962fSPatrick Mooney
123*a98a962fSPatrick Mooney /* Get the number of repetitions per test */
124*a98a962fSPatrick Mooney const uint32_t count = inl(IOP_TEST_PARAM0);
125*a98a962fSPatrick Mooney
126*a98a962fSPatrick Mooney if (count * sizeof (uint64_t) > HEAP_LIMIT) {
127*a98a962fSPatrick Mooney test_msg("excessive test count for memory sz");
128*a98a962fSPatrick Mooney test_result_fail();
129*a98a962fSPatrick Mooney return;
130*a98a962fSPatrick Mooney }
131*a98a962fSPatrick Mooney
132*a98a962fSPatrick Mooney test_data_t td = {
133*a98a962fSPatrick Mooney .count = count,
134*a98a962fSPatrick Mooney .data = (uint64_t *)(uintptr_t)MEM_LOC_HEAP,
135*a98a962fSPatrick Mooney };
136*a98a962fSPatrick Mooney
137*a98a962fSPatrick Mooney do_test_rdmsr(&td);
138*a98a962fSPatrick Mooney do_test_inb(&td);
139*a98a962fSPatrick Mooney do_test_mmio_cheap(&td);
140*a98a962fSPatrick Mooney
141*a98a962fSPatrick Mooney test_result_pass();
142*a98a962fSPatrick Mooney }
143