]> xenbits.xensource.com Git - xen.git/commitdiff
[IA64] Add support to physdev_ops
authorawilliam@xenbuild.aw <awilliam@xenbuild.aw>
Fri, 21 Apr 2006 15:03:19 +0000 (09:03 -0600)
committerawilliam@xenbuild.aw <awilliam@xenbuild.aw>
Fri, 21 Apr 2006 15:03:19 +0000 (09:03 -0600)
Add support to physdev ops, and thus give IOSAPIC RTEs
managed by Xen now. Dom0 now issues hypercall to r/w
RTE entry. Another change is the irq vector allocation
which is also owned by xen now.

After this change, the IOSAPIC is almost owned by xen
with only exception as IOSAPIC EOI which is still issued
by dom0 directly. But that's OK since currently dom0
owns all external physical devices. Later full event
channel mechanism will provide necessary support for
driver domain, and at that time, dom0 instead issues
physdev_op (PHYSDEVOP_IRQ_UNMASK_NOTIFY) naturally as
replace of IOSAPIC EOI.

Signed-off-by Kevin Tian <kevin.tian@intel.com>

linux-2.6-xen-sparse/arch/ia64/kernel/iosapic.c
linux-2.6-xen-sparse/arch/ia64/kernel/irq_ia64.c
linux-2.6-xen-sparse/include/asm-ia64/iosapic.h
xen/arch/ia64/linux-xen/iosapic.c
xen/arch/ia64/linux-xen/irq_ia64.c
xen/arch/ia64/xen/acpi.c
xen/arch/ia64/xen/hypercall.c
xen/arch/ia64/xen/irq.c
xen/include/asm-ia64/linux-xen/asm/iosapic.h

index 574084f343fad79b32406c6e8b354c23554594bf..8fba804bbeeb7a21d0af5f67aaeae2adff7c8e91 100644 (file)
@@ -140,6 +140,68 @@ static unsigned char pcat_compat __devinitdata;    /* 8259 compatibility flag */
 static int iosapic_kmalloc_ok;
 static LIST_HEAD(free_rte_list);
 
+#ifdef CONFIG_XEN
+#include <xen/interface/xen.h>
+#include <xen/interface/physdev.h>
+#include <asm/hypervisor.h>
+static inline unsigned int xen_iosapic_read(char __iomem *iosapic, unsigned int reg)
+{
+       physdev_op_t op;
+       int ret;
+
+       op.cmd = PHYSDEVOP_APIC_READ;
+       op.u.apic_op.apic_physbase = (unsigned long)iosapic -
+                                       __IA64_UNCACHED_OFFSET;
+       op.u.apic_op.reg = reg;
+       ret = HYPERVISOR_physdev_op(&op);
+       if (ret)
+               return ret;
+       return op.u.apic_op.value;
+}
+
+static inline void xen_iosapic_write(char __iomem *iosapic, unsigned int reg, u32 val)
+{
+       physdev_op_t op;
+
+       op.cmd = PHYSDEVOP_APIC_WRITE;
+       op.u.apic_op.apic_physbase = (unsigned long)iosapic - 
+                                       __IA64_UNCACHED_OFFSET;
+       op.u.apic_op.reg = reg;
+       op.u.apic_op.value = val;
+       HYPERVISOR_physdev_op(&op);
+}
+
+static inline unsigned int iosapic_read(char __iomem *iosapic, unsigned int reg)
+{
+       if (!running_on_xen) {
+               writel(reg, iosapic + IOSAPIC_REG_SELECT);
+               return readl(iosapic + IOSAPIC_WINDOW);
+       } else
+               return xen_iosapic_read(iosapic, reg);
+}
+
+static inline void iosapic_write(char __iomem *iosapic, unsigned int reg, u32 val)
+{
+       if (!running_on_xen) {
+               writel(reg, iosapic + IOSAPIC_REG_SELECT);
+               writel(val, iosapic + IOSAPIC_WINDOW);
+       } else
+               xen_iosapic_write(iosapic, reg, val);
+}
+
+int xen_assign_irq_vector(int irq)
+{
+       physdev_op_t op;
+
+       op.cmd = PHYSDEVOP_ASSIGN_VECTOR;
+       op.u.irq_op.irq = irq;
+       if (HYPERVISOR_physdev_op(&op))
+               return -ENOSPC;
+
+       return op.u.irq_op.vector;
+}
+#endif /* XEN */
+
 /*
  * Find an IOSAPIC associated with a GSI
  */
@@ -953,6 +1015,10 @@ iosapic_system_init (int system_pcat_compat)
        }
 
        pcat_compat = system_pcat_compat;
+#ifdef CONFIG_XEN
+       if (running_on_xen)
+               return;
+#endif
        if (pcat_compat) {
                /*
                 * Disable the compatibility mode interrupts (8259 style), needs IN/OUT support
index 6c4d59fd03641dbd6ec0543f0f00095e844b7a6b..647b0ca608043d45bdc07cbb46945e8886c4e850 100644 (file)
@@ -66,6 +66,11 @@ int
 assign_irq_vector (int irq)
 {
        int pos, vector;
+#ifdef CONFIG_XEN
+       extern int xen_assign_irq_vector(int);
+       if (running_on_xen)
+               return xen_assign_irq_vector(irq);
+#endif /* CONFIG_XEN */
  again:
        pos = find_first_zero_bit(ia64_vector_mask, IA64_NUM_DEVICE_VECTORS);
        vector = IA64_FIRST_DEVICE_VECTOR + pos;
index 20f98f1751a19677795035356972993d8aa24709..87de4873f50f15b45b0d2fc72dea61617bedc4fc 100644 (file)
@@ -53,6 +53,7 @@
 
 #define NR_IOSAPICS                    256
 
+#ifndef CONFIG_XEN
 static inline unsigned int iosapic_read(char __iomem *iosapic, unsigned int reg)
 {
        writel(reg, iosapic + IOSAPIC_REG_SELECT);
@@ -64,6 +65,7 @@ static inline void iosapic_write(char __iomem *iosapic, unsigned int reg, u32 va
        writel(reg, iosapic + IOSAPIC_REG_SELECT);
        writel(val, iosapic + IOSAPIC_WINDOW);
 }
+#endif
 
 static inline void iosapic_eoi(char __iomem *iosapic, u32 vector)
 {
index 9d964abba492d3e7a6723800dcb3f2af123275de..e5639ffe7ff4ce80e77140cccd5c34c7c87acfb6 100644 (file)
@@ -1118,3 +1118,114 @@ static int __init iosapic_enable_kmalloc (void)
        return 0;
 }
 core_initcall (iosapic_enable_kmalloc);
+
+#ifdef XEN
+/* nop for now */
+void set_irq_affinity_info(unsigned int irq, int hwid, int redir) {}
+
+static int iosapic_physbase_to_id(unsigned long physbase)
+{
+       int i;
+       unsigned long addr = physbase | __IA64_UNCACHED_OFFSET;
+
+       for (i = 0; i < NR_IOSAPICS; i++) {
+           if ((unsigned long)(iosapic_lists[i].addr) == addr)
+               return i;
+       }
+
+       return -1;
+}
+
+int iosapic_guest_read(unsigned long physbase, unsigned int reg, u32 *pval)
+{
+       int id;
+       unsigned long flags;
+
+       if ((id = (iosapic_physbase_to_id(physbase))) < 0)
+           return id;
+
+       spin_lock_irqsave(&iosapic_lock, flags);
+       *pval = iosapic_read(iosapic_lists[id].addr, reg);
+       spin_unlock_irqrestore(&iosapic_lock, flags);
+
+       return 0;
+}
+
+int iosapic_guest_write(unsigned long physbase, unsigned int reg, u32 val)
+{
+       unsigned int id, gsi, vec, dest, high32;
+       char rte_index;
+       struct iosapic *ios;
+       struct iosapic_intr_info *info;
+       struct rte_entry rte;
+       unsigned long flags;
+
+       if ((id = (iosapic_physbase_to_id(physbase))) < 0)
+           return -EINVAL;
+       ios = &iosapic_lists[id];
+
+       /* Only handle first half of RTE update */
+       if ((reg < 0x10) || (reg & 1))
+           return 0;
+
+       rte.val = val;
+       rte_index = IOSAPIC_RTEINDEX(reg);
+       vec = rte.lo.vector;
+#if 0
+       /* Take PMI/NMI/INIT/EXTINT handled by xen */ 
+       if (rte.delivery_mode > IOSAPIC_LOWEST_PRIORITY) {
+           printk("Attempt to write IOSAPIC dest mode owned by xen!\n");
+           printk("IOSAPIC/PIN = (%d/%d), lo = 0x%x\n",
+               id, rte_index, val);
+           return -EINVAL;
+       }
+#endif
+
+       /* Sanity check. Vector should be allocated before this update */
+       if ((rte_index > ios->num_rte) ||
+           ((vec > IA64_FIRST_DEVICE_VECTOR) &&
+            (vec < IA64_LAST_DEVICE_VECTOR) &&
+            (!test_bit(vec - IA64_FIRST_DEVICE_VECTOR, ia64_vector_mask))))
+           return -EINVAL;
+
+       gsi = ios->gsi_base + rte_index;
+       info = &iosapic_intr_info[vec];
+       spin_lock_irqsave(&irq_descp(vec)->lock, flags);
+       spin_lock(&iosapic_lock);
+       if (!gsi_vector_to_rte(gsi, vec)) {
+           register_intr(gsi, vec, IOSAPIC_LOWEST_PRIORITY,
+               rte.lo.polarity, rte.lo.trigger);
+       } else if (vector_is_shared(vec)) {
+           if ((info->trigger != rte.lo.trigger) ||
+               (info->polarity != rte.lo.polarity)) {
+               printk("WARN: can't override shared interrupt vec\n");
+               printk("IOSAPIC/PIN = (%d/%d), ori = 0x%x, new = 0x%x\n",
+                       id, rte_index, info->low32, rte.val);
+               spin_unlock(&iosapic_lock);
+               spin_unlock_irqrestore(&irq_descp(vec)->lock, flags);
+               return -EINVAL;
+           }
+
+           /* If the vector is shared and already unmasked for other
+            * interrupt sources, don't mask it.
+            *
+            * Same check may also apply to single gsi pin, which may
+            * be shared by devices belonging to different domain. But
+            * let's see how to act later on demand.
+            */
+           if (!(info->low32 & IOSAPIC_MASK))
+               rte.lo.mask = 0;
+       }
+
+       /* time to update physical RTE */
+       dest = cpu_physical_id(smp_processor_id());
+       high32 = (dest << IOSAPIC_DEST_SHIFT);
+       iosapic_write(iosapic_lists[id].addr, reg + 1, high32);
+       iosapic_write(iosapic_lists[id].addr, reg, rte.val);
+       info->low32 = rte.val;
+       info->dest = dest;
+       spin_unlock(&iosapic_lock);
+       spin_unlock_irqrestore(&irq_descp(vec)->lock, flags);
+       return 0;
+}
+#endif /* XEN */
index 94c437bc6426508016a6d8a50bc871e82208c081..d16b38e539bb3bd0ff14a45f812fa205181cb33c 100644 (file)
@@ -60,7 +60,11 @@ __u8 isa_irq_to_vector_map[16] = {
 };
 EXPORT_SYMBOL(isa_irq_to_vector_map);
 
+#ifdef XEN
+unsigned long ia64_vector_mask[BITS_TO_LONGS(IA64_NUM_DEVICE_VECTORS)];
+#else
 static unsigned long ia64_vector_mask[BITS_TO_LONGS(IA64_NUM_DEVICE_VECTORS)];
+#endif
 
 int
 assign_irq_vector (int irq)
index b75bdaefaeb58b7b3bf2f9a725face9ed2c825d7..449124820aea75769db836dc49d1d40841b0a59f 100644 (file)
@@ -44,7 +44,7 @@
 #include <linux/efi.h>
 #include <linux/mmzone.h>
 #include <asm/io.h>
-//#include <asm/iosapic.h>
+#include <asm/iosapic.h>
 #include <asm/machvec.h>
 #include <asm/page.h>
 #include <asm/system.h>
@@ -121,9 +121,7 @@ acpi_get_sysname (void)
 #ifdef CONFIG_ACPI_BOOT
 
 #define ACPI_MAX_PLATFORM_INTERRUPTS   256
-#define NR_IOSAPICS 4
 
-#if 0
 /* Array to record platform interrupt vectors for generic interrupt routing. */
 int platform_intr_list[ACPI_MAX_PLATFORM_INTERRUPTS] = {
        [0 ... ACPI_MAX_PLATFORM_INTERRUPTS - 1] = -1
@@ -147,7 +145,7 @@ acpi_request_vector (u32 int_type)
                printk(KERN_ERR "acpi_request_vector(): invalid interrupt type\n");
        return vector;
 }
-#endif
+
 char *
 __acpi_map_table (unsigned long phys_addr, unsigned long size)
 {
@@ -253,9 +251,7 @@ acpi_parse_iosapic (acpi_table_entry_header *header, const unsigned long end)
 
        acpi_table_print_madt_entry(header);
 
-#if 0
        iosapic_init(iosapic->address, iosapic->global_irq_base);
-#endif
 
        return 0;
 }
@@ -265,9 +261,7 @@ acpi_parse_plat_int_src (
        acpi_table_entry_header *header, const unsigned long end)
 {
        struct acpi_table_plat_int_src *plintsrc;
-#if 0
        int vector;
-#endif
 
        plintsrc = (struct acpi_table_plat_int_src *) header;
 
@@ -276,7 +270,6 @@ acpi_parse_plat_int_src (
 
        acpi_table_print_madt_entry(header);
 
-#if 0
        /*
         * Get vector assignment for this interrupt, set attributes,
         * and program the IOSAPIC routing table.
@@ -290,7 +283,6 @@ acpi_parse_plat_int_src (
                                                (plintsrc->flags.trigger == 1) ? IOSAPIC_EDGE : IOSAPIC_LEVEL);
 
        platform_intr_list[plintsrc->type] = vector;
-#endif
        return 0;
 }
 
@@ -308,11 +300,9 @@ acpi_parse_int_src_ovr (
 
        acpi_table_print_madt_entry(header);
 
-#if 0
        iosapic_override_isa_irq(p->bus_irq, p->global_irq,
                                 (p->flags.polarity == 1) ? IOSAPIC_POL_HIGH : IOSAPIC_POL_LOW,
                                 (p->flags.trigger == 1) ? IOSAPIC_EDGE : IOSAPIC_LEVEL);
-#endif
        return 0;
 }
 
@@ -364,9 +354,7 @@ acpi_parse_madt (unsigned long phys_addr, unsigned long size)
 #else
        has_8259 = acpi_madt->flags.pcat_compat;
 #endif
-#if 0
        iosapic_system_init(has_8259);
-#endif
 
        /* Get base address of IPI Message Block */
 
index 22d2a2fb41a9436b19dfa303d964775bec192f52..f0696cdbe73486fe70eff6eb44546b2db1ad3aa1 100644 (file)
 #include <public/event_channel.h>
 #include <public/memory.h>
 #include <public/sched.h>
+#include <xen/irq.h>
+#include <asm/hw_irq.h>
+#include <public/physdev.h>
 
 extern unsigned long translate_domain_mpaddr(unsigned long);
+static long do_physdev_op(GUEST_HANDLE(physdev_op_t) uop);
 /* FIXME: where these declarations should be there ? */
 extern int dump_privop_counts_to_user(char *, int);
 extern int zero_privop_counts_to_user(char *, int);
@@ -51,7 +55,7 @@ hypercall_t ia64_hypercall_table[] =
        (hypercall_t)do_event_channel_op,
        (hypercall_t)do_xen_version,
        (hypercall_t)do_console_io,
-       (hypercall_t)do_ni_hypercall,           /* do_physdev_op */
+       (hypercall_t)do_physdev_op,             /* do_physdev_op */
        (hypercall_t)do_grant_table_op,                                         /* 20 */
        (hypercall_t)do_ni_hypercall,           /* do_vm_assist */
        (hypercall_t)do_ni_hypercall,           /* do_update_va_mapping_otherdomain */
@@ -89,6 +93,11 @@ xen_hypercall (struct pt_regs *regs)
                regs->r8 = do_event_channel_op(guest_handle_from_ptr(regs->r14, evtchn_op_t));
                break;
 
+           case __HYPERVISOR_physdev_op:
+               regs->r8 = do_physdev_op(guest_handle_from_ptr(regs->r14,
+                       physdev_op_t));
+               break;
+
            case __HYPERVISOR_grant_table_op:
                regs->r8 = do_grant_table_op((unsigned int) regs->r14,
                        guest_handle_from_ptr(regs->r15, void),
@@ -282,3 +291,80 @@ ia64_hypercall (struct pt_regs *regs)
        else
            return xen_hypercall (regs);
 }
+
+/* Need make this function common */
+extern int
+iosapic_guest_read(
+    unsigned long physbase, unsigned int reg, u32 *pval);
+extern int
+iosapic_guest_write(
+    unsigned long physbase, unsigned int reg, u32 pval);
+
+static long do_physdev_op(GUEST_HANDLE(physdev_op_t) uop)
+{
+    struct physdev_op op;
+    long ret;
+    int  irq;
+
+    if ( unlikely(copy_from_guest(&op, uop, 1) != 0) )
+        return -EFAULT;
+
+    switch ( op.cmd )
+    {
+    case PHYSDEVOP_IRQ_UNMASK_NOTIFY:
+        ret = pirq_guest_unmask(current->domain);
+        break;
+
+    case PHYSDEVOP_IRQ_STATUS_QUERY:
+        irq = op.u.irq_status_query.irq;
+        ret = -EINVAL;
+        if ( (irq < 0) || (irq >= NR_IRQS) )
+            break;
+        op.u.irq_status_query.flags = 0;
+        /* Edge-triggered interrupts don't need an explicit unmask downcall. */
+        if ( !strstr(irq_desc[irq_to_vector(irq)].handler->typename, "edge") )
+            op.u.irq_status_query.flags |= PHYSDEVOP_IRQ_NEEDS_UNMASK_NOTIFY;
+        ret = 0;
+        break;
+
+    case PHYSDEVOP_APIC_READ:
+        ret = -EPERM;
+        if ( !IS_PRIV(current->domain) )
+            break;
+        ret = iosapic_guest_read(
+            op.u.apic_op.apic_physbase,
+            op.u.apic_op.reg,
+            &op.u.apic_op.value);
+        break;
+
+    case PHYSDEVOP_APIC_WRITE:
+        ret = -EPERM;
+        if ( !IS_PRIV(current->domain) )
+            break;
+        ret = iosapic_guest_write(
+            op.u.apic_op.apic_physbase,
+            op.u.apic_op.reg,
+            op.u.apic_op.value);
+        break;
+
+    case PHYSDEVOP_ASSIGN_VECTOR:
+        if ( !IS_PRIV(current->domain) )
+            return -EPERM;
+
+        if ( (irq = op.u.irq_op.irq) >= NR_IRQS )
+            return -EINVAL;
+        
+        op.u.irq_op.vector = assign_irq_vector(irq);
+        ret = 0;
+        break;
+
+    default:
+        ret = -EINVAL;
+        break;
+    }
+
+    if ( copy_to_guest(uop, &op, 1) )
+        ret = -EFAULT;
+
+    return ret;
+}
index cdf97eb03e8937bf86e1b28c94f052df6a547e98..5fbffdf82b55b52578a3e573111870032942dc83 100644 (file)
@@ -33,7 +33,6 @@
 #include <linux/slab.h>
 #include <linux/ctype.h>
 #include <linux/init.h>
-#include <linux/irq.h>
 #include <linux/seq_file.h>
 
 #include <asm/atomic.h>
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
 #include <asm/delay.h>
-#include <asm/irq.h>
+#include <xen/irq.h>
+#include <asm/hw_irq.h>
 
 #include <xen/event.h>
-#define _irq_desc irq_desc
-#define irq_descp(irq) &irq_desc[irq]
 #define apicid_to_phys_cpu_present(x)  1
 
 /*
@@ -70,7 +68,7 @@
 /*
  * Controller mappings for all interrupt sources:
  */
-irq_desc_t _irq_desc[NR_IRQS] __cacheline_aligned = {
+irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = {
        [0 ... NR_IRQS-1] = {
                .status = IRQ_DISABLED | IRQ_GUEST,
                .handler = &no_irq_type,
index ab6c584a7508e475976514ba2614cae8211a7659..55e559b2b8a13d623cc456335f756f9ee07c4169 100644 (file)
@@ -134,10 +134,30 @@ static inline void list_move(struct list_head *list, struct list_head *head)
 #undef nop
 #endif
 
-/* nop for now */
-static inline void
-set_irq_affinity_info(unsigned int irq, int hwid, int redir) {}
-
+struct rte_entry {
+    union {
+       struct {
+       u32     vector          : 8,
+               delivery_mode   : 3,
+               dest_mode       : 1,    /* always 0 for iosapic */
+               delivery_status : 1,
+               polarity        : 1,
+               __reserved0     : 1,
+               trigger         : 1,
+               mask            : 1,
+               __reserved1     : 15;
+       } lo;
+       struct {
+       u32     __reserved2     : 16,
+               eid             : 8,
+               id              : 8;
+       } hi;
+       u32 val;
+    };
+};
+
+#define IOSAPIC_RTEINDEX(reg)  (((reg) - 0x10) >> 1)
+extern unsigned long ia64_vector_mask[];
 #endif /* XEN */
 
 # endif /* !__ASSEMBLY__ */