# HG changeset patch
# User kaf24@xxxxxxxxxxxxxxxxxxxxx
# Node ID d37b210bb8a7fc3154c72adb6b673f47d57864c4
# Parent 074b4b34e049269f25b3134bae8f6f1efd7b0cdb
[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@xxxxxxxxxxxxx>
---
xen/arch/x86/hvm/i8259.c | 515 ---------------------
tools/firmware/hvmloader/apic_regs.h | 108 ++++
tools/firmware/hvmloader/hvmloader.c | 16
tools/firmware/hvmloader/util.c | 26 -
tools/firmware/hvmloader/util.h | 6
unmodified_drivers/linux-2.6/platform-pci/evtchn.c | 32 -
xen/arch/x86/hvm/Makefile | 2
xen/arch/x86/hvm/hvm.c | 20
xen/arch/x86/hvm/irq.c | 20
xen/arch/x86/hvm/svm/intr.c | 49 +
xen/arch/x86/hvm/vlapic.c | 16
xen/arch/x86/hvm/vmx/io.c | 24
xen/arch/x86/hvm/vpic.c | 463 ++++++++++++++++++
xen/include/asm-x86/hvm/irq.h | 2
xen/include/asm-x86/hvm/vlapic.h | 1
xen/include/asm-x86/hvm/vpic.h | 73 +-
16 files changed, 716 insertions(+), 657 deletions(-)
diff -r 074b4b34e049 -r d37b210bb8a7 tools/firmware/hvmloader/hvmloader.c
--- a/tools/firmware/hvmloader/hvmloader.c Fri Nov 24 15:42:14 2006 +0000
+++ b/tools/firmware/hvmloader/hvmloader.c Sun Nov 26 13:37:27 2006 +0000
@@ -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)
diff -r 074b4b34e049 -r d37b210bb8a7 tools/firmware/hvmloader/util.c
--- a/tools/firmware/hvmloader/util.c Fri Nov 24 15:42:14 2006 +0000
+++ b/tools/firmware/hvmloader/util.c Sun Nov 26 13:37:27 2006 +0000
@@ -309,22 +309,24 @@ uint64_t e820_malloc(uint64_t size, uint
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);
-
- *ioregsel = reg;
- wmb();
- *iowin = val;
+ *(volatile uint32_t *)(IOAPIC_BASE_ADDRESS + 0x00) = reg;
+ *(volatile uint32_t *)(IOAPIC_BASE_ADDRESS + 0x10) = 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) \
diff -r 074b4b34e049 -r d37b210bb8a7 tools/firmware/hvmloader/util.h
--- a/tools/firmware/hvmloader/util.h Fri Nov 24 15:42:14 2006 +0000
+++ b/tools/firmware/hvmloader/util.h Sun Nov 26 13:37:27 2006 +0000
@@ -19,6 +19,12 @@ uint8_t inb(uint16_t addr);
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);
diff -r 074b4b34e049 -r d37b210bb8a7
unmodified_drivers/linux-2.6/platform-pci/evtchn.c
--- a/unmodified_drivers/linux-2.6/platform-pci/evtchn.c Fri Nov 24
15:42:14 2006 +0000
+++ b/unmodified_drivers/linux-2.6/platform-pci/evtchn.c Sun Nov 26
13:37:27 2006 +0000
@@ -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, vo
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);
diff -r 074b4b34e049 -r d37b210bb8a7 xen/arch/x86/hvm/Makefile
--- a/xen/arch/x86/hvm/Makefile Fri Nov 24 15:42:14 2006 +0000
+++ b/xen/arch/x86/hvm/Makefile Sun Nov 26 13:37:27 2006 +0000
@@ -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 += rtc.o
obj-y += rtc.o
obj-y += vioapic.o
obj-y += vlapic.o
+obj-y += vpic.o
diff -r 074b4b34e049 -r d37b210bb8a7 xen/arch/x86/hvm/hvm.c
--- a/xen/arch/x86/hvm/hvm.c Fri Nov 24 15:42:14 2006 +0000
+++ b/xen/arch/x86/hvm/hvm.c Sun Nov 26 13:37:27 2006 +0000
@@ -168,14 +168,13 @@ int hvm_domain_initialise(struct domain
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;
-
- if ( (irq = cpu_get_apic_interrupt(v, type)) != -1 )
- return irq;
-
- if ( (v->vcpu_id == 0) && ((irq = cpu_get_pic_interrupt(v, type)) != -1) )
- return irq;
+ int vector;
+
+ if ( (vector = cpu_get_apic_interrupt(v, type)) != -1 )
+ return vector;
+
+ if ( (v->vcpu_id == 0) &&
+ ((vector = cpu_get_pic_interrupt(v, type)) != -1) )
+ return vector;
return -1;
}
diff -r 074b4b34e049 -r d37b210bb8a7 xen/arch/x86/hvm/irq.c
--- a/xen/arch/x86/hvm/irq.c Fri Nov 24 15:42:14 2006 +0000
+++ b/xen/arch/x86/hvm/irq.c Sun Nov 26 13:37:27 2006 +0000
@@ -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 domai
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
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:
diff -r 074b4b34e049 -r d37b210bb8a7 xen/arch/x86/hvm/svm/intr.c
--- a/xen/arch/x86/hvm/svm/intr.c Fri Nov 24 15:42:14 2006 +0000
+++ b/xen/arch/x86/hvm/svm/intr.c Sun Nov 26 13:37:27 2006 +0000
@@ -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);
}
/*
diff -r 074b4b34e049 -r d37b210bb8a7 xen/arch/x86/hvm/vlapic.c
--- a/xen/arch/x86/hvm/vlapic.c Fri Nov 24 15:42:14 2006 +0000
+++ b/xen/arch/x86/hvm/vlapic.c Sun Nov 26 13:37:27 2006 +0000
@@ -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 vlapi
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;
}
diff -r 074b4b34e049 -r d37b210bb8a7 xen/arch/x86/hvm/vmx/io.c
--- a/xen/arch/x86/hvm/vmx/io.c Fri Nov 24 15:42:14 2006 +0000
+++ b/xen/arch/x86/hvm/vmx/io.c Sun Nov 26 13:37:27 2006 +0000
@@ -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 (unlikely(is_interruptibility_state())) {
+ if ( likely(!has_ext_irq) )
+ return;
+
+ 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;
}
/*
diff -r 074b4b34e049 -r d37b210bb8a7 xen/include/asm-x86/hvm/irq.h
--- a/xen/include/asm-x86/hvm/irq.h Fri Nov 24 15:42:14 2006 +0000
+++ b/xen/include/asm-x86/hvm/irq.h Sun Nov 26 13:37:27 2006 +0000
@@ -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. */
diff -r 074b4b34e049 -r d37b210bb8a7 xen/include/asm-x86/hvm/vlapic.h
--- a/xen/include/asm-x86/hvm/vlapic.h Fri Nov 24 15:42:14 2006 +0000
+++ b/xen/include/asm-x86/hvm/vlapic.h Sun Nov 26 13:37:27 2006 +0000
@@ -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,
diff -r 074b4b34e049 -r d37b210bb8a7 xen/include/asm-x86/hvm/vpic.h
--- a/xen/include/asm-x86/hvm/vpic.h Fri Nov 24 15:42:14 2006 +0000
+++ b/xen/include/asm-x86/hvm/vpic.h Sun Nov 26 13:37:27 2006 +0000
@@ -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)
+struct vpic {
+ /* IR line bitmasks. */
+ uint8_t irr, imr, isr;
-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 */
+ /* Line IRx maps to IRQ irq_base+x */
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;
+ /*
+ * 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);
diff -r 074b4b34e049 -r d37b210bb8a7 tools/firmware/hvmloader/apic_regs.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/firmware/hvmloader/apic_regs.h Sun Nov 26 13:37:27 2006 +0000
@@ -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
diff -r 074b4b34e049 -r d37b210bb8a7 xen/arch/x86/hvm/vpic.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/arch/x86/hvm/vpic.c Sun Nov 26 13:37:27 2006 +0000
@@ -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);
+}
diff -r 074b4b34e049 -r d37b210bb8a7 xen/arch/x86/hvm/i8259.c
--- a/xen/arch/x86/hvm/i8259.c Fri Nov 24 15:42:14 2006 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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);
-}
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|