diff --git a/lib/libvmmapi/vmmapi.h b/lib/libvmmapi/vmmapi.h --- a/lib/libvmmapi/vmmapi.h +++ b/lib/libvmmapi/vmmapi.h @@ -155,7 +155,7 @@ const int *regnums, uint64_t *regvals); int vm_get_register_set(struct vcpu *vcpu, unsigned int count, const int *regnums, uint64_t *regvals); -int vm_run(struct vcpu *vcpu, struct vm_exit *ret_vmexit); +int vm_run(struct vcpu *vcpu, struct vm_run *vmrun); int vm_suspend(struct vmctx *ctx, enum vm_suspend_how how); int vm_reinit(struct vmctx *ctx); int vm_apicid2vcpu(struct vmctx *ctx, int apicid); diff --git a/lib/libvmmapi/vmmapi.c b/lib/libvmmapi/vmmapi.c --- a/lib/libvmmapi/vmmapi.c +++ b/lib/libvmmapi/vmmapi.c @@ -721,16 +721,9 @@ } int -vm_run(struct vcpu *vcpu, struct vm_exit *vmexit) +vm_run(struct vcpu *vcpu, struct vm_run *vmrun) { - int error; - struct vm_run vmrun; - - bzero(&vmrun, sizeof(vmrun)); - - error = vcpu_ioctl(vcpu, VM_RUN, &vmrun); - bcopy(&vmrun.vm_exit, vmexit, sizeof(struct vm_exit)); - return (error); + return (vcpu_ioctl(vcpu, VM_RUN, vmrun)); } int diff --git a/sys/amd64/include/vmm.h b/sys/amd64/include/vmm.h --- a/sys/amd64/include/vmm.h +++ b/sys/amd64/include/vmm.h @@ -273,7 +273,7 @@ struct seg_desc *ret_desc); int vm_set_seg_desc(struct vcpu *vcpu, int reg, struct seg_desc *desc); -int vm_run(struct vcpu *vcpu, struct vm_exit *vme_user); +int vm_run(struct vcpu *vcpu); int vm_suspend(struct vm *vm, enum vm_suspend_how how); int vm_inject_nmi(struct vcpu *vcpu); int vm_nmi_pending(struct vcpu *vcpu); @@ -297,6 +297,7 @@ int vm_resume_cpu(struct vm *vm, struct vcpu *vcpu); int vm_restart_instruction(struct vcpu *vcpu); struct vm_exit *vm_exitinfo(struct vcpu *vcpu); +cpuset_t *vm_exitinfo_cpuset(struct vcpu *vcpu); void vm_exit_suspended(struct vcpu *vcpu, uint64_t rip); void vm_exit_debug(struct vcpu *vcpu, uint64_t rip); void vm_exit_rendezvous(struct vcpu *vcpu, uint64_t rip); @@ -754,9 +755,14 @@ enum vm_suspend_how how; } suspended; struct { + /* + * The destination vCPU mask is saved in vcpu->cpuset + * and is copied out to userspace separately to avoid + * ABI concerns. + */ uint32_t mode; uint8_t vector; - cpuset_t dmask; + cpuset_t *dmask; } ipi; struct vm_task_switch task_switch; } u; diff --git a/sys/amd64/include/vmm_dev.h b/sys/amd64/include/vmm_dev.h --- a/sys/amd64/include/vmm_dev.h +++ b/sys/amd64/include/vmm_dev.h @@ -89,7 +89,9 @@ struct vm_run { int cpuid; - struct vm_exit vm_exit; + cpuset_t *cpuset; /* CPU set storage */ + size_t cpusetsize; + struct vm_exit *vm_exit; }; struct vm_exception { @@ -349,7 +351,7 @@ }; #define VM_RUN \ - _IOWR('v', IOCNUM_RUN, struct vm_run) + _IOW('v', IOCNUM_RUN, struct vm_run) #define VM_SUSPEND \ _IOW('v', IOCNUM_SUSPEND, struct vm_suspend) #define VM_REINIT \ diff --git a/sys/amd64/vmm/io/vlapic.c b/sys/amd64/vmm/io/vlapic.c --- a/sys/amd64/vmm/io/vlapic.c +++ b/sys/amd64/vmm/io/vlapic.c @@ -1146,7 +1146,8 @@ vmexit->exitcode = VM_EXITCODE_IPI; vmexit->u.ipi.mode = mode; vmexit->u.ipi.vector = vec; - vmexit->u.ipi.dmask = ipimask; + vmexit->u.ipi.dmask = vm_exitinfo_cpuset(vlapic->vcpu); + memcpy(vmexit->u.ipi.dmask, &ipimask, sizeof(cpuset_t)); *retu = true; } @@ -1166,7 +1167,7 @@ vm_handle_ipi(struct vcpu *vcpu, struct vm_exit *vme, bool *retu) { struct vlapic *vlapic = vm_lapic(vcpu); - cpuset_t *dmask = &vme->u.ipi.dmask; + cpuset_t *dmask = vm_exitinfo_cpuset(vcpu); uint8_t vec = vme->u.ipi.vector; *retu = true; diff --git a/sys/amd64/vmm/vmm.c b/sys/amd64/vmm/vmm.c --- a/sys/amd64/vmm/vmm.c +++ b/sys/amd64/vmm/vmm.c @@ -123,6 +123,7 @@ uint64_t guest_xcr0; /* (i) guest %xcr0 register */ void *stats; /* (a,i) statistics */ struct vm_exit exitinfo; /* (x) exit reason and collateral */ + cpuset_t cpuset; /* (x) storage for vmexit handlers */ uint64_t nextrip; /* (x) next instruction to execute */ uint64_t tsc_offset; /* (o) TSC offsetting */ }; @@ -399,6 +400,12 @@ return (&vcpu->exitinfo); } +cpuset_t * +vm_exitinfo_cpuset(struct vcpu *vcpu) +{ + return (&vcpu->cpuset); +} + static int vmm_init(void) { @@ -1837,7 +1844,7 @@ } int -vm_run(struct vcpu *vcpu, struct vm_exit *vme_user) +vm_run(struct vcpu *vcpu) { struct vm *vm = vcpu->vm; struct vm_eventinfo evinfo; @@ -1858,6 +1865,7 @@ pmap = vmspace_pmap(vm->vmspace); vme = &vcpu->exitinfo; + CPU_ZERO(&vcpu->cpuset); evinfo.rptr = &vm->rendezvous_req_cpus; evinfo.sptr = &vm->suspend; evinfo.iptr = &vcpu->reqidle; @@ -1938,8 +1946,6 @@ vmm_stat_incr(vcpu, VMEXIT_USERSPACE, 1); VMM_CTR2(vcpu, "retu %d/%d", error, vme->exitcode); - /* copy the exit information */ - *vme_user = *vme; return (error); } diff --git a/sys/amd64/vmm/vmm_dev.c b/sys/amd64/vmm/vmm_dev.c --- a/sys/amd64/vmm/vmm_dev.c +++ b/sys/amd64/vmm/vmm_dev.c @@ -93,7 +93,29 @@ #define VM_SNAPSHOT_REQ_OLD \ _IOWR('v', IOCNUM_SNAPSHOT_REQ, struct vm_snapshot_meta_old) -#endif + +struct vm_exit_ipi_old { + uint32_t mode; + uint8_t vector; + __BITSET_DEFINE(, 256) dmask; +}; + +struct vm_exit_old { + uint32_t exitcode; + int32_t inst_length; + uint64_t rip; + uint64_t u[120 / sizeof(uint64_t)]; +}; + +struct vm_run_old { + int cpuid; + struct vm_exit_old vm_exit; +}; + +#define VM_RUN_OLD \ + _IOWR('v', IOCNUM_RUN, struct vm_run_old) + +#endif /* COMPAT_FREEBSD13 */ struct devmem_softc { int segid; @@ -396,6 +418,9 @@ struct vm_seg_desc *vmsegdesc; struct vm_register_set *vmregset; struct vm_run *vmrun; +#ifdef COMPAT_FREEBSD13 + struct vm_run_old *vmrun_old; +#endif struct vm_exception *vmexc; struct vm_lapic_irq *vmirq; struct vm_lapic_msi *vmmsi; @@ -459,6 +484,9 @@ */ switch (cmd) { case VM_RUN: +#ifdef COMPAT_FREEBSD13 + case VM_RUN_OLD: +#endif case VM_GET_REGISTER: case VM_SET_REGISTER: case VM_GET_SEGMENT_DESCRIPTOR: @@ -579,11 +607,61 @@ break; } - switch(cmd) { - case VM_RUN: + switch (cmd) { + case VM_RUN: { + struct vm_exit *vme; + vmrun = (struct vm_run *)data; - error = vm_run(vcpu, &vmrun->vm_exit); + vme = vm_exitinfo(vcpu); + + error = vm_run(vcpu); + if (error == 0) { + if (vme->exitcode == VM_EXITCODE_IPI) { + /* Fix up the user cpuset pointer. */ + vme->u.ipi.dmask = vmrun->cpuset; + } + error = copyout(vme, vmrun->vm_exit, sizeof(*vme)); + if (error == 0 && vme->exitcode == VM_EXITCODE_IPI) { + error = copyout(vm_exitinfo_cpuset(vcpu), + vmrun->cpuset, + min(vmrun->cpusetsize, sizeof(cpuset_t))); + } + } break; + } +#ifdef COMPAT_FREEBSD13 + case VM_RUN_OLD: { + struct vm_exit *vme; + struct vm_exit_old *vme_old; + + vmrun_old = (struct vm_run_old *)data; + vme_old = &vmrun_old->vm_exit; + vme = vm_exitinfo(vcpu); + + error = vm_run(vcpu); + if (error == 0) { + vme_old->exitcode = vme->exitcode; + vme_old->inst_length = vme->inst_length; + vme_old->rip = vme->rip; + memcpy(vme_old->u, &vme->u, sizeof(vme_old->u)); + if (vme->exitcode == VM_EXITCODE_IPI) { + struct vm_exit_ipi_old *ipi; + cpuset_t *dmask; + int cpu; + + dmask = vm_exitinfo_cpuset(vcpu); + ipi = (struct vm_exit_ipi_old *)&vme_old->u[0]; + BIT_ZERO(256, &ipi->dmask); + CPU_FOREACH_ISSET(cpu, dmask) { + if (cpu >= 256) + break; + BIT_SET(256, cpu, &ipi->dmask); + } + } + } + break; + } +#endif case VM_SUSPEND: vmsuspend = (struct vm_suspend *)data; error = vm_suspend(sc->vm, vmsuspend->how); diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c --- a/usr.sbin/bhyve/bhyverun.c +++ b/usr.sbin/bhyve/bhyverun.c @@ -927,9 +927,10 @@ { int error = -1; int i; + switch (vme->u.ipi.mode) { case APIC_DELMODE_INIT: - CPU_FOREACH_ISSET(i, &vme->u.ipi.dmask) { + CPU_FOREACH_ISSET(i, vme->u.ipi.dmask) { error = vm_suspend_cpu(vcpu_info[i].vcpu); if (error) { warnx("%s: failed to suspend cpu %d\n", @@ -939,7 +940,7 @@ } break; case APIC_DELMODE_STARTUP: - CPU_FOREACH_ISSET(i, &vme->u.ipi.dmask) { + CPU_FOREACH_ISSET(i, vme->u.ipi.dmask) { spinup_ap(vcpu_info[i].vcpu, vme->u.ipi.vector << PAGE_SHIFT); } @@ -974,15 +975,21 @@ vm_loop(struct vmctx *ctx, struct vcpu *vcpu) { struct vm_exit vme; + struct vm_run vmrun; int error, rc; enum vm_exitcode exitcode; - cpuset_t active_cpus; + cpuset_t active_cpus, dmask; error = vm_active_cpus(ctx, &active_cpus); assert(CPU_ISSET(vcpu_id(vcpu), &active_cpus)); + vmrun.vm_exit = &vme; + CPU_ZERO(&dmask); + vmrun.cpuset = &dmask; + vmrun.cpusetsize = sizeof(dmask); + while (1) { - error = vm_run(vcpu, &vme); + error = vm_run(vcpu, &vmrun); if (error != 0) break;