mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
[HVM] Update VPIC device model for new interrupt delivery code.
Move BSP VLAPIC initialisation to hvmloader. Remove callback_irq update hack from Linux unmodified drivers. Signed-off-by: Keir Fraser <keir@xensource.com>
This commit is contained in:
1 parent
9d4052382a
commit
00a70f44a6
16 files changed
+714
-655
No files matched your search
@@ -0,0 +1,108 @@
|
||||
#ifndef __ASM_APICDEF_H
|
||||
#define __ASM_APICDEF_H
|
||||
|
||||
#define APIC_DEFAULT_PHYS_BASE 0xfee00000
|
||||
|
||||
#define APIC_ID 0x20
|
||||
#define APIC_ID_MASK (0xFFu<<24)
|
||||
#define GET_APIC_ID(x) (((x)>>24)&0xFFu)
|
||||
#define SET_APIC_ID(x) (((x)<<24))
|
||||
#define APIC_LVR 0x30
|
||||
#define APIC_LVR_MASK 0xFF00FF
|
||||
#define GET_APIC_VERSION(x) ((x)&0xFF)
|
||||
#define GET_APIC_MAXLVT(x) (((x)>>16)&0xFF)
|
||||
#define APIC_INTEGRATED(x) ((x)&0xF0)
|
||||
#define APIC_XAPIC(x) ((x) >= 0x14)
|
||||
#define APIC_TASKPRI 0x80
|
||||
#define APIC_TPRI_MASK 0xFF
|
||||
#define APIC_ARBPRI 0x90
|
||||
#define APIC_ARBPRI_MASK 0xFF
|
||||
#define APIC_PROCPRI 0xA0
|
||||
#define APIC_EOI 0xB0
|
||||
#define APIC_EIO_ACK 0x0
|
||||
#define APIC_RRR 0xC0
|
||||
#define APIC_LDR 0xD0
|
||||
#define APIC_LDR_MASK (0xFF<<24)
|
||||
#define GET_APIC_LOGICAL_ID(x) (((x)>>24)&0xFF)
|
||||
#define SET_APIC_LOGICAL_ID(x) (((x)<<24))
|
||||
#define APIC_ALL_CPUS 0xFF
|
||||
#define APIC_DFR 0xE0
|
||||
#define APIC_DFR_CLUSTER 0x0FFFFFFFul
|
||||
#define APIC_DFR_FLAT 0xFFFFFFFFul
|
||||
#define APIC_SPIV 0xF0
|
||||
#define APIC_SPIV_FOCUS_DISABLED (1<<9)
|
||||
#define APIC_SPIV_APIC_ENABLED (1<<8)
|
||||
#define APIC_ISR 0x100
|
||||
#define APIC_TMR 0x180
|
||||
#define APIC_IRR 0x200
|
||||
#define APIC_ESR 0x280
|
||||
#define APIC_ESR_SEND_CS 0x00001
|
||||
#define APIC_ESR_RECV_CS 0x00002
|
||||
#define APIC_ESR_SEND_ACC 0x00004
|
||||
#define APIC_ESR_RECV_ACC 0x00008
|
||||
#define APIC_ESR_SENDILL 0x00020
|
||||
#define APIC_ESR_RECVILL 0x00040
|
||||
#define APIC_ESR_ILLREGA 0x00080
|
||||
#define APIC_ICR 0x300
|
||||
#define APIC_DEST_SELF 0x40000
|
||||
#define APIC_DEST_ALLINC 0x80000
|
||||
#define APIC_DEST_ALLBUT 0xC0000
|
||||
#define APIC_ICR_RR_MASK 0x30000
|
||||
#define APIC_ICR_RR_INVALID 0x00000
|
||||
#define APIC_ICR_RR_INPROG 0x10000
|
||||
#define APIC_ICR_RR_VALID 0x20000
|
||||
#define APIC_INT_LEVELTRIG 0x08000
|
||||
#define APIC_INT_ASSERT 0x04000
|
||||
#define APIC_ICR_BUSY 0x01000
|
||||
#define APIC_DEST_LOGICAL 0x00800
|
||||
#define APIC_DEST_PHYSICAL 0x00000
|
||||
#define APIC_DM_FIXED 0x00000
|
||||
#define APIC_DM_LOWEST 0x00100
|
||||
#define APIC_DM_SMI 0x00200
|
||||
#define APIC_DM_REMRD 0x00300
|
||||
#define APIC_DM_NMI 0x00400
|
||||
#define APIC_DM_INIT 0x00500
|
||||
#define APIC_DM_STARTUP 0x00600
|
||||
#define APIC_DM_EXTINT 0x00700
|
||||
#define APIC_VECTOR_MASK 0x000FF
|
||||
#define APIC_ICR2 0x310
|
||||
#define GET_APIC_DEST_FIELD(x) (((x)>>24)&0xFF)
|
||||
#define SET_APIC_DEST_FIELD(x) ((x)<<24)
|
||||
#define APIC_LVTT 0x320
|
||||
#define APIC_LVTTHMR 0x330
|
||||
#define APIC_LVTPC 0x340
|
||||
#define APIC_LVT0 0x350
|
||||
#define APIC_LVT_TIMER_BASE_MASK (0x3<<18)
|
||||
#define GET_APIC_TIMER_BASE(x) (((x)>>18)&0x3)
|
||||
#define SET_APIC_TIMER_BASE(x) (((x)<<18))
|
||||
#define APIC_TIMER_BASE_CLKIN 0x0
|
||||
#define APIC_TIMER_BASE_TMBASE 0x1
|
||||
#define APIC_TIMER_BASE_DIV 0x2
|
||||
#define APIC_LVT_TIMER_PERIODIC (1<<17)
|
||||
#define APIC_LVT_MASKED (1<<16)
|
||||
#define APIC_LVT_LEVEL_TRIGGER (1<<15)
|
||||
#define APIC_LVT_REMOTE_IRR (1<<14)
|
||||
#define APIC_INPUT_POLARITY (1<<13)
|
||||
#define APIC_SEND_PENDING (1<<12)
|
||||
#define APIC_MODE_MASK 0x700
|
||||
#define GET_APIC_DELIVERY_MODE(x) (((x)>>8)&0x7)
|
||||
#define SET_APIC_DELIVERY_MODE(x,y) (((x)&~0x700)|((y)<<8))
|
||||
#define APIC_MODE_FIXED 0x0
|
||||
#define APIC_MODE_NMI 0x4
|
||||
#define APIC_MODE_EXTINT 0x7
|
||||
#define APIC_LVT1 0x360
|
||||
#define APIC_LVTERR 0x370
|
||||
#define APIC_TMICT 0x380
|
||||
#define APIC_TMCCT 0x390
|
||||
#define APIC_TDCR 0x3E0
|
||||
#define APIC_TDR_DIV_TMBASE (1<<2)
|
||||
#define APIC_TDR_DIV_1 0xB
|
||||
#define APIC_TDR_DIV_2 0x0
|
||||
#define APIC_TDR_DIV_4 0x1
|
||||
#define APIC_TDR_DIV_8 0x2
|
||||
#define APIC_TDR_DIV_16 0x3
|
||||
#define APIC_TDR_DIV_32 0x8
|
||||
#define APIC_TDR_DIV_64 0x9
|
||||
#define APIC_TDR_DIV_128 0xA
|
||||
|
||||
#endif
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "acpi_utils.h"
|
||||
#include "smbios.h"
|
||||
#include "config.h"
|
||||
#include "apic_regs.h"
|
||||
#include "pci_regs.h"
|
||||
#include <xen/version.h>
|
||||
#include <xen/hvm/params.h>
|
||||
@@ -154,16 +155,13 @@ init_hypercalls(void)
|
||||
|
||||
static void apic_setup(void)
|
||||
{
|
||||
volatile uint32_t *ioregsel;
|
||||
volatile uint32_t *iowin;
|
||||
|
||||
/* IOAPIC memory-mapped access window registers. */
|
||||
ioregsel = (volatile uint32_t *)(IOAPIC_BASE_ADDRESS + 0x00);
|
||||
iowin = (volatile uint32_t *)(IOAPIC_BASE_ADDRESS + 0x10);
|
||||
|
||||
/* Set the IOAPIC ID to tha static value used in the MP/ACPI tables. */
|
||||
*ioregsel = 0;
|
||||
*iowin = IOAPIC_ID;
|
||||
ioapic_write(0x00, IOAPIC_ID);
|
||||
|
||||
/* Set up Virtual Wire mode. */
|
||||
lapic_write(APIC_SPIV, APIC_SPIV_APIC_ENABLED | 0xFF);
|
||||
lapic_write(APIC_LVT0, APIC_MODE_EXTINT << 8);
|
||||
lapic_write(APIC_LVT1, APIC_MODE_NMI << 8);
|
||||
}
|
||||
|
||||
static void pci_setup(void)
|
||||
|
||||
@@ -309,22 +309,24 @@ uint64_t e820_malloc(uint64_t size, uint32_t type, uint64_t mask)
|
||||
|
||||
uint32_t ioapic_read(uint32_t reg)
|
||||
{
|
||||
uint32_t *ioregsel = (uint32_t *)(IOAPIC_BASE_ADDRESS + 0x00);
|
||||
uint32_t *iowin = (uint32_t *)(IOAPIC_BASE_ADDRESS + 0x10);
|
||||
|
||||
*ioregsel = reg;
|
||||
mb();
|
||||
return *iowin;
|
||||
*(volatile uint32_t *)(IOAPIC_BASE_ADDRESS + 0x00) = reg;
|
||||
return *(volatile uint32_t *)(IOAPIC_BASE_ADDRESS + 0x10);
|
||||
}
|
||||
|
||||
void ioapic_write(uint32_t reg, uint32_t val)
|
||||
{
|
||||
uint32_t *ioregsel = (uint32_t *)(IOAPIC_BASE_ADDRESS + 0x00);
|
||||
uint32_t *iowin = (uint32_t *)(IOAPIC_BASE_ADDRESS + 0x10);
|
||||
*(volatile uint32_t *)(IOAPIC_BASE_ADDRESS + 0x00) = reg;
|
||||
*(volatile uint32_t *)(IOAPIC_BASE_ADDRESS + 0x10) = val;
|
||||
}
|
||||
|
||||
*ioregsel = reg;
|
||||
wmb();
|
||||
*iowin = val;
|
||||
uint32_t lapic_read(uint32_t reg)
|
||||
{
|
||||
return *(volatile uint32_t *)(LAPIC_BASE_ADDRESS + reg);
|
||||
}
|
||||
|
||||
void lapic_write(uint32_t reg, uint32_t val)
|
||||
{
|
||||
*(volatile uint32_t *)(LAPIC_BASE_ADDRESS + reg) = val;
|
||||
}
|
||||
|
||||
#define PCI_CONF1_ADDRESS(bus, devfn, reg) \
|
||||
|
||||
@@ -20,6 +20,12 @@ uint8_t inb(uint16_t addr);
|
||||
uint16_t inw(uint16_t addr);
|
||||
uint32_t inl(uint16_t addr);
|
||||
|
||||
/* APIC access */
|
||||
uint32_t ioapic_read(uint32_t reg);
|
||||
void ioapic_write(uint32_t reg, uint32_t val);
|
||||
uint32_t lapic_read(uint32_t reg);
|
||||
void lapic_write(uint32_t reg, uint32_t val);
|
||||
|
||||
/* PCI access */
|
||||
uint32_t pci_read(uint32_t devfn, uint32_t reg, uint32_t len);
|
||||
#define pci_readb(devfn, reg) ((uint8_t) pci_read(devfn, reg, 1))
|
||||
|
||||
@@ -132,7 +132,7 @@ EXPORT_SYMBOL(notify_remote_via_irq);
|
||||
|
||||
irqreturn_t evtchn_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
unsigned int l1i, l2i, port;
|
||||
unsigned int l1i, port;
|
||||
int cpu = smp_processor_id();
|
||||
irqreturn_t(*handler) (int, void *, struct pt_regs *);
|
||||
shared_info_t *s = shared_info_area;
|
||||
@@ -140,44 +140,28 @@ irqreturn_t evtchn_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
||||
unsigned long l1, l2;
|
||||
|
||||
v->evtchn_upcall_pending = 0;
|
||||
/* NB. No need for a barrier here -- XCHG is a barrier
|
||||
* on x86. */
|
||||
/* NB. No need for a barrier here -- XCHG is a barrier on x86. */
|
||||
l1 = xchg(&v->evtchn_pending_sel, 0);
|
||||
while (l1 != 0)
|
||||
{
|
||||
while (l1 != 0) {
|
||||
l1i = __ffs(l1);
|
||||
l1 &= ~(1 << l1i);
|
||||
|
||||
l2 = s->evtchn_pending[l1i] & ~s->evtchn_mask[l1i];
|
||||
while (l2 != 0)
|
||||
{
|
||||
l2i = __ffs(l2);
|
||||
|
||||
port = (l1i * BITS_PER_LONG) + l2i;
|
||||
while ((l2 = s->evtchn_pending[l1i] & ~s->evtchn_mask[l1i])) {
|
||||
port = (l1i * BITS_PER_LONG) + __ffs(l2);
|
||||
synch_clear_bit(port, &s->evtchn_pending[0]);
|
||||
if ((handler = evtchns[port].handler) != NULL)
|
||||
{
|
||||
handler(port, evtchns[port].dev_id,
|
||||
regs);
|
||||
}
|
||||
else
|
||||
{
|
||||
printk(KERN_WARNING "unexpected event channel upcall on port %d!\n", port);
|
||||
}
|
||||
l2 = s->evtchn_pending[l1i] & ~s->evtchn_mask[l1i];
|
||||
printk(KERN_WARNING "unexpected event channel "
|
||||
"upcall on port %d!\n", port);
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure the hypervisor has a chance to notice that the
|
||||
upcall_pending condition has been cleared, so that we don't
|
||||
try and reinject the interrupt again. */
|
||||
(void)HYPERVISOR_xen_version(0, NULL);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void force_evtchn_callback(void)
|
||||
{
|
||||
(void)HYPERVISOR_xen_version(0, NULL);
|
||||
(void)HYPERVISOR_xen_version(0, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(force_evtchn_callback);
|
||||
@@ -3,7 +3,6 @@ subdir-y += vmx
|
||||
|
||||
obj-y += hvm.o
|
||||
obj-y += i8254.o
|
||||
obj-y += i8259.o
|
||||
obj-y += instrlen.o
|
||||
obj-y += intercept.o
|
||||
obj-y += io.o
|
||||
@@ -13,3 +12,4 @@ obj-y += pmtimer.o
|
||||
obj-y += rtc.o
|
||||
obj-y += vioapic.o
|
||||
obj-y += vlapic.o
|
||||
obj-y += vpic.o
|
||||
@@ -168,14 +168,13 @@ int hvm_domain_initialise(struct domain *d)
|
||||
|
||||
spin_lock_init(&d->arch.hvm_domain.pbuf_lock);
|
||||
spin_lock_init(&d->arch.hvm_domain.buffered_io_lock);
|
||||
spin_lock_init(&d->arch.hvm_domain.irq.lock);
|
||||
|
||||
rc = shadow_enable(d, SHM2_refcounts|SHM2_translate|SHM2_external);
|
||||
if ( rc != 0 )
|
||||
return rc;
|
||||
|
||||
pic_init(domain_vpic(d));
|
||||
register_pic_io_hook(d);
|
||||
|
||||
vpic_init(d);
|
||||
vioapic_init(d);
|
||||
|
||||
return 0;
|
||||
@@ -244,13 +243,14 @@ void hvm_vcpu_destroy(struct vcpu *v)
|
||||
|
||||
int cpu_get_interrupt(struct vcpu *v, int *type)
|
||||
{
|
||||
int irq;
|
||||
int vector;
|
||||
|
||||
if ( (irq = cpu_get_apic_interrupt(v, type)) != -1 )
|
||||
return irq;
|
||||
if ( (vector = cpu_get_apic_interrupt(v, type)) != -1 )
|
||||
return vector;
|
||||
|
||||
if ( (v->vcpu_id == 0) && ((irq = cpu_get_pic_interrupt(v, type)) != -1) )
|
||||
return irq;
|
||||
if ( (v->vcpu_id == 0) &&
|
||||
((vector = cpu_get_pic_interrupt(v, type)) != -1) )
|
||||
return vector;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1,515 +0,0 @@
|
||||
/*
|
||||
* i8259 interrupt controller emulation
|
||||
*
|
||||
* Copyright (c) 2003-2004 Fabrice Bellard
|
||||
* Copyright (c) 2005 Intel Corperation
|
||||
* Copyright (c) 2006 Keir Fraser, XenSource Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <xen/config.h>
|
||||
#include <xen/types.h>
|
||||
#include <xen/mm.h>
|
||||
#include <xen/xmalloc.h>
|
||||
#include <xen/lib.h>
|
||||
#include <xen/errno.h>
|
||||
#include <xen/sched.h>
|
||||
#include <asm/hvm/hvm.h>
|
||||
#include <asm/hvm/io.h>
|
||||
#include <asm/hvm/support.h>
|
||||
#include <asm/current.h>
|
||||
|
||||
static inline void pic_set_irq1(PicState *s, int irq, int level)
|
||||
{
|
||||
int mask = 1 << irq;
|
||||
|
||||
ASSERT(spin_is_locked(vpic_lock(s->pics_state)));
|
||||
|
||||
if ( s->elcr & mask )
|
||||
{
|
||||
/* Level triggered. */
|
||||
if ( level )
|
||||
{
|
||||
s->irr |= mask;
|
||||
s->last_irr |= mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
s->irr &= ~mask;
|
||||
s->last_irr &= ~mask;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Edge triggered. */
|
||||
if ( level )
|
||||
{
|
||||
if ( (s->last_irr & mask) == 0 )
|
||||
s->irr |= mask;
|
||||
s->last_irr |= mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
s->last_irr &= ~mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the highest priority found in mask. Return 8 if no irq. */
|
||||
static inline int get_priority(PicState *s, int mask)
|
||||
{
|
||||
int priority;
|
||||
|
||||
ASSERT(spin_is_locked(vpic_lock(s->pics_state)));
|
||||
|
||||
if ( mask == 0 )
|
||||
return 8;
|
||||
|
||||
priority = 0;
|
||||
while ( (mask & (1 << ((priority + s->priority_add) & 7))) == 0 )
|
||||
priority++;
|
||||
|
||||
return priority;
|
||||
}
|
||||
|
||||
/* Return the PIC's highest priority pending interrupt. Return -1 if none. */
|
||||
static int pic_get_irq(PicState *s)
|
||||
{
|
||||
int mask, cur_priority, priority;
|
||||
|
||||
ASSERT(spin_is_locked(vpic_lock(s->pics_state)));
|
||||
|
||||
mask = s->irr & ~s->imr;
|
||||
priority = get_priority(s, mask);
|
||||
if ( priority == 8 )
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Compute current priority. If special fully nested mode on the master,
|
||||
* the IRQ coming from the slave is not taken into account for the
|
||||
* priority computation.
|
||||
*/
|
||||
mask = s->isr;
|
||||
if ( s->special_fully_nested_mode && (s == &s->pics_state->pics[0]) )
|
||||
mask &= ~(1 << 2);
|
||||
cur_priority = get_priority(s, mask);
|
||||
if ( priority < cur_priority )
|
||||
/* Higher priority found: an irq should be generated. */
|
||||
return (priority + s->priority_add) & 7;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Raise irq to CPU if necessary. */
|
||||
static void pic_update_irq(struct vpic *vpic)
|
||||
{
|
||||
int irq2, irq;
|
||||
|
||||
ASSERT(spin_is_locked(vpic_lock(vpic)));
|
||||
|
||||
/* First look at slave PIC. */
|
||||
irq2 = pic_get_irq(&vpic->pics[1]);
|
||||
if ( irq2 >= 0 )
|
||||
{
|
||||
/* If irq request by slave pic, signal master PIC. */
|
||||
pic_set_irq1(&vpic->pics[0], 2, 1);
|
||||
pic_set_irq1(&vpic->pics[0], 2, 0);
|
||||
}
|
||||
|
||||
/* Look at requested IRQ. */
|
||||
irq = pic_get_irq(&vpic->pics[0]);
|
||||
if ( irq >= 0 )
|
||||
vpic->irq_pending = 1;
|
||||
}
|
||||
|
||||
void pic_set_irq(struct vpic *vpic, int irq, int level)
|
||||
{
|
||||
ASSERT(spin_is_locked(vpic_lock(vpic)));
|
||||
pic_set_irq1(&vpic->pics[irq >> 3], irq & 7, level);
|
||||
pic_update_irq(vpic);
|
||||
}
|
||||
|
||||
/* Acknowledge interrupt @irq. */
|
||||
static inline void pic_intack(PicState *s, int irq)
|
||||
{
|
||||
ASSERT(spin_is_locked(vpic_lock(s->pics_state)));
|
||||
|
||||
if ( s->auto_eoi )
|
||||
{
|
||||
if ( s->rotate_on_auto_eoi )
|
||||
s->priority_add = (irq + 1) & 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
s->isr |= (1 << irq);
|
||||
}
|
||||
|
||||
/* We don't clear a level sensitive interrupt here */
|
||||
if ( !(s->elcr & (1 << irq)) )
|
||||
s->irr &= ~(1 << irq);
|
||||
}
|
||||
|
||||
static int pic_read_irq(struct vpic *vpic)
|
||||
{
|
||||
int irq, irq2, intno;
|
||||
|
||||
spin_lock(vpic_lock(vpic));
|
||||
|
||||
irq = pic_get_irq(&vpic->pics[0]);
|
||||
if ( irq >= 0 )
|
||||
{
|
||||
pic_intack(&vpic->pics[0], irq);
|
||||
if ( irq == 2 )
|
||||
{
|
||||
irq2 = pic_get_irq(&vpic->pics[1]);
|
||||
if ( irq2 >= 0 )
|
||||
{
|
||||
pic_intack(&vpic->pics[1], irq2);
|
||||
}
|
||||
else
|
||||
{
|
||||
gdprintk(XENLOG_WARNING, "Spurious irq on slave i8259.\n");
|
||||
irq2 = 7;
|
||||
}
|
||||
intno = vpic->pics[1].irq_base + irq2;
|
||||
irq = irq2 + 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
intno = vpic->pics[0].irq_base + irq;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
irq = 7;
|
||||
intno = vpic->pics[0].irq_base + irq;
|
||||
gdprintk(XENLOG_WARNING, "Spurious irq on master i8259.\n");
|
||||
}
|
||||
|
||||
pic_update_irq(vpic);
|
||||
|
||||
spin_unlock(vpic_lock(vpic));
|
||||
|
||||
return intno;
|
||||
}
|
||||
|
||||
static void pic_reset(void *opaque)
|
||||
{
|
||||
PicState *s = opaque;
|
||||
|
||||
s->last_irr = 0;
|
||||
s->irr = 0;
|
||||
s->imr = 0;
|
||||
s->isr = 0;
|
||||
s->priority_add = 0;
|
||||
s->irq_base = 0;
|
||||
s->read_reg_select = 0;
|
||||
s->poll = 0;
|
||||
s->special_mask = 0;
|
||||
s->init_state = 0;
|
||||
s->auto_eoi = 0;
|
||||
s->rotate_on_auto_eoi = 0;
|
||||
s->special_fully_nested_mode = 0;
|
||||
s->init4 = 0;
|
||||
/* Note: ELCR is not reset */
|
||||
}
|
||||
|
||||
static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
PicState *s = opaque;
|
||||
int priority, cmd, irq;
|
||||
|
||||
ASSERT(spin_is_locked(vpic_lock(s->pics_state)));
|
||||
|
||||
addr &= 1;
|
||||
if ( addr == 0 )
|
||||
{
|
||||
if ( val & 0x10 )
|
||||
{
|
||||
pic_reset(s);
|
||||
s->pics_state->irq_pending = 0;
|
||||
s->init_state = 1;
|
||||
s->init4 = val & 1;
|
||||
}
|
||||
else if ( val & 0x08 )
|
||||
{
|
||||
if ( val & 0x04 )
|
||||
s->poll = 1;
|
||||
if ( val & 0x02 )
|
||||
s->read_reg_select = val & 1;
|
||||
if ( val & 0x40 )
|
||||
s->special_mask = (val >> 5) & 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd = val >> 5;
|
||||
switch ( cmd )
|
||||
{
|
||||
case 0:
|
||||
case 4:
|
||||
s->rotate_on_auto_eoi = cmd >> 2;
|
||||
break;
|
||||
case 1:
|
||||
case 5:
|
||||
priority = get_priority(s, s->isr);
|
||||
if (priority != 8) {
|
||||
irq = (priority + s->priority_add) & 7;
|
||||
s->isr &= ~(1 << irq);
|
||||
if (cmd == 5)
|
||||
s->priority_add = (irq + 1) & 7;
|
||||
pic_update_irq(s->pics_state);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
irq = val & 7;
|
||||
s->isr &= ~(1 << irq);
|
||||
pic_update_irq(s->pics_state);
|
||||
break;
|
||||
case 6:
|
||||
s->priority_add = (val + 1) & 7;
|
||||
pic_update_irq(s->pics_state);
|
||||
break;
|
||||
case 7:
|
||||
irq = val & 7;
|
||||
s->isr &= ~(1 << irq);
|
||||
s->priority_add = (irq + 1) & 7;
|
||||
pic_update_irq(s->pics_state);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch ( s->init_state )
|
||||
{
|
||||
case 0:
|
||||
s->imr = val;
|
||||
pic_update_irq(s->pics_state);
|
||||
break;
|
||||
case 1:
|
||||
s->irq_base = val & 0xf8;
|
||||
s->init_state = 2;
|
||||
break;
|
||||
case 2:
|
||||
s->init_state = s->init4 ? 3 : 0;
|
||||
break;
|
||||
case 3:
|
||||
s->special_fully_nested_mode = (val >> 4) & 1;
|
||||
s->auto_eoi = (val >> 1) & 1;
|
||||
s->init_state = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t pic_poll_read (PicState *s, uint32_t addr1)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ASSERT(spin_is_locked(vpic_lock(s->pics_state)));
|
||||
|
||||
ret = pic_get_irq(s);
|
||||
if ( ret >= 0 )
|
||||
{
|
||||
if ( addr1 >> 7 )
|
||||
{
|
||||
s->pics_state->pics[0].isr &= ~(1 << 2);
|
||||
s->pics_state->pics[0].irr &= ~(1 << 2);
|
||||
}
|
||||
s->irr &= ~(1 << ret);
|
||||
s->isr &= ~(1 << ret);
|
||||
if ( (addr1 >> 7) || (ret != 2) )
|
||||
pic_update_irq(s->pics_state);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = 0x07;
|
||||
pic_update_irq(s->pics_state);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t pic_ioport_read(void *opaque, uint32_t addr1)
|
||||
{
|
||||
PicState *s = opaque;
|
||||
|
||||
ASSERT(spin_is_locked(vpic_lock(s->pics_state)));
|
||||
|
||||
if ( s->poll )
|
||||
{
|
||||
s->poll = 0;
|
||||
return pic_poll_read(s, addr1);
|
||||
}
|
||||
|
||||
if ( (addr1 & 1) == 0 )
|
||||
return (s->read_reg_select ? s->isr : s->irr);
|
||||
|
||||
return s->imr;
|
||||
}
|
||||
|
||||
static void elcr_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
PicState *s = opaque;
|
||||
|
||||
ASSERT(spin_is_locked(vpic_lock(s->pics_state)));
|
||||
|
||||
s->elcr = val & s->elcr_mask;
|
||||
}
|
||||
|
||||
static uint32_t elcr_ioport_read(void *opaque, uint32_t addr1)
|
||||
{
|
||||
PicState *s = opaque;
|
||||
return s->elcr;
|
||||
}
|
||||
|
||||
static void pic_init1(int io_addr, int elcr_addr, PicState *s)
|
||||
{
|
||||
pic_reset(s);
|
||||
}
|
||||
|
||||
void pic_init(struct vpic *vpic)
|
||||
{
|
||||
memset(vpic, 0, sizeof(*vpic));
|
||||
spin_lock_init(vpic_lock(vpic));
|
||||
vpic->pics[0].pics_state = vpic;
|
||||
vpic->pics[1].pics_state = vpic;
|
||||
vpic->pics[0].elcr_mask = 0xf8;
|
||||
vpic->pics[1].elcr_mask = 0xde;
|
||||
pic_init1(0x20, 0x4d0, &vpic->pics[0]);
|
||||
pic_init1(0xa0, 0x4d1, &vpic->pics[1]);
|
||||
}
|
||||
|
||||
static int intercept_pic_io(ioreq_t *p)
|
||||
{
|
||||
struct vpic *vpic = domain_vpic(current->domain);
|
||||
uint32_t data;
|
||||
|
||||
if ( (p->size != 1) || (p->count != 1) )
|
||||
{
|
||||
gdprintk(XENLOG_WARNING,
|
||||
"PIC_IO wrong access size %d!\n", (int)p->size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ( p->dir == IOREQ_WRITE )
|
||||
{
|
||||
if ( p->data_is_ptr )
|
||||
(void)hvm_copy_from_guest_phys(&data, p->data, p->size);
|
||||
else
|
||||
data = p->data;
|
||||
spin_lock(vpic_lock(vpic));
|
||||
pic_ioport_write((void*)&vpic->pics[p->addr>>7],
|
||||
(uint32_t) p->addr, (uint32_t) (data & 0xff));
|
||||
spin_unlock(vpic_lock(vpic));
|
||||
}
|
||||
else
|
||||
{
|
||||
spin_lock(vpic_lock(vpic));
|
||||
data = pic_ioport_read(
|
||||
(void*)&vpic->pics[p->addr>>7], (uint32_t) p->addr);
|
||||
spin_unlock(vpic_lock(vpic));
|
||||
if ( p->data_is_ptr )
|
||||
(void)hvm_copy_to_guest_phys(p->data, &data, p->size);
|
||||
else
|
||||
p->data = (u64)data;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int intercept_elcr_io(ioreq_t *p)
|
||||
{
|
||||
struct vpic *vpic = domain_vpic(current->domain);
|
||||
uint32_t data;
|
||||
|
||||
if ( (p->size != 1) || (p->count != 1) )
|
||||
{
|
||||
gdprintk(XENLOG_WARNING,
|
||||
"PIC_IO wrong access size %d!\n", (int)p->size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ( p->dir == IOREQ_WRITE )
|
||||
{
|
||||
if ( p->data_is_ptr )
|
||||
(void)hvm_copy_from_guest_phys(&data, p->data, p->size);
|
||||
else
|
||||
data = p->data;
|
||||
spin_lock(vpic_lock(vpic));
|
||||
elcr_ioport_write((void*)&vpic->pics[p->addr&1],
|
||||
(uint32_t) p->addr, (uint32_t)( data & 0xff));
|
||||
spin_unlock(vpic_lock(vpic));
|
||||
}
|
||||
else
|
||||
{
|
||||
data = (u64) elcr_ioport_read(
|
||||
(void*)&vpic->pics[p->addr&1], (uint32_t) p->addr);
|
||||
if ( p->data_is_ptr )
|
||||
(void)hvm_copy_to_guest_phys(p->data, &data, p->size);
|
||||
else
|
||||
p->data = (u64)data;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void register_pic_io_hook(struct domain *d)
|
||||
{
|
||||
register_portio_handler(d, 0x20, 2, intercept_pic_io);
|
||||
register_portio_handler(d, 0x4d0, 1, intercept_elcr_io);
|
||||
register_portio_handler(d, 0xa0, 2, intercept_pic_io);
|
||||
register_portio_handler(d, 0x4d1, 1, intercept_elcr_io);
|
||||
}
|
||||
|
||||
int cpu_get_pic_interrupt(struct vcpu *v, int *type)
|
||||
{
|
||||
int intno;
|
||||
struct vpic *vpic = domain_vpic(v->domain);
|
||||
|
||||
if ( !vlapic_accept_pic_intr(v) )
|
||||
return -1;
|
||||
|
||||
if ( xchg(&vpic->irq_pending, 0) == 0 )
|
||||
return -1;
|
||||
|
||||
/* Read the irq from the PIC. */
|
||||
intno = pic_read_irq(vpic);
|
||||
*type = APIC_DM_EXTINT;
|
||||
return intno;
|
||||
}
|
||||
|
||||
int is_periodic_irq(struct vcpu *v, int irq, int type)
|
||||
{
|
||||
int vec;
|
||||
struct periodic_time *pt = &v->domain->arch.hvm_domain.pl_time.periodic_tm;
|
||||
|
||||
if ( pt->irq != 0 )
|
||||
return 0;
|
||||
|
||||
if ( type == APIC_DM_EXTINT )
|
||||
vec = domain_vpic(v->domain)->pics[0].irq_base;
|
||||
else
|
||||
vec = domain_vioapic(v->domain)->redirtbl[0].fields.vector;
|
||||
|
||||
return (irq == vec);
|
||||
}
|
||||
+10
-10
@@ -48,7 +48,7 @@ void hvm_pci_intx_assert(
|
||||
(hvm_irq->gsi_assert_count[isa_irq]++ == 0) )
|
||||
{
|
||||
vioapic_irq_positive_edge(d, isa_irq);
|
||||
pic_set_irq(&hvm_irq->vpic, isa_irq, 1);
|
||||
vpic_irq_positive_edge(d, isa_irq);
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -75,7 +75,7 @@ void hvm_pci_intx_deassert(
|
||||
isa_irq = hvm_irq->pci_link_route[link];
|
||||
if ( (--hvm_irq->pci_link_assert_count[link] == 0) && isa_irq &&
|
||||
(--hvm_irq->gsi_assert_count[isa_irq] == 0) )
|
||||
pic_set_irq(&hvm_irq->vpic, isa_irq, 0);
|
||||
vpic_irq_negative_edge(d, isa_irq);
|
||||
|
||||
out:
|
||||
spin_unlock(&hvm_irq->lock);
|
||||
@@ -94,7 +94,7 @@ void hvm_isa_irq_assert(
|
||||
(hvm_irq->gsi_assert_count[isa_irq]++ == 0) )
|
||||
{
|
||||
vioapic_irq_positive_edge(d, isa_irq);
|
||||
pic_set_irq(&hvm_irq->vpic, isa_irq, 1);
|
||||
vpic_irq_positive_edge(d, isa_irq);
|
||||
}
|
||||
|
||||
spin_unlock(&hvm_irq->lock);
|
||||
@@ -111,7 +111,7 @@ void hvm_isa_irq_deassert(
|
||||
|
||||
if ( __test_and_clear_bit(isa_irq, &hvm_irq->isa_irq) &&
|
||||
(--hvm_irq->gsi_assert_count[isa_irq] == 0) )
|
||||
pic_set_irq(&hvm_irq->vpic, isa_irq, 0);
|
||||
vpic_irq_negative_edge(d, isa_irq);
|
||||
|
||||
spin_unlock(&hvm_irq->lock);
|
||||
}
|
||||
@@ -140,7 +140,7 @@ void hvm_set_callback_irq_level(void)
|
||||
{
|
||||
vioapic_irq_positive_edge(d, gsi);
|
||||
if ( gsi <= 15 )
|
||||
pic_set_irq(&hvm_irq->vpic, gsi, 1);
|
||||
vpic_irq_positive_edge(d, gsi);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -149,7 +149,7 @@ void hvm_set_callback_irq_level(void)
|
||||
(--hvm_irq->gsi_assert_count[gsi] == 0) )
|
||||
{
|
||||
if ( gsi <= 15 )
|
||||
pic_set_irq(&hvm_irq->vpic, gsi, 0);
|
||||
vpic_irq_negative_edge(d, gsi);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,12 +175,12 @@ void hvm_set_pci_link_route(struct domain *d, u8 link, u8 isa_irq)
|
||||
goto out;
|
||||
|
||||
if ( old_isa_irq && (--hvm_irq->gsi_assert_count[old_isa_irq] == 0) )
|
||||
pic_set_irq(&hvm_irq->vpic, isa_irq, 0);
|
||||
vpic_irq_negative_edge(d, isa_irq);
|
||||
|
||||
if ( isa_irq && (hvm_irq->gsi_assert_count[isa_irq]++ == 0) )
|
||||
{
|
||||
vioapic_irq_positive_edge(d, isa_irq);
|
||||
pic_set_irq(&hvm_irq->vpic, isa_irq, 1);
|
||||
vpic_irq_positive_edge(d, isa_irq);
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -210,13 +210,13 @@ void hvm_set_callback_gsi(struct domain *d, unsigned int gsi)
|
||||
|
||||
if ( old_gsi && (--hvm_irq->gsi_assert_count[old_gsi] == 0) )
|
||||
if ( old_gsi <= 15 )
|
||||
pic_set_irq(&hvm_irq->vpic, old_gsi, 0);
|
||||
vpic_irq_negative_edge(d, old_gsi);
|
||||
|
||||
if ( gsi && (hvm_irq->gsi_assert_count[gsi]++ == 0) )
|
||||
{
|
||||
vioapic_irq_positive_edge(d, gsi);
|
||||
if ( gsi <= 15 )
|
||||
pic_set_irq(&hvm_irq->vpic, gsi, 1);
|
||||
vpic_irq_positive_edge(d, gsi);
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
+25
-24
@@ -128,32 +128,33 @@ asmlinkage void svm_intr_assist(void)
|
||||
}
|
||||
|
||||
/* have we got an interrupt to inject? */
|
||||
if ( intr_vector >= 0 )
|
||||
if ( intr_vector < 0 )
|
||||
return;
|
||||
|
||||
switch ( intr_type )
|
||||
{
|
||||
switch ( intr_type )
|
||||
{
|
||||
case APIC_DM_EXTINT:
|
||||
case APIC_DM_FIXED:
|
||||
case APIC_DM_LOWEST:
|
||||
/* Re-injecting a PIT interruptt? */
|
||||
if ( re_injecting && pt->enabled &&
|
||||
is_periodic_irq(v, intr_vector, intr_type) )
|
||||
++pt->pending_intr_nr;
|
||||
/* let's inject this interrupt */
|
||||
TRACE_3D(TRC_VMX_INTR, v->domain->domain_id, intr_vector, 0);
|
||||
svm_inject_extint(v, intr_vector);
|
||||
break;
|
||||
case APIC_DM_SMI:
|
||||
case APIC_DM_NMI:
|
||||
case APIC_DM_INIT:
|
||||
case APIC_DM_STARTUP:
|
||||
default:
|
||||
printk("Unsupported interrupt type: %d\n", intr_type);
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
hvm_interrupt_post(v, intr_vector, intr_type);
|
||||
case APIC_DM_EXTINT:
|
||||
case APIC_DM_FIXED:
|
||||
case APIC_DM_LOWEST:
|
||||
/* Re-injecting a PIT interruptt? */
|
||||
if ( re_injecting && pt->enabled &&
|
||||
is_periodic_irq(v, intr_vector, intr_type) )
|
||||
++pt->pending_intr_nr;
|
||||
/* let's inject this interrupt */
|
||||
TRACE_3D(TRC_VMX_INTR, v->domain->domain_id, intr_vector, 0);
|
||||
svm_inject_extint(v, intr_vector);
|
||||
break;
|
||||
case APIC_DM_SMI:
|
||||
case APIC_DM_NMI:
|
||||
case APIC_DM_INIT:
|
||||
case APIC_DM_STARTUP:
|
||||
default:
|
||||
printk("Unsupported interrupt type: %d\n", intr_type);
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
|
||||
hvm_interrupt_post(v, intr_vector, intr_type);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* vlapic.c: virtualize LAPIC for HVM vcpus.
|
||||
*
|
||||
* Copyright (c) 2004, Intel Corporation.
|
||||
* Copyright (c) 2006 Keir Fraser, XenSource Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
@@ -38,9 +39,6 @@
|
||||
#define VLAPIC_VERSION 0x00050014
|
||||
#define VLAPIC_LVT_NUM 6
|
||||
|
||||
/* XXX remove this definition after GFW enabled */
|
||||
#define VLAPIC_NO_BIOS
|
||||
|
||||
extern u32 get_apic_bus_cycle(void);
|
||||
|
||||
#define APIC_BUS_CYCLE_NS (((s_time_t)get_apic_bus_cycle()) / 1000)
|
||||
@@ -147,7 +145,6 @@ int vlapic_find_highest_irr(struct vlapic *vlapic)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int vlapic_set_irq(struct vlapic *vlapic, uint8_t vec, uint8_t trig)
|
||||
{
|
||||
int ret;
|
||||
@@ -860,7 +857,7 @@ int cpu_has_pending_irq(struct vcpu *v)
|
||||
if ( !vlapic_accept_pic_intr(v) )
|
||||
return 0;
|
||||
|
||||
return plat->irq.vpic.irq_pending;
|
||||
return plat->irq.vpic[0].int_output;
|
||||
}
|
||||
|
||||
void vlapic_post_injection(struct vcpu *v, int vector, int deliver_mode)
|
||||
@@ -960,15 +957,6 @@ int vlapic_init(struct vcpu *v)
|
||||
init_timer(&vlapic->vlapic_timer,
|
||||
vlapic_timer_fn, vlapic, v->processor);
|
||||
|
||||
#ifdef VLAPIC_NO_BIOS
|
||||
/* According to mp specification, BIOS will enable LVT0/1. */
|
||||
if ( v->vcpu_id == 0 )
|
||||
{
|
||||
vlapic_set_reg(vlapic, APIC_LVT0, APIC_MODE_EXTINT << 8);
|
||||
vlapic_set_reg(vlapic, APIC_LVT1, APIC_MODE_NMI << 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,8 @@ asmlinkage void vmx_intr_assist(void)
|
||||
|
||||
has_ext_irq = cpu_has_pending_irq(v);
|
||||
|
||||
if (unlikely(v->arch.hvm_vmx.vector_injected)) {
|
||||
if ( unlikely(v->arch.hvm_vmx.vector_injected) )
|
||||
{
|
||||
v->arch.hvm_vmx.vector_injected=0;
|
||||
if (unlikely(has_ext_irq)) enable_irq_window(v);
|
||||
return;
|
||||
@@ -123,7 +124,8 @@ asmlinkage void vmx_intr_assist(void)
|
||||
|
||||
/* This could be moved earlier in the VMX resume sequence. */
|
||||
idtv_info_field = __vmread(IDT_VECTORING_INFO_FIELD);
|
||||
if (unlikely(idtv_info_field & INTR_INFO_VALID_MASK)) {
|
||||
if ( unlikely(idtv_info_field & INTR_INFO_VALID_MASK) )
|
||||
{
|
||||
__vmwrite(VM_ENTRY_INTR_INFO_FIELD, idtv_info_field);
|
||||
|
||||
/*
|
||||
@@ -145,9 +147,11 @@ asmlinkage void vmx_intr_assist(void)
|
||||
return;
|
||||
}
|
||||
|
||||
if (likely(!has_ext_irq)) return;
|
||||
if ( likely(!has_ext_irq) )
|
||||
return;
|
||||
|
||||
if (unlikely(is_interruptibility_state())) {
|
||||
if ( unlikely(is_interruptibility_state()) )
|
||||
{
|
||||
/* pre-cleared for emulated instruction */
|
||||
enable_irq_window(v);
|
||||
HVM_DBG_LOG(DBG_LEVEL_1, "interruptibility");
|
||||
@@ -155,13 +159,18 @@ asmlinkage void vmx_intr_assist(void)
|
||||
}
|
||||
|
||||
eflags = __vmread(GUEST_RFLAGS);
|
||||
if (irq_masked(eflags)) {
|
||||
if ( irq_masked(eflags) )
|
||||
{
|
||||
enable_irq_window(v);
|
||||
return;
|
||||
}
|
||||
|
||||
highest_vector = cpu_get_interrupt(v, &intr_type);
|
||||
switch (intr_type) {
|
||||
if ( highest_vector < 0 )
|
||||
return;
|
||||
|
||||
switch ( intr_type )
|
||||
{
|
||||
case APIC_DM_EXTINT:
|
||||
case APIC_DM_FIXED:
|
||||
case APIC_DM_LOWEST:
|
||||
@@ -180,7 +189,6 @@ asmlinkage void vmx_intr_assist(void)
|
||||
}
|
||||
|
||||
hvm_interrupt_post(v, highest_vector, intr_type);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -0,0 +1,463 @@
|
||||
/*
|
||||
* i8259 interrupt controller emulation
|
||||
*
|
||||
* Copyright (c) 2003-2004 Fabrice Bellard
|
||||
* Copyright (c) 2005 Intel Corperation
|
||||
* Copyright (c) 2006 Keir Fraser, XenSource Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <xen/config.h>
|
||||
#include <xen/types.h>
|
||||
#include <xen/event.h>
|
||||
#include <xen/lib.h>
|
||||
#include <xen/errno.h>
|
||||
#include <xen/sched.h>
|
||||
#include <asm/hvm/hvm.h>
|
||||
#include <asm/hvm/io.h>
|
||||
#include <asm/hvm/support.h>
|
||||
|
||||
#define vpic_domain(v) (container_of((v), struct domain, \
|
||||
arch.hvm_domain.irq.vpic[!vpic->is_master]))
|
||||
#define __vpic_lock(v) &container_of((v), struct hvm_irq, \
|
||||
vpic[!(v)->is_master])->lock
|
||||
#define vpic_lock(v) spin_lock(__vpic_lock(v))
|
||||
#define vpic_unlock(v) spin_unlock(__vpic_lock(v))
|
||||
#define vpic_is_locked(v) spin_is_locked(__vpic_lock(v))
|
||||
#define vpic_elcr_mask(v) (vpic->is_master ? (uint8_t)0xd8 : (uint8_t)0xfe);
|
||||
|
||||
/* Return the highest priority found in mask. Return 8 if none. */
|
||||
#define VPIC_PRIO_NONE 8
|
||||
static int vpic_get_priority(struct vpic *vpic, uint8_t mask)
|
||||
{
|
||||
int prio;
|
||||
|
||||
ASSERT(vpic_is_locked(vpic));
|
||||
|
||||
if ( mask == 0 )
|
||||
return VPIC_PRIO_NONE;
|
||||
|
||||
/* prio = ffs(mask ROL vpic->priority_add); */
|
||||
asm ( "rol %%cl,%b1 ; bsf %1,%0"
|
||||
: "=r" (prio) : "r" ((uint32_t)mask), "c" (vpic->priority_add) );
|
||||
return prio;
|
||||
}
|
||||
|
||||
/* Return the PIC's highest priority pending interrupt. Return -1 if none. */
|
||||
static int vpic_get_highest_priority_irq(struct vpic *vpic)
|
||||
{
|
||||
int cur_priority, priority, irq;
|
||||
uint8_t mask;
|
||||
|
||||
ASSERT(vpic_is_locked(vpic));
|
||||
|
||||
mask = vpic->irr & ~vpic->imr;
|
||||
priority = vpic_get_priority(vpic, mask);
|
||||
if ( priority == VPIC_PRIO_NONE )
|
||||
return -1;
|
||||
|
||||
irq = (priority + vpic->priority_add) & 7;
|
||||
|
||||
/*
|
||||
* Compute current priority. If special fully nested mode on the master,
|
||||
* the IRQ coming from the slave is not taken into account for the
|
||||
* priority computation. In special mask mode, masked interrupts do not
|
||||
* block lower-priority interrupts even if their IS bit is set.
|
||||
*/
|
||||
mask = vpic->isr;
|
||||
if ( vpic->special_fully_nested_mode && vpic->is_master && (irq == 2) )
|
||||
mask &= ~(1 << 2);
|
||||
if ( vpic->special_mask_mode )
|
||||
mask &= ~vpic->imr;
|
||||
cur_priority = vpic_get_priority(vpic, mask);
|
||||
|
||||
/* If a higher priority is found then an irq should be generated. */
|
||||
return (priority < cur_priority) ? irq : -1;
|
||||
}
|
||||
|
||||
static void vpic_update_int_output(struct vpic *vpic)
|
||||
{
|
||||
int irq;
|
||||
|
||||
ASSERT(vpic_is_locked(vpic));
|
||||
|
||||
irq = vpic_get_highest_priority_irq(vpic);
|
||||
if ( vpic->int_output == (irq >= 0) )
|
||||
return;
|
||||
|
||||
/* INT line transition L->H or H->L. */
|
||||
vpic->int_output = !vpic->int_output;
|
||||
|
||||
if ( vpic->int_output )
|
||||
{
|
||||
if ( vpic->is_master )
|
||||
{
|
||||
/* Master INT line is connected to VCPU0's VLAPIC LVT0. */
|
||||
struct vcpu *v = vpic_domain(vpic)->vcpu[0];
|
||||
if ( (v != NULL) && vlapic_accept_pic_intr(v) )
|
||||
vcpu_kick(v);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Assert slave line in master PIC. */
|
||||
(--vpic)->irr |= 1 << 2;
|
||||
vpic_update_int_output(vpic);
|
||||
}
|
||||
}
|
||||
else if ( !vpic->is_master )
|
||||
{
|
||||
/* Clear slave line in master PIC. */
|
||||
(--vpic)->irr &= ~(1 << 2);
|
||||
vpic_update_int_output(vpic);
|
||||
}
|
||||
}
|
||||
|
||||
static void __vpic_intack(struct vpic *vpic, int irq)
|
||||
{
|
||||
uint8_t mask = 1 << irq;
|
||||
|
||||
ASSERT(vpic_is_locked(vpic));
|
||||
|
||||
/* Edge-triggered: clear the IRR (forget the edge). */
|
||||
if ( !(vpic->elcr & mask) )
|
||||
vpic->irr &= ~mask;
|
||||
|
||||
if ( !vpic->auto_eoi )
|
||||
vpic->isr |= mask;
|
||||
else if ( vpic->rotate_on_auto_eoi )
|
||||
vpic->priority_add = (irq + 1) & 7;
|
||||
|
||||
vpic_update_int_output(vpic);
|
||||
}
|
||||
|
||||
static int vpic_intack(struct vpic *vpic)
|
||||
{
|
||||
int irq = -1;
|
||||
|
||||
vpic_lock(vpic);
|
||||
|
||||
if ( !vpic->int_output )
|
||||
goto out;
|
||||
|
||||
irq = vpic_get_highest_priority_irq(vpic);
|
||||
BUG_ON(irq < 0);
|
||||
__vpic_intack(vpic, irq);
|
||||
|
||||
if ( (irq == 2) && vpic->is_master )
|
||||
{
|
||||
vpic++; /* Slave PIC */
|
||||
irq = vpic_get_highest_priority_irq(vpic);
|
||||
BUG_ON(irq < 0);
|
||||
__vpic_intack(vpic, irq);
|
||||
irq += 8;
|
||||
}
|
||||
|
||||
out:
|
||||
vpic_unlock(vpic);
|
||||
return irq;
|
||||
}
|
||||
|
||||
static void vpic_ioport_write(struct vpic *vpic, uint32_t addr, uint32_t val)
|
||||
{
|
||||
int priority, cmd, irq;
|
||||
uint8_t mask;
|
||||
|
||||
vpic_lock(vpic);
|
||||
|
||||
addr &= 1;
|
||||
if ( addr == 0 )
|
||||
{
|
||||
if ( val & 0x10 )
|
||||
{
|
||||
/* ICW1 */
|
||||
/* Clear edge-sensing logic. */
|
||||
vpic->irr &= vpic->elcr;
|
||||
|
||||
/* No interrupts masked or in service. */
|
||||
vpic->imr = vpic->isr = 0;
|
||||
|
||||
/* IR7 is lowest priority. */
|
||||
vpic->priority_add = 0;
|
||||
vpic->rotate_on_auto_eoi = 0;
|
||||
|
||||
vpic->special_mask_mode = 0;
|
||||
vpic->readsel_isr = 0;
|
||||
vpic->poll = 0;
|
||||
|
||||
if ( !(val & 1) )
|
||||
{
|
||||
/* NO ICW4: ICW4 features are cleared. */
|
||||
vpic->auto_eoi = 0;
|
||||
vpic->special_fully_nested_mode = 0;
|
||||
}
|
||||
|
||||
vpic->init_state = ((val & 3) << 2) | 1;
|
||||
}
|
||||
else if ( val & 0x08 )
|
||||
{
|
||||
/* OCW3 */
|
||||
if ( val & 0x04 )
|
||||
vpic->poll = 1;
|
||||
if ( val & 0x02 )
|
||||
vpic->readsel_isr = val & 1;
|
||||
if ( val & 0x40 )
|
||||
vpic->special_mask_mode = (val >> 5) & 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* OCW2 */
|
||||
cmd = val >> 5;
|
||||
switch ( cmd )
|
||||
{
|
||||
case 0: /* Rotate in AEOI Mode (Clear) */
|
||||
case 4: /* Rotate in AEOI Mode (Set) */
|
||||
vpic->rotate_on_auto_eoi = cmd >> 2;
|
||||
break;
|
||||
case 1: /* Non-Specific EOI */
|
||||
case 5: /* Non-Specific EOI & Rotate */
|
||||
mask = vpic->isr;
|
||||
if ( vpic->special_mask_mode )
|
||||
mask &= ~vpic->imr; /* SMM: ignore masked IRs. */
|
||||
priority = vpic_get_priority(vpic, mask);
|
||||
if ( priority == VPIC_PRIO_NONE )
|
||||
break;
|
||||
irq = (priority + vpic->priority_add) & 7;
|
||||
vpic->isr &= ~(1 << irq);
|
||||
if ( cmd == 5 )
|
||||
vpic->priority_add = (irq + 1) & 7;
|
||||
break;
|
||||
case 3: /* Specific EOI */
|
||||
case 7: /* Specific EOI & Rotate */
|
||||
irq = val & 7;
|
||||
vpic->isr &= ~(1 << irq);
|
||||
if ( cmd == 7 )
|
||||
vpic->priority_add = (irq + 1) & 7;
|
||||
break;
|
||||
case 6: /* Set Priority */
|
||||
vpic->priority_add = (val + 1) & 7;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch ( vpic->init_state & 3 )
|
||||
{
|
||||
case 0:
|
||||
/* OCW1 */
|
||||
vpic->imr = val;
|
||||
break;
|
||||
case 1:
|
||||
/* ICW2 */
|
||||
vpic->irq_base = val & 0xf8;
|
||||
vpic->init_state++;
|
||||
if ( !(vpic->init_state & 8) )
|
||||
break; /* CASCADE mode: wait for write to ICW3. */
|
||||
/* SNGL mode: fall through (no ICW3). */
|
||||
case 2:
|
||||
/* ICW3 */
|
||||
vpic->init_state++;
|
||||
if ( !(vpic->init_state & 4) )
|
||||
vpic->init_state = 0; /* No ICW4: init done */
|
||||
break;
|
||||
case 3:
|
||||
/* ICW4 */
|
||||
vpic->special_fully_nested_mode = (val >> 4) & 1;
|
||||
vpic->auto_eoi = (val >> 1) & 1;
|
||||
vpic->init_state = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vpic_update_int_output(vpic);
|
||||
|
||||
vpic_unlock(vpic);
|
||||
}
|
||||
|
||||
static uint32_t vpic_ioport_read(struct vpic *vpic, uint32_t addr)
|
||||
{
|
||||
if ( vpic->poll )
|
||||
{
|
||||
vpic->poll = 0;
|
||||
return vpic_intack(vpic);
|
||||
}
|
||||
|
||||
if ( (addr & 1) == 0 )
|
||||
return (vpic->readsel_isr ? vpic->isr : vpic->irr);
|
||||
|
||||
return vpic->imr;
|
||||
}
|
||||
|
||||
static int vpic_intercept_pic_io(ioreq_t *p)
|
||||
{
|
||||
struct vpic *vpic;
|
||||
uint32_t data;
|
||||
|
||||
if ( (p->size != 1) || (p->count != 1) )
|
||||
{
|
||||
gdprintk(XENLOG_WARNING, "PIC_IO bad access size %d\n", (int)p->size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vpic = ¤t->domain->arch.hvm_domain.irq.vpic[p->addr >> 7];
|
||||
|
||||
if ( p->dir == IOREQ_WRITE )
|
||||
{
|
||||
if ( p->data_is_ptr )
|
||||
(void)hvm_copy_from_guest_phys(&data, p->data, p->size);
|
||||
else
|
||||
data = p->data;
|
||||
vpic_ioport_write(vpic, (uint32_t)p->addr, (uint8_t)data);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = vpic_ioport_read(vpic, (uint32_t)p->addr);
|
||||
if ( p->data_is_ptr )
|
||||
(void)hvm_copy_to_guest_phys(p->data, &data, p->size);
|
||||
else
|
||||
p->data = (u64)data;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int vpic_intercept_elcr_io(ioreq_t *p)
|
||||
{
|
||||
struct vpic *vpic;
|
||||
uint32_t data;
|
||||
|
||||
if ( (p->size != 1) || (p->count != 1) )
|
||||
{
|
||||
gdprintk(XENLOG_WARNING, "PIC_IO bad access size %d\n", (int)p->size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vpic = ¤t->domain->arch.hvm_domain.irq.vpic[p->addr & 1];
|
||||
|
||||
if ( p->dir == IOREQ_WRITE )
|
||||
{
|
||||
if ( p->data_is_ptr )
|
||||
(void)hvm_copy_from_guest_phys(&data, p->data, p->size);
|
||||
else
|
||||
data = p->data;
|
||||
|
||||
/* Some IRs are always edge trig. Slave IR is always level trig. */
|
||||
data &= vpic_elcr_mask(vpic);
|
||||
if ( vpic->is_master )
|
||||
data |= 1 << 2;
|
||||
vpic->elcr = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Reader should not see hardcoded level-triggered slave IR. */
|
||||
data = vpic->elcr & vpic_elcr_mask(vpic);
|
||||
|
||||
if ( p->data_is_ptr )
|
||||
(void)hvm_copy_to_guest_phys(p->data, &data, p->size);
|
||||
else
|
||||
p->data = data;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void vpic_init(struct domain *d)
|
||||
{
|
||||
struct vpic *vpic;
|
||||
|
||||
/* Master PIC. */
|
||||
vpic = &d->arch.hvm_domain.irq.vpic[0];
|
||||
memset(vpic, 0, sizeof(*vpic));
|
||||
vpic->is_master = 1;
|
||||
vpic->elcr = 1 << 2;
|
||||
register_portio_handler(d, 0x20, 2, vpic_intercept_pic_io);
|
||||
register_portio_handler(d, 0x4d0, 1, vpic_intercept_elcr_io);
|
||||
|
||||
/* Slave PIC. */
|
||||
vpic++;
|
||||
memset(vpic, 0, sizeof(*vpic));
|
||||
register_portio_handler(d, 0xa0, 2, vpic_intercept_pic_io);
|
||||
register_portio_handler(d, 0x4d1, 1, vpic_intercept_elcr_io);
|
||||
}
|
||||
|
||||
void vpic_irq_positive_edge(struct domain *d, int irq)
|
||||
{
|
||||
struct vpic *vpic = &d->arch.hvm_domain.irq.vpic[irq >> 3];
|
||||
uint8_t mask = 1 << (irq & 7);
|
||||
|
||||
ASSERT(irq <= 15);
|
||||
ASSERT(vpic_is_locked(vpic));
|
||||
|
||||
if ( irq == 2 )
|
||||
return;
|
||||
|
||||
vpic->irr |= mask;
|
||||
if ( !(vpic->imr & mask) )
|
||||
vpic_update_int_output(vpic);
|
||||
}
|
||||
|
||||
void vpic_irq_negative_edge(struct domain *d, int irq)
|
||||
{
|
||||
struct vpic *vpic = &d->arch.hvm_domain.irq.vpic[irq >> 3];
|
||||
uint8_t mask = 1 << (irq & 7);
|
||||
|
||||
ASSERT(irq <= 15);
|
||||
ASSERT(vpic_is_locked(vpic));
|
||||
|
||||
if ( irq == 2 )
|
||||
return;
|
||||
|
||||
vpic->irr &= ~mask;
|
||||
if ( !(vpic->imr & mask) )
|
||||
vpic_update_int_output(vpic);
|
||||
}
|
||||
|
||||
int cpu_get_pic_interrupt(struct vcpu *v, int *type)
|
||||
{
|
||||
int irq, vector;
|
||||
struct vpic *vpic = &v->domain->arch.hvm_domain.irq.vpic[0];
|
||||
|
||||
if ( !vlapic_accept_pic_intr(v) || !vpic->int_output )
|
||||
return -1;
|
||||
|
||||
irq = vpic_intack(vpic);
|
||||
if ( irq == -1 )
|
||||
return -1;
|
||||
|
||||
vector = vpic[irq >> 3].irq_base + (irq & 7);
|
||||
*type = APIC_DM_EXTINT;
|
||||
return vector;
|
||||
}
|
||||
|
||||
int is_periodic_irq(struct vcpu *v, int irq, int type)
|
||||
{
|
||||
int vec;
|
||||
struct periodic_time *pt = &v->domain->arch.hvm_domain.pl_time.periodic_tm;
|
||||
|
||||
if ( pt->irq != 0 )
|
||||
return 0;
|
||||
|
||||
if ( type == APIC_DM_EXTINT )
|
||||
vec = v->domain->arch.hvm_domain.irq.vpic[0].irq_base;
|
||||
else
|
||||
vec = domain_vioapic(v->domain)->redirtbl[0].fields.vector;
|
||||
|
||||
return (irq == vec);
|
||||
}
|
||||
@@ -75,7 +75,7 @@ struct hvm_irq {
|
||||
* 8-15: Slave 8259 PIC, IO-APIC pins 8-15
|
||||
* 16+ : IO-APIC pins 16+
|
||||
*/
|
||||
struct vpic vpic;
|
||||
struct vpic vpic[2]; /* 0=master; 1=slave */
|
||||
struct vioapic vioapic;
|
||||
|
||||
/* Last VCPU that was delivered a LowestPrio interrupt. */
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* hvm_vlapic.h: virtualize LAPIC definitions.
|
||||
*
|
||||
* Copyright (c) 2004, Intel Corporation.
|
||||
* Copyright (c) 2006 Keir Fraser, XenSource Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
|
||||
@@ -27,40 +27,55 @@
|
||||
#ifndef __ASM_X86_HVM_VPIC_H__
|
||||
#define __ASM_X86_HVM_VPIC_H__
|
||||
|
||||
#define domain_vpic(d) (&(d)->arch.hvm_domain.irq.vpic)
|
||||
#define vpic_domain(v) (container_of((v), struct domain, \
|
||||
arch.hvm_domain.irq.vpic))
|
||||
#define vpic_lock(v) (&container_of((v), struct hvm_irq, vpic)->lock)
|
||||
|
||||
typedef struct PicState {
|
||||
uint8_t last_irr; /* edge detection */
|
||||
uint8_t irr; /* interrupt request register */
|
||||
uint8_t imr; /* interrupt mask register */
|
||||
uint8_t isr; /* interrupt service register */
|
||||
uint8_t priority_add; /* highest irq priority */
|
||||
uint8_t irq_base;
|
||||
uint8_t read_reg_select;
|
||||
uint8_t poll;
|
||||
uint8_t special_mask;
|
||||
uint8_t init_state;
|
||||
uint8_t auto_eoi;
|
||||
uint8_t rotate_on_auto_eoi;
|
||||
uint8_t special_fully_nested_mode;
|
||||
uint8_t init4; /* true if 4 byte init */
|
||||
uint8_t elcr; /* PIIX edge/trigger selection*/
|
||||
uint8_t elcr_mask;
|
||||
struct vpic *pics_state;
|
||||
} PicState;
|
||||
|
||||
struct vpic {
|
||||
/* 0 is master pic, 1 is slave pic */
|
||||
PicState pics[2];
|
||||
int irq_pending;
|
||||
/* IR line bitmasks. */
|
||||
uint8_t irr, imr, isr;
|
||||
|
||||
/* Line IRx maps to IRQ irq_base+x */
|
||||
uint8_t irq_base;
|
||||
|
||||
/*
|
||||
* Where are we in ICW2-4 initialisation (0 means no init in progress)?
|
||||
* Bits 0-1 (=x): Next write at A=1 sets ICW(x+1).
|
||||
* Bit 2: ICW1.IC4 (1 == ICW4 included in init sequence)
|
||||
* Bit 3: ICW1.SNGL (0 == ICW3 included in init sequence)
|
||||
*/
|
||||
uint8_t init_state:4;
|
||||
|
||||
/* IR line with highest priority. */
|
||||
uint8_t priority_add:4;
|
||||
|
||||
/* Reads from A=0 obtain ISR or IRR? */
|
||||
uint8_t readsel_isr:1;
|
||||
|
||||
/* Reads perform a polling read? */
|
||||
uint8_t poll:1;
|
||||
|
||||
/* Automatically clear IRQs from the ISR during INTA? */
|
||||
uint8_t auto_eoi:1;
|
||||
|
||||
/* Automatically rotate IRQ priorities during AEOI? */
|
||||
uint8_t rotate_on_auto_eoi:1;
|
||||
|
||||
/* Exclude slave inputs when considering in-service IRQs? */
|
||||
uint8_t special_fully_nested_mode:1;
|
||||
|
||||
/* Special mask mode excludes masked IRs from AEOI and priority checks. */
|
||||
uint8_t special_mask_mode:1;
|
||||
|
||||
/* Is this a master PIC or slave PIC? (NB. This is not programmable.) */
|
||||
uint8_t is_master:1;
|
||||
|
||||
/* Edge/trigger selection. */
|
||||
uint8_t elcr;
|
||||
|
||||
/* Virtual INT output. */
|
||||
uint8_t int_output;
|
||||
};
|
||||
|
||||
void pic_set_irq(struct vpic *vpic, int irq, int level);
|
||||
void pic_init(struct vpic *vpic);
|
||||
void register_pic_io_hook(struct domain *d);
|
||||
void vpic_irq_positive_edge(struct domain *d, int irq);
|
||||
void vpic_irq_negative_edge(struct domain *d, int irq);
|
||||
void vpic_init(struct domain *d);
|
||||
int cpu_get_pic_interrupt(struct vcpu *v, int *type);
|
||||
int is_periodic_irq(struct vcpu *v, int irq, int type);
|
||||
|
||||
|
||||