Replace the userspace atpic stub with a more functional vmm.ko model.
New ioctls VM_ISA_ASSERT_IRQ, VM_ISA_DEASSERT_IRQ and VM_ISA_PULSE_IRQ can be used to manipulate the pic, and optionally the ioapic, pin state. Reviewed by: jhb, neel Approved by: neel (co-mentor)
This commit is contained in:
parent
b77b13a20e
commit
762fd20804
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=263035
@ -457,6 +457,41 @@ vm_ioapic_pincount(struct vmctx *ctx, int *pincount)
|
||||
return (ioctl(ctx->fd, VM_IOAPIC_PINCOUNT, pincount));
|
||||
}
|
||||
|
||||
int
|
||||
vm_isa_assert_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;
|
||||
|
||||
return (ioctl(ctx->fd, VM_ISA_ASSERT_IRQ, &isa_irq));
|
||||
}
|
||||
|
||||
int
|
||||
vm_isa_deassert_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;
|
||||
|
||||
return (ioctl(ctx->fd, VM_ISA_DEASSERT_IRQ, &isa_irq));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return (ioctl(ctx->fd, VM_ISA_PULSE_IRQ, &isa_irq));
|
||||
}
|
||||
|
||||
int
|
||||
vm_inject_nmi(struct vmctx *ctx, int vcpu)
|
||||
{
|
||||
|
@ -71,6 +71,9 @@ 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_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_pulse_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq);
|
||||
int vm_inject_nmi(struct vmctx *ctx, int vcpu);
|
||||
int vm_capability_name2type(const char *capname);
|
||||
const char *vm_capability_type2name(int type);
|
||||
|
@ -187,6 +187,7 @@ void vcpu_notify_event(struct vm *vm, int vcpuid, bool lapic_intr);
|
||||
struct vmspace *vm_get_vmspace(struct vm *vm);
|
||||
int vm_assign_pptdev(struct vm *vm, int bus, int slot, int func);
|
||||
int vm_unassign_pptdev(struct vm *vm, int bus, int slot, int func);
|
||||
struct vatpic *vm_atpic(struct vm *vm);
|
||||
|
||||
/*
|
||||
* Inject exception 'vme' into the guest vcpu. This function returns 0 on
|
||||
|
@ -79,6 +79,11 @@ struct vm_ioapic_irq {
|
||||
int irq;
|
||||
};
|
||||
|
||||
struct vm_isa_irq {
|
||||
int atpic_irq;
|
||||
int ioapic_irq;
|
||||
};
|
||||
|
||||
struct vm_capability {
|
||||
int cpuid;
|
||||
enum vm_cap_type captype;
|
||||
@ -198,6 +203,11 @@ enum {
|
||||
IOCNUM_SET_X2APIC_STATE = 60,
|
||||
IOCNUM_GET_X2APIC_STATE = 61,
|
||||
IOCNUM_GET_HPET_CAPABILITIES = 62,
|
||||
|
||||
/* legacy interrupt injection */
|
||||
IOCNUM_ISA_ASSERT_IRQ = 80,
|
||||
IOCNUM_ISA_DEASSERT_IRQ = 81,
|
||||
IOCNUM_ISA_PULSE_IRQ = 82,
|
||||
};
|
||||
|
||||
#define VM_RUN \
|
||||
@ -230,6 +240,12 @@ enum {
|
||||
_IOW('v', IOCNUM_IOAPIC_PULSE_IRQ, struct vm_ioapic_irq)
|
||||
#define VM_IOAPIC_PINCOUNT \
|
||||
_IOR('v', IOCNUM_IOAPIC_PINCOUNT, int)
|
||||
#define VM_ISA_ASSERT_IRQ \
|
||||
_IOW('v', IOCNUM_ISA_ASSERT_IRQ, struct vm_isa_irq)
|
||||
#define VM_ISA_DEASSERT_IRQ \
|
||||
_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_SET_CAPABILITY \
|
||||
_IOW('v', IOCNUM_SET_CAPABILITY, struct vm_capability)
|
||||
#define VM_GET_CAPABILITY \
|
||||
|
@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/vmm.h>
|
||||
#include <machine/vmm_dev.h>
|
||||
#include "vmm_host.h"
|
||||
#include "vmm_ioport.h"
|
||||
#include "vmm_ipi.h"
|
||||
#include "vmm_msr.h"
|
||||
#include "vmm_ktr.h"
|
||||
@ -1861,6 +1862,11 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit)
|
||||
vmexit->u.inout.rep = (qual & 0x20) ? 1 : 0;
|
||||
vmexit->u.inout.port = (uint16_t)(qual >> 16);
|
||||
vmexit->u.inout.eax = (uint32_t)(vmxctx->guest_rax);
|
||||
error = emulate_ioport(vmx->vm, vcpu, vmexit);
|
||||
if (error == 0) {
|
||||
handled = 1;
|
||||
vmxctx->guest_rax = vmexit->u.inout.eax;
|
||||
}
|
||||
break;
|
||||
case EXIT_REASON_CPUID:
|
||||
vmm_stat_incr(vmx->vm, vcpu, VMEXIT_CPUID, 1);
|
||||
|
595
sys/amd64/vmm/io/vatpic.c
Normal file
595
sys/amd64/vmm/io/vatpic.c
Normal file
@ -0,0 +1,595 @@
|
||||
/*-
|
||||
* Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
|
||||
* 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 ``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 <sys/types.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/cpuset.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <x86/apicreg.h>
|
||||
#include <dev/ic/i8259.h>
|
||||
|
||||
#include <machine/vmm.h>
|
||||
|
||||
#include "vmm_ktr.h"
|
||||
#include "vmm_lapic.h"
|
||||
#include "vioapic.h"
|
||||
#include "vatpic.h"
|
||||
|
||||
static MALLOC_DEFINE(M_VATPIC, "atpic", "bhyve virtual atpic (8259)");
|
||||
|
||||
#define VATPIC_LOCK(vatpic) mtx_lock_spin(&((vatpic)->mtx))
|
||||
#define VATPIC_UNLOCK(vatpic) mtx_unlock_spin(&((vatpic)->mtx))
|
||||
#define VATPIC_LOCKED(vatpic) mtx_owned(&((vatpic)->mtx))
|
||||
|
||||
enum irqstate {
|
||||
IRQSTATE_ASSERT,
|
||||
IRQSTATE_DEASSERT,
|
||||
IRQSTATE_PULSE
|
||||
};
|
||||
|
||||
struct atpic {
|
||||
bool ready;
|
||||
int icw_num;
|
||||
int rd_cmd_reg;
|
||||
|
||||
bool aeoi;
|
||||
bool poll;
|
||||
bool rotate;
|
||||
|
||||
int irq_base;
|
||||
uint8_t request; /* Interrupt Request Register (IIR) */
|
||||
uint8_t service; /* Interrupt Service (ISR) */
|
||||
uint8_t mask; /* Interrupt Mask Register (IMR) */
|
||||
|
||||
int acnt[8]; /* sum of pin asserts and deasserts */
|
||||
int priority; /* current pin priority */
|
||||
};
|
||||
|
||||
struct vatpic {
|
||||
struct vm *vm;
|
||||
struct mtx mtx;
|
||||
struct atpic atpic[2];
|
||||
uint8_t elc[2];
|
||||
};
|
||||
|
||||
#define VATPIC_CTR0(vatpic, fmt) \
|
||||
VM_CTR0((vatpic)->vm, fmt)
|
||||
|
||||
#define VATPIC_CTR1(vatpic, fmt, a1) \
|
||||
VM_CTR1((vatpic)->vm, fmt, a1)
|
||||
|
||||
#define VATPIC_CTR2(vatpic, fmt, a1, a2) \
|
||||
VM_CTR2((vatpic)->vm, fmt, a1, a2)
|
||||
|
||||
#define VATPIC_CTR3(vatpic, fmt, a1, a2, a3) \
|
||||
VM_CTR3((vatpic)->vm, fmt, a1, a2, a3)
|
||||
|
||||
#define VATPIC_CTR4(vatpic, fmt, a1, a2, a3, a4) \
|
||||
VM_CTR4((vatpic)->vm, fmt, a1, a2, a3, a4)
|
||||
|
||||
|
||||
static __inline int
|
||||
vatpic_get_highest_isrpin(struct atpic *atpic)
|
||||
{
|
||||
int bit, pin;
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= 7; i++) {
|
||||
pin = ((i + 7 - atpic->priority) & 0x7);
|
||||
bit = (1 << pin);
|
||||
|
||||
if (atpic->service & bit)
|
||||
return (pin);
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static __inline int
|
||||
vatpic_get_highest_irrpin(struct atpic *atpic)
|
||||
{
|
||||
int bit, pin;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i <= 7; i++) {
|
||||
pin = ((i + 7 - atpic->priority) & 0x7);
|
||||
bit = (1 << pin);
|
||||
if (atpic->service & bit)
|
||||
break;
|
||||
}
|
||||
|
||||
for (j = 0; j < i; j++) {
|
||||
pin = ((j + 7 - atpic->priority) & 0x7);
|
||||
bit = (1 << pin);
|
||||
if (atpic->request & bit && (~atpic->mask & bit))
|
||||
return (pin);
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static void
|
||||
vatpic_notify_intr(struct vatpic *vatpic)
|
||||
{
|
||||
struct atpic *atpic;
|
||||
int pin;
|
||||
|
||||
KASSERT(VATPIC_LOCKED(vatpic), ("vatpic_notify_intr not locked"));
|
||||
|
||||
/* XXX master only */
|
||||
atpic = &vatpic->atpic[0];
|
||||
|
||||
if ((pin = vatpic_get_highest_irrpin(atpic)) != -1) {
|
||||
VATPIC_CTR4(vatpic, "atpic notify pin = %d "
|
||||
"(imr 0x%x irr 0x%x isr 0x%x)", pin,
|
||||
atpic->mask, atpic->request, atpic->service);
|
||||
lapic_set_local_intr(vatpic->vm, -1, APIC_LVT_LINT0);
|
||||
vioapic_pulse_irq(vatpic->vm, 0);
|
||||
} else {
|
||||
VATPIC_CTR3(vatpic, "atpic no eligible interrupts "
|
||||
"(imr 0x%x irr 0x%x isr 0x%x)",
|
||||
atpic->mask, atpic->request, atpic->service);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
vatpic_icw1(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
|
||||
{
|
||||
VATPIC_CTR1(vatpic, "atpic icw1 0x%x", val);
|
||||
|
||||
atpic->ready = false;
|
||||
|
||||
atpic->icw_num = 1;
|
||||
atpic->mask = 0;
|
||||
atpic->priority = 0;
|
||||
atpic->rd_cmd_reg = 0;
|
||||
|
||||
if ((val & ICW1_SNGL) != 0) {
|
||||
VATPIC_CTR0(vatpic, "vatpic cascade mode required");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((val & ICW1_IC4) == 0) {
|
||||
VATPIC_CTR0(vatpic, "vatpic icw4 required");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
atpic->icw_num++;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
vatpic_icw2(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
|
||||
{
|
||||
VATPIC_CTR1(vatpic, "atpic icw2 0x%x", val);
|
||||
|
||||
atpic->irq_base = val & 0xf8;
|
||||
|
||||
atpic->icw_num++;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
vatpic_icw3(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
|
||||
{
|
||||
VATPIC_CTR1(vatpic, "atpic icw3 0x%x", val);
|
||||
|
||||
atpic->icw_num++;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
vatpic_icw4(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
|
||||
{
|
||||
VATPIC_CTR1(vatpic, "atpic icw4 0x%x", val);
|
||||
|
||||
if ((val & ICW4_8086) == 0) {
|
||||
VATPIC_CTR0(vatpic, "vatpic microprocessor mode required");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((val & ICW4_AEOI) != 0)
|
||||
atpic->aeoi = true;
|
||||
|
||||
atpic->icw_num = 0;
|
||||
atpic->ready = true;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
vatpic_ocw1(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
|
||||
{
|
||||
VATPIC_CTR1(vatpic, "atpic ocw1 0x%x", val);
|
||||
|
||||
atpic->mask = val & 0xff;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
vatpic_ocw2(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
|
||||
{
|
||||
VATPIC_CTR1(vatpic, "atpic ocw2 0x%x", val);
|
||||
|
||||
atpic->rotate = ((val & OCW2_R) != 0);
|
||||
|
||||
if ((val & OCW2_EOI) != 0) {
|
||||
int isr_bit;
|
||||
|
||||
if ((val & OCW2_SL) != 0) {
|
||||
/* specific EOI */
|
||||
isr_bit = val & 0x7;
|
||||
} else {
|
||||
/* non-specific EOI */
|
||||
isr_bit = vatpic_get_highest_isrpin(atpic);
|
||||
}
|
||||
|
||||
if (isr_bit != -1) {
|
||||
atpic->service &= ~(1 << isr_bit);
|
||||
|
||||
if (atpic->rotate)
|
||||
atpic->priority = isr_bit;
|
||||
}
|
||||
} else if ((val & OCW2_SL) != 0 && atpic->rotate == true) {
|
||||
/* specific priority */
|
||||
atpic->priority = val & 0x7;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
vatpic_ocw3(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
|
||||
{
|
||||
VATPIC_CTR1(vatpic, "atpic ocw3 0x%x", val);
|
||||
|
||||
atpic->poll = ((val & OCW3_P) != 0);
|
||||
|
||||
if (val & OCW3_RR) {
|
||||
/* read register command */
|
||||
atpic->rd_cmd_reg = val & OCW3_RIS;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
vatpic_set_pinstate(struct vatpic *vatpic, int pin, bool newstate)
|
||||
{
|
||||
struct atpic *atpic;
|
||||
int oldcnt, newcnt;
|
||||
bool level;
|
||||
|
||||
KASSERT(pin >= 0 && pin < 16,
|
||||
("vatpic_set_pinstate: invalid pin number %d", pin));
|
||||
KASSERT(VATPIC_LOCKED(vatpic),
|
||||
("vatpic_set_pinstate: vatpic is not locked"));
|
||||
|
||||
atpic = &vatpic->atpic[pin >> 3];
|
||||
|
||||
oldcnt = atpic->acnt[pin & 0x7];
|
||||
if (newstate)
|
||||
atpic->acnt[pin & 0x7]++;
|
||||
else
|
||||
atpic->acnt[pin & 0x7]--;
|
||||
newcnt = atpic->acnt[pin & 0x7];
|
||||
|
||||
if (newcnt < 0) {
|
||||
VATPIC_CTR2(vatpic, "atpic pin%d: bad acnt %d", pin, newcnt);
|
||||
}
|
||||
|
||||
level = ((vatpic->elc[pin >> 3] & (1 << (pin & 0x7))) != 0);
|
||||
|
||||
if ((oldcnt == 0 && newcnt == 1) || (newcnt > 0 && level == true)) {
|
||||
/* rising edge or level */
|
||||
VATPIC_CTR1(vatpic, "atpic pin%d: asserted", pin);
|
||||
atpic->request |= (1 << (pin & 0x7));
|
||||
} else if (oldcnt == 1 && newcnt == 0) {
|
||||
/* falling edge */
|
||||
VATPIC_CTR1(vatpic, "atpic pin%d: deasserted", pin);
|
||||
} else {
|
||||
VATPIC_CTR3(vatpic, "atpic pin%d: %s, ignored, acnt %d",
|
||||
pin, newstate ? "asserted" : "deasserted", newcnt);
|
||||
}
|
||||
|
||||
vatpic_notify_intr(vatpic);
|
||||
}
|
||||
|
||||
static int
|
||||
vatpic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate)
|
||||
{
|
||||
struct vatpic *vatpic;
|
||||
struct atpic *atpic;
|
||||
|
||||
if (irq < 0 || irq > 15)
|
||||
return (EINVAL);
|
||||
|
||||
vatpic = vm_atpic(vm);
|
||||
atpic = &vatpic->atpic[irq >> 3];
|
||||
|
||||
if (atpic->ready == false)
|
||||
return (0);
|
||||
|
||||
VATPIC_LOCK(vatpic);
|
||||
switch (irqstate) {
|
||||
case IRQSTATE_ASSERT:
|
||||
vatpic_set_pinstate(vatpic, irq, true);
|
||||
break;
|
||||
case IRQSTATE_DEASSERT:
|
||||
vatpic_set_pinstate(vatpic, irq, false);
|
||||
break;
|
||||
case IRQSTATE_PULSE:
|
||||
vatpic_set_pinstate(vatpic, irq, true);
|
||||
vatpic_set_pinstate(vatpic, irq, false);
|
||||
break;
|
||||
default:
|
||||
panic("vatpic_set_irqstate: invalid irqstate %d", irqstate);
|
||||
}
|
||||
VATPIC_UNLOCK(vatpic);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
vatpic_assert_irq(struct vm *vm, int irq)
|
||||
{
|
||||
return (vatpic_set_irqstate(vm, irq, IRQSTATE_ASSERT));
|
||||
}
|
||||
|
||||
int
|
||||
vatpic_deassert_irq(struct vm *vm, int irq)
|
||||
{
|
||||
return (vatpic_set_irqstate(vm, irq, IRQSTATE_DEASSERT));
|
||||
}
|
||||
|
||||
int
|
||||
vatpic_pulse_irq(struct vm *vm, int irq)
|
||||
{
|
||||
return (vatpic_set_irqstate(vm, irq, IRQSTATE_PULSE));
|
||||
}
|
||||
|
||||
int
|
||||
vatpic_pending_intr(struct vm *vm, int *vecptr)
|
||||
{
|
||||
struct vatpic *vatpic;
|
||||
struct atpic *atpic;
|
||||
int pin;
|
||||
|
||||
vatpic = vm_atpic(vm);
|
||||
|
||||
/* XXX master only */
|
||||
atpic = &vatpic->atpic[0];
|
||||
|
||||
VATPIC_LOCK(vatpic);
|
||||
|
||||
pin = vatpic_get_highest_irrpin(atpic);
|
||||
if (pin == -1)
|
||||
pin = 7;
|
||||
|
||||
*vecptr = atpic->irq_base + pin;
|
||||
|
||||
VATPIC_UNLOCK(vatpic);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
void
|
||||
vatpic_intr_accepted(struct vm *vm, int vector)
|
||||
{
|
||||
struct vatpic *vatpic;
|
||||
struct atpic *atpic;
|
||||
int pin;
|
||||
|
||||
vatpic = vm_atpic(vm);
|
||||
|
||||
/* XXX master only */
|
||||
atpic = &vatpic->atpic[0];
|
||||
|
||||
VATPIC_LOCK(vatpic);
|
||||
pin = vector & 0x7;
|
||||
|
||||
if (atpic->acnt[pin] == 0)
|
||||
atpic->request &= ~(1 << pin);
|
||||
|
||||
if (atpic->aeoi == true) {
|
||||
if (atpic->rotate == true)
|
||||
atpic->priority = pin;
|
||||
} else {
|
||||
atpic->service |= (1 << pin);
|
||||
}
|
||||
|
||||
vatpic_notify_intr(vatpic);
|
||||
|
||||
VATPIC_UNLOCK(vatpic);
|
||||
}
|
||||
|
||||
int
|
||||
vatpic_master_handler(void *vm, int vcpuid, struct vm_exit *vmexit)
|
||||
{
|
||||
struct vatpic *vatpic;
|
||||
struct atpic *atpic;
|
||||
int error;
|
||||
uint8_t val;
|
||||
|
||||
vatpic = vm_atpic(vm);
|
||||
atpic = &vatpic->atpic[0];
|
||||
|
||||
if (vmexit->u.inout.bytes != 1)
|
||||
return (-1);
|
||||
|
||||
if (vmexit->u.inout.in) {
|
||||
VATPIC_LOCK(vatpic);
|
||||
if (atpic->poll) {
|
||||
VATPIC_CTR0(vatpic, "vatpic polled mode not "
|
||||
"supported");
|
||||
VATPIC_UNLOCK(vatpic);
|
||||
return (-1);
|
||||
} else {
|
||||
if (vmexit->u.inout.port & ICU_IMR_OFFSET) {
|
||||
/* read interrrupt mask register */
|
||||
vmexit->u.inout.eax = atpic->mask;
|
||||
} else {
|
||||
if (atpic->rd_cmd_reg == OCW3_RIS) {
|
||||
/* read interrupt service register */
|
||||
vmexit->u.inout.eax = atpic->service;
|
||||
} else {
|
||||
/* read interrupt request register */
|
||||
vmexit->u.inout.eax = atpic->request;
|
||||
}
|
||||
}
|
||||
}
|
||||
VATPIC_UNLOCK(vatpic);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
val = vmexit->u.inout.eax;
|
||||
|
||||
VATPIC_LOCK(vatpic);
|
||||
|
||||
if (vmexit->u.inout.port & ICU_IMR_OFFSET) {
|
||||
if (atpic->ready) {
|
||||
error = vatpic_ocw1(vatpic, atpic, val);
|
||||
} else {
|
||||
switch (atpic->icw_num) {
|
||||
case 2:
|
||||
error = vatpic_icw2(vatpic, atpic, val);
|
||||
break;
|
||||
case 3:
|
||||
error = vatpic_icw3(vatpic, atpic, val);
|
||||
break;
|
||||
case 4:
|
||||
error = vatpic_icw4(vatpic, atpic, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (val & (1 << 4))
|
||||
error = vatpic_icw1(vatpic, atpic, val);
|
||||
|
||||
if (atpic->ready) {
|
||||
if (val & (1 << 3))
|
||||
error = vatpic_ocw3(vatpic, atpic, val);
|
||||
else
|
||||
error = vatpic_ocw2(vatpic, atpic, val);
|
||||
}
|
||||
}
|
||||
|
||||
if (atpic->ready)
|
||||
vatpic_notify_intr(vatpic);
|
||||
|
||||
VATPIC_UNLOCK(vatpic);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
vatpic_slave_handler(void *vm, int vcpuid, struct vm_exit *vmexit)
|
||||
{
|
||||
if (vmexit->u.inout.bytes != 1)
|
||||
return (-1);
|
||||
|
||||
if (vmexit->u.inout.in) {
|
||||
if (vmexit->u.inout.port & ICU_IMR_OFFSET) {
|
||||
/* all interrupts masked */
|
||||
vmexit->u.inout.eax = 0xff;
|
||||
} else {
|
||||
vmexit->u.inout.eax = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pretend all accesses to the slave 8259 are alright */
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
vatpic_elc_handler(void *vm, int vcpuid, struct vm_exit *vmexit)
|
||||
{
|
||||
struct vatpic *vatpic;
|
||||
bool is_master;
|
||||
|
||||
vatpic = vm_atpic(vm);
|
||||
is_master = (vmexit->u.inout.port == IO_ELCR1);
|
||||
|
||||
if (vmexit->u.inout.bytes != 1)
|
||||
return (-1);
|
||||
|
||||
if (vmexit->u.inout.in) {
|
||||
if (is_master)
|
||||
vmexit->u.inout.eax = vatpic->elc[0];
|
||||
else
|
||||
vmexit->u.inout.eax = vatpic->elc[1];
|
||||
} else {
|
||||
/*
|
||||
* For the master PIC the cascade channel (IRQ2), the
|
||||
* heart beat timer (IRQ0), and the keyboard
|
||||
* controller (IRQ1) cannot be programmed for level
|
||||
* mode.
|
||||
*
|
||||
* For the slave PIC the real time clock (IRQ8) and
|
||||
* the floating point error interrupt (IRQ13) cannot
|
||||
* be programmed for level mode.
|
||||
*/
|
||||
if (is_master)
|
||||
vatpic->elc[0] = (vmexit->u.inout.eax & 0xf8);
|
||||
else
|
||||
vatpic->elc[1] = (vmexit->u.inout.eax & 0xde);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct vatpic *
|
||||
vatpic_init(struct vm *vm)
|
||||
{
|
||||
struct vatpic *vatpic;
|
||||
|
||||
vatpic = malloc(sizeof(struct vatpic), M_VATPIC, M_WAITOK | M_ZERO);
|
||||
vatpic->vm = vm;
|
||||
|
||||
mtx_init(&vatpic->mtx, "vatpic lock", NULL, MTX_SPIN);
|
||||
|
||||
return (vatpic);
|
||||
}
|
||||
|
||||
void
|
||||
vatpic_cleanup(struct vatpic *vatpic)
|
||||
{
|
||||
free(vatpic, M_VATPIC);
|
||||
}
|
53
sys/amd64/vmm/io/vatpic.h
Normal file
53
sys/amd64/vmm/io/vatpic.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*-
|
||||
* Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
|
||||
* 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 ``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 _VATPIC_H_
|
||||
#define _VATPIC_H_
|
||||
|
||||
#include <isa/isareg.h>
|
||||
|
||||
#define ICU_IMR_OFFSET 1
|
||||
|
||||
#define IO_ELCR1 0x4d0
|
||||
#define IO_ELCR2 0x4d1
|
||||
|
||||
struct vatpic *vatpic_init(struct vm *vm);
|
||||
void vatpic_cleanup(struct vatpic *vatpic);
|
||||
|
||||
int vatpic_master_handler(void *vm, int vcpuid, struct vm_exit *vmexit);
|
||||
int vatpic_slave_handler(void *vm, int vcpuid, struct vm_exit *vmexit);
|
||||
int vatpic_elc_handler(void *vm, int vcpuid, struct vm_exit *vmexit);
|
||||
|
||||
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_pending_intr(struct vm *vm, int *vecptr);
|
||||
void vatpic_intr_accepted(struct vm *vm, int vector);
|
||||
|
||||
#endif /* _VATPIC_H_ */
|
@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/vmm_dev.h>
|
||||
|
||||
#include "vmm_lapic.h"
|
||||
#include "vatpic.h"
|
||||
#include "vioapic.h"
|
||||
#include "vhpet.h"
|
||||
|
||||
@ -167,6 +168,25 @@ vhpet_timer_ioapic_pin(struct vhpet *vhpet, int n)
|
||||
return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
|
||||
}
|
||||
|
||||
static __inline int
|
||||
vhpet_timer_atpic_pin(struct vhpet *vhpet, int n)
|
||||
{
|
||||
if (vhpet->config & HPET_CNF_LEG_RT) {
|
||||
/*
|
||||
* In "legacy routing" timers 0 and 1 are connected to
|
||||
* 8259 master pin 0 and slave pin 0 respectively.
|
||||
*/
|
||||
switch (n) {
|
||||
case 0:
|
||||
return (0);
|
||||
case 1:
|
||||
return (8);
|
||||
}
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
vhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr)
|
||||
{
|
||||
@ -196,12 +216,17 @@ vhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr)
|
||||
static void
|
||||
vhpet_timer_clear_isr(struct vhpet *vhpet, int n)
|
||||
{
|
||||
int pin;
|
||||
int pin, legacy_pin;
|
||||
|
||||
if (vhpet->isr & (1 << n)) {
|
||||
pin = vhpet_timer_ioapic_pin(vhpet, n);
|
||||
KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n));
|
||||
vioapic_deassert_irq(vhpet->vm, pin);
|
||||
|
||||
legacy_pin = vhpet_timer_atpic_pin(vhpet, n);
|
||||
if (legacy_pin != -1)
|
||||
vatpic_deassert_irq(vhpet->vm, legacy_pin);
|
||||
|
||||
vhpet->isr &= ~(1 << n);
|
||||
}
|
||||
}
|
||||
@ -242,7 +267,7 @@ vhpet_timer_edge_trig(struct vhpet *vhpet, int n)
|
||||
static void
|
||||
vhpet_timer_interrupt(struct vhpet *vhpet, int n)
|
||||
{
|
||||
int pin;
|
||||
int pin, legacy_pin;
|
||||
|
||||
/* If interrupts are not enabled for this timer then just return. */
|
||||
if (!vhpet_timer_interrupt_enabled(vhpet, n))
|
||||
@ -268,11 +293,17 @@ vhpet_timer_interrupt(struct vhpet *vhpet, int n)
|
||||
return;
|
||||
}
|
||||
|
||||
legacy_pin = vhpet_timer_atpic_pin(vhpet, n);
|
||||
|
||||
if (vhpet_timer_edge_trig(vhpet, n)) {
|
||||
vioapic_pulse_irq(vhpet->vm, pin);
|
||||
if (legacy_pin != -1)
|
||||
vatpic_pulse_irq(vhpet->vm, legacy_pin);
|
||||
} else {
|
||||
vhpet->isr |= 1 << n;
|
||||
vioapic_assert_irq(vhpet->vm, pin);
|
||||
if (legacy_pin != -1)
|
||||
vatpic_assert_irq(vhpet->vm, legacy_pin);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "vlapic.h"
|
||||
#include "vlapic_priv.h"
|
||||
#include "vatpic.h"
|
||||
#include "vioapic.h"
|
||||
|
||||
#define PRIO(x) ((x) >> 4)
|
||||
@ -299,6 +300,16 @@ vlapic_set_intr_ready(struct vlapic *vlapic, int vector, bool level)
|
||||
return (1);
|
||||
}
|
||||
|
||||
static VMM_STAT(VLAPIC_EXTINT_COUNT, "number of ExtINTs received by vlapic");
|
||||
|
||||
static void
|
||||
vlapic_deliver_extint(struct vlapic *vlapic)
|
||||
{
|
||||
vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_EXTINT_COUNT, 1);
|
||||
vlapic->extint_pending = true;
|
||||
vcpu_notify_event(vlapic->vm, vlapic->vcpuid, false);
|
||||
}
|
||||
|
||||
static __inline uint32_t *
|
||||
vlapic_get_lvtptr(struct vlapic *vlapic, uint32_t offset)
|
||||
{
|
||||
@ -448,6 +459,9 @@ vlapic_fire_lvt(struct vlapic *vlapic, uint32_t lvt)
|
||||
case APIC_LVT_DM_NMI:
|
||||
vm_inject_nmi(vlapic->vm, vlapic->vcpuid);
|
||||
break;
|
||||
case APIC_LVT_DM_EXTINT:
|
||||
vlapic_deliver_extint(vlapic);
|
||||
break;
|
||||
default:
|
||||
// Other modes ignored
|
||||
return (0);
|
||||
@ -651,6 +665,25 @@ vlapic_trigger_lvt(struct vlapic *vlapic, int vector)
|
||||
{
|
||||
uint32_t lvt;
|
||||
|
||||
if (vlapic_enabled(vlapic) == false) {
|
||||
/*
|
||||
* When the local APIC is global/hardware disabled,
|
||||
* LINT[1:0] pins are configured as INTR and NMI pins,
|
||||
* respectively.
|
||||
*/
|
||||
switch (vector) {
|
||||
case APIC_LVT_LINT0:
|
||||
vlapic_deliver_extint(vlapic);
|
||||
break;
|
||||
case APIC_LVT_LINT1:
|
||||
vm_inject_nmi(vlapic->vm, vlapic->vcpuid);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
switch (vector) {
|
||||
case APIC_LVT_LINT0:
|
||||
lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_LINT0_LVT);
|
||||
@ -1020,6 +1053,9 @@ vlapic_pending_intr(struct vlapic *vlapic, int *vecptr)
|
||||
int idx, i, bitpos, vector;
|
||||
uint32_t *irrptr, val;
|
||||
|
||||
if (vlapic->extint_pending)
|
||||
return (vatpic_pending_intr(vlapic->vm, vecptr));
|
||||
|
||||
if (vlapic->ops.pending_intr)
|
||||
return ((*vlapic->ops.pending_intr)(vlapic, vecptr));
|
||||
|
||||
@ -1054,6 +1090,12 @@ vlapic_intr_accepted(struct vlapic *vlapic, int vector)
|
||||
uint32_t *irrptr, *isrptr;
|
||||
int idx, stk_top;
|
||||
|
||||
if (vlapic->extint_pending) {
|
||||
vlapic->extint_pending = false;
|
||||
vatpic_intr_accepted(vlapic->vm, vector);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vlapic->ops.intr_accepted)
|
||||
return ((*vlapic->ops.intr_accepted)(vlapic, vector));
|
||||
|
||||
@ -1474,11 +1516,13 @@ vlapic_deliver_intr(struct vm *vm, bool level, uint32_t dest, bool phys,
|
||||
int vcpuid;
|
||||
cpuset_t dmask;
|
||||
|
||||
if (delmode != APIC_DELMODE_FIXED && delmode != APIC_DELMODE_LOWPRIO) {
|
||||
if (delmode != IOART_DELFIXED &&
|
||||
delmode != IOART_DELLOPRI &&
|
||||
delmode != IOART_DELEXINT) {
|
||||
VM_CTR1(vm, "vlapic intr invalid delmode %#x", delmode);
|
||||
return;
|
||||
}
|
||||
lowprio = (delmode == APIC_DELMODE_LOWPRIO);
|
||||
lowprio = (delmode == IOART_DELLOPRI);
|
||||
|
||||
/*
|
||||
* We don't provide any virtual interrupt redirection hardware so
|
||||
@ -1490,7 +1534,11 @@ vlapic_deliver_intr(struct vm *vm, bool level, uint32_t dest, bool phys,
|
||||
while ((vcpuid = CPU_FFS(&dmask)) != 0) {
|
||||
vcpuid--;
|
||||
CPU_CLR(vcpuid, &dmask);
|
||||
lapic_set_intr(vm, vcpuid, vec, level);
|
||||
if (delmode == IOART_DELEXINT) {
|
||||
vlapic_deliver_extint(vm_lapic(vm, vcpuid));
|
||||
} else {
|
||||
lapic_set_intr(vm, vcpuid, vec, level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,6 +156,8 @@ struct vlapic {
|
||||
uint32_t esr_pending;
|
||||
int esr_firing;
|
||||
|
||||
bool extint_pending;
|
||||
|
||||
struct callout callout; /* vlapic timer */
|
||||
struct bintime timer_fire_bt; /* callout expiry time */
|
||||
struct bintime timer_freq_bt; /* timer frequency */
|
||||
|
@ -67,6 +67,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include "vmm_host.h"
|
||||
#include "vmm_mem.h"
|
||||
#include "vmm_util.h"
|
||||
#include "vatpic.h"
|
||||
#include "vhpet.h"
|
||||
#include "vioapic.h"
|
||||
#include "vlapic.h"
|
||||
@ -116,6 +117,7 @@ struct vm {
|
||||
void *iommu; /* iommu-specific data */
|
||||
struct vhpet *vhpet; /* virtual HPET */
|
||||
struct vioapic *vioapic; /* virtual ioapic */
|
||||
struct vatpic *vatpic; /* virtual atpic */
|
||||
struct vmspace *vmspace; /* guest's address space */
|
||||
struct vcpu vcpu[VM_MAXCPU];
|
||||
int num_mem_segs;
|
||||
@ -345,6 +347,7 @@ vm_create(const char *name, struct vm **retvm)
|
||||
vm->cookie = VMINIT(vm, vmspace_pmap(vmspace));
|
||||
vm->vioapic = vioapic_init(vm);
|
||||
vm->vhpet = vhpet_init(vm);
|
||||
vm->vatpic = vatpic_init(vm);
|
||||
|
||||
for (i = 0; i < VM_MAXCPU; i++) {
|
||||
vcpu_init(vm, i);
|
||||
@ -378,6 +381,7 @@ vm_destroy(struct vm *vm)
|
||||
iommu_destroy_domain(vm->iommu);
|
||||
|
||||
vhpet_cleanup(vm->vhpet);
|
||||
vatpic_cleanup(vm->vatpic);
|
||||
vioapic_cleanup(vm->vioapic);
|
||||
|
||||
for (i = 0; i < vm->num_mem_segs; i++)
|
||||
@ -1637,3 +1641,9 @@ vm_smp_rendezvous(struct vm *vm, int vcpuid, cpuset_t dest,
|
||||
|
||||
vm_handle_rendezvous(vm, vcpuid);
|
||||
}
|
||||
|
||||
struct vatpic *
|
||||
vm_atpic(struct vm *vm)
|
||||
{
|
||||
return (vm->vatpic);
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include "vmm_stat.h"
|
||||
#include "vmm_mem.h"
|
||||
#include "io/ppt.h"
|
||||
#include "io/vatpic.h"
|
||||
#include "io/vioapic.h"
|
||||
#include "io/vhpet.h"
|
||||
|
||||
@ -154,6 +155,7 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
|
||||
struct vm_lapic_irq *vmirq;
|
||||
struct vm_lapic_msi *vmmsi;
|
||||
struct vm_ioapic_irq *ioapic_irq;
|
||||
struct vm_isa_irq *isa_irq;
|
||||
struct vm_capability *vmcap;
|
||||
struct vm_pptdev *pptdev;
|
||||
struct vm_pptdev_mmio *pptmmio;
|
||||
@ -318,6 +320,26 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
|
||||
case VM_IOAPIC_PINCOUNT:
|
||||
*(int *)data = vioapic_pincount(sc->vm);
|
||||
break;
|
||||
case VM_ISA_ASSERT_IRQ:
|
||||
isa_irq = (struct vm_isa_irq *)data;
|
||||
error = vatpic_assert_irq(sc->vm, isa_irq->atpic_irq);
|
||||
if (error == 0 && isa_irq->ioapic_irq != -1)
|
||||
error = vioapic_assert_irq(sc->vm,
|
||||
isa_irq->ioapic_irq);
|
||||
break;
|
||||
case VM_ISA_DEASSERT_IRQ:
|
||||
isa_irq = (struct vm_isa_irq *)data;
|
||||
error = vatpic_deassert_irq(sc->vm, isa_irq->atpic_irq);
|
||||
if (error == 0 && isa_irq->ioapic_irq != -1)
|
||||
error = vioapic_deassert_irq(sc->vm,
|
||||
isa_irq->ioapic_irq);
|
||||
break;
|
||||
case VM_ISA_PULSE_IRQ:
|
||||
isa_irq = (struct vm_isa_irq *)data;
|
||||
error = vatpic_pulse_irq(sc->vm, isa_irq->atpic_irq);
|
||||
if (error == 0 && isa_irq->ioapic_irq != -1)
|
||||
error = vioapic_pulse_irq(sc->vm, isa_irq->ioapic_irq);
|
||||
break;
|
||||
case VM_MAP_MEMORY:
|
||||
seg = (struct vm_memory_segment *)data;
|
||||
error = vm_malloc(sc->vm, seg->gpa, seg->len);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2011 NetApp, Inc.
|
||||
* Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -11,10 +11,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 ``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,46 +22,44 @@
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/cpuset.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include "inout.h"
|
||||
#include "pci_lpc.h"
|
||||
#include <machine/vmm.h>
|
||||
|
||||
/*
|
||||
* EISA interrupt Level Control Register.
|
||||
*
|
||||
* This is a 16-bit register with one bit for each of the IRQ0 through IRQ15.
|
||||
* A level triggered irq is indicated by setting the corresponding bit to '1'.
|
||||
*/
|
||||
#define ELCR_PORT 0x4d0
|
||||
#include "vatpic.h"
|
||||
#include "vmm_ioport.h"
|
||||
|
||||
static uint8_t elcr[2] = { 0x00, 0x00 };
|
||||
#define MAX_IOPORTS 1280
|
||||
|
||||
static int
|
||||
elcr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
||||
uint32_t *eax, void *arg)
|
||||
ioport_handler_func_t ioport_handler[MAX_IOPORTS] = {
|
||||
[IO_ICU1] = vatpic_master_handler,
|
||||
[IO_ICU1 + ICU_IMR_OFFSET] = vatpic_master_handler,
|
||||
[IO_ICU2] = vatpic_slave_handler,
|
||||
[IO_ICU2 + ICU_IMR_OFFSET] = vatpic_slave_handler,
|
||||
[IO_ELCR1] = vatpic_elc_handler,
|
||||
[IO_ELCR2] = vatpic_elc_handler,
|
||||
};
|
||||
|
||||
int
|
||||
emulate_ioport(struct vm *vm, int vcpuid, struct vm_exit *vmexit)
|
||||
{
|
||||
int idx;
|
||||
ioport_handler_func_t handler;
|
||||
|
||||
if (bytes != 1)
|
||||
if (vmexit->u.inout.port >= MAX_IOPORTS)
|
||||
return (-1);
|
||||
|
||||
idx = port - ELCR_PORT;
|
||||
handler = ioport_handler[vmexit->u.inout.port];
|
||||
if (handler == NULL)
|
||||
return (-1);
|
||||
|
||||
if (in)
|
||||
*eax = elcr[idx];
|
||||
else
|
||||
elcr[idx] = *eax;
|
||||
|
||||
return (0);
|
||||
return ((*handler)(vm, vcpuid, vmexit));
|
||||
}
|
||||
INOUT_PORT(elcr, ELCR_PORT + 0, IOPORT_F_INOUT, elcr_handler);
|
||||
INOUT_PORT(elcr, ELCR_PORT + 1, IOPORT_F_INOUT, elcr_handler);
|
||||
SYSRES_IO(ELCR_PORT, 2);
|
37
sys/amd64/vmm/vmm_ioport.h
Normal file
37
sys/amd64/vmm/vmm_ioport.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*-
|
||||
* Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
|
||||
* 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 ``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 _VMM_IOPORT_H_
|
||||
#define _VMM_IOPORT_H_
|
||||
|
||||
typedef int (*ioport_handler_func_t)(void *vm, int vcpuid,
|
||||
struct vm_exit *vmexit);
|
||||
|
||||
int emulate_ioport(struct vm *vm, int vcpuid, struct vm_exit *vmexit);
|
||||
|
||||
#endif /* _VMM_IOPORT_H_ */
|
@ -15,6 +15,7 @@ SRCS+= vmm.c \
|
||||
vmm_dev.c \
|
||||
vmm_host.c \
|
||||
vmm_instruction_emul.c \
|
||||
vmm_ioport.c \
|
||||
vmm_ipi.c \
|
||||
vmm_lapic.c \
|
||||
vmm_mem.c \
|
||||
@ -27,6 +28,7 @@ SRCS+= vmm.c \
|
||||
.PATH: ${.CURDIR}/../../amd64/vmm/io
|
||||
SRCS+= iommu.c \
|
||||
ppt.c \
|
||||
vatpic.c \
|
||||
vhpet.c \
|
||||
vioapic.c \
|
||||
vlapic.c
|
||||
|
@ -10,12 +10,10 @@ MAN= bhyve.8
|
||||
|
||||
SRCS= \
|
||||
acpi.c \
|
||||
atpic.c \
|
||||
bhyverun.c \
|
||||
block_if.c \
|
||||
consport.c \
|
||||
dbgport.c \
|
||||
elcr.c \
|
||||
inout.c \
|
||||
ioapic.c \
|
||||
mem.c \
|
||||
|
@ -1,89 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2011 NetApp, Inc.
|
||||
* 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 NETAPP, INC ``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
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "acpi.h"
|
||||
#include "inout.h"
|
||||
#include "pci_lpc.h"
|
||||
|
||||
#define IO_ICU1 0x20
|
||||
#define IO_ICU2 0xA0
|
||||
#define ICU_IMR_OFFSET 1
|
||||
|
||||
static int
|
||||
atpic_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
||||
uint32_t *eax, void *arg)
|
||||
{
|
||||
if (bytes != 1)
|
||||
return (-1);
|
||||
|
||||
if (in) {
|
||||
if (port & ICU_IMR_OFFSET) {
|
||||
/* all interrupts masked */
|
||||
*eax = 0xff;
|
||||
} else {
|
||||
*eax = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pretend all writes to the 8259 are alright */
|
||||
return (0);
|
||||
}
|
||||
|
||||
INOUT_PORT(atpic, IO_ICU1, IOPORT_F_INOUT, atpic_handler);
|
||||
INOUT_PORT(atpic, IO_ICU1 + ICU_IMR_OFFSET, IOPORT_F_INOUT, atpic_handler);
|
||||
INOUT_PORT(atpic, IO_ICU2, IOPORT_F_INOUT, atpic_handler);
|
||||
INOUT_PORT(atpic, IO_ICU2 + ICU_IMR_OFFSET, IOPORT_F_INOUT, atpic_handler);
|
||||
|
||||
static void
|
||||
atpic_dsdt(void)
|
||||
{
|
||||
|
||||
dsdt_line("");
|
||||
dsdt_line("Device (PIC)");
|
||||
dsdt_line("{");
|
||||
dsdt_line(" Name (_HID, EisaId (\"PNP0000\"))");
|
||||
dsdt_line(" Name (_CRS, ResourceTemplate ()");
|
||||
dsdt_line(" {");
|
||||
dsdt_indent(2);
|
||||
dsdt_fixed_ioport(IO_ICU1, 2);
|
||||
dsdt_fixed_ioport(IO_ICU2, 2);
|
||||
dsdt_fixed_irq(2);
|
||||
dsdt_unindent(2);
|
||||
dsdt_line(" })");
|
||||
dsdt_line("}");
|
||||
}
|
||||
LPC_DSDT(atpic_dsdt);
|
@ -46,9 +46,15 @@ __FBSDID("$FreeBSD$");
|
||||
#include "pci_lpc.h"
|
||||
#include "uart_emul.h"
|
||||
|
||||
#define IO_ICU1 0x20
|
||||
#define IO_ICU2 0xA0
|
||||
|
||||
SET_DECLARE(lpc_dsdt_set, struct lpc_dsdt);
|
||||
SET_DECLARE(lpc_sysres_set, struct lpc_sysres);
|
||||
|
||||
#define ELCR_PORT 0x4d0
|
||||
SYSRES_IO(ELCR_PORT, 2);
|
||||
|
||||
static struct pci_devinst *lpc_bridge;
|
||||
|
||||
#define LPC_UART_NUM 2
|
||||
@ -100,7 +106,7 @@ lpc_uart_intr_assert(void *arg)
|
||||
|
||||
assert(sc->irq >= 0);
|
||||
|
||||
vm_ioapic_pulse_irq(lpc_bridge->pi_vmctx, sc->irq);
|
||||
vm_isa_pulse_irq(lpc_bridge->pi_vmctx, sc->irq, sc->irq);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -192,6 +198,20 @@ pci_lpc_write_dsdt(struct pci_devinst *pi)
|
||||
ldp = *ldpp;
|
||||
ldp->handler();
|
||||
}
|
||||
|
||||
dsdt_line("");
|
||||
dsdt_line("Device (PIC)");
|
||||
dsdt_line("{");
|
||||
dsdt_line(" Name (_HID, EisaId (\"PNP0000\"))");
|
||||
dsdt_line(" Name (_CRS, ResourceTemplate ()");
|
||||
dsdt_line(" {");
|
||||
dsdt_indent(2);
|
||||
dsdt_fixed_ioport(IO_ICU1, 2);
|
||||
dsdt_fixed_ioport(IO_ICU2, 2);
|
||||
dsdt_fixed_irq(2);
|
||||
dsdt_unindent(2);
|
||||
dsdt_line(" })");
|
||||
dsdt_line("}");
|
||||
dsdt_unindent(1);
|
||||
|
||||
dsdt_line("}");
|
||||
|
@ -107,7 +107,7 @@ pit_mevent_cb(int fd, enum ev_type type, void *param)
|
||||
|
||||
pit_mev_count++;
|
||||
|
||||
vm_ioapic_pulse_irq(c->ctx, 2);
|
||||
vm_isa_pulse_irq(c->ctx, 0, 2);
|
||||
|
||||
/*
|
||||
* Delete the timer for one-shots
|
||||
|
Loading…
Reference in New Issue
Block a user