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 <stdio.h> 17 #include <unistd.h> 18 #include <stdlib.h> 19 #include <fcntl.h> 20 #include <libgen.h> 21 #include <sys/stat.h> 22 #include <errno.h> 23 #include <err.h> 24 #include <assert.h> 25 #include <sys/sysmacros.h> 26 #include <stdbool.h> 27 28 #include <sys/vmm.h> 29 #include <sys/vmm_dev.h> 30 #include <sys/vmm_data.h> 31 #include <vmmapi.h> 32 #include <sys/x86_archext.h> 33 #include <sys/controlregs.h> 34 35 #include "common.h" 36 37 static void 38 do_data_write(int vmfd, struct vm_data_xfer *vdx) 39 { 40 if (ioctl(vmfd, VM_DATA_WRITE, vdx) != 0) { 41 err(EXIT_FAILURE, "valid vmm_data_write failed"); 42 } 43 if (vdx->vdx_result_len != vdx->vdx_len) { 44 errx(EXIT_FAILURE, "unexpected vdx_result_len %u != %u", 45 vdx->vdx_len, vdx->vdx_result_len); 46 } 47 } 48 49 static void 50 do_data_read(int vmfd, struct vm_data_xfer *vdx) 51 { 52 if (ioctl(vmfd, VM_DATA_READ, vdx) != 0) { 53 err(EXIT_FAILURE, "valid vmm_data_read failed"); 54 } 55 if (vdx->vdx_result_len != vdx->vdx_len) { 56 errx(EXIT_FAILURE, "unexpected vdx_result_len %u != %u", 57 vdx->vdx_len, vdx->vdx_result_len); 58 } 59 } 60 61 static uint32_t 62 query_data_size(int vmfd, struct vm_data_xfer *vdx) 63 { 64 vdx->vdx_len = 0; 65 vdx->vdx_data = NULL; 66 vdx->vdx_flags = 0; 67 68 if (ioctl(vmfd, VM_DATA_READ, vdx) == 0) { 69 errx(EXIT_FAILURE, 70 "expected VM_DATA_READ to fail for size query"); 71 } 72 if (errno != ENOSPC) { 73 err(EXIT_FAILURE, 74 "expected ENOSPC error for VM_DATA_READ size query"); 75 } 76 return (vdx->vdx_result_len); 77 } 78 79 int 80 main(int argc, char *argv[]) 81 { 82 const char *suite_name = basename(argv[0]); 83 struct vmctx *ctx; 84 struct vcpu *vcpu; 85 86 ctx = create_test_vm(suite_name); 87 if (ctx == NULL) { 88 errx(EXIT_FAILURE, "could not open test VM"); 89 } 90 91 if ((vcpu = vm_vcpu_open(ctx, 0)) == NULL) { 92 err(EXIT_FAILURE, "could not open vcpu0"); 93 } 94 95 if (vm_activate_cpu(vcpu) != 0) { 96 err(EXIT_FAILURE, "could not activate vcpu0"); 97 } 98 99 const int vmfd = vm_get_device_fd(ctx); 100 101 /* Pause the instance before attempting to manipulate vcpu data */ 102 if (ioctl(vmfd, VM_PAUSE, 0) != 0) { 103 err(EXIT_FAILURE, "VM_PAUSE failed"); 104 } 105 106 struct vm_data_xfer vdx = { 107 .vdx_class = VDC_MSR, 108 .vdx_version = 1, 109 .vdx_vcpuid = 0, 110 }; 111 112 const uint32_t msr_sz = query_data_size(vmfd, &vdx); 113 const uint32_t msr_count = msr_sz / sizeof (struct vdi_field_entry_v1); 114 115 struct vdi_field_entry_v1 *entries = 116 calloc(msr_count, sizeof (struct vdi_field_entry_v1)); 117 if (entries == NULL) { 118 err(EXIT_FAILURE, "could not allocate space for MSR data"); 119 } 120 121 /* Attempt to read all the (default) entries */ 122 vdx.vdx_data = entries; 123 vdx.vdx_len = msr_sz; 124 do_data_read(vmfd, &vdx); 125 126 /* Spot check a few MSRs which we expect to be present */ 127 struct expected_msr { 128 const char *name; 129 uint32_t msr; 130 bool present; 131 } spot_check[] = { 132 { .msr = MSR_AMD_EFER, .name = "EFER" }, 133 { .msr = REG_TSC, .name = "TSC" }, 134 { .msr = MSR_AMD_CSTAR, .name = "CSTAR" }, 135 { .msr = MSR_AMD_KGSBASE, .name = "KGSBASE" }, 136 }; 137 for (uint_t i = 0; i < msr_count; i++) { 138 for (uint_t j = 0; j < ARRAY_SIZE(spot_check); j++) { 139 if (spot_check[j].msr == entries[i].vfe_ident) { 140 spot_check[j].present = true; 141 } 142 } 143 } 144 for (uint_t j = 0; j < ARRAY_SIZE(spot_check); j++) { 145 if (!spot_check[j].present) { 146 errx(EXIT_FAILURE, 147 "did not find %s(%x) MSR in VM_DATA_READ results", 148 spot_check[j].name, spot_check[j].msr); 149 } 150 } 151 152 /* Attempt to write those same values back to the instance */ 153 do_data_write(vmfd, &vdx); 154 free(entries); 155 entries = NULL; 156 157 /* Do a targeted read of a few values */ 158 struct vdi_field_entry_v1 small_list[] = { 159 { .vfe_ident = REG_TSC }, 160 { .vfe_ident = MSR_INTC_SEP_EIP }, 161 { .vfe_ident = REG_PAT }, 162 }; 163 vdx.vdx_data = small_list; 164 vdx.vdx_len = sizeof (small_list); 165 vdx.vdx_flags = VDX_FLAG_READ_COPYIN; 166 do_data_read(vmfd, &vdx); 167 168 /* 169 * Test access to DEBUGCTL and LBR-related MSRs on AMD. 170 * 171 * Because support for these varies between CPUs, they are (currently) 172 * not included in the default set of MSRs emitted by a blanket read of 173 * MSRs via the vmm-data interface. 174 */ 175 if (cpu_vendor_amd()) { 176 struct vdi_field_entry_v1 dbg_entries[] = { 177 { .vfe_ident = MSR_DEBUGCTL }, 178 { .vfe_ident = MSR_LBR_FROM }, 179 { .vfe_ident = MSR_LBR_TO }, 180 { .vfe_ident = MSR_LEX_FROM }, 181 { .vfe_ident = MSR_LEX_TO }, 182 }; 183 184 vdx.vdx_data = &dbg_entries; 185 vdx.vdx_len = sizeof (dbg_entries); 186 vdx.vdx_flags = VDX_FLAG_READ_COPYIN; 187 188 do_data_read(vmfd, &vdx); 189 190 vdx.vdx_flags = 0; 191 do_data_write(vmfd, &vdx); 192 } 193 194 195 vm_destroy(ctx); 196 (void) printf("%s\tPASS\n", suite_name); 197 return (EXIT_SUCCESS); 198 } 199