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