mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-27 11:55:06 +00:00
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:
parent
819c6bfc81
commit
3cbf3585cb
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=261268
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
||||
|
@ -17,7 +17,7 @@ SRCS= \
|
||||
dbgport.c \
|
||||
elcr.c \
|
||||
inout.c \
|
||||
legacy_irq.c \
|
||||
ioapic.c \
|
||||
mem.c \
|
||||
mevent.c \
|
||||
mptbl.c \
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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));
|
||||
}
|
@ -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
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user