Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F170789299
D35623.id111607.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D35623.id111607.diff
View Options
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
@@ -31,6 +31,7 @@
#ifndef _VMM_H_
#define _VMM_H_
+#include <sys/cpuset.h>
#include <sys/sdt.h>
#include <x86/segments.h>
@@ -483,6 +484,7 @@
VM_CAP_BPT_EXIT,
VM_CAP_RDPID,
VM_CAP_RDTSCP,
+ VM_CAP_IPI_EXIT,
VM_CAP_MAX
};
@@ -630,6 +632,7 @@
VM_EXITCODE_DEBUG,
VM_EXITCODE_VMINSN,
VM_EXITCODE_BPT,
+ VM_EXITCODE_IPI,
VM_EXITCODE_MAX
};
@@ -737,6 +740,11 @@
struct {
enum vm_suspend_how how;
} suspended;
+ struct {
+ uint32_t mode;
+ uint8_t vector;
+ cpuset_t dmask;
+ } ipi;
struct vm_task_switch task_switch;
} u;
};
diff --git a/sys/amd64/vmm/amd/svm.c b/sys/amd64/vmm/amd/svm.c
--- a/sys/amd64/vmm/amd/svm.c
+++ b/sys/amd64/vmm/amd/svm.c
@@ -2315,6 +2315,7 @@
svm_setcap(void *arg, int vcpu, int type, int val)
{
struct svm_softc *sc;
+ struct vlapic *vlapic;
int error;
sc = arg;
@@ -2333,6 +2334,10 @@
if (val == 0)
error = EINVAL;
break;
+ case VM_CAP_IPI_EXIT:
+ vlapic = vm_lapic(sc->vm, vcpu);
+ vlapic->ipi_exit = val;
+ break;
default:
error = ENOENT;
break;
@@ -2344,6 +2349,7 @@
svm_getcap(void *arg, int vcpu, int type, int *retval)
{
struct svm_softc *sc;
+ struct vlapic *vlapic;
int error;
sc = arg;
@@ -2361,6 +2367,10 @@
case VM_CAP_UNRESTRICTED_GUEST:
*retval = 1; /* unrestricted guest is always enabled */
break;
+ case VM_CAP_IPI_EXIT:
+ vlapic = vm_lapic(sc->vm, vcpu);
+ *retval = vlapic->ipi_exit;
+ break;
default:
error = ENOENT;
break;
diff --git a/sys/amd64/vmm/intel/vmx.c b/sys/amd64/vmm/intel/vmx.c
--- a/sys/amd64/vmm/intel/vmx.c
+++ b/sys/amd64/vmm/intel/vmx.c
@@ -3504,6 +3504,7 @@
ret = 0;
break;
case VM_CAP_BPT_EXIT:
+ case VM_CAP_IPI_EXIT:
ret = 0;
break;
default:
@@ -3521,6 +3522,7 @@
{
struct vmx *vmx = arg;
struct vmcs *vmcs = &vmx->vmcs[vcpu];
+ struct vlapic *vlapic;
uint32_t baseval;
uint32_t *pptr;
int error;
@@ -3599,6 +3601,12 @@
reg = VMCS_EXCEPTION_BITMAP;
}
break;
+ case VM_CAP_IPI_EXIT:
+ retval = 0;
+
+ vlapic = vm_lapic(vmx->vm, vcpu);
+ vlapic->ipi_exit = val;
+ break;
default:
break;
}
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
@@ -61,7 +61,7 @@
#define PRIO(x) ((x) >> 4)
-#define VLAPIC_VERSION (16)
+#define VLAPIC_VERSION (0x14)
#define x2apic(vlapic) (((vlapic)->msr_apicbase & APICBASE_X2APIC) ? 1 : 0)
@@ -84,6 +84,7 @@
static void vlapic_set_error(struct vlapic *, uint32_t, bool);
static void vlapic_callout_handler(void *arg);
+static void vlapic_reset(struct vlapic *vlapic);
static __inline uint32_t
vlapic_get_id(struct vlapic *vlapic)
@@ -952,6 +953,31 @@
return (tpr >> 4);
}
+static void
+vlapic_handle_init(struct vm *vm, int vcpuid, void *arg)
+{
+ printf("%s @ %d\n", __func__, vcpuid);
+ struct vlapic *vlapic = vm_lapic(vm, vcpuid);
+
+ vlapic_reset(vlapic);
+ vlapic->boot_state = BS_SIPI;
+}
+
+static void
+vlapic_handle_sipi(struct vm *vm, int vcpuid, void *arg)
+{
+ printf("%s @ %d\n", __func__, vcpuid);
+ struct vlapic *vlapic = vm_lapic(vm, vcpuid);
+
+ /*
+ * Ignore SIPIs in any state other than wait-for-SIPI
+ */
+ if (vlapic->boot_state != BS_SIPI)
+ return;
+
+ vlapic->boot_state = BS_RUNNING;
+}
+
int
vlapic_icrlo_write_handler(struct vlapic *vlapic, bool *retu)
{
@@ -959,11 +985,9 @@
bool phys;
cpuset_t dmask;
uint64_t icrval;
- uint32_t dest, vec, mode;
- struct vlapic *vlapic2;
+ uint32_t dest, vec, mode, shorthand;
struct vm_exit *vmexit;
struct LAPIC *lapic;
- uint16_t maxcpus;
lapic = vlapic->apic_page;
lapic->icr_lo &= ~APIC_DELSTAT_PEND;
@@ -975,97 +999,98 @@
dest = icrval >> (32 + 24);
vec = icrval & APIC_VECTOR_MASK;
mode = icrval & APIC_DELMODE_MASK;
-
- if (mode == APIC_DELMODE_FIXED && vec < 16) {
- vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR, false);
- VLAPIC_CTR1(vlapic, "Ignoring invalid IPI %d", vec);
- return (0);
- }
+ phys = (icrval & APIC_DESTMODE_LOG) == 0;
+ shorthand = icrval & APIC_DEST_MASK;
VLAPIC_CTR2(vlapic, "icrlo 0x%016lx triggered ipi %d", icrval, vec);
- if (mode == APIC_DELMODE_FIXED || mode == APIC_DELMODE_NMI) {
- switch (icrval & APIC_DEST_MASK) {
- case APIC_DEST_DESTFLD:
- phys = ((icrval & APIC_DESTMODE_LOG) == 0);
- vlapic_calcdest(vlapic->vm, &dmask, dest, phys, false,
- x2apic(vlapic));
- break;
- case APIC_DEST_SELF:
- CPU_SETOF(vlapic->vcpuid, &dmask);
- break;
- case APIC_DEST_ALLISELF:
- dmask = vm_active_cpus(vlapic->vm);
- break;
- case APIC_DEST_ALLESELF:
- dmask = vm_active_cpus(vlapic->vm);
- CPU_CLR(vlapic->vcpuid, &dmask);
- break;
- default:
- CPU_ZERO(&dmask); /* satisfy gcc */
- break;
+ switch (shorthand) {
+ case APIC_DEST_DESTFLD:
+ vlapic_calcdest(vlapic->vm, &dmask, dest, phys, false, x2apic(vlapic));
+ break;
+ case APIC_DEST_SELF:
+ CPU_SETOF(vlapic->vcpuid, &dmask);
+ break;
+ case APIC_DEST_ALLISELF:
+ dmask = vm_active_cpus(vlapic->vm);
+ break;
+ case APIC_DEST_ALLESELF:
+ dmask = vm_active_cpus(vlapic->vm);
+ CPU_CLR(vlapic->vcpuid, &dmask);
+ break;
+ default:
+ __assert_unreachable();
+ }
+
+ switch (mode) {
+ case APIC_DELMODE_FIXED:
+ if (vec < 16) {
+ vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR,
+ false);
+ VLAPIC_CTR1(vlapic, "Ignoring invalid IPI %d", vec);
+ return (0);
}
CPU_FOREACH_ISSET(i, &dmask) {
- if (mode == APIC_DELMODE_FIXED) {
- lapic_intr_edge(vlapic->vm, i, vec);
- vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid,
- IPIS_SENT, i, 1);
- VLAPIC_CTR2(vlapic, "vlapic sending ipi %d "
- "to vcpuid %d", vec, i);
- } else {
- vm_inject_nmi(vlapic->vm, i);
- VLAPIC_CTR1(vlapic, "vlapic sending ipi nmi "
- "to vcpuid %d", i);
- }
+ lapic_intr_edge(vlapic->vm, i, vec);
+ vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid,
+ IPIS_SENT, i, 1);
+ VLAPIC_CTR2(vlapic,
+ "vlapic sending ipi %d to vcpuid %d", vec, i);
}
- return (0); /* handled completely in the kernel */
- }
+ break;
+ case APIC_DELMODE_NMI:
+ CPU_FOREACH_ISSET(i, &dmask) {
+ vm_inject_nmi(vlapic->vm, i);
+ VLAPIC_CTR1(vlapic,
+ "vlapic sending ipi nmi to vcpuid %d", i);
+ }
- maxcpus = vm_get_maxcpus(vlapic->vm);
- if (mode == APIC_DELMODE_INIT) {
+ break;
+ case APIC_DELMODE_INIT:
if ((icrval & APIC_LEVEL_MASK) == APIC_LEVEL_DEASSERT)
- return (0);
+ break;
- if (vlapic->vcpuid == 0 && dest != 0 && dest < maxcpus) {
- vlapic2 = vm_lapic(vlapic->vm, dest);
+ vm_smp_rendezvous(vlapic->vm, vlapic->vcpuid, dmask,
+ vlapic_handle_init, NULL);
- /* move from INIT to waiting-for-SIPI state */
- if (vlapic2->boot_state == BS_INIT) {
- vlapic2->boot_state = BS_SIPI;
- }
+ break;
+ case APIC_DELMODE_STARTUP:
+ vm_smp_rendezvous(vlapic->vm, vlapic->vcpuid, dmask, vlapic_handle_sipi,
+ NULL);
- return (0);
- }
+ break;
+ default:
+ return (1);
}
- if (mode == APIC_DELMODE_STARTUP) {
- if (vlapic->vcpuid == 0 && dest != 0 && dest < maxcpus) {
- vlapic2 = vm_lapic(vlapic->vm, dest);
-
- /*
- * Ignore SIPIs in any state other than wait-for-SIPI
- */
- if (vlapic2->boot_state != BS_SIPI)
- return (0);
-
- vlapic2->boot_state = BS_RUNNING;
+ /* INIT and SIPI interrupts do require userland handling */
+ if (mode == APIC_DELMODE_INIT || mode == APIC_DELMODE_STARTUP) {
+ vmexit = vm_exitinfo(vlapic->vm, vlapic->vcpuid);
+ vmexit->exitcode = VM_EXITCODE_IPI;
+ vmexit->u.ipi.mode = mode;
+ vmexit->u.ipi.vector = vec;
+ vmexit->u.ipi.dmask = dmask;
- *retu = true;
- vmexit = vm_exitinfo(vlapic->vm, vlapic->vcpuid);
- vmexit->exitcode = VM_EXITCODE_SPINUP_AP;
- vmexit->u.spinup_ap.vcpu = dest;
- vmexit->u.spinup_ap.rip = vec << PAGE_SHIFT;
+ *retu = true;
- return (0);
+ /*
+ * Old bhyve versions don't support the IPI exit. Translate it
+ * into the old style.
+ */
+ if (!vlapic->ipi_exit) {
+ if (mode == APIC_DELMODE_STARTUP) {
+ vmexit->exitcode = VM_EXITCODE_SPINUP_AP;
+ vmexit->u.spinup_ap.vcpu = CPU_FFS(&dmask) - 1;
+ vmexit->u.spinup_ap.rip = vec << PAGE_SHIFT;
+ } else {
+ *retu = false;
+ }
}
}
- /*
- * This will cause a return to userland.
- */
- return (1);
+ return (0);
}
void
@@ -1467,6 +1492,8 @@
if (vlapic->vcpuid == 0)
vlapic->msr_apicbase |= APICBASE_BSP;
+ vlapic->ipi_exit = false;
+
vlapic_reset(vlapic);
}
diff --git a/sys/amd64/vmm/io/vlapic_priv.h b/sys/amd64/vmm/io/vlapic_priv.h
--- a/sys/amd64/vmm/io/vlapic_priv.h
+++ b/sys/amd64/vmm/io/vlapic_priv.h
@@ -183,6 +183,8 @@
*/
uint32_t svr_last;
uint32_t lvt_last[VLAPIC_MAXLVT_INDEX + 1];
+
+ bool ipi_exit;
};
void vlapic_init(struct vlapic *vlapic);
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
@@ -46,6 +46,7 @@
#endif
#include <amd64/vmm/intel/vmcs.h>
+#include <x86/apicreg.h>
#include <machine/atomic.h>
#include <machine/segments.h>
@@ -939,6 +940,35 @@
return (VMEXIT_CONTINUE);
}
+static int
+vmexit_ipi(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
+{
+ int error = -1;
+ int i;
+ switch (vmexit->u.ipi.mode) {
+ case APIC_DELMODE_INIT:
+ CPU_FOREACH_ISSET (i, &vmexit->u.ipi.dmask) {
+ error = vm_suspend_cpu(ctx, i);
+ if (error) {
+ warnx("%s: failed to suspend cpu %d\n",
+ __func__, i);
+ break;
+ }
+ }
+ break;
+ case APIC_DELMODE_STARTUP:
+ CPU_FOREACH_ISSET (i, &vmexit->u.ipi.dmask) {
+ spinup_ap(ctx, i, vmexit->u.ipi.vector << PAGE_SHIFT);
+ }
+ error = 0;
+ break;
+ default:
+ break;
+ }
+
+ return (error);
+}
+
static vmexit_handler_t handler[VM_EXITCODE_MAX] = {
[VM_EXITCODE_INOUT] = vmexit_inout,
[VM_EXITCODE_INOUT_STR] = vmexit_inout,
@@ -955,6 +985,7 @@
[VM_EXITCODE_TASK_SWITCH] = vmexit_task_switch,
[VM_EXITCODE_DEBUG] = vmexit_debug,
[VM_EXITCODE_BPT] = vmexit_breakpoint,
+ [VM_EXITCODE_IPI] = vmexit_ipi,
};
static void
@@ -1155,6 +1186,9 @@
error = vm_set_capability(ctx, vcpu, VM_CAP_UNRESTRICTED_GUEST, 1);
assert(error == 0);
+ error = vm_set_capability(ctx, vcpu, VM_CAP_IPI_EXIT, 1);
+ assert(error == 0);
+
fbsdrun_addcpu(ctx, vcpu, rip, suspend);
}
diff --git a/usr.sbin/bhyve/spinup_ap.c b/usr.sbin/bhyve/spinup_ap.c
--- a/usr.sbin/bhyve/spinup_ap.c
+++ b/usr.sbin/bhyve/spinup_ap.c
@@ -98,6 +98,9 @@
error = vm_set_capability(ctx, newcpu, VM_CAP_UNRESTRICTED_GUEST, 1);
assert(error == 0);
+ error = vm_set_capability(ctx, newcpu, VM_CAP_IPI_EXIT, 1);
+ assert(error == 0);
+
spinup_ap_realmode(ctx, newcpu, &rip);
vm_resume_cpu(ctx, newcpu);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Sep 7, 3:38 PM (9 h, 18 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38474187
Default Alt Text
D35623.id111607.diff (10 KB)
Attached To
Mode
D35623: vmm: permit some IPIs to be handled by userspace
Attached
Detach File
Event Timeline
Log In to Comment