Implement a PCI interrupt router to route PCI legacy INTx interrupts to
the legacy 8259A PICs. - Implement an ICH-comptabile PCI interrupt router on the lpc device with 8 steerable pins configured via config space access to byte-wide registers at 0x60-63 and 0x68-6b. - For each configured PCI INTx interrupt, route it to both an I/O APIC pin and a PCI interrupt router pin. When a PCI INTx interrupt is asserted, ensure that both pins are asserted. - Provide an initial routing of PCI interrupt router (PIRQ) pins to 8259A pins (ISA IRQs) and initialize the interrupt line config register for the corresponding PCI function with the ISA IRQ as this matches existing hardware. - Add a global _PIC method for OSPM to select the desired interrupt routing configuration. - Update the _PRT methods for PCI bridges to provide both APIC and legacy PRT tables and return the appropriate table based on the configured routing configuration. Note that if the lpc device is not configured, no routing information is provided. - When the lpc device is enabled, provide ACPI PCI link devices corresponding to each PIRQ pin. - Add a VMM ioctl to adjust the trigger mode (edge vs level) for 8259A pins via the ELCR. - Mark the power management SCI as level triggered. - Don't hardcode the number of elements in Packages in the source for the DSDT. iasl(8) will fill in the actual number of elements, and this makes it simpler to generate a Package with a variable number of elements. Reviewed by: tycho
This commit is contained in:
parent
4f3ddacf9b
commit
b3e9732a76
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=266125
@ -507,6 +507,7 @@ int
|
||||
vm_isa_pulse_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq)
|
||||
{
|
||||
struct vm_isa_irq isa_irq;
|
||||
|
||||
bzero(&isa_irq, sizeof(struct vm_isa_irq));
|
||||
isa_irq.atpic_irq = atpic_irq;
|
||||
isa_irq.ioapic_irq = ioapic_irq;
|
||||
@ -514,6 +515,19 @@ vm_isa_pulse_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq)
|
||||
return (ioctl(ctx->fd, VM_ISA_PULSE_IRQ, &isa_irq));
|
||||
}
|
||||
|
||||
int
|
||||
vm_isa_set_irq_trigger(struct vmctx *ctx, int atpic_irq,
|
||||
enum vm_intr_trigger trigger)
|
||||
{
|
||||
struct vm_isa_irq_trigger isa_irq_trigger;
|
||||
|
||||
bzero(&isa_irq_trigger, sizeof(struct vm_isa_irq_trigger));
|
||||
isa_irq_trigger.atpic_irq = atpic_irq;
|
||||
isa_irq_trigger.trigger = trigger;
|
||||
|
||||
return (ioctl(ctx->fd, VM_ISA_SET_IRQ_TRIGGER, &isa_irq_trigger));
|
||||
}
|
||||
|
||||
int
|
||||
vm_inject_nmi(struct vmctx *ctx, int vcpu)
|
||||
{
|
||||
|
@ -78,6 +78,8 @@ int vm_ioapic_pincount(struct vmctx *ctx, int *pincount);
|
||||
int vm_isa_assert_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq);
|
||||
int vm_isa_deassert_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq);
|
||||
int vm_isa_pulse_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq);
|
||||
int vm_isa_set_irq_trigger(struct vmctx *ctx, int atpic_irq,
|
||||
enum vm_intr_trigger trigger);
|
||||
int vm_inject_nmi(struct vmctx *ctx, int vcpu);
|
||||
int vm_capability_name2type(const char *capname);
|
||||
const char *vm_capability_type2name(int type);
|
||||
|
@ -301,6 +301,11 @@ enum x2apic_state {
|
||||
X2APIC_STATE_LAST
|
||||
};
|
||||
|
||||
enum vm_intr_trigger {
|
||||
EDGE_TRIGGER,
|
||||
LEVEL_TRIGGER
|
||||
};
|
||||
|
||||
/*
|
||||
* The 'access' field has the format specified in Table 21-2 of the Intel
|
||||
* Architecture Manual vol 3b.
|
||||
|
@ -84,6 +84,11 @@ struct vm_isa_irq {
|
||||
int ioapic_irq;
|
||||
};
|
||||
|
||||
struct vm_isa_irq_trigger {
|
||||
int atpic_irq;
|
||||
enum vm_intr_trigger trigger;
|
||||
};
|
||||
|
||||
struct vm_capability {
|
||||
int cpuid;
|
||||
enum vm_cap_type captype;
|
||||
@ -213,6 +218,7 @@ enum {
|
||||
IOCNUM_ISA_ASSERT_IRQ = 80,
|
||||
IOCNUM_ISA_DEASSERT_IRQ = 81,
|
||||
IOCNUM_ISA_PULSE_IRQ = 82,
|
||||
IOCNUM_ISA_SET_IRQ_TRIGGER = 83,
|
||||
};
|
||||
|
||||
#define VM_RUN \
|
||||
@ -253,6 +259,8 @@ enum {
|
||||
_IOW('v', IOCNUM_ISA_DEASSERT_IRQ, struct vm_isa_irq)
|
||||
#define VM_ISA_PULSE_IRQ \
|
||||
_IOW('v', IOCNUM_ISA_PULSE_IRQ, struct vm_isa_irq)
|
||||
#define VM_ISA_SET_IRQ_TRIGGER \
|
||||
_IOW('v', IOCNUM_ISA_SET_IRQ_TRIGGER, struct vm_isa_irq_trigger)
|
||||
#define VM_SET_CAPABILITY \
|
||||
_IOW('v', IOCNUM_SET_CAPABILITY, struct vm_capability)
|
||||
#define VM_GET_CAPABILITY \
|
||||
|
@ -446,6 +446,43 @@ vatpic_pulse_irq(struct vm *vm, int irq)
|
||||
return (vatpic_set_irqstate(vm, irq, IRQSTATE_PULSE));
|
||||
}
|
||||
|
||||
int
|
||||
vatpic_set_irq_trigger(struct vm *vm, int irq, enum vm_intr_trigger trigger)
|
||||
{
|
||||
struct vatpic *vatpic;
|
||||
|
||||
if (irq < 0 || irq > 15)
|
||||
return (EINVAL);
|
||||
|
||||
/*
|
||||
* See comment in vatpic_elc_handler. These IRQs must be
|
||||
* edge triggered.
|
||||
*/
|
||||
if (trigger == LEVEL_TRIGGER) {
|
||||
switch (irq) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 8:
|
||||
case 13:
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
vatpic = vm_atpic(vm);
|
||||
|
||||
VATPIC_LOCK(vatpic);
|
||||
|
||||
if (trigger == LEVEL_TRIGGER)
|
||||
vatpic->elc[irq >> 3] |= 1 << (irq & 0x7);
|
||||
else
|
||||
vatpic->elc[irq >> 3] &= ~(1 << (irq & 0x7));
|
||||
|
||||
VATPIC_UNLOCK(vatpic);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
vatpic_pending_intr(struct vm *vm, int *vecptr)
|
||||
{
|
||||
|
@ -49,6 +49,7 @@ int vatpic_elc_handler(void *vm, int vcpuid, bool in, int port, int bytes,
|
||||
int vatpic_assert_irq(struct vm *vm, int irq);
|
||||
int vatpic_deassert_irq(struct vm *vm, int irq);
|
||||
int vatpic_pulse_irq(struct vm *vm, int irq);
|
||||
int vatpic_set_irq_trigger(struct vm *vm, int irq, enum vm_intr_trigger trigger);
|
||||
|
||||
void vatpic_pending_intr(struct vm *vm, int *vecptr);
|
||||
void vatpic_intr_accepted(struct vm *vm, int vector);
|
||||
|
@ -156,6 +156,7 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
|
||||
struct vm_lapic_msi *vmmsi;
|
||||
struct vm_ioapic_irq *ioapic_irq;
|
||||
struct vm_isa_irq *isa_irq;
|
||||
struct vm_isa_irq_trigger *isa_irq_trigger;
|
||||
struct vm_capability *vmcap;
|
||||
struct vm_pptdev *pptdev;
|
||||
struct vm_pptdev_mmio *pptmmio;
|
||||
@ -346,6 +347,11 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
|
||||
if (error == 0 && isa_irq->ioapic_irq != -1)
|
||||
error = vioapic_pulse_irq(sc->vm, isa_irq->ioapic_irq);
|
||||
break;
|
||||
case VM_ISA_SET_IRQ_TRIGGER:
|
||||
isa_irq_trigger = (struct vm_isa_irq_trigger *)data;
|
||||
error = vatpic_set_irq_trigger(sc->vm,
|
||||
isa_irq_trigger->atpic_irq, isa_irq_trigger->trigger);
|
||||
break;
|
||||
case VM_MAP_MEMORY:
|
||||
seg = (struct vm_memory_segment *)data;
|
||||
error = vm_malloc(sc->vm, seg->gpa, seg->len);
|
||||
|
@ -23,6 +23,7 @@ SRCS= \
|
||||
pci_ahci.c \
|
||||
pci_emul.c \
|
||||
pci_hostbridge.c \
|
||||
pci_irq.c \
|
||||
pci_lpc.c \
|
||||
pci_passthru.c \
|
||||
pci_virtio_block.c \
|
||||
|
@ -704,7 +704,7 @@ basl_fwrite_dsdt(FILE *fp)
|
||||
dsdt_line("DefinitionBlock (\"bhyve_dsdt.aml\", \"DSDT\", 2,"
|
||||
"\"BHYVE \", \"BVDSDT \", 0x00000001)");
|
||||
dsdt_line("{");
|
||||
dsdt_line(" Name (_S5, Package (0x02)");
|
||||
dsdt_line(" Name (_S5, Package ()");
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" 0x05,");
|
||||
dsdt_line(" Zero,");
|
||||
|
@ -49,5 +49,6 @@ void dsdt_fixed_irq(uint8_t irq);
|
||||
void dsdt_fixed_mem32(uint32_t base, uint32_t length);
|
||||
void dsdt_indent(int levels);
|
||||
void dsdt_unindent(int levels);
|
||||
void sci_init(struct vmctx *ctx);
|
||||
|
||||
#endif /* _ACPI_H_ */
|
||||
|
@ -60,6 +60,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include "mevent.h"
|
||||
#include "mptbl.h"
|
||||
#include "pci_emul.h"
|
||||
#include "pci_irq.h"
|
||||
#include "pci_lpc.h"
|
||||
#include "smbiostbl.h"
|
||||
#include "xmsr.h"
|
||||
@ -770,9 +771,11 @@ main(int argc, char *argv[])
|
||||
|
||||
init_mem();
|
||||
init_inout();
|
||||
pci_irq_init(ctx);
|
||||
ioapic_init(ctx);
|
||||
|
||||
rtc_init(ctx);
|
||||
sci_init(ctx);
|
||||
|
||||
/*
|
||||
* Exit if a device emulation finds an error in it's initilization
|
||||
|
@ -210,7 +210,8 @@ mpt_count_ioint_entries(void)
|
||||
}
|
||||
|
||||
static void
|
||||
mpt_generate_pci_int(int bus, int slot, int pin, int ioapic_irq, void *arg)
|
||||
mpt_generate_pci_int(int bus, int slot, int pin, int pirq_pin, int ioapic_irq,
|
||||
void *arg)
|
||||
{
|
||||
int_entry_ptr *mpiep, mpie;
|
||||
|
||||
|
@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include "ioapic.h"
|
||||
#include "mem.h"
|
||||
#include "pci_emul.h"
|
||||
#include "pci_irq.h"
|
||||
#include "pci_lpc.h"
|
||||
|
||||
#define CONF1_ADDR_PORT 0x0cf8
|
||||
@ -81,6 +82,7 @@ struct funcinfo {
|
||||
|
||||
struct intxinfo {
|
||||
int ii_count;
|
||||
int ii_pirq_pin;
|
||||
int ii_ioapic_irq;
|
||||
};
|
||||
|
||||
@ -113,6 +115,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_route(struct pci_devinst *pi);
|
||||
static void pci_lintr_update(struct pci_devinst *pi);
|
||||
|
||||
static struct mem_range pci_mem_hole;
|
||||
@ -697,6 +700,7 @@ pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot,
|
||||
pthread_mutex_init(&pdi->pi_lintr.lock, NULL);
|
||||
pdi->pi_lintr.pin = 0;
|
||||
pdi->pi_lintr.state = IDLE;
|
||||
pdi->pi_lintr.pirq_pin = 0;
|
||||
pdi->pi_lintr.ioapic_irq = 0;
|
||||
pdi->pi_d = pde;
|
||||
snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot);
|
||||
@ -1066,6 +1070,27 @@ init_pci(struct vmctx *ctx)
|
||||
bi->memlimit64 = pci_emul_membase64;
|
||||
}
|
||||
|
||||
/*
|
||||
* PCI backends are initialized before routing INTx interrupts
|
||||
* so that LPC devices are able to reserve ISA IRQs before
|
||||
* routing PIRQ pins.
|
||||
*/
|
||||
for (bus = 0; bus < MAXBUSES; bus++) {
|
||||
if ((bi = pci_businfo[bus]) == NULL)
|
||||
continue;
|
||||
|
||||
for (slot = 0; slot < MAXSLOTS; slot++) {
|
||||
si = &bi->slotinfo[slot];
|
||||
for (func = 0; func < MAXFUNCS; func++) {
|
||||
fi = &si->si_funcs[func];
|
||||
if (fi->fi_devi == NULL)
|
||||
continue;
|
||||
pci_lintr_route(fi->fi_devi);
|
||||
}
|
||||
}
|
||||
}
|
||||
lpc_pirq_routed();
|
||||
|
||||
/*
|
||||
* The guest physical memory map looks like the following:
|
||||
* [0, lowmem) guest system memory
|
||||
@ -1093,19 +1118,36 @@ init_pci(struct vmctx *ctx)
|
||||
}
|
||||
|
||||
static void
|
||||
pci_prt_entry(int bus, int slot, int pin, int ioapic_irq, void *arg)
|
||||
pci_apic_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq,
|
||||
void *arg)
|
||||
{
|
||||
int *count;
|
||||
|
||||
count = arg;
|
||||
dsdt_line(" Package (0x04)");
|
||||
dsdt_line(" Package ()");
|
||||
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)--;
|
||||
dsdt_line(" },");
|
||||
}
|
||||
|
||||
static void
|
||||
pci_pirq_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq,
|
||||
void *arg)
|
||||
{
|
||||
char *name;
|
||||
|
||||
name = lpc_pirq_name(pirq_pin);
|
||||
if (name == NULL)
|
||||
return;
|
||||
dsdt_line(" Package ()");
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" 0x%X,", slot << 16 | 0xffff);
|
||||
dsdt_line(" 0x%02X,", pin - 1);
|
||||
dsdt_line(" %s,", name);
|
||||
dsdt_line(" 0x00");
|
||||
dsdt_line(" },");
|
||||
free(name);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1118,7 +1160,7 @@ pci_bus_write_dsdt(int bus)
|
||||
struct businfo *bi;
|
||||
struct slotinfo *si;
|
||||
struct pci_devinst *pi;
|
||||
int count, slot, func;
|
||||
int count, func, slot;
|
||||
|
||||
/*
|
||||
* If there are no devices on this 'bus' then just return.
|
||||
@ -1133,9 +1175,6 @@ pci_bus_write_dsdt(int bus)
|
||||
return;
|
||||
}
|
||||
|
||||
dsdt_indent(1);
|
||||
dsdt_line("Scope (_SB)");
|
||||
dsdt_line("{");
|
||||
dsdt_line(" Device (PC%02X)", bus);
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" Name (_HID, EisaId (\"PNP0A03\"))");
|
||||
@ -1228,10 +1267,25 @@ pci_bus_write_dsdt(int bus)
|
||||
count = pci_count_lintr(bus);
|
||||
if (count != 0) {
|
||||
dsdt_indent(2);
|
||||
dsdt_line("Name (_PRT, Package (0x%02X)", count);
|
||||
dsdt_line("Name (PPRT, Package ()");
|
||||
dsdt_line("{");
|
||||
pci_walk_lintr(bus, pci_prt_entry, &count);
|
||||
dsdt_line("})");
|
||||
pci_walk_lintr(bus, pci_pirq_prt_entry, NULL);
|
||||
dsdt_line("})");
|
||||
dsdt_line("Name (APRT, Package ()");
|
||||
dsdt_line("{");
|
||||
pci_walk_lintr(bus, pci_apic_prt_entry, NULL);
|
||||
dsdt_line("})");
|
||||
dsdt_line("Method (_PRT, 0, NotSerialized)");
|
||||
dsdt_line("{");
|
||||
dsdt_line(" If (PICM)");
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" Return (APRT)");
|
||||
dsdt_line(" }");
|
||||
dsdt_line(" Else");
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" Return (PPRT)");
|
||||
dsdt_line(" }");
|
||||
dsdt_line("}");
|
||||
dsdt_unindent(2);
|
||||
}
|
||||
|
||||
@ -1247,8 +1301,6 @@ pci_bus_write_dsdt(int bus)
|
||||
dsdt_unindent(2);
|
||||
done:
|
||||
dsdt_line(" }");
|
||||
dsdt_line("}");
|
||||
dsdt_unindent(1);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1256,8 +1308,19 @@ pci_write_dsdt(void)
|
||||
{
|
||||
int bus;
|
||||
|
||||
dsdt_indent(1);
|
||||
dsdt_line("Name (PICM, 0x00)");
|
||||
dsdt_line("Method (_PIC, 1, NotSerialized)");
|
||||
dsdt_line("{");
|
||||
dsdt_line(" Store (Arg0, PICM)");
|
||||
dsdt_line("}");
|
||||
dsdt_line("");
|
||||
dsdt_line("Scope (_SB)");
|
||||
dsdt_line("{");
|
||||
for (bus = 0; bus < MAXBUSES; bus++)
|
||||
pci_bus_write_dsdt(bus);
|
||||
dsdt_line("}");
|
||||
dsdt_unindent(1);
|
||||
}
|
||||
|
||||
int
|
||||
@ -1330,18 +1393,19 @@ pci_lintr_permitted(struct pci_devinst *pi)
|
||||
(cmd & PCIM_CMD_INTxDIS)));
|
||||
}
|
||||
|
||||
int
|
||||
void
|
||||
pci_lintr_request(struct pci_devinst *pi)
|
||||
{
|
||||
struct businfo *bi;
|
||||
struct slotinfo *si;
|
||||
int bestpin, bestcount, irq, pin;
|
||||
int bestpin, bestcount, pin;
|
||||
|
||||
bi = pci_businfo[pi->pi_bus];
|
||||
assert(bi != NULL);
|
||||
|
||||
/*
|
||||
* First, allocate a pin from our slot.
|
||||
* Just allocate a pin from our slot. The pin will be
|
||||
* assigned IRQs later when interrupts are routed.
|
||||
*/
|
||||
si = &bi->slotinfo[pi->pi_slot];
|
||||
bestpin = 0;
|
||||
@ -1353,26 +1417,43 @@ pci_lintr_request(struct pci_devinst *pi)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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, bestpin + 1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
pci_lintr_route(struct pci_devinst *pi)
|
||||
{
|
||||
struct businfo *bi;
|
||||
struct intxinfo *ii;
|
||||
|
||||
if (pi->pi_lintr.pin == 0)
|
||||
return;
|
||||
|
||||
bi = pci_businfo[pi->pi_bus];
|
||||
assert(bi != NULL);
|
||||
ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1];
|
||||
|
||||
/*
|
||||
* Attempt to allocate an I/O APIC pin for this intpin if one
|
||||
* is not yet assigned.
|
||||
*/
|
||||
if (ii->ii_ioapic_irq == 0)
|
||||
ii->ii_ioapic_irq = ioapic_pci_alloc_irq();
|
||||
assert(ii->ii_ioapic_irq > 0);
|
||||
|
||||
/*
|
||||
* Attempt to allocate a PIRQ pin for this intpin if one is
|
||||
* not yet assigned.
|
||||
*/
|
||||
if (ii->ii_pirq_pin == 0)
|
||||
ii->ii_pirq_pin = pirq_alloc_pin(pi->pi_vmctx);
|
||||
assert(ii->ii_pirq_pin > 0);
|
||||
|
||||
pi->pi_lintr.ioapic_irq = ii->ii_ioapic_irq;
|
||||
pi->pi_lintr.pirq_pin = ii->ii_pirq_pin;
|
||||
pci_set_cfgdata8(pi, PCIR_INTLINE, pirq_irq(ii->ii_pirq_pin));
|
||||
}
|
||||
|
||||
void
|
||||
@ -1385,8 +1466,7 @@ pci_lintr_assert(struct pci_devinst *pi)
|
||||
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);
|
||||
pci_irq_assert(pi);
|
||||
} else
|
||||
pi->pi_lintr.state = PENDING;
|
||||
}
|
||||
@ -1402,7 +1482,7 @@ pci_lintr_deassert(struct pci_devinst *pi)
|
||||
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);
|
||||
pci_irq_deassert(pi);
|
||||
} else if (pi->pi_lintr.state == PENDING)
|
||||
pi->pi_lintr.state = IDLE;
|
||||
pthread_mutex_unlock(&pi->pi_lintr.lock);
|
||||
@ -1414,11 +1494,11 @@ 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);
|
||||
pci_irq_deassert(pi);
|
||||
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);
|
||||
pci_irq_assert(pi);
|
||||
}
|
||||
pthread_mutex_unlock(&pi->pi_lintr.lock);
|
||||
}
|
||||
@ -1458,7 +1538,8 @@ pci_walk_lintr(int bus, pci_lintr_cb cb, void *arg)
|
||||
for (pin = 0; pin < 4; pin++) {
|
||||
ii = &si->si_intpins[pin];
|
||||
if (ii->ii_count != 0)
|
||||
cb(bus, slot, pin + 1, ii->ii_ioapic_irq, arg);
|
||||
cb(bus, slot, pin + 1, ii->ii_pirq_pin,
|
||||
ii->ii_ioapic_irq, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1755,20 +1836,6 @@ INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata);
|
||||
INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata);
|
||||
INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata);
|
||||
|
||||
/*
|
||||
* I/O ports to configure PCI IRQ routing. We ignore all writes to it.
|
||||
*/
|
||||
static int
|
||||
pci_irq_port_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
||||
uint32_t *eax, void *arg)
|
||||
{
|
||||
assert(in == 0);
|
||||
return (0);
|
||||
}
|
||||
INOUT_PORT(pci_irq, 0xC00, IOPORT_F_OUT, pci_irq_port_handler);
|
||||
INOUT_PORT(pci_irq, 0xC01, IOPORT_F_OUT, pci_irq_port_handler);
|
||||
SYSRES_IO(0xC00, 2);
|
||||
|
||||
#define PCI_EMUL_TEST
|
||||
#ifdef PCI_EMUL_TEST
|
||||
/*
|
||||
|
@ -120,6 +120,7 @@ struct pci_devinst {
|
||||
struct {
|
||||
int8_t pin;
|
||||
enum lintr_stat state;
|
||||
int pirq_pin;
|
||||
int ioapic_irq;
|
||||
pthread_mutex_t lock;
|
||||
} pi_lintr;
|
||||
@ -200,7 +201,8 @@ struct pciecap {
|
||||
uint16_t slot_status2;
|
||||
} __packed;
|
||||
|
||||
typedef void (*pci_lintr_cb)(int b, int s, int pin, int ioapic_irq, void *arg);
|
||||
typedef void (*pci_lintr_cb)(int b, int s, int pin, int pirq_pin,
|
||||
int ioapic_irq, void *arg);
|
||||
|
||||
int init_pci(struct vmctx *ctx);
|
||||
void msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
|
||||
@ -218,7 +220,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);
|
||||
void 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);
|
||||
|
349
usr.sbin/bhyve/pci_irq.c
Normal file
349
usr.sbin/bhyve/pci_irq.c
Normal file
@ -0,0 +1,349 @@
|
||||
/*-
|
||||
* 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
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* 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 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 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)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <machine/vmm.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <vmmapi.h>
|
||||
|
||||
#include "acpi.h"
|
||||
#include "inout.h"
|
||||
#include "pci_emul.h"
|
||||
#include "pci_irq.h"
|
||||
#include "pci_lpc.h"
|
||||
|
||||
/*
|
||||
* Implement an 8 pin PCI interrupt router compatible with the router
|
||||
* present on Intel's ICH10 chip.
|
||||
*/
|
||||
|
||||
/* Fields in each PIRQ register. */
|
||||
#define PIRQ_DIS 0x80
|
||||
#define PIRQ_IRQ 0x0f
|
||||
|
||||
/* Only IRQs 3-7, 9-12, and 14-15 are permitted. */
|
||||
#define PERMITTED_IRQS 0xdef8
|
||||
#define IRQ_PERMITTED(irq) (((1U << (irq)) & PERMITTED_IRQS) != 0)
|
||||
|
||||
/* IRQ count to disable an IRQ. */
|
||||
#define IRQ_DISABLED 0xff
|
||||
|
||||
static struct pirq {
|
||||
uint8_t reg;
|
||||
int use_count;
|
||||
int active_count;
|
||||
pthread_mutex_t lock;
|
||||
} pirqs[8];
|
||||
|
||||
static u_char irq_counts[16];
|
||||
static int pirq_cold = 1;
|
||||
|
||||
/*
|
||||
* Returns true if this pin is enabled with a valid IRQ. Setting the
|
||||
* register to a reserved IRQ causes interrupts to not be asserted as
|
||||
* if the pin was disabled.
|
||||
*/
|
||||
static bool
|
||||
pirq_valid_irq(int reg)
|
||||
{
|
||||
|
||||
if (reg & PIRQ_DIS)
|
||||
return (false);
|
||||
return (IRQ_PERMITTED(reg & PIRQ_IRQ));
|
||||
}
|
||||
|
||||
uint8_t
|
||||
pirq_read(int pin)
|
||||
{
|
||||
|
||||
assert(pin > 0 && pin <= nitems(pirqs));
|
||||
return (pirqs[pin - 1].reg);
|
||||
}
|
||||
|
||||
void
|
||||
pirq_write(struct vmctx *ctx, int pin, uint8_t val)
|
||||
{
|
||||
struct pirq *pirq;
|
||||
|
||||
assert(pin > 0 && pin <= nitems(pirqs));
|
||||
pirq = &pirqs[pin - 1];
|
||||
pthread_mutex_lock(&pirq->lock);
|
||||
if (pirq->reg != (val & (PIRQ_DIS | PIRQ_IRQ))) {
|
||||
if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
|
||||
vm_isa_deassert_irq(ctx, pirq->reg & PIRQ_IRQ, -1);
|
||||
pirq->reg = val & (PIRQ_DIS | PIRQ_IRQ);
|
||||
if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
|
||||
vm_isa_assert_irq(ctx, pirq->reg & PIRQ_IRQ, -1);
|
||||
}
|
||||
pthread_mutex_unlock(&pirq->lock);
|
||||
}
|
||||
|
||||
void
|
||||
pci_irq_reserve(int irq)
|
||||
{
|
||||
|
||||
assert(irq < nitems(irq_counts));
|
||||
assert(pirq_cold);
|
||||
assert(irq_counts[irq] == 0 || irq_counts[irq] == IRQ_DISABLED);
|
||||
irq_counts[irq] = IRQ_DISABLED;
|
||||
}
|
||||
|
||||
void
|
||||
pci_irq_use(int irq)
|
||||
{
|
||||
|
||||
assert(irq < nitems(irq_counts));
|
||||
assert(pirq_cold);
|
||||
if (irq_counts[irq] != IRQ_DISABLED)
|
||||
irq_counts[irq]++;
|
||||
}
|
||||
|
||||
void
|
||||
pci_irq_init(struct vmctx *ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nitems(pirqs); i++) {
|
||||
pirqs[i].reg = PIRQ_DIS;
|
||||
pirqs[i].use_count = 0;
|
||||
pirqs[i].active_count = 0;
|
||||
pthread_mutex_init(&pirqs[i].lock, NULL);
|
||||
}
|
||||
for (i = 0; i < nitems(irq_counts); i++) {
|
||||
if (IRQ_PERMITTED(i))
|
||||
irq_counts[i] = 0;
|
||||
else
|
||||
irq_counts[i] = IRQ_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pci_irq_assert(struct pci_devinst *pi)
|
||||
{
|
||||
struct pirq *pirq;
|
||||
|
||||
if (pi->pi_lintr.pirq_pin > 0) {
|
||||
assert(pi->pi_lintr.pirq_pin <= nitems(pirqs));
|
||||
pirq = &pirqs[pi->pi_lintr.pirq_pin - 1];
|
||||
pthread_mutex_lock(&pirq->lock);
|
||||
pirq->active_count++;
|
||||
if (pirq->active_count == 1 && pirq_valid_irq(pirq->reg)) {
|
||||
vm_isa_assert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
|
||||
pi->pi_lintr.ioapic_irq);
|
||||
pthread_mutex_unlock(&pirq->lock);
|
||||
return;
|
||||
}
|
||||
pthread_mutex_unlock(&pirq->lock);
|
||||
}
|
||||
vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
|
||||
}
|
||||
|
||||
void
|
||||
pci_irq_deassert(struct pci_devinst *pi)
|
||||
{
|
||||
struct pirq *pirq;
|
||||
|
||||
if (pi->pi_lintr.pirq_pin > 0) {
|
||||
assert(pi->pi_lintr.pirq_pin <= nitems(pirqs));
|
||||
pirq = &pirqs[pi->pi_lintr.pirq_pin - 1];
|
||||
pthread_mutex_lock(&pirq->lock);
|
||||
pirq->active_count--;
|
||||
if (pirq->active_count == 0 && pirq_valid_irq(pirq->reg)) {
|
||||
vm_isa_deassert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
|
||||
pi->pi_lintr.ioapic_irq);
|
||||
pthread_mutex_unlock(&pirq->lock);
|
||||
return;
|
||||
}
|
||||
pthread_mutex_unlock(&pirq->lock);
|
||||
}
|
||||
vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
|
||||
}
|
||||
|
||||
int
|
||||
pirq_alloc_pin(struct vmctx *ctx)
|
||||
{
|
||||
int best_count, best_irq, best_pin, irq, pin;
|
||||
|
||||
pirq_cold = 1;
|
||||
|
||||
/* First, find the least-used PIRQ pin. */
|
||||
best_pin = 0;
|
||||
best_count = pirqs[0].use_count;
|
||||
for (pin = 1; pin < nitems(pirqs); pin++) {
|
||||
if (pirqs[pin].use_count < best_count) {
|
||||
best_pin = pin;
|
||||
best_count = pirqs[pin].use_count;
|
||||
}
|
||||
}
|
||||
pirqs[best_pin].use_count++;
|
||||
|
||||
/* Second, route this pin to an IRQ. */
|
||||
if (pirqs[best_pin].reg == PIRQ_DIS) {
|
||||
best_irq = -1;
|
||||
best_count = 0;
|
||||
for (irq = 0; irq < nitems(irq_counts); irq++) {
|
||||
if (irq_counts[irq] == IRQ_DISABLED)
|
||||
continue;
|
||||
if (best_irq == -1 || irq_counts[irq] < best_count) {
|
||||
best_irq = irq;
|
||||
best_count = irq_counts[irq];
|
||||
}
|
||||
}
|
||||
assert(best_irq != 0);
|
||||
irq_counts[best_irq]++;
|
||||
pirqs[best_pin].reg = best_irq;
|
||||
vm_isa_set_irq_trigger(ctx, best_irq, LEVEL_TRIGGER);
|
||||
}
|
||||
|
||||
return (best_pin + 1);
|
||||
}
|
||||
|
||||
int
|
||||
pirq_irq(int pin)
|
||||
{
|
||||
|
||||
if (pin == -1)
|
||||
return (255);
|
||||
assert(pin > 0 && pin <= nitems(pirqs));
|
||||
return (pirqs[pin - 1].reg & PIRQ_IRQ);
|
||||
}
|
||||
|
||||
/* XXX: Generate $PIR table. */
|
||||
|
||||
static void
|
||||
pirq_dsdt(void)
|
||||
{
|
||||
char *irq_prs, *old;
|
||||
int irq, pin;
|
||||
|
||||
irq_prs = NULL;
|
||||
for (irq = 0; irq < nitems(irq_counts); irq++) {
|
||||
if (!IRQ_PERMITTED(irq))
|
||||
continue;
|
||||
if (irq_prs == NULL)
|
||||
asprintf(&irq_prs, "%d", irq);
|
||||
else {
|
||||
old = irq_prs;
|
||||
asprintf(&irq_prs, "%s,%d", old, irq);
|
||||
free(old);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A helper method to validate a link register's value. This
|
||||
* duplicates pirq_valid_irq().
|
||||
*/
|
||||
dsdt_line("");
|
||||
dsdt_line("Method (PIRV, 1, NotSerialized)");
|
||||
dsdt_line("{");
|
||||
dsdt_line(" If (And (Arg0, 0x%02X))", PIRQ_DIS);
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" Return (0x00)");
|
||||
dsdt_line(" }");
|
||||
dsdt_line(" And (Arg0, 0x%02X, Local0)", PIRQ_IRQ);
|
||||
dsdt_line(" If (LLess (Local0, 0x03))");
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" Return (0x00)");
|
||||
dsdt_line(" }");
|
||||
dsdt_line(" If (LEqual (Local0, 0x08))");
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" Return (0x00)");
|
||||
dsdt_line(" }");
|
||||
dsdt_line(" If (LEqual (Local0, 0x0D))");
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" Return (0x00)");
|
||||
dsdt_line(" }");
|
||||
dsdt_line(" Return (0x01)");
|
||||
dsdt_line("}");
|
||||
|
||||
for (pin = 0; pin < nitems(pirqs); pin++) {
|
||||
dsdt_line("");
|
||||
dsdt_line("Device (LNK%c)", 'A' + pin);
|
||||
dsdt_line("{");
|
||||
dsdt_line(" Name (_HID, EisaId (\"PNP0C0F\"))");
|
||||
dsdt_line(" Name (_UID, 0x%02X)", pin + 1);
|
||||
dsdt_line(" Method (_STA, 0, NotSerialized)");
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" If (PIRV (PIR%c))", 'A' + pin);
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" Return (0x0B)");
|
||||
dsdt_line(" }");
|
||||
dsdt_line(" Else");
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" Return (0x09)");
|
||||
dsdt_line(" }");
|
||||
dsdt_line(" }");
|
||||
dsdt_line(" Name (_PRS, ResourceTemplate ()");
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" IRQ (Level, ActiveLow, Shared, )");
|
||||
dsdt_line(" {%s}", irq_prs);
|
||||
dsdt_line(" })");
|
||||
dsdt_line(" Name (CB%02X, ResourceTemplate ()", pin + 1);
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" IRQ (Level, ActiveLow, Shared, )");
|
||||
dsdt_line(" {}");
|
||||
dsdt_line(" })");
|
||||
dsdt_line(" CreateWordField (CB%02X, 0x01, CIR%c)",
|
||||
pin + 1, 'A' + pin);
|
||||
dsdt_line(" Method (_CRS, 0, NotSerialized)");
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" And (PIR%c, 0x%02X, Local0)", 'A' + pin,
|
||||
PIRQ_DIS | PIRQ_IRQ);
|
||||
dsdt_line(" If (PIRV (Local0))");
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" ShiftLeft (0x01, Local0, CIR%c)", 'A' + pin);
|
||||
dsdt_line(" }");
|
||||
dsdt_line(" Else");
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" Store (0x00, CIR%c)", 'A' + pin);
|
||||
dsdt_line(" }");
|
||||
dsdt_line(" Return (CB%02X)", pin + 1);
|
||||
dsdt_line(" }");
|
||||
dsdt_line(" Method (_DIS, 0, NotSerialized)");
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" Store (0x80, PIR%c)", 'A' + pin);
|
||||
dsdt_line(" }");
|
||||
dsdt_line(" Method (_SRS, 1, NotSerialized)");
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" CreateWordField (Arg0, 0x01, SIR%c)", 'A' + pin);
|
||||
dsdt_line(" FindSetRightBit (SIR%c, Local0)", 'A' + pin);
|
||||
dsdt_line(" Store (Decrement (Local0), PIR%c)", 'A' + pin);
|
||||
dsdt_line(" }");
|
||||
dsdt_line("}");
|
||||
}
|
||||
free(irq_prs);
|
||||
}
|
||||
LPC_DSDT(pirq_dsdt);
|
45
usr.sbin/bhyve/pci_irq.h
Normal file
45
usr.sbin/bhyve/pci_irq.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*-
|
||||
* 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
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* 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 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 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)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#ifndef __PCI_IRQ_H__
|
||||
#define __PCI_IRQ_H__
|
||||
|
||||
struct pci_devinst;
|
||||
|
||||
void pci_irq_assert(struct pci_devinst *pi);
|
||||
void pci_irq_deassert(struct pci_devinst *pi);
|
||||
void pci_irq_init(struct vmctx *ctx);
|
||||
void pci_irq_reserve(int irq);
|
||||
void pci_irq_use(int irq);
|
||||
int pirq_alloc_pin(struct vmctx *ctx);
|
||||
int pirq_irq(int pin);
|
||||
uint8_t pirq_read(int pin);
|
||||
void pirq_write(struct vmctx *ctx, int pin, uint8_t val);
|
||||
|
||||
#endif
|
@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include "acpi.h"
|
||||
#include "inout.h"
|
||||
#include "pci_emul.h"
|
||||
#include "pci_irq.h"
|
||||
#include "pci_lpc.h"
|
||||
#include "uart_emul.h"
|
||||
|
||||
@ -173,6 +174,7 @@ lpc_init(void)
|
||||
"LPC device %s\n", name);
|
||||
return (-1);
|
||||
}
|
||||
pci_irq_reserve(sc->irq);
|
||||
|
||||
sc->uart_softc = uart_init(lpc_uart_intr_assert,
|
||||
lpc_uart_intr_deassert, sc);
|
||||
@ -208,7 +210,21 @@ pci_lpc_write_dsdt(struct pci_devinst *pi)
|
||||
dsdt_line("Device (ISA)");
|
||||
dsdt_line("{");
|
||||
dsdt_line(" Name (_ADR, 0x%04X%04X)", pi->pi_slot, pi->pi_func);
|
||||
dsdt_line(" OperationRegion (P40C, PCI_Config, 0x60, 0x04)");
|
||||
dsdt_line(" OperationRegion (LPCR, PCI_Config, 0x00, 0x100)");
|
||||
dsdt_line(" Field (LPCR, AnyAcc, NoLock, Preserve)");
|
||||
dsdt_line(" {");
|
||||
dsdt_line(" Offset (0x60),");
|
||||
dsdt_line(" PIRA, 8,");
|
||||
dsdt_line(" PIRB, 8,");
|
||||
dsdt_line(" PIRC, 8,");
|
||||
dsdt_line(" PIRD, 8,");
|
||||
dsdt_line(" Offset (0x68),");
|
||||
dsdt_line(" PIRE, 8,");
|
||||
dsdt_line(" PIRF, 8,");
|
||||
dsdt_line(" PIRG, 8,");
|
||||
dsdt_line(" PIRH, 8");
|
||||
dsdt_line(" }");
|
||||
dsdt_line("");
|
||||
|
||||
dsdt_indent(1);
|
||||
SET_FOREACH(ldpp, lpc_dsdt_set) {
|
||||
@ -305,13 +321,34 @@ pci_lpc_uart_dsdt(void)
|
||||
}
|
||||
LPC_DSDT(pci_lpc_uart_dsdt);
|
||||
|
||||
static int
|
||||
pci_lpc_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
|
||||
int coff, int bytes, uint32_t val)
|
||||
{
|
||||
int pirq_pin;
|
||||
|
||||
if (bytes == 1) {
|
||||
pirq_pin = 0;
|
||||
if (coff >= 0x60 && coff <= 0x63)
|
||||
pirq_pin = coff - 0x60 + 1;
|
||||
if (coff >= 0x68 && coff <= 0x6b)
|
||||
pirq_pin = coff - 0x68 + 5;
|
||||
if (pirq_pin != 0) {
|
||||
pirq_write(ctx, pirq_pin, val);
|
||||
pci_set_cfgdata8(pi, coff, pirq_read(pirq_pin));
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static void
|
||||
pci_lpc_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
|
||||
int baridx, uint64_t offset, int size, uint64_t value)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t
|
||||
static uint64_t
|
||||
pci_lpc_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
|
||||
int baridx, uint64_t offset, int size)
|
||||
{
|
||||
@ -324,6 +361,7 @@ pci_lpc_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
|
||||
static int
|
||||
pci_lpc_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
|
||||
{
|
||||
|
||||
/*
|
||||
* Do not allow more than one LPC bridge to be configured.
|
||||
*/
|
||||
@ -356,10 +394,36 @@ pci_lpc_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
|
||||
return (0);
|
||||
}
|
||||
|
||||
char *
|
||||
lpc_pirq_name(int pin)
|
||||
{
|
||||
char *name;
|
||||
|
||||
if (lpc_bridge == NULL)
|
||||
return (NULL);
|
||||
asprintf(&name, "\\_SB.PC00.ISA.LNK%c,", 'A' + pin - 1);
|
||||
return (name);
|
||||
}
|
||||
|
||||
void
|
||||
lpc_pirq_routed(void)
|
||||
{
|
||||
int pin;
|
||||
|
||||
if (lpc_bridge == NULL)
|
||||
return;
|
||||
|
||||
for (pin = 0; pin < 4; pin++)
|
||||
pci_set_cfgdata8(lpc_bridge, 0x60 + pin, pirq_read(pin + 1));
|
||||
for (pin = 0; pin < 4; pin++)
|
||||
pci_set_cfgdata8(lpc_bridge, 0x68 + pin, pirq_read(pin + 5));
|
||||
}
|
||||
|
||||
struct pci_devemu pci_de_lpc = {
|
||||
.pe_emu = "lpc",
|
||||
.pe_init = pci_lpc_init,
|
||||
.pe_write_dsdt = pci_lpc_write_dsdt,
|
||||
.pe_cfgwrite = pci_lpc_cfgwrite,
|
||||
.pe_barwrite = pci_lpc_write,
|
||||
.pe_barread = pci_lpc_read
|
||||
};
|
||||
|
@ -66,5 +66,7 @@ struct lpc_sysres {
|
||||
#define SYSRES_MEM(base, length) LPC_SYSRES(LPC_SYSRES_MEM, base, length)
|
||||
|
||||
int lpc_device_parse(const char *opt);
|
||||
char *lpc_pirq_name(int pin);
|
||||
void lpc_pirq_routed(void);
|
||||
|
||||
#endif
|
||||
|
@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include "acpi.h"
|
||||
#include "inout.h"
|
||||
#include "mevent.h"
|
||||
#include "pci_irq.h"
|
||||
#include "pci_lpc.h"
|
||||
|
||||
static pthread_mutex_t pm_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
@ -289,3 +290,15 @@ smi_cmd_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
||||
}
|
||||
INOUT_PORT(smi_cmd, SMI_CMD, IOPORT_F_OUT, smi_cmd_handler);
|
||||
SYSRES_IO(SMI_CMD, 1);
|
||||
|
||||
void
|
||||
sci_init(struct vmctx *ctx)
|
||||
{
|
||||
|
||||
/*
|
||||
* Mark ACPI's SCI as level trigger and bump its use count
|
||||
* in the PIRQ router.
|
||||
*/
|
||||
pci_irq_use(SCI_INT);
|
||||
vm_isa_set_irq_trigger(ctx, SCI_INT, LEVEL_TRIGGER);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user