1a98a962fSPatrick Mooney /*
2a98a962fSPatrick Mooney  * This file and its contents are supplied under the terms of the
3a98a962fSPatrick Mooney  * Common Development and Distribution License ("CDDL"), version 1.0.
4a98a962fSPatrick Mooney  * You may only use this file in accordance with the terms of version
5a98a962fSPatrick Mooney  * 1.0 of the CDDL.
6a98a962fSPatrick Mooney  *
7a98a962fSPatrick Mooney  * A full copy of the text of the CDDL should have accompanied this
8a98a962fSPatrick Mooney  * source.  A copy of the CDDL is also available via the Internet at
9a98a962fSPatrick Mooney  * http://www.illumos.org/license/CDDL.
10a98a962fSPatrick Mooney  */
11a98a962fSPatrick Mooney 
12a98a962fSPatrick Mooney /*
13a98a962fSPatrick Mooney  * Copyright 2023 Oxide Computer Company
14a98a962fSPatrick Mooney  */
15a98a962fSPatrick Mooney 
16a98a962fSPatrick Mooney #include <stdio.h>
17a98a962fSPatrick Mooney #include <unistd.h>
18a98a962fSPatrick Mooney #include <stdlib.h>
19a98a962fSPatrick Mooney #include <strings.h>
20a98a962fSPatrick Mooney #include <libgen.h>
21a98a962fSPatrick Mooney #include <assert.h>
22a98a962fSPatrick Mooney #include <errno.h>
23a98a962fSPatrick Mooney #include <err.h>
24a98a962fSPatrick Mooney 
25a98a962fSPatrick Mooney #include <sys/types.h>
26a98a962fSPatrick Mooney #include <sys/sysmacros.h>
27a98a962fSPatrick Mooney #include <sys/debug.h>
28a98a962fSPatrick Mooney #include <sys/vmm.h>
29a98a962fSPatrick Mooney #include <sys/vmm_dev.h>
30a98a962fSPatrick Mooney #include <vmmapi.h>
31a98a962fSPatrick Mooney 
32a98a962fSPatrick Mooney #include "in_guest.h"
33a98a962fSPatrick Mooney 
34a98a962fSPatrick Mooney static uint32_t opt_repeat_count = 1000;
35a98a962fSPatrick Mooney static bool opt_summarize = false;
36a98a962fSPatrick Mooney 
37a98a962fSPatrick Mooney /* Names of the test phases running in guest context */
38a98a962fSPatrick Mooney static const char *test_metric_idents[] = {
39a98a962fSPatrick Mooney 	"MSR",
40a98a962fSPatrick Mooney 	"PIO",
41a98a962fSPatrick Mooney 	"MMIO",
42a98a962fSPatrick Mooney };
43a98a962fSPatrick Mooney 
44a98a962fSPatrick Mooney /* Track which test phase the guest is executing */
45a98a962fSPatrick Mooney static uint_t current_test = 0;
46a98a962fSPatrick Mooney /* Cache the queried CPU frequency */
47a98a962fSPatrick Mooney static uint64_t cpu_freq = 0;
48a98a962fSPatrick Mooney 
49a98a962fSPatrick Mooney static uint64_t
query_cpu_freq(struct vmctx * ctx)50a98a962fSPatrick Mooney query_cpu_freq(struct vmctx *ctx)
51a98a962fSPatrick Mooney {
52a98a962fSPatrick Mooney 	const int vmfd = vm_get_device_fd(ctx);
53a98a962fSPatrick Mooney 	struct vdi_time_info_v1 time_info;
54a98a962fSPatrick Mooney 	struct vm_data_xfer xfer = {
55a98a962fSPatrick Mooney 		.vdx_class = VDC_VMM_TIME,
56a98a962fSPatrick Mooney 		.vdx_version = 1,
57a98a962fSPatrick Mooney 		.vdx_len = sizeof (struct vdi_time_info_v1),
58a98a962fSPatrick Mooney 		.vdx_data = &time_info,
59a98a962fSPatrick Mooney 	};
60a98a962fSPatrick Mooney 
61a98a962fSPatrick Mooney 	if (ioctl(vmfd, VM_DATA_READ, &xfer) != 0) {
62a98a962fSPatrick Mooney 		errx(EXIT_FAILURE, "VMM_DATA_READ of time info failed");
63a98a962fSPatrick Mooney 	}
64a98a962fSPatrick Mooney 	return (time_info.vt_guest_freq);
65a98a962fSPatrick Mooney }
66a98a962fSPatrick Mooney 
67a98a962fSPatrick Mooney static double
cycles_to_ns(uint64_t cycles)68a98a962fSPatrick Mooney cycles_to_ns(uint64_t cycles)
69a98a962fSPatrick Mooney {
70a98a962fSPatrick Mooney 	return ((cycles * 1000000000.0) / cpu_freq);
71a98a962fSPatrick Mooney }
72a98a962fSPatrick Mooney 
73a98a962fSPatrick Mooney static void
print_result(struct vmctx * ctx,uintptr_t gpa,uint_t test_idx)74a98a962fSPatrick Mooney print_result(struct vmctx *ctx, uintptr_t gpa, uint_t test_idx)
75a98a962fSPatrick Mooney {
76a98a962fSPatrick Mooney 	if (test_idx >= ARRAY_SIZE(test_metric_idents)) {
77a98a962fSPatrick Mooney 		test_fail_msg("unexpected test iteration");
78a98a962fSPatrick Mooney 		return;
79a98a962fSPatrick Mooney 	}
80a98a962fSPatrick Mooney 
81a98a962fSPatrick Mooney 	const uint64_t *data =
82a98a962fSPatrick Mooney 	    vm_map_gpa(ctx, gpa, opt_repeat_count * sizeof (uint64_t));
83a98a962fSPatrick Mooney 	assert(data != NULL);
84a98a962fSPatrick Mooney 
85a98a962fSPatrick Mooney 	printf("%s", test_metric_idents[test_idx]);
86a98a962fSPatrick Mooney 	if (opt_summarize) {
87a98a962fSPatrick Mooney 		double sum = 0.0;
88a98a962fSPatrick Mooney 		for (uint32_t i = 0; i < opt_repeat_count; i++) {
89a98a962fSPatrick Mooney 			sum += cycles_to_ns(data[i]);
90a98a962fSPatrick Mooney 		}
91a98a962fSPatrick Mooney 		printf(",%0.2f", sum / opt_repeat_count);
92a98a962fSPatrick Mooney 	} else {
93a98a962fSPatrick Mooney 		for (uint32_t i = 0; i < opt_repeat_count; i++) {
94a98a962fSPatrick Mooney 			printf(",%0.2f", cycles_to_ns(data[i]));
95a98a962fSPatrick Mooney 		}
96a98a962fSPatrick Mooney 	}
97a98a962fSPatrick Mooney 	printf("\n");
98a98a962fSPatrick Mooney }
99a98a962fSPatrick Mooney 
100a98a962fSPatrick Mooney static void
handle_exit(struct vmctx * ctx,const struct vm_exit * vexit,struct vm_entry * ventry)101a98a962fSPatrick Mooney handle_exit(struct vmctx *ctx, const struct vm_exit *vexit,
102a98a962fSPatrick Mooney     struct vm_entry *ventry)
103a98a962fSPatrick Mooney {
104a98a962fSPatrick Mooney 	uint32_t outval;
105a98a962fSPatrick Mooney 
106a98a962fSPatrick Mooney 	if (vexit_match_inout(vexit, true, IOP_TEST_PARAM0, 4, NULL)) {
107a98a962fSPatrick Mooney 		ventry_fulfill_inout(vexit, ventry, opt_repeat_count);
108a98a962fSPatrick Mooney 		return;
109a98a962fSPatrick Mooney 	}
110a98a962fSPatrick Mooney 	if (vexit_match_inout(vexit, false, IOP_TEST_VALUE, 4, &outval)) {
111a98a962fSPatrick Mooney 		ventry_fulfill_inout(vexit, ventry, 0);
112a98a962fSPatrick Mooney 		print_result(ctx, (uintptr_t)outval, current_test);
113a98a962fSPatrick Mooney 		/* proceed to next test */
114a98a962fSPatrick Mooney 		current_test++;
115a98a962fSPatrick Mooney 		return;
116a98a962fSPatrick Mooney 	}
117a98a962fSPatrick Mooney 
118a98a962fSPatrick Mooney 	test_fail_vmexit(vexit);
119a98a962fSPatrick Mooney }
120a98a962fSPatrick Mooney 
121a98a962fSPatrick Mooney static void
usage(const char * progname,int status)122a98a962fSPatrick Mooney usage(const char *progname, int status)
123a98a962fSPatrick Mooney {
124a98a962fSPatrick Mooney 	char *base = strdup(progname);
125a98a962fSPatrick Mooney 
126a98a962fSPatrick Mooney 	(void) printf("usage: %s [args]\n"
127a98a962fSPatrick Mooney 	    "\t-n <count>\tNumber of repetitions (default: 1000)\n"
128a98a962fSPatrick Mooney 	    "\t-s\t\tSummarize (average) results\n"
129a98a962fSPatrick Mooney 	    "\t-h\t\tPrint this help\n", basename(base));
130a98a962fSPatrick Mooney 	exit(status);
131a98a962fSPatrick Mooney }
132a98a962fSPatrick Mooney 
133a98a962fSPatrick Mooney static void
parse_args(int argc,char * argv[])134a98a962fSPatrick Mooney parse_args(int argc, char *argv[])
135a98a962fSPatrick Mooney {
136a98a962fSPatrick Mooney 	int c;
137a98a962fSPatrick Mooney 	unsigned long num_parsed;
138a98a962fSPatrick Mooney 
139a98a962fSPatrick Mooney 	while ((c = getopt(argc, argv, ":hsn:")) != -1) {
140a98a962fSPatrick Mooney 		switch (c) {
141a98a962fSPatrick Mooney 		case 'h':
142a98a962fSPatrick Mooney 			usage(argv[0], EXIT_SUCCESS);
143a98a962fSPatrick Mooney 			break;
144a98a962fSPatrick Mooney 		case 's':
145a98a962fSPatrick Mooney 			opt_summarize = true;
146a98a962fSPatrick Mooney 			break;
147a98a962fSPatrick Mooney 		case 'n':
148a98a962fSPatrick Mooney 			errno = 0;
149a98a962fSPatrick Mooney 			num_parsed = strtoul(optarg, NULL, 10);
150a98a962fSPatrick Mooney 			if (num_parsed == 0 && errno != 0) {
151a98a962fSPatrick Mooney 				perror("Invalid repeat count");
152a98a962fSPatrick Mooney 				usage(argv[0], EXIT_FAILURE);
153a98a962fSPatrick Mooney 			}
154a98a962fSPatrick Mooney 			if (num_parsed <= 0 || num_parsed > UINT32_MAX) {
155a98a962fSPatrick Mooney 				(void) printf(
156a98a962fSPatrick Mooney 				    "Repeat count must be between 1 - %lu\n",
157a98a962fSPatrick Mooney 				    UINT32_MAX);
158a98a962fSPatrick Mooney 				usage(argv[0], EXIT_FAILURE);
159a98a962fSPatrick Mooney 			}
160a98a962fSPatrick Mooney 			opt_repeat_count = num_parsed;
161a98a962fSPatrick Mooney 			break;
162a98a962fSPatrick Mooney 		case ':':
163a98a962fSPatrick Mooney 			(void) printf("Missing argument for option '%c'\n",
164a98a962fSPatrick Mooney 			    optopt);
165a98a962fSPatrick Mooney 			usage(argv[0], EXIT_FAILURE);
166a98a962fSPatrick Mooney 			break;
167a98a962fSPatrick Mooney 		case '?':
168a98a962fSPatrick Mooney 			(void) printf("Unrecognized option '%c'\n", optopt);
169a98a962fSPatrick Mooney 			usage(argv[0], EXIT_FAILURE);
170a98a962fSPatrick Mooney 			break;
171a98a962fSPatrick Mooney 		}
172a98a962fSPatrick Mooney 	}
173a98a962fSPatrick Mooney }
174a98a962fSPatrick Mooney 
175a98a962fSPatrick Mooney int
main(int argc,char * argv[])176a98a962fSPatrick Mooney main(int argc, char *argv[])
177a98a962fSPatrick Mooney {
178a98a962fSPatrick Mooney 	const char *test_suite_name = basename(argv[0]);
179a98a962fSPatrick Mooney 	struct vmctx *ctx = NULL;
180*32640292SAndy Fiddaman 	struct vcpu *vcpu;
181a98a962fSPatrick Mooney 	int err;
182a98a962fSPatrick Mooney 
183a98a962fSPatrick Mooney 	parse_args(argc, argv);
184a98a962fSPatrick Mooney 
185a98a962fSPatrick Mooney 	ctx = test_initialize(test_suite_name);
186a98a962fSPatrick Mooney 
187*32640292SAndy Fiddaman 	if ((vcpu = vm_vcpu_open(ctx, 0)) == NULL) {
188*32640292SAndy Fiddaman 		test_fail_errno(errno, "Could not open vcpu0");
189*32640292SAndy Fiddaman 	}
190*32640292SAndy Fiddaman 
191*32640292SAndy Fiddaman 	err = test_setup_vcpu(vcpu, MEM_LOC_PAYLOAD, MEM_LOC_STACK);
192a98a962fSPatrick Mooney 	if (err != 0) {
193a98a962fSPatrick Mooney 		test_fail_errno(err, "Could not initialize vcpu0");
194a98a962fSPatrick Mooney 	}
195a98a962fSPatrick Mooney 	cpu_freq = query_cpu_freq(ctx);
196a98a962fSPatrick Mooney 
197a98a962fSPatrick Mooney 	struct vm_entry ventry = { 0 };
198a98a962fSPatrick Mooney 	struct vm_exit vexit = { 0 };
199a98a962fSPatrick Mooney 
200a98a962fSPatrick Mooney 	do {
201a98a962fSPatrick Mooney 		const enum vm_exit_kind kind =
202*32640292SAndy Fiddaman 		    test_run_vcpu(vcpu, &ventry, &vexit);
203a98a962fSPatrick Mooney 		switch (kind) {
204a98a962fSPatrick Mooney 		case VEK_REENTR:
205a98a962fSPatrick Mooney 			break;
206a98a962fSPatrick Mooney 		case VEK_UNHANDLED:
207a98a962fSPatrick Mooney 			handle_exit(ctx, &vexit, &ventry);
208a98a962fSPatrick Mooney 			break;
209a98a962fSPatrick Mooney 
210a98a962fSPatrick Mooney 		case VEK_TEST_PASS:
211a98a962fSPatrick Mooney 			/*
212a98a962fSPatrick Mooney 			 * Skip the normal "PASS" message, since the consumer is
213a98a962fSPatrick Mooney 			 * interested in the data itself.
214a98a962fSPatrick Mooney 			 */
215a98a962fSPatrick Mooney 			exit(EXIT_SUCCESS);
216a98a962fSPatrick Mooney 			break;
217a98a962fSPatrick Mooney 		case VEK_TEST_MSG:
218a98a962fSPatrick Mooney 			test_msg_print(ctx);
219a98a962fSPatrick Mooney 			break;
220a98a962fSPatrick Mooney 		case VEK_TEST_FAIL:
221a98a962fSPatrick Mooney 		default:
222a98a962fSPatrick Mooney 			test_fail_vmexit(&vexit);
223a98a962fSPatrick Mooney 			break;
224a98a962fSPatrick Mooney 		}
225a98a962fSPatrick Mooney 	} while (true);
226a98a962fSPatrick Mooney }
227