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:
Tycho Nightingale 2014-03-11 16:56:00 +00:00
parent b77b13a20e
commit 762fd20804
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=263035
19 changed files with 916 additions and 128 deletions

View File

@ -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)
{

View File

@ -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);

View File

@ -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

View File

@ -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 \

View File

@ -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
View 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
View 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_ */

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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 */

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View 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_ */

View File

@ -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

View File

@ -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 \

View File

@ -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);

View File

@ -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("}");

View File

@ -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