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 /* 17 * Test that adjusting the guest TSC with VMM time data interface is visible 18 * in guest. 19 * 20 * Note: requires `vmm_allow_state_writes` to be set 21 */ 22 23 #include <unistd.h> 24 #include <stdlib.h> 25 #include <libgen.h> 26 #include <errno.h> 27 #include <err.h> 28 29 #include <sys/vmm_data.h> 30 #include <sys/vmm_dev.h> 31 #include <vmmapi.h> 32 33 #include "in_guest.h" 34 35 int 36 main(int argc, char *argv[]) 37 { 38 const char *test_suite_name = basename(argv[0]); 39 struct vmctx *ctx = NULL; 40 int err; 41 42 ctx = test_initialize(test_suite_name); 43 44 err = test_setup_vcpu(ctx, 0, MEM_LOC_PAYLOAD, MEM_LOC_STACK); 45 if (err != 0) { 46 test_fail_errno(err, "Could not initialize vcpu0"); 47 } 48 49 const int vmfd = vm_get_device_fd(ctx); 50 51 /* Read time data to get baseline guest time values */ 52 struct vdi_time_info_v1 time_info; 53 struct vm_data_xfer xfer = { 54 .vdx_class = VDC_VMM_TIME, 55 .vdx_version = 1, 56 .vdx_len = sizeof (struct vdi_time_info_v1), 57 .vdx_data = &time_info, 58 }; 59 if (ioctl(vmfd, VM_DATA_READ, &xfer) != 0) { 60 errx(EXIT_FAILURE, "VMM_DATA_READ of time info failed"); 61 } 62 63 /* Change the guest TSC to a much larger value */ 64 uint64_t expect_tsc = 500000000000; 65 time_info.vt_guest_tsc = expect_tsc; 66 if (ioctl(vmfd, VM_DATA_WRITE, &xfer) != 0) { 67 int error; 68 error = errno; 69 if (error == EPERM) { 70 warn("VMM_DATA_WRITE got EPERM: is " 71 "vmm_allow_state_writes set?"); 72 } 73 errx(EXIT_FAILURE, "VMM_DATA_WRITE of time info failed"); 74 } 75 76 struct vm_entry ventry = { 0 }; 77 struct vm_exit vexit = { 0 }; 78 79 bool half_read = false; 80 uint64_t tsc; 81 82 do { 83 const enum vm_exit_kind kind = 84 test_run_vcpu(ctx, 0, &ventry, &vexit); 85 86 if (kind == VEK_REENTR) { 87 continue; 88 } else if (kind != VEK_UNHANDLED) { 89 test_fail_vmexit(&vexit); 90 } 91 92 uint32_t val; 93 if (vexit_match_inout(&vexit, false, IOP_TEST_VALUE, 4, 94 &val)) { 95 if (!half_read) { 96 /* low 32-bits of TSC first */ 97 tsc = val; 98 half_read = true; 99 ventry_fulfill_inout(&vexit, &ventry, 0); 100 } else { 101 /* high 32-bits of TSC */ 102 tsc |= ((uint64_t)val << 32); 103 104 /* 105 * Check that the TSC reading is at least the 106 * absurdly high value it was set to. 107 */ 108 if (tsc >= expect_tsc) { 109 (void) printf("tsc=%ld\n", tsc); 110 test_pass(); 111 } else { 112 test_fail_msg("TSC %lu < %lu\n", tsc, 113 expect_tsc); 114 } 115 } 116 } else { 117 test_fail_vmexit(&vexit); 118 } 119 } while (true); 120 } 121