Skip to content

Commit 4bd36be

Browse files
committed
16393 bhyve should take more care with MSR_AMD_TSC_RATIO
Reviewed by: Dan McDonald <[email protected]> Reviewed by: Jordan Paige Hendricks <[email protected]> Approved by: Richard Lowe <[email protected]>
1 parent 8921fdc commit 4bd36be

File tree

5 files changed

+89
-19
lines changed

5 files changed

+89
-19
lines changed

usr/src/test/bhyve-tests/tests/vmm/datarw_constraints.c

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*/
1111

1212
/*
13-
* Copyright 2023 Oxide Computer Company
13+
* Copyright 2024 Oxide Computer Company
1414
*/
1515

1616
#include <stdio.h>
@@ -145,6 +145,7 @@ test_vm_classes(int vmfd)
145145
.vdx_version = cases[i].ctc_version,
146146
.vdx_len = bufsz,
147147
.vdx_data = buf,
148+
.vdx_vcpuid = -1,
148149
};
149150

150151
/* First do a read */
@@ -340,6 +341,49 @@ test_vcpuid_combos(int vmfd)
340341
free(buf);
341342
}
342343

344+
static void
345+
test_vcpuid_time(int vmfd)
346+
{
347+
struct vdi_time_info_v1 data;
348+
struct vm_data_xfer vdx = {
349+
.vdx_class = VDC_VMM_TIME,
350+
.vdx_version = 1,
351+
.vdx_len = sizeof (data),
352+
.vdx_data = &data,
353+
};
354+
355+
/* This should work with the system-wide vcpuid */
356+
vdx.vdx_vcpuid = -1;
357+
if (ioctl(vmfd, VM_DATA_READ, &vdx) != 0) {
358+
err(EXIT_FAILURE, "VM_DATA_READ failed for valid vcpuid");
359+
}
360+
361+
/* But fail for other vcpuids */
362+
vdx.vdx_vcpuid = 0;
363+
if (ioctl(vmfd, VM_DATA_READ, &vdx) == 0) {
364+
err(EXIT_FAILURE, "VM_DATA_READ should fail for vcpuid %d",
365+
vdx.vdx_vcpuid);
366+
}
367+
368+
/*
369+
* Perform same check for writes
370+
*
371+
* Normally this would require care to handle hosts which lack frequency
372+
* scaling functionality, but since we are writing back the same data,
373+
* the guest frequency should match that of the host, requiring no real
374+
* scaling be done for the instance.
375+
*/
376+
vdx.vdx_vcpuid = -1;
377+
if (ioctl(vmfd, VM_DATA_WRITE, &vdx) != 0) {
378+
err(EXIT_FAILURE, "VM_DATA_WRITE failed for valid vcpuid");
379+
}
380+
vdx.vdx_vcpuid = 0;
381+
if (ioctl(vmfd, VM_DATA_WRITE, &vdx) == 0) {
382+
errx(EXIT_FAILURE, "VM_DATA_READ should fail for vcpuid %d",
383+
vdx.vdx_vcpuid);
384+
}
385+
}
386+
343387
int
344388
main(int argc, char *argv[])
345389
{
@@ -372,6 +416,9 @@ main(int argc, char *argv[])
372416
/* Try some weird vdx_vcpuid cases */
373417
test_vcpuid_combos(vmfd);
374418

419+
/* VMM_TIME is picky about vcpuid */
420+
test_vcpuid_time(vmfd);
421+
375422
vm_destroy(ctx);
376423
(void) printf("%s\tPASS\n", suite_name);
377424
return (EXIT_SUCCESS);

usr/src/uts/intel/io/vmm/amd/svm_msr.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,10 @@ svm_msr_guest_exit(struct svm_softc *sc, int vcpu)
118118
wrmsr(MSR_STAR, host_msrs[IDX_MSR_STAR]);
119119
wrmsr(MSR_SF_MASK, host_msrs[IDX_MSR_SF_MASK]);
120120

121-
/* Reset frequency multiplier MSR */
122-
wrmsr(MSR_AMD_TSC_RATIO, AMD_TSCM_RESET_VAL);
121+
/* Reset frequency multiplier MSR if any scaling is configured */
122+
if (vm_get_freq_multiplier(sc->vm) != VM_TSCM_NOSCALE) {
123+
wrmsr(MSR_AMD_TSC_RATIO, AMD_TSCM_RESET_VAL);
124+
}
123125

124126
/* MSR_KGSBASE will be restored on the way back to userspace */
125127
}

usr/src/uts/intel/io/vmm/sys/vmm_kernel.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
*
3838
* Copyright 2015 Pluribus Networks Inc.
3939
* Copyright 2019 Joyent, Inc.
40-
* Copyright 2023 Oxide Computer Company
40+
* Copyright 2024 Oxide Computer Company
4141
* Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
4242
*/
4343

@@ -506,6 +506,7 @@ typedef struct vmm_data_req {
506506
uint32_t vdr_len;
507507
void *vdr_data;
508508
uint32_t *vdr_result_len;
509+
int vdr_vcpuid;
509510
} vmm_data_req_t;
510511

511512
typedef int (*vmm_data_writef_t)(void *, const vmm_data_req_t *);
@@ -564,8 +565,8 @@ typedef struct vmm_data_version_entry {
564565

565566
#define VMM_DATA_VERSION(sym) SET_ENTRY(vmm_data_version_entries, sym)
566567

567-
int vmm_data_read(struct vm *, int, const vmm_data_req_t *);
568-
int vmm_data_write(struct vm *, int, const vmm_data_req_t *);
568+
int vmm_data_read(struct vm *, const vmm_data_req_t *);
569+
int vmm_data_write(struct vm *, const vmm_data_req_t *);
569570

570571
/*
571572
* TSC Scaling

usr/src/uts/intel/io/vmm/vmm.c

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
*
3838
* Copyright 2015 Pluribus Networks Inc.
3939
* Copyright 2018 Joyent, Inc.
40-
* Copyright 2023 Oxide Computer Company
40+
* Copyright 2024 Oxide Computer Company
4141
* Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
4242
*/
4343

@@ -3934,8 +3934,7 @@ vmm_kstat_update_vcpu(struct kstat *ksp, int rw)
39343934
SET_DECLARE(vmm_data_version_entries, const vmm_data_version_entry_t);
39353935

39363936
static int
3937-
vmm_data_find(const vmm_data_req_t *req, int vcpuid,
3938-
const vmm_data_version_entry_t **resp)
3937+
vmm_data_find(const vmm_data_req_t *req, const vmm_data_version_entry_t **resp)
39393938
{
39403939
const vmm_data_version_entry_t **vdpp, *vdp;
39413940

@@ -3977,7 +3976,8 @@ vmm_data_find(const vmm_data_req_t *req, int vcpuid,
39773976
* others expect vcpuid [0, VM_MAXCPU).
39783977
*/
39793978
const int llimit = vdp->vdve_vcpu_wildcard ? -1 : 0;
3980-
if (vcpuid < llimit || vcpuid >= VM_MAXCPU) {
3979+
if (req->vdr_vcpuid < llimit ||
3980+
req->vdr_vcpuid >= VM_MAXCPU) {
39813981
return (EINVAL);
39823982
}
39833983
} else {
@@ -4882,6 +4882,14 @@ vmm_data_read_vmm_time(void *arg, const vmm_data_req_t *req)
48824882
struct vm *vm = arg;
48834883
struct vdi_time_info_v1 *out = req->vdr_data;
48844884

4885+
/*
4886+
* Since write operations on VMM_TIME data are strict about vcpuid
4887+
* (see: vmm_data_write_vmm_time()), read operations should be as well.
4888+
*/
4889+
if (req->vdr_vcpuid != -1) {
4890+
return (EINVAL);
4891+
}
4892+
48854893
/* Take a snapshot of this point in time */
48864894
uint64_t tsc;
48874895
hrtime_t hrtime;
@@ -4937,6 +4945,17 @@ vmm_data_write_vmm_time(void *arg, const vmm_data_req_t *req)
49374945
struct vm *vm = arg;
49384946
const struct vdi_time_info_v1 *src = req->vdr_data;
49394947

4948+
/*
4949+
* While vcpuid values != -1 are tolerated by the vmm_data machinery for
4950+
* VM-wide endpoints, the time-related data is more strict: It relies on
4951+
* write-locking the VM (implied by the vcpuid -1) to prevent vCPUs or
4952+
* other bits from observing inconsistent values while the state is
4953+
* being written.
4954+
*/
4955+
if (req->vdr_vcpuid != -1) {
4956+
return (EINVAL);
4957+
}
4958+
49404959
/*
49414960
* Platform-specific checks will verify the requested frequency against
49424961
* the supported range further, but a frequency of 0 is never valid.
@@ -5064,12 +5083,12 @@ static const vmm_data_version_entry_t versions_v1 = {
50645083
VMM_DATA_VERSION(versions_v1);
50655084

50665085
int
5067-
vmm_data_read(struct vm *vm, int vcpuid, const vmm_data_req_t *req)
5086+
vmm_data_read(struct vm *vm, const vmm_data_req_t *req)
50685087
{
50695088
int err = 0;
50705089

50715090
const vmm_data_version_entry_t *entry = NULL;
5072-
err = vmm_data_find(req, vcpuid, &entry);
5091+
err = vmm_data_find(req, &entry);
50735092
if (err != 0) {
50745093
return (err);
50755094
}
@@ -5080,7 +5099,7 @@ vmm_data_read(struct vm *vm, int vcpuid, const vmm_data_req_t *req)
50805099

50815100
err = entry->vdve_readf(datap, req);
50825101
} else if (entry->vdve_vcpu_readf != NULL) {
5083-
err = entry->vdve_vcpu_readf(vm, vcpuid, req);
5102+
err = entry->vdve_vcpu_readf(vm, req->vdr_vcpuid, req);
50845103
} else {
50855104
err = EINVAL;
50865105
}
@@ -5097,12 +5116,12 @@ vmm_data_read(struct vm *vm, int vcpuid, const vmm_data_req_t *req)
50975116
}
50985117

50995118
int
5100-
vmm_data_write(struct vm *vm, int vcpuid, const vmm_data_req_t *req)
5119+
vmm_data_write(struct vm *vm, const vmm_data_req_t *req)
51015120
{
51025121
int err = 0;
51035122

51045123
const vmm_data_version_entry_t *entry = NULL;
5105-
err = vmm_data_find(req, vcpuid, &entry);
5124+
err = vmm_data_find(req, &entry);
51065125
if (err != 0) {
51075126
return (err);
51085127
}
@@ -5113,7 +5132,7 @@ vmm_data_write(struct vm *vm, int vcpuid, const vmm_data_req_t *req)
51135132

51145133
err = entry->vdve_writef(datap, req);
51155134
} else if (entry->vdve_vcpu_writef != NULL) {
5116-
err = entry->vdve_vcpu_writef(vm, vcpuid, req);
5135+
err = entry->vdve_vcpu_writef(vm, req->vdr_vcpuid, req);
51175136
} else {
51185137
err = EINVAL;
51195138
}

usr/src/uts/intel/io/vmm/vmm_sol_dev.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1848,8 +1848,9 @@ vmmdev_do_ioctl(vmm_softc_t *sc, int cmd, intptr_t arg, int md,
18481848
.vdr_len = len,
18491849
.vdr_data = buf,
18501850
.vdr_result_len = &vdx.vdx_result_len,
1851+
.vdr_vcpuid = vdx.vdx_vcpuid,
18511852
};
1852-
error = vmm_data_read(sc->vmm_vm, vdx.vdx_vcpuid, &req);
1853+
error = vmm_data_read(sc->vmm_vm, &req);
18531854

18541855
if (error == 0 && buf != NULL) {
18551856
if (ddi_copyout(buf, vdx.vdx_data, len, md) != 0) {
@@ -1906,10 +1907,10 @@ vmmdev_do_ioctl(vmm_softc_t *sc, int cmd, intptr_t arg, int md,
19061907
.vdr_len = len,
19071908
.vdr_data = buf,
19081909
.vdr_result_len = &vdx.vdx_result_len,
1910+
.vdr_vcpuid = vdx.vdx_vcpuid,
19091911
};
19101912
if (vmm_allow_state_writes != 0) {
1911-
error = vmm_data_write(sc->vmm_vm, vdx.vdx_vcpuid,
1912-
&req);
1913+
error = vmm_data_write(sc->vmm_vm, &req);
19131914
} else {
19141915
/*
19151916
* Reject the write if somone has thrown the switch back

0 commit comments

Comments
 (0)