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