summaryrefslogtreecommitdiffstats
path: root/sys/amd64
diff options
context:
space:
mode:
authorneel <neel@FreeBSD.org>2013-12-16 19:59:31 +0000
committerneel <neel@FreeBSD.org>2013-12-16 19:59:31 +0000
commite62c100b9074abf0c5045dbae2bfcc173275a3ef (patch)
tree8ec9e1a8b6bf2777a53d91e51e51248ba5823e74 /sys/amd64
parent2977cb4d97c105f640bb6bcf17b86d9d91295a99 (diff)
downloadFreeBSD-src-e62c100b9074abf0c5045dbae2bfcc173275a3ef.zip
FreeBSD-src-e62c100b9074abf0c5045dbae2bfcc173275a3ef.tar.gz
Add an API to deliver message signalled interrupts to vcpus. This allows
callers treat the MSI 'addr' and 'data' fields as opaque and also lets bhyve implement multiple destination modes: physical, flat and clustered. Submitted by: Tycho Nightingale (tycho.nightingale@pluribusnetworks.com) Reviewed by: grehan@
Diffstat (limited to 'sys/amd64')
-rw-r--r--sys/amd64/include/vmm_dev.h14
-rw-r--r--sys/amd64/vmm/io/ppt.c24
-rw-r--r--sys/amd64/vmm/io/ppt.h4
-rw-r--r--sys/amd64/vmm/io/vhpet.c25
-rw-r--r--sys/amd64/vmm/io/vioapic.c58
-rw-r--r--sys/amd64/vmm/io/vlapic.c224
-rw-r--r--sys/amd64/vmm/io/vlapic.h2
-rw-r--r--sys/amd64/vmm/vmm_dev.c11
-rw-r--r--sys/amd64/vmm/vmm_lapic.c47
-rw-r--r--sys/amd64/vmm/vmm_lapic.h1
10 files changed, 309 insertions, 101 deletions
diff --git a/sys/amd64/include/vmm_dev.h b/sys/amd64/include/vmm_dev.h
index 19a5b02..b71b745 100644
--- a/sys/amd64/include/vmm_dev.h
+++ b/sys/amd64/include/vmm_dev.h
@@ -66,6 +66,11 @@ struct vm_event {
int error_code_valid;
};
+struct vm_lapic_msi {
+ uint64_t msg;
+ uint64_t addr;
+};
+
struct vm_lapic_irq {
int cpuid;
int vector;
@@ -103,8 +108,8 @@ struct vm_pptdev_msi {
int slot;
int func;
int numvec; /* 0 means disabled */
- int vector;
- int destcpu;
+ uint64_t msg;
+ uint64_t addr;
};
struct vm_pptdev_msix {
@@ -113,7 +118,7 @@ struct vm_pptdev_msix {
int slot;
int func;
int idx;
- uint32_t msg;
+ uint64_t msg;
uint32_t vector_control;
uint64_t addr;
};
@@ -175,6 +180,7 @@ enum {
IOCNUM_IOAPIC_ASSERT_IRQ = 33,
IOCNUM_IOAPIC_DEASSERT_IRQ = 34,
IOCNUM_IOAPIC_PULSE_IRQ = 35,
+ IOCNUM_LAPIC_MSI = 36,
/* PCI pass-thru */
IOCNUM_BIND_PPTDEV = 40,
@@ -211,6 +217,8 @@ enum {
_IOW('v', IOCNUM_INJECT_EVENT, struct vm_event)
#define VM_LAPIC_IRQ \
_IOW('v', IOCNUM_LAPIC_IRQ, struct vm_lapic_irq)
+#define VM_LAPIC_MSI \
+ _IOW('v', IOCNUM_LAPIC_MSI, struct vm_lapic_msi)
#define VM_IOAPIC_ASSERT_IRQ \
_IOW('v', IOCNUM_IOAPIC_ASSERT_IRQ, struct vm_ioapic_irq)
#define VM_IOAPIC_DEASSERT_IRQ \
diff --git a/sys/amd64/vmm/io/ppt.c b/sys/amd64/vmm/io/ppt.c
index fce4bbd..32d59a0 100644
--- a/sys/amd64/vmm/io/ppt.c
+++ b/sys/amd64/vmm/io/ppt.c
@@ -72,8 +72,8 @@ MALLOC_DEFINE(M_PPTMSIX, "pptmsix", "Passthru MSI-X resources");
struct pptintr_arg { /* pptintr(pptintr_arg) */
struct pptdev *pptdev;
- int vec;
- int vcpu;
+ uint64_t addr;
+ uint64_t msg_data;
};
static struct pptdev {
@@ -412,16 +412,14 @@ ppt_map_mmio(struct vm *vm, int bus, int slot, int func,
static int
pptintr(void *arg)
{
- int vec;
struct pptdev *ppt;
struct pptintr_arg *pptarg;
pptarg = arg;
ppt = pptarg->pptdev;
- vec = pptarg->vec;
if (ppt->vm != NULL)
- lapic_intr_edge(ppt->vm, pptarg->vcpu, vec);
+ lapic_intr_msi(ppt->vm, pptarg->addr, pptarg->msg_data);
else {
/*
* XXX
@@ -441,15 +439,13 @@ pptintr(void *arg)
int
ppt_setup_msi(struct vm *vm, int vcpu, int bus, int slot, int func,
- int destcpu, int vector, int numvec)
+ uint64_t addr, uint64_t msg, int numvec)
{
int i, rid, flags;
int msi_count, startrid, error, tmp;
struct pptdev *ppt;
- if ((destcpu >= VM_MAXCPU || destcpu < 0) ||
- (vector < 0 || vector > 255) ||
- (numvec < 0 || numvec > MAX_MSIMSGS))
+ if (numvec < 0 || numvec > MAX_MSIMSGS)
return (EINVAL);
ppt = ppt_find(bus, slot, func);
@@ -513,8 +509,8 @@ ppt_setup_msi(struct vm *vm, int vcpu, int bus, int slot, int func,
break;
ppt->msi.arg[i].pptdev = ppt;
- ppt->msi.arg[i].vec = vector + i;
- ppt->msi.arg[i].vcpu = destcpu;
+ ppt->msi.arg[i].addr = addr;
+ ppt->msi.arg[i].msg_data = msg + i;
error = bus_setup_intr(ppt->dev, ppt->msi.res[i],
INTR_TYPE_NET | INTR_MPSAFE,
@@ -534,7 +530,7 @@ ppt_setup_msi(struct vm *vm, int vcpu, int bus, int slot, int func,
int
ppt_setup_msix(struct vm *vm, int vcpu, int bus, int slot, int func,
- int idx, uint32_t msg, uint32_t vector_control, uint64_t addr)
+ int idx, uint64_t addr, uint64_t msg, uint32_t vector_control)
{
struct pptdev *ppt;
struct pci_devinfo *dinfo;
@@ -605,8 +601,8 @@ ppt_setup_msix(struct vm *vm, int vcpu, int bus, int slot, int func,
return (ENXIO);
ppt->msix.arg[idx].pptdev = ppt;
- ppt->msix.arg[idx].vec = msg & 0xFF;
- ppt->msix.arg[idx].vcpu = (addr >> 12) & 0xFF;
+ ppt->msix.arg[idx].addr = addr;
+ ppt->msix.arg[idx].msg_data = msg;
/* Setup the MSI-X interrupt */
error = bus_setup_intr(ppt->dev, ppt->msix.res[idx],
diff --git a/sys/amd64/vmm/io/ppt.h b/sys/amd64/vmm/io/ppt.h
index 7670bc4..45ba323 100644
--- a/sys/amd64/vmm/io/ppt.h
+++ b/sys/amd64/vmm/io/ppt.h
@@ -33,9 +33,9 @@ int ppt_unassign_all(struct vm *vm);
int ppt_map_mmio(struct vm *vm, int bus, int slot, int func,
vm_paddr_t gpa, size_t len, vm_paddr_t hpa);
int ppt_setup_msi(struct vm *vm, int vcpu, int bus, int slot, int func,
- int destcpu, int vector, int numvec);
+ uint64_t addr, uint64_t msg, int numvec);
int ppt_setup_msix(struct vm *vm, int vcpu, int bus, int slot, int func,
- int idx, uint32_t msg, uint32_t vector_control, uint64_t addr);
+ int idx, uint64_t addr, uint64_t msg, uint32_t vector_control);
int ppt_num_devices(struct vm *vm);
boolean_t ppt_is_mmio(struct vm *vm, vm_paddr_t gpa);
diff --git a/sys/amd64/vmm/io/vhpet.c b/sys/amd64/vmm/io/vhpet.c
index 112480ee..929b343 100644
--- a/sys/amd64/vmm/io/vhpet.c
+++ b/sys/amd64/vmm/io/vhpet.c
@@ -240,8 +240,7 @@ vhpet_timer_edge_trig(struct vhpet *vhpet, int n)
static void
vhpet_timer_interrupt(struct vhpet *vhpet, int n)
{
- int apicid, vector, vcpuid, pin;
- cpuset_t dmask;
+ int pin;
/* If interrupts are not enabled for this timer then just return. */
if (!vhpet_timer_interrupt_enabled(vhpet, n))
@@ -256,26 +255,8 @@ vhpet_timer_interrupt(struct vhpet *vhpet, int n)
}
if (vhpet_timer_msi_enabled(vhpet, n)) {
- /*
- * XXX should have an API 'vlapic_deliver_msi(vm, addr, data)'
- * - assuming physical delivery mode
- * - no need to interpret contents of 'msireg' here
- */
- vector = vhpet->timer[n].msireg & 0xff;
- apicid = (vhpet->timer[n].msireg >> (32 + 12)) & 0xff;
- if (apicid != 0xff) {
- /* unicast */
- vcpuid = vm_apicid2vcpuid(vhpet->vm, apicid);
- lapic_intr_edge(vhpet->vm, vcpuid, vector);
- } else {
- /* broadcast */
- dmask = vm_active_cpus(vhpet->vm);
- while ((vcpuid = CPU_FFS(&dmask)) != 0) {
- vcpuid--;
- CPU_CLR(vcpuid, &dmask);
- lapic_intr_edge(vhpet->vm, vcpuid, vector);
- }
- }
+ lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32,
+ vhpet->timer[n].msireg & 0xffffffff);
return;
}
diff --git a/sys/amd64/vmm/io/vioapic.c b/sys/amd64/vmm/io/vioapic.c
index 167e8ab..151065a 100644
--- a/sys/amd64/vmm/io/vioapic.c
+++ b/sys/amd64/vmm/io/vioapic.c
@@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
#include "vmm_ktr.h"
#include "vmm_lapic.h"
+#include "vlapic.h"
#include "vioapic.h"
#define IOREGSEL 0x00
@@ -91,25 +92,14 @@ pinstate_str(bool asserted)
else
return ("deasserted");
}
-
-static const char *
-trigger_str(bool level)
-{
-
- if (level)
- return ("level");
- else
- return ("edge");
-}
#endif
static void
vioapic_send_intr(struct vioapic *vioapic, int pin)
{
- int vector, apicid, vcpuid;
- uint32_t low, high;
- cpuset_t dmask;
- bool level;
+ int vector, delmode;
+ uint32_t low, high, dest;
+ bool level, phys;
KASSERT(pin >= 0 && pin < REDIR_ENTRIES,
("vioapic_set_pinstate: invalid pin number %d", pin));
@@ -120,52 +110,20 @@ vioapic_send_intr(struct vioapic *vioapic, int pin)
low = vioapic->rtbl[pin].reg;
high = vioapic->rtbl[pin].reg >> 32;
- /*
- * XXX We only deal with:
- * - physical destination
- * - fixed delivery mode
- */
- if ((low & IOART_DESTMOD) != IOART_DESTPHY) {
- VIOAPIC_CTR2(vioapic, "ioapic pin%d: unsupported dest mode "
- "0x%08x", pin, low);
- return;
- }
-
- if ((low & IOART_DELMOD) != IOART_DELFIXED) {
- VIOAPIC_CTR2(vioapic, "ioapic pin%d: unsupported delivery mode "
- "0x%08x", pin, low);
- return;
- }
-
if ((low & IOART_INTMASK) == IOART_INTMSET) {
VIOAPIC_CTR1(vioapic, "ioapic pin%d: masked", pin);
return;
}
+ phys = ((low & IOART_DESTMOD) == IOART_DESTPHY);
+ delmode = low & IOART_DELMOD;
level = low & IOART_TRGRLVL ? true : false;
if (level)
vioapic->rtbl[pin].reg |= IOART_REM_IRR;
vector = low & IOART_INTVEC;
- apicid = high >> APIC_ID_SHIFT;
- if (apicid != 0xff) {
- /* unicast */
- vcpuid = vm_apicid2vcpuid(vioapic->vm, apicid);
- VIOAPIC_CTR4(vioapic, "ioapic pin%d: %s triggered intr "
- "vector %d on vcpuid %d", pin, trigger_str(level),
- vector, vcpuid);
- lapic_set_intr(vioapic->vm, vcpuid, vector, level);
- } else {
- /* broadcast */
- VIOAPIC_CTR3(vioapic, "ioapic pin%d: %s triggered intr "
- "vector %d on all vcpus", pin, trigger_str(level), vector);
- dmask = vm_active_cpus(vioapic->vm);
- while ((vcpuid = CPU_FFS(&dmask)) != 0) {
- vcpuid--;
- CPU_CLR(vcpuid, &dmask);
- lapic_set_intr(vioapic->vm, vcpuid, vector, level);
- }
- }
+ dest = high >> APIC_ID_SHIFT;
+ vlapic_deliver_intr(vioapic->vm, level, dest, phys, delmode, vector);
}
static void
diff --git a/sys/amd64/vmm/io/vlapic.c b/sys/amd64/vmm/io/vlapic.c
index 71bc142..4a5f503 100644
--- a/sys/amd64/vmm/io/vlapic.c
+++ b/sys/amd64/vmm/io/vlapic.c
@@ -145,6 +145,84 @@ struct vlapic {
#define VLAPIC_BUS_FREQ tsc_freq
+static __inline uint32_t
+vlapic_get_id(struct vlapic *vlapic)
+{
+
+ if (x2apic(vlapic))
+ return (vlapic->vcpuid);
+ else
+ return (vlapic->vcpuid << 24);
+}
+
+static __inline uint32_t
+vlapic_get_ldr(struct vlapic *vlapic)
+{
+ struct LAPIC *lapic;
+ int apicid;
+ uint32_t ldr;
+
+ lapic = &vlapic->apic;
+ if (x2apic(vlapic)) {
+ apicid = vlapic_get_id(vlapic);
+ ldr = 1 << (apicid & 0xf);
+ ldr |= (apicid & 0xffff0) << 12;
+ return (ldr);
+ } else
+ return (lapic->ldr);
+}
+
+static __inline uint32_t
+vlapic_get_dfr(struct vlapic *vlapic)
+{
+ struct LAPIC *lapic;
+
+ lapic = &vlapic->apic;
+ if (x2apic(vlapic))
+ return (0);
+ else
+ return (lapic->dfr);
+}
+
+static void
+vlapic_set_dfr(struct vlapic *vlapic, uint32_t data)
+{
+ uint32_t dfr;
+ struct LAPIC *lapic;
+
+ if (x2apic(vlapic)) {
+ VM_CTR1(vlapic->vm, "write to DFR in x2apic mode: %#x", data);
+ return;
+ }
+
+ lapic = &vlapic->apic;
+ dfr = (lapic->dfr & APIC_DFR_RESERVED) | (data & APIC_DFR_MODEL_MASK);
+ if ((dfr & APIC_DFR_MODEL_MASK) == APIC_DFR_MODEL_FLAT)
+ VLAPIC_CTR0(vlapic, "vlapic DFR in Flat Model");
+ else if ((dfr & APIC_DFR_MODEL_MASK) == APIC_DFR_MODEL_CLUSTER)
+ VLAPIC_CTR0(vlapic, "vlapic DFR in Cluster Model");
+ else
+ VLAPIC_CTR1(vlapic, "vlapic DFR in Unknown Model %#x", dfr);
+
+ lapic->dfr = dfr;
+}
+
+static void
+vlapic_set_ldr(struct vlapic *vlapic, uint32_t data)
+{
+ struct LAPIC *lapic;
+
+ /* LDR is read-only in x2apic mode */
+ if (x2apic(vlapic)) {
+ VLAPIC_CTR1(vlapic, "write to LDR in x2apic mode: %#x", data);
+ return;
+ }
+
+ lapic = &vlapic->apic;
+ lapic->ldr = data & ~APIC_LDR_RESERVED;
+ VLAPIC_CTR1(vlapic, "vlapic LDR set to %#x", lapic->ldr);
+}
+
static int
vlapic_timer_divisor(uint32_t dcr)
{
@@ -610,12 +688,115 @@ vlapic_set_icr_timer(struct vlapic *vlapic, uint32_t icr_timer)
VLAPIC_TIMER_UNLOCK(vlapic);
}
+/*
+ * This function populates 'dmask' with the set of vcpus that match the
+ * addressing specified by the (dest, phys, lowprio) tuple.
+ *
+ * 'x2apic_dest' specifies whether 'dest' is interpreted as x2APIC (32-bit)
+ * or xAPIC (8-bit) destination field.
+ */
+static void
+vlapic_calcdest(struct vm *vm, cpuset_t *dmask, uint32_t dest, bool phys,
+ bool lowprio, bool x2apic_dest)
+{
+ struct vlapic *vlapic;
+ uint32_t dfr, ldr, ldest, cluster;
+ uint32_t mda_flat_ldest, mda_cluster_ldest, mda_ldest, mda_cluster_id;
+ cpuset_t amask;
+ int vcpuid;
+
+ if ((x2apic_dest && dest == 0xffffffff) ||
+ (!x2apic_dest && dest == 0xff)) {
+ /*
+ * Broadcast in both logical and physical modes.
+ */
+ *dmask = vm_active_cpus(vm);
+ return;
+ }
+
+ if (phys) {
+ /*
+ * Physical mode: destination is APIC ID.
+ */
+ CPU_ZERO(dmask);
+ vcpuid = vm_apicid2vcpuid(vm, dest);
+ if (vcpuid < VM_MAXCPU)
+ CPU_SET(vcpuid, dmask);
+ } else {
+ /*
+ * In the "Flat Model" the MDA is interpreted as an 8-bit wide
+ * bitmask. This model is only avilable in the xAPIC mode.
+ */
+ mda_flat_ldest = dest & 0xff;
+
+ /*
+ * In the "Cluster Model" the MDA is used to identify a
+ * specific cluster and a set of APICs in that cluster.
+ */
+ if (x2apic_dest) {
+ mda_cluster_id = dest >> 16;
+ mda_cluster_ldest = dest & 0xffff;
+ } else {
+ mda_cluster_id = (dest >> 4) & 0xf;
+ mda_cluster_ldest = dest & 0xf;
+ }
+
+ /*
+ * Logical mode: match each APIC that has a bit set
+ * in it's LDR that matches a bit in the ldest.
+ */
+ CPU_ZERO(dmask);
+ amask = vm_active_cpus(vm);
+ while ((vcpuid = CPU_FFS(&amask)) != 0) {
+ vcpuid--;
+ CPU_CLR(vcpuid, &amask);
+
+ vlapic = vm_lapic(vm, vcpuid);
+ dfr = vlapic_get_dfr(vlapic);
+ ldr = vlapic_get_ldr(vlapic);
+
+ if ((dfr & APIC_DFR_MODEL_MASK) ==
+ APIC_DFR_MODEL_FLAT) {
+ ldest = ldr >> 24;
+ mda_ldest = mda_flat_ldest;
+ } else if ((dfr & APIC_DFR_MODEL_MASK) ==
+ APIC_DFR_MODEL_CLUSTER) {
+ if (x2apic(vlapic)) {
+ cluster = ldr >> 16;
+ ldest = ldr & 0xffff;
+ } else {
+ cluster = ldr >> 28;
+ ldest = (ldr >> 24) & 0xf;
+ }
+ if (cluster != mda_cluster_id)
+ continue;
+ mda_ldest = mda_cluster_ldest;
+ } else {
+ /*
+ * Guest has configured a bad logical
+ * model for this vcpu - skip it.
+ */
+ VLAPIC_CTR1(vlapic, "vlapic has bad logical "
+ "model %x - cannot deliver interrupt", dfr);
+ continue;
+ }
+
+ if ((mda_ldest & ldest) != 0) {
+ CPU_SET(vcpuid, dmask);
+ if (lowprio)
+ break;
+ }
+ }
+ }
+}
+
static VMM_STAT_ARRAY(IPIS_SENT, VM_MAXCPU, "ipis sent to vcpu");
static int
lapic_process_icr(struct vlapic *vlapic, uint64_t icrval, bool *retu)
{
int i;
+ bool phys;
cpuset_t dmask;
uint32_t dest, vec, mode;
struct vlapic *vlapic2;
@@ -631,7 +812,9 @@ lapic_process_icr(struct vlapic *vlapic, uint64_t icrval, bool *retu)
if (mode == APIC_DELMODE_FIXED || mode == APIC_DELMODE_NMI) {
switch (icrval & APIC_DEST_MASK) {
case APIC_DEST_DESTFLD:
- CPU_SETOF(dest, &dmask);
+ 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);
@@ -820,10 +1003,7 @@ vlapic_read(struct vlapic *vlapic, uint64_t offset, uint64_t *data, bool *retu)
switch(offset)
{
case APIC_OFFSET_ID:
- if (x2apic(vlapic))
- *data = vlapic->vcpuid;
- else
- *data = vlapic->vcpuid << 24;
+ *data = vlapic_get_id(vlapic);
break;
case APIC_OFFSET_VER:
*data = lapic->version;
@@ -841,10 +1021,10 @@ vlapic_read(struct vlapic *vlapic, uint64_t offset, uint64_t *data, bool *retu)
*data = lapic->eoi;
break;
case APIC_OFFSET_LDR:
- *data = lapic->ldr;
+ *data = vlapic_get_ldr(vlapic);
break;
case APIC_OFFSET_DFR:
- *data = lapic->dfr;
+ *data = vlapic_get_dfr(vlapic);
break;
case APIC_OFFSET_SVR:
*data = lapic->svr;
@@ -921,8 +1101,10 @@ vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu)
vlapic_process_eoi(vlapic);
break;
case APIC_OFFSET_LDR:
+ vlapic_set_ldr(vlapic, data);
break;
case APIC_OFFSET_DFR:
+ vlapic_set_dfr(vlapic, data);
break;
case APIC_OFFSET_SVR:
lapic_set_svr(vlapic, data);
@@ -1041,6 +1223,34 @@ vlapic_set_x2apic_state(struct vm *vm, int vcpuid, enum x2apic_state state)
vlapic->msr_apicbase &= ~APICBASE_X2APIC;
}
+void
+vlapic_deliver_intr(struct vm *vm, bool level, uint32_t dest, bool phys,
+ int delmode, int vec)
+{
+ bool lowprio;
+ int vcpuid;
+ cpuset_t dmask;
+
+ if (delmode != APIC_DELMODE_FIXED && delmode != APIC_DELMODE_LOWPRIO) {
+ VM_CTR1(vm, "vlapic intr invalid delmode %#x", delmode);
+ return;
+ }
+ lowprio = (delmode == APIC_DELMODE_LOWPRIO);
+
+ /*
+ * We don't provide any virtual interrupt redirection hardware so
+ * all interrupts originating from the ioapic or MSI specify the
+ * 'dest' in the legacy xAPIC format.
+ */
+ vlapic_calcdest(vm, &dmask, dest, phys, lowprio, false);
+
+ while ((vcpuid = CPU_FFS(&dmask)) != 0) {
+ vcpuid--;
+ CPU_CLR(vcpuid, &dmask);
+ lapic_set_intr(vm, vcpuid, vec, level);
+ }
+}
+
bool
vlapic_enabled(struct vlapic *vlapic)
{
diff --git a/sys/amd64/vmm/io/vlapic.h b/sys/amd64/vmm/io/vlapic.h
index d88fa71..f669940 100644
--- a/sys/amd64/vmm/io/vlapic.h
+++ b/sys/amd64/vmm/io/vlapic.h
@@ -103,4 +103,6 @@ void vlapic_set_apicbase(struct vlapic *vlapic, uint64_t val);
void vlapic_set_x2apic_state(struct vm *vm, int vcpuid, enum x2apic_state s);
bool vlapic_enabled(struct vlapic *vlapic);
+void vlapic_deliver_intr(struct vm *vm, bool level, uint32_t dest, bool phys,
+ int delmode, int vec);
#endif /* _VLAPIC_H_ */
diff --git a/sys/amd64/vmm/vmm_dev.c b/sys/amd64/vmm/vmm_dev.c
index f248f68..c3f25de 100644
--- a/sys/amd64/vmm/vmm_dev.c
+++ b/sys/amd64/vmm/vmm_dev.c
@@ -152,6 +152,7 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
struct vm_run *vmrun;
struct vm_event *vmevent;
struct vm_lapic_irq *vmirq;
+ struct vm_lapic_msi *vmmsi;
struct vm_ioapic_irq *ioapic_irq;
struct vm_capability *vmcap;
struct vm_pptdev *pptdev;
@@ -254,7 +255,7 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
pptmsi = (struct vm_pptdev_msi *)data;
error = ppt_setup_msi(sc->vm, pptmsi->vcpu,
pptmsi->bus, pptmsi->slot, pptmsi->func,
- pptmsi->destcpu, pptmsi->vector,
+ pptmsi->addr, pptmsi->msg,
pptmsi->numvec);
break;
case VM_PPTDEV_MSIX:
@@ -262,8 +263,8 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
error = ppt_setup_msix(sc->vm, pptmsix->vcpu,
pptmsix->bus, pptmsix->slot,
pptmsix->func, pptmsix->idx,
- pptmsix->msg, pptmsix->vector_control,
- pptmsix->addr);
+ pptmsix->addr, pptmsix->msg,
+ pptmsix->vector_control);
break;
case VM_MAP_PPTDEV_MMIO:
pptmmio = (struct vm_pptdev_mmio *)data;
@@ -296,6 +297,10 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
vmirq = (struct vm_lapic_irq *)data;
error = lapic_intr_edge(sc->vm, vmirq->cpuid, vmirq->vector);
break;
+ case VM_LAPIC_MSI:
+ vmmsi = (struct vm_lapic_msi *)data;
+ error = lapic_intr_msi(sc->vm, vmmsi->addr, vmmsi->msg);
+ break;
case VM_IOAPIC_ASSERT_IRQ:
ioapic_irq = (struct vm_ioapic_irq *)data;
error = vioapic_assert_irq(sc->vm, ioapic_irq->irq);
diff --git a/sys/amd64/vmm/vmm_lapic.c b/sys/amd64/vmm/vmm_lapic.c
index 13fab8c..96234a1 100644
--- a/sys/amd64/vmm/vmm_lapic.c
+++ b/sys/amd64/vmm/vmm_lapic.c
@@ -38,9 +38,18 @@ __FBSDID("$FreeBSD$");
#include <machine/vmm.h>
#include "vmm_ipi.h"
+#include "vmm_ktr.h"
#include "vmm_lapic.h"
#include "vlapic.h"
+/*
+ * Some MSI message definitions
+ */
+#define MSI_X86_ADDR_MASK 0xfff00000
+#define MSI_X86_ADDR_BASE 0xfee00000
+#define MSI_X86_ADDR_RH 0x00000008 /* Redirection Hint */
+#define MSI_X86_ADDR_LOG 0x00000004 /* Destination Mode */
+
int
lapic_pending_intr(struct vm *vm, int cpu)
{
@@ -80,6 +89,44 @@ lapic_set_intr(struct vm *vm, int cpu, int vector, bool level)
return (0);
}
+int
+lapic_intr_msi(struct vm *vm, uint64_t addr, uint64_t msg)
+{
+ int delmode, vec;
+ uint32_t dest;
+ bool phys;
+
+ VM_CTR2(vm, "lapic MSI addr: %#lx msg: %#lx", addr, msg);
+
+ if ((addr & MSI_X86_ADDR_MASK) != MSI_X86_ADDR_BASE) {
+ VM_CTR1(vm, "lapic MSI invalid addr %#lx", addr);
+ return (-1);
+ }
+
+ /*
+ * Extract the x86-specific fields from the MSI addr/msg
+ * params according to the Intel Arch spec, Vol3 Ch 10.
+ *
+ * The PCI specification does not support level triggered
+ * MSI/MSI-X so ignore trigger level in 'msg'.
+ *
+ * The 'dest' is interpreted as a logical APIC ID if both
+ * the Redirection Hint and Destination Mode are '1' and
+ * physical otherwise.
+ */
+ dest = (addr >> 12) & 0xff;
+ phys = ((addr & (MSI_X86_ADDR_RH | MSI_X86_ADDR_LOG)) !=
+ (MSI_X86_ADDR_RH | MSI_X86_ADDR_LOG));
+ delmode = msg & APIC_DELMODE_MASK;
+ vec = msg & 0xff;
+
+ VM_CTR3(vm, "lapic MSI %s dest %#x, vec %d",
+ phys ? "physical" : "logical", dest, vec);
+
+ vlapic_deliver_intr(vm, LAPIC_TRIG_EDGE, dest, phys, delmode, vec);
+ return (0);
+}
+
static boolean_t
x2apic_msr(u_int msr)
{
diff --git a/sys/amd64/vmm/vmm_lapic.h b/sys/amd64/vmm/vmm_lapic.h
index 09b7f57..d7e75a9 100644
--- a/sys/amd64/vmm/vmm_lapic.h
+++ b/sys/amd64/vmm/vmm_lapic.h
@@ -84,4 +84,5 @@ lapic_intr_edge(struct vm *vm, int cpu, int vector)
return (lapic_set_intr(vm, cpu, vector, LAPIC_TRIG_EDGE));
}
+int lapic_intr_msi(struct vm *vm, uint64_t addr, uint64_t msg);
#endif
OpenPOWER on IntegriCloud