Enhance the support for PCI legacy INTx interrupts and enable them in

the virtio backends.
- Add a new ioctl to export the count of pins on the I/O APIC from vmm
  to the hypervisor.
- Use pins on the I/O APIC >= 16 for PCI interrupts leaving 0-15 for
  ISA interrupts.
- Populate the MP Table with I/O interrupt entries for any PCI INTx
  interrupts.
- Create a _PRT table under the PCI root bridge in ACPI to route any
  PCI INTx interrupts appropriately.
- Track which INTx interrupts are in use per-slot so that functions
  that share a slot attempt to distribute their INTx interrupts across
  the four available pins.
- Implicitly mask INTx interrupts if either MSI or MSI-X is enabled
  and when the INTx DIS bit is set in a function's PCI command register.
  Either assert or deassert the associated I/O APIC pin when the
  state of one of those conditions changes.
- Add INTx support to the virtio backends.
- Always advertise the MSI capability in the virtio backends.

Submitted by:	neel (7)
Reviewed by:	neel
MFC after:	2 weeks
This commit is contained in:
John Baldwin 2014-01-29 14:56:48 +00:00
parent 819c6bfc81
commit 3cbf3585cb
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=261268
16 changed files with 339 additions and 122 deletions

View File

@ -453,6 +453,13 @@ vm_ioapic_pulse_irq(struct vmctx *ctx, int irq)
return (ioctl(ctx->fd, VM_IOAPIC_PULSE_IRQ, &ioapic_irq));
}
int
vm_ioapic_pincount(struct vmctx *ctx, int *pincount)
{
return (ioctl(ctx->fd, VM_IOAPIC_PINCOUNT, pincount));
}
int
vm_inject_nmi(struct vmctx *ctx, int vcpu)
{

View File

@ -72,6 +72,7 @@ int vm_lapic_msi(struct vmctx *ctx, uint64_t addr, uint64_t msg);
int vm_ioapic_assert_irq(struct vmctx *ctx, int irq);
int vm_ioapic_deassert_irq(struct vmctx *ctx, int irq);
int vm_ioapic_pulse_irq(struct vmctx *ctx, int irq);
int vm_ioapic_pincount(struct vmctx *ctx, int *pincount);
int vm_inject_nmi(struct vmctx *ctx, int vcpu);
int vm_capability_name2type(const char *capname);
const char *vm_capability_type2name(int type);

View File

@ -181,7 +181,8 @@ enum {
IOCNUM_IOAPIC_DEASSERT_IRQ = 34,
IOCNUM_IOAPIC_PULSE_IRQ = 35,
IOCNUM_LAPIC_MSI = 36,
IOCNUM_LAPIC_LOCAL_IRQ = 37,
IOCNUM_LAPIC_LOCAL_IRQ = 37,
IOCNUM_IOAPIC_PINCOUNT = 38,
/* PCI pass-thru */
IOCNUM_BIND_PPTDEV = 40,
@ -228,6 +229,8 @@ enum {
_IOW('v', IOCNUM_IOAPIC_DEASSERT_IRQ, struct vm_ioapic_irq)
#define VM_IOAPIC_PULSE_IRQ \
_IOW('v', IOCNUM_IOAPIC_PULSE_IRQ, struct vm_ioapic_irq)
#define VM_IOAPIC_PINCOUNT \
_IOR('v', IOCNUM_IOAPIC_PINCOUNT, int)
#define VM_SET_CAPABILITY \
_IOW('v', IOCNUM_SET_CAPABILITY, struct vm_capability)
#define VM_GET_CAPABILITY \

View File

@ -318,6 +318,9 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
ioapic_irq = (struct vm_ioapic_irq *)data;
error = vioapic_pulse_irq(sc->vm, ioapic_irq->irq);
break;
case VM_IOAPIC_PINCOUNT:
*(int *)data = vioapic_pincount(sc->vm);
break;
case VM_MAP_MEMORY:
seg = (struct vm_memory_segment *)data;
error = vm_malloc(sc->vm, seg->gpa, seg->len);

View File

@ -17,7 +17,7 @@ SRCS= \
dbgport.c \
elcr.c \
inout.c \
legacy_irq.c \
ioapic.c \
mem.c \
mevent.c \
mptbl.c \

View File

@ -55,7 +55,7 @@ __FBSDID("$FreeBSD$");
#include "acpi.h"
#include "inout.h"
#include "dbgport.h"
#include "legacy_irq.h"
#include "ioapic.h"
#include "mem.h"
#include "mevent.h"
#include "mptbl.h"
@ -695,7 +695,7 @@ main(int argc, char *argv[])
init_mem();
init_inout();
legacy_irq_init();
ioapic_init(ctx);
rtc_init(ctx);

View File

@ -1,5 +1,6 @@
/*-
* Copyright (c) 2013 Neel Natu <neel@freebsd.org>
* Copyright (c) 2014 Advanced Computing Technologies LLC
* Written by: John H. Baldwin <jhb@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -11,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -22,59 +23,52 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <stdbool.h>
#include <assert.h>
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <machine/vmm.h>
#include <vmmapi.h>
#include "ioapic.h"
/*
* Used to keep track of legacy interrupt owners/requestors
* Assign PCI INTx interrupts to I/O APIC pins in a round-robin
* fashion. Note that we have no idea what the HPET is using, but the
* HPET is also programmable whereas this is intended for hardwired
* PCI interrupts.
*
* This assumes a single I/O APIC where pins >= 16 are permitted for
* PCI devices.
*/
#define NLIRQ 16
static struct lirqinfo {
bool li_generic;
bool li_allocated;
} lirq[NLIRQ];
static int pci_pins;
void
legacy_irq_init(void)
ioapic_init(struct vmctx *ctx)
{
/*
* Allow ISA IRQs 5,10,11,12, and 15 to be available for generic use.
*/
lirq[5].li_generic = true;
lirq[10].li_generic = true;
lirq[11].li_generic = true;
lirq[12].li_generic = true;
lirq[15].li_generic = true;
if (vm_ioapic_pincount(ctx, &pci_pins) < 0) {
pci_pins = 0;
return;
}
/* Ignore the first 16 pins. */
if (pci_pins <= 16) {
pci_pins = 0;
return;
}
pci_pins -= 16;
}
int
legacy_irq_alloc(int irq)
ioapic_pci_alloc_irq(void)
{
int i;
static int last_pin;
assert(irq < NLIRQ);
if (irq < 0) {
for (i = 0; i < NLIRQ; i++) {
if (lirq[i].li_generic && !lirq[i].li_allocated) {
irq = i;
break;
}
}
} else {
if (lirq[irq].li_allocated)
irq = -1;
}
if (irq >= 0) {
lirq[irq].li_allocated = true;
return (irq);
} else
if (pci_pins == 0)
return (-1);
return (16 + (last_pin++ % pci_pins));
}

View File

@ -1,5 +1,6 @@
/*-
* Copyright (c) 2013 Neel Natu <neel@freebsd.org>
* Copyright (c) 2014 Advanced Computing Technologies LLC
* Written by: John H. Baldwin <jhb@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -11,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -26,16 +27,13 @@
* $FreeBSD$
*/
#ifndef _LEGACY_IRQ_H_
#define _LEGACY_IRQ_H_
#ifndef _IOAPIC_H_
#define _IOAPIC_H_
/*
* Allocate a legacy irq. The argument 'irq' can be set to -1 to allocate any
* available irq.
*
* Returns -1 on failure or the allocated irq number on success.
* Allocate a PCI IRQ from the I/O APIC.
*/
int legacy_irq_alloc(int irq);
void legacy_irq_init(void);
void ioapic_init(struct vmctx *ctx);
int ioapic_pci_alloc_irq(void);
#endif

View File

@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$");
#include "acpi.h"
#include "bhyverun.h"
#include "mptbl.h"
#include "pci_emul.h"
#define MPTABLE_BASE 0xF0000
@ -75,9 +76,6 @@ __FBSDID("$FreeBSD$");
/* Number of local intr entries */
#define MPEII_NUM_LOCAL_IRQ 2
/* Number of i/o intr entries */
#define MPEII_MAX_IRQ 24
/* Bus entry defines */
#define MPE_NUM_BUSES 2
#define MPE_BUSNAME_LEN 6
@ -195,8 +193,42 @@ mpt_build_ioapic_entries(io_apic_entry_ptr mpei, int id)
mpei->apic_address = IOAPIC_PADDR;
}
static int
mpt_count_ioint_entries(void)
{
/*
* Always include entries for the first 16 pins along with a entry
* for each active PCI INTx pin.
*/
return (16 + pci_count_lintr());
}
static void
mpt_build_ioint_entries(int_entry_ptr mpie, int num_pins, int id)
mpt_generate_pci_int(int slot, int pin, int ioapic_irq, void *arg)
{
int_entry_ptr *mpiep, mpie;
mpiep = arg;
mpie = *mpiep;
memset(mpie, 0, sizeof(*mpie));
/*
* This is always after another I/O interrupt entry, so cheat
* and fetch the I/O APIC ID from the prior entry.
*/
mpie->type = MPCT_ENTRY_INT;
mpie->int_type = INTENTRY_TYPE_INT;
mpie->src_bus_id = 0;
mpie->src_bus_irq = slot << 2 | (pin - 1);
mpie->dst_apic_id = mpie[-1].dst_apic_id;
mpie->dst_apic_int = ioapic_irq;
*mpiep = mpie + 1;
}
static void
mpt_build_ioint_entries(int_entry_ptr mpie, int id)
{
int pin;
@ -206,8 +238,8 @@ mpt_build_ioint_entries(int_entry_ptr mpie, int num_pins, int id)
* just use the default config, tweek later if needed.
*/
/* Run through all 16 pins. */
for (pin = 0; pin < num_pins; pin++) {
/* First, generate the first 16 pins. */
for (pin = 0; pin < 16; pin++) {
memset(mpie, 0, sizeof(*mpie));
mpie->type = MPCT_ENTRY_INT;
mpie->src_bus_id = 1;
@ -235,16 +267,6 @@ mpt_build_ioint_entries(int_entry_ptr mpie, int num_pins, int id)
mpie->int_type = INTENTRY_TYPE_INT;
mpie->src_bus_irq = SCI_INT;
break;
case 5:
case 10:
case 11:
/*
* PCI Irqs set to level triggered and active-lo.
*/
mpie->int_flags = INTENTRY_FLAGS_POLARITY_ACTIVELO |
INTENTRY_FLAGS_TRIGGER_LEVEL;
mpie->src_bus_id = 0;
/* fall through.. */
default:
/* All other pins are identity mapped. */
mpie->int_type = INTENTRY_TYPE_INT;
@ -254,6 +276,8 @@ mpt_build_ioint_entries(int_entry_ptr mpie, int num_pins, int id)
mpie++;
}
/* Next, generate entries for any PCI INTx interrupts. */
pci_walk_lintr(mpt_generate_pci_int, &mpie);
}
void
@ -273,6 +297,7 @@ mptable_build(struct vmctx *ctx, int ncpu)
proc_entry_ptr mpep;
mpfps_t mpfp;
int_entry_ptr mpie;
int ioints;
char *curraddr;
char *startaddr;
@ -307,9 +332,10 @@ mptable_build(struct vmctx *ctx, int ncpu)
mpch->entry_count++;
mpie = (int_entry_ptr) curraddr;
mpt_build_ioint_entries(mpie, MPEII_MAX_IRQ, 0);
curraddr += sizeof(*mpie) * MPEII_MAX_IRQ;
mpch->entry_count += MPEII_MAX_IRQ;
ioints = mpt_count_ioint_entries();
mpt_build_ioint_entries(mpie, 0);
curraddr += sizeof(*mpie) * ioints;
mpch->entry_count += ioints;
mpie = (int_entry_ptr)curraddr;
mpt_build_localint_entries(mpie);

View File

@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$");
#include <sys/errno.h>
#include <ctype.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -47,7 +48,7 @@ __FBSDID("$FreeBSD$");
#include "acpi.h"
#include "bhyverun.h"
#include "inout.h"
#include "legacy_irq.h"
#include "ioapic.h"
#include "mem.h"
#include "pci_emul.h"
#include "pci_lpc.h"
@ -71,11 +72,21 @@ do { \
#define MAXSLOTS (PCI_SLOTMAX + 1)
#define MAXFUNCS (PCI_FUNCMAX + 1)
static struct slotinfo {
char *si_name;
char *si_param;
struct pci_devinst *si_devi;
} pci_slotinfo[MAXSLOTS][MAXFUNCS];
struct funcinfo {
char *fi_name;
char *fi_param;
struct pci_devinst *fi_devi;
};
struct intxinfo {
int ii_count;
int ii_ioapic_irq;
};
struct slotinfo {
struct intxinfo si_intpins[4];
struct funcinfo si_funcs[MAXFUNCS];
} pci_slotinfo[MAXSLOTS];
SET_DECLARE(pci_devemu_set, struct pci_devemu);
@ -92,6 +103,7 @@ static uint64_t pci_emul_membase64;
#define PCI_EMUL_MEMLIMIT64 0xFD00000000UL
static struct pci_devemu *pci_emul_finddev(char *name);
static void pci_lintr_update(struct pci_devinst *pi);
static int pci_emul_devices;
static struct mem_range pci_mem_hole;
@ -154,7 +166,7 @@ pci_parse_slot(char *opt)
goto done;
}
if (pci_slotinfo[snum][fnum].si_name != NULL) {
if (pci_slotinfo[snum].si_funcs[fnum].fi_name != NULL) {
fprintf(stderr, "pci slot %d:%d already occupied!\n",
snum, fnum);
goto done;
@ -167,8 +179,8 @@ pci_parse_slot(char *opt)
}
error = 0;
pci_slotinfo[snum][fnum].si_name = emul;
pci_slotinfo[snum][fnum].si_param = config;
pci_slotinfo[snum].si_funcs[fnum].fi_name = emul;
pci_slotinfo[snum].si_funcs[fnum].fi_param = config;
done:
if (error)
@ -666,7 +678,10 @@ pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, int func,
pdi->pi_bus = 0;
pdi->pi_slot = slot;
pdi->pi_func = func;
pdi->pi_lintr_pin = -1;
pthread_mutex_init(&pdi->pi_lintr.lock, NULL);
pdi->pi_lintr.pin = 0;
pdi->pi_lintr.state = IDLE;
pdi->pi_lintr.ioapic_irq = 0;
pdi->pi_d = pde;
snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot);
@ -682,7 +697,7 @@ pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, int func,
free(pdi);
} else {
pci_emul_devices++;
pci_slotinfo[slot][func].si_devi = pdi;
pci_slotinfo[slot].si_funcs[func].fi_devi = pdi;
}
return (err);
@ -816,6 +831,7 @@ msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE;
pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK;
pci_lintr_update(pi);
}
CFGWRITE(pi, offset, val, bytes);
@ -854,6 +870,7 @@ msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
} else {
pi->pi_msi.maxmsgnum = 0;
}
pci_lintr_update(pi);
}
CFGWRITE(pi, offset, val, bytes);
@ -993,7 +1010,7 @@ int
init_pci(struct vmctx *ctx)
{
struct pci_devemu *pde;
struct slotinfo *si;
struct funcinfo *fi;
size_t lowmem;
int slot, func;
int error;
@ -1004,12 +1021,12 @@ init_pci(struct vmctx *ctx)
for (slot = 0; slot < MAXSLOTS; slot++) {
for (func = 0; func < MAXFUNCS; func++) {
si = &pci_slotinfo[slot][func];
if (si->si_name != NULL) {
pde = pci_emul_finddev(si->si_name);
fi = &pci_slotinfo[slot].si_funcs[func];
if (fi->fi_name != NULL) {
pde = pci_emul_finddev(fi->fi_name);
assert(pde != NULL);
error = pci_emul_init(ctx, pde, slot, func,
si->si_param);
fi->fi_param);
if (error)
return (error);
}
@ -1042,11 +1059,27 @@ init_pci(struct vmctx *ctx)
return (0);
}
static void
pci_prt_entry(int slot, int pin, int ioapic_irq, void *arg)
{
int *count;
count = arg;
dsdt_line(" Package (0x04)");
dsdt_line(" {");
dsdt_line(" 0x%X,", slot << 16 | 0xffff);
dsdt_line(" 0x%02X,", pin - 1);
dsdt_line(" Zero,");
dsdt_line(" 0x%X", ioapic_irq);
dsdt_line(" }%s", *count == 1 ? "" : ",");
(*count)--;
}
void
pci_write_dsdt(void)
{
struct pci_devinst *pi;
int slot, func;
int count, slot, func;
dsdt_indent(1);
dsdt_line("Scope (_SB)");
@ -1107,11 +1140,20 @@ pci_write_dsdt(void)
PCI_EMUL_MEMLIMIT64 - PCI_EMUL_MEMBASE64);
dsdt_line(" ,, , AddressRangeMemory, TypeStatic)");
dsdt_line(" })");
count = pci_count_lintr();
if (count != 0) {
dsdt_indent(2);
dsdt_line("Name (_PRT, Package (0x%02X)", count);
dsdt_line("{");
pci_walk_lintr(pci_prt_entry, &count);
dsdt_line("})");
dsdt_unindent(2);
}
dsdt_indent(2);
for (slot = 0; slot < MAXSLOTS; slot++) {
for (func = 0; func < MAXFUNCS; func++) {
pi = pci_slotinfo[slot][func].si_devi;
pi = pci_slotinfo[slot].si_funcs[func].fi_devi;
if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL)
pi->pi_d->pe_write_dsdt(pi);
}
@ -1176,18 +1218,54 @@ pci_generate_msi(struct pci_devinst *pi, int index)
}
}
int
pci_lintr_request(struct pci_devinst *pi, int req)
static bool
pci_lintr_permitted(struct pci_devinst *pi)
{
int irq;
uint16_t cmd;
irq = legacy_irq_alloc(req);
if (irq < 0)
return (-1);
cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);
return (!(pi->pi_msi.enabled || pi->pi_msix.enabled ||
(cmd & PCIM_CMD_INTxDIS)));
}
pi->pi_lintr_pin = irq;
int
pci_lintr_request(struct pci_devinst *pi)
{
struct slotinfo *si;
int bestpin, bestcount, irq, pin;
/*
* First, allocate a pin from our slot.
*/
si = &pci_slotinfo[pi->pi_slot];
bestpin = 0;
bestcount = si->si_intpins[0].ii_count;
for (pin = 1; pin < 4; pin++) {
if (si->si_intpins[pin].ii_count < bestcount) {
bestpin = pin;
bestcount = si->si_intpins[pin].ii_count;
}
}
/*
* Attempt to allocate an I/O APIC pin for this intpin. If
* 8259A support is added we will need a separate field to
* assign the intpin to an input pin on the PCI interrupt
* router.
*/
if (si->si_intpins[bestpin].ii_count == 0) {
irq = ioapic_pci_alloc_irq();
if (irq < 0)
return (-1);
si->si_intpins[bestpin].ii_ioapic_irq = irq;
} else
irq = si->si_intpins[bestpin].ii_ioapic_irq;
si->si_intpins[bestpin].ii_count++;
pi->pi_lintr.pin = bestpin + 1;
pi->pi_lintr.ioapic_irq = irq;
pci_set_cfgdata8(pi, PCIR_INTLINE, irq);
pci_set_cfgdata8(pi, PCIR_INTPIN, 1);
pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1);
return (0);
}
@ -1195,23 +1273,77 @@ void
pci_lintr_assert(struct pci_devinst *pi)
{
assert(pi->pi_lintr_pin >= 0);
assert(pi->pi_lintr.pin > 0);
if (pi->pi_lintr_state == 0) {
pi->pi_lintr_state = 1;
vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr_pin);
pthread_mutex_lock(&pi->pi_lintr.lock);
if (pi->pi_lintr.state == IDLE) {
if (pci_lintr_permitted(pi)) {
pi->pi_lintr.state = ASSERTED;
vm_ioapic_assert_irq(pi->pi_vmctx,
pi->pi_lintr.ioapic_irq);
} else
pi->pi_lintr.state = PENDING;
}
pthread_mutex_unlock(&pi->pi_lintr.lock);
}
void
pci_lintr_deassert(struct pci_devinst *pi)
{
assert(pi->pi_lintr_pin >= 0);
assert(pi->pi_lintr.pin > 0);
if (pi->pi_lintr_state == 1) {
pi->pi_lintr_state = 0;
vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr_pin);
pthread_mutex_lock(&pi->pi_lintr.lock);
if (pi->pi_lintr.state == ASSERTED) {
pi->pi_lintr.state = IDLE;
vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
} else if (pi->pi_lintr.state == PENDING)
pi->pi_lintr.state = IDLE;
pthread_mutex_unlock(&pi->pi_lintr.lock);
}
static void
pci_lintr_update(struct pci_devinst *pi)
{
pthread_mutex_lock(&pi->pi_lintr.lock);
if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) {
vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
pi->pi_lintr.state = PENDING;
} else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) {
pi->pi_lintr.state = ASSERTED;
vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
}
pthread_mutex_unlock(&pi->pi_lintr.lock);
}
int
pci_count_lintr(void)
{
int count, slot, pin;
count = 0;
for (slot = 0; slot < MAXSLOTS; slot++) {
for (pin = 0; pin < 4; pin++) {
if (pci_slotinfo[slot].si_intpins[pin].ii_count != 0)
count++;
}
}
return (count);
}
void
pci_walk_lintr(pci_lintr_cb cb, void *arg)
{
struct intxinfo *ii;
int slot, pin;
for (slot = 0; slot < MAXSLOTS; slot++) {
for (pin = 0; pin < 4; pin++) {
ii = &pci_slotinfo[slot].si_intpins[pin];
if (ii->ii_count != 0)
cb(slot, pin + 1, ii->ii_ioapic_irq, arg);
}
}
}
@ -1226,7 +1358,7 @@ pci_emul_is_mfdev(int slot)
numfuncs = 0;
for (f = 0; f < MAXFUNCS; f++) {
if (pci_slotinfo[slot][f].si_devi != NULL) {
if (pci_slotinfo[slot].si_funcs[f].fi_devi != NULL) {
numfuncs++;
}
}
@ -1348,6 +1480,12 @@ pci_emul_cmdwrite(struct pci_devinst *pi, uint32_t new, int bytes)
assert(0);
}
}
/*
* If INTx has been unmasked and is pending, assert the
* interrupt.
*/
pci_lintr_update(pi);
}
static int
@ -1362,7 +1500,7 @@ pci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
assert(bytes == 1 || bytes == 2 || bytes == 4);
if (cfgbus == 0)
pi = pci_slotinfo[cfgslot][cfgfunc].si_devi;
pi = pci_slotinfo[cfgslot].si_funcs[cfgfunc].fi_devi;
else
pi = NULL;

View File

@ -32,6 +32,7 @@
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/kernel.h>
#include <sys/_pthreadtypes.h>
#include <dev/pci/pcireg.h>
@ -102,15 +103,26 @@ struct msix_table_entry {
#define MAX_MSIX_TABLE_ENTRIES 2048
#define PBA_TABLE_ENTRY_SIZE 8
enum lintr_stat {
IDLE,
ASSERTED,
PENDING
};
struct pci_devinst {
struct pci_devemu *pi_d;
struct vmctx *pi_vmctx;
uint8_t pi_bus, pi_slot, pi_func;
int8_t pi_lintr_pin;
int8_t pi_lintr_state;
char pi_name[PI_NAMESZ];
int pi_bar_getsize;
struct {
int8_t pin;
enum lintr_stat state;
int ioapic_irq;
pthread_mutex_t lock;
} pi_lintr;
struct {
int enabled;
uint64_t addr;
@ -187,6 +199,8 @@ struct pciecap {
uint16_t slot_status2;
} __packed;
typedef void (*pci_lintr_cb)(int slot, int pin, int ioapic_irq, void *arg);
int init_pci(struct vmctx *ctx);
void msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
int bytes, uint32_t val);
@ -203,7 +217,7 @@ void pci_generate_msi(struct pci_devinst *pi, int msgnum);
void pci_generate_msix(struct pci_devinst *pi, int msgnum);
void pci_lintr_assert(struct pci_devinst *pi);
void pci_lintr_deassert(struct pci_devinst *pi);
int pci_lintr_request(struct pci_devinst *pi, int ivec);
int pci_lintr_request(struct pci_devinst *pi);
int pci_msi_enabled(struct pci_devinst *pi);
int pci_msix_enabled(struct pci_devinst *pi);
int pci_msix_table_bar(struct pci_devinst *pi);
@ -215,6 +229,8 @@ int pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum);
int pci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size,
uint64_t value);
uint64_t pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size);
int pci_count_lintr(void);
void pci_walk_lintr(pci_lintr_cb cb, void *arg);
void pci_write_dsdt(void);
static __inline void

View File

@ -91,7 +91,7 @@ pci_uart_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
struct uart_softc *sc;
pci_emul_alloc_bar(pi, 0, PCIBAR_IO, UART_IO_BAR_SIZE);
pci_lintr_request(pi, -1);
pci_lintr_request(pi);
/* initialize config space */
pci_set_cfgdata16(pi, PCIR_DEVICE, COM_DEV);

View File

@ -117,6 +117,7 @@ static int pci_vtblk_debug;
*/
struct pci_vtblk_softc {
struct virtio_softc vbsc_vs;
pthread_mutex_t vsc_mtx;
struct vqueue_info vbsc_vq;
int vbsc_fd;
struct vtblk_config vbsc_cfg;
@ -304,8 +305,12 @@ pci_vtblk_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
/* record fd of storage device/file */
sc->vbsc_fd = fd;
pthread_mutex_init(&sc->vsc_mtx, NULL);
/* init virtio softc and virtqueues */
vi_softc_linkup(&sc->vbsc_vs, &vtblk_vi_consts, sc, pi, &sc->vbsc_vq);
sc->vbsc_vs.vs_mtx = &sc->vsc_mtx;
sc->vbsc_vq.vq_qsize = VTBLK_RINGSZ;
/* sc->vbsc_vq.vq_notify = we have no per-queue notify */
@ -339,6 +344,8 @@ pci_vtblk_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE);
pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_BLOCK);
pci_lintr_request(pi);
if (vi_intr_init(&sc->vbsc_vs, 1, fbsdrun_virtio_msix()))
return (1);
vi_set_io_bar(&sc->vbsc_vs, 0);

View File

@ -519,6 +519,8 @@ pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
pthread_mutex_init(&sc->vsc_mtx, NULL);
vi_softc_linkup(&sc->vsc_vs, &vtnet_vi_consts, sc, pi, sc->vsc_queues);
sc->vsc_vs.vs_mtx = &sc->vsc_mtx;
sc->vsc_queues[VTNET_RXQ].vq_qsize = VTNET_RINGSZ;
sc->vsc_queues[VTNET_RXQ].vq_notify = pci_vtnet_ping_rxq;
sc->vsc_queues[VTNET_TXQ].vq_qsize = VTNET_RINGSZ;
@ -608,6 +610,8 @@ pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_NETWORK);
pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_NET);
pci_lintr_request(pi);
/* link always up */
sc->vsc_config.status = 1;

View File

@ -99,7 +99,11 @@ vi_reset_dev(struct virtio_softc *vs)
vs->vs_negotiated_caps = 0;
vs->vs_curq = 0;
/* vs->vs_status = 0; -- redundant */
VS_LOCK(vs);
if (vs->vs_isr)
pci_lintr_deassert(vs->vs_pi);
vs->vs_isr = 0;
VS_UNLOCK(vs);
vs->vs_msix_cfg_idx = VIRTIO_MSI_NO_VECTOR;
}
@ -137,11 +141,10 @@ vi_intr_init(struct virtio_softc *vs, int barnum, int use_msix)
nvec = vs->vs_vc->vc_nvq + 1;
if (pci_emul_add_msixcap(vs->vs_pi, nvec, barnum))
return (1);
} else {
} else
vs->vs_flags &= ~VIRTIO_USE_MSIX;
/* Only 1 MSI vector for bhyve */
pci_emul_add_msicap(vs->vs_pi, 1);
}
/* Only 1 MSI vector for bhyve */
pci_emul_add_msicap(vs->vs_pi, 1);
return (0);
}
@ -591,6 +594,8 @@ vi_pci_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
case VTCFG_R_ISR:
value = vs->vs_isr;
vs->vs_isr = 0; /* a read clears this flag */
if (value)
pci_lintr_deassert(pi);
break;
case VTCFG_R_CFGVEC:
value = vs->vs_msix_cfg_idx;

View File

@ -328,6 +328,18 @@ struct virtio_softc {
uint16_t vs_msix_cfg_idx; /* MSI-X vector for config event */
};
#define VS_LOCK(vs) \
do { \
if (vs->vs_mtx) \
pthread_mutex_lock(vs->vs_mtx); \
} while (0)
#define VS_UNLOCK(vs) \
do { \
if (vs->vs_mtx) \
pthread_mutex_unlock(vs->vs_mtx); \
} while (0)
struct virtio_consts {
const char *vc_name; /* name of driver (for diagnostics) */
int vc_nvq; /* number of virtual queues */
@ -431,11 +443,14 @@ static inline void
vq_interrupt(struct virtio_softc *vs, struct vqueue_info *vq)
{
if (vs->vs_flags & VIRTIO_USE_MSIX)
if (pci_msix_enabled(vs->vs_pi))
pci_generate_msix(vs->vs_pi, vq->vq_msix_idx);
else {
VS_LOCK(vs);
vs->vs_isr |= VTCFG_ISR_QUEUES;
pci_generate_msi(vs->vs_pi, 0);
pci_lintr_assert(vs->vs_pi);
VS_UNLOCK(vs);
}
}