string: factorize skip_spaces and export it to be generally available
[safe/jmp/linux-2.6] / arch / x86 / kvm / i8259.c
index c31164e..d057c0c 100644 (file)
  *   Port from Qemu.
  */
 #include <linux/mm.h>
+#include <linux/bitops.h>
 #include "irq.h"
 
 #include <linux/kvm_host.h>
+#include "trace.h"
+
+static void pic_clear_isr(struct kvm_kpic_state *s, int irq)
+{
+       s->isr &= ~(1 << irq);
+       s->isr_ack |= (1 << irq);
+       if (s != &s->pics_state->pics[0])
+               irq += 8;
+       /*
+        * We are dropping lock while calling ack notifiers since ack
+        * notifier callbacks for assigned devices call into PIC recursively.
+        * Other interrupt may be delivered to PIC while lock is dropped but
+        * it should be safe since PIC state is already updated at this stage.
+        */
+       spin_unlock(&s->pics_state->lock);
+       kvm_notify_acked_irq(s->pics_state->kvm, SELECT_PIC(irq), irq);
+       spin_lock(&s->pics_state->lock);
+}
+
+void kvm_pic_clear_isr_ack(struct kvm *kvm)
+{
+       struct kvm_pic *s = pic_irqchip(kvm);
+       spin_lock(&s->lock);
+       s->pics[0].isr_ack = 0xff;
+       s->pics[1].isr_ack = 0xff;
+       spin_unlock(&s->lock);
+}
 
 /*
  * set irq level. If an edge is detected, then the IRR is set to 1
  */
-static inline void pic_set_irq1(struct kvm_kpic_state *s, int irq, int level)
+static inline int pic_set_irq1(struct kvm_kpic_state *s, int irq, int level)
 {
-       int mask;
+       int mask, ret = 1;
        mask = 1 << irq;
        if (s->elcr & mask)     /* level triggered */
                if (level) {
+                       ret = !(s->irr & mask);
                        s->irr |= mask;
                        s->last_irr |= mask;
                } else {
@@ -47,11 +76,15 @@ static inline void pic_set_irq1(struct kvm_kpic_state *s, int irq, int level)
                }
        else    /* edge triggered */
                if (level) {
-                       if ((s->last_irr & mask) == 0)
+                       if ((s->last_irr & mask) == 0) {
+                               ret = !(s->irr & mask);
                                s->irr |= mask;
+                       }
                        s->last_irr |= mask;
                } else
                        s->last_irr &= ~mask;
+
+       return (s->imr & mask) ? -1 : ret;
 }
 
 /*
@@ -123,17 +156,26 @@ static void pic_update_irq(struct kvm_pic *s)
 
 void kvm_pic_update_irq(struct kvm_pic *s)
 {
+       spin_lock(&s->lock);
        pic_update_irq(s);
+       spin_unlock(&s->lock);
 }
 
-void kvm_pic_set_irq(void *opaque, int irq, int level)
+int kvm_pic_set_irq(void *opaque, int irq, int level)
 {
        struct kvm_pic *s = opaque;
+       int ret = -1;
 
+       spin_lock(&s->lock);
        if (irq >= 0 && irq < PIC_NUM_PINS) {
-               pic_set_irq1(&s->pics[irq >> 3], irq & 7, level);
+               ret = pic_set_irq1(&s->pics[irq >> 3], irq & 7, level);
                pic_update_irq(s);
+               trace_kvm_pic_set_irq(irq >> 3, irq & 7, s->pics[irq >> 3].elcr,
+                                     s->pics[irq >> 3].imr, ret == 0);
        }
+       spin_unlock(&s->lock);
+
+       return ret;
 }
 
 /*
@@ -141,22 +183,27 @@ void kvm_pic_set_irq(void *opaque, int irq, int level)
  */
 static inline void pic_intack(struct kvm_kpic_state *s, int irq)
 {
-       if (s->auto_eoi) {
-               if (s->rotate_on_auto_eoi)
-                       s->priority_add = (irq + 1) & 7;
-       } else
-               s->isr |= (1 << irq);
+       s->isr |= 1 << irq;
        /*
         * We don't clear a level sensitive interrupt here
         */
        if (!(s->elcr & (1 << irq)))
                s->irr &= ~(1 << irq);
+
+       if (s->auto_eoi) {
+               if (s->rotate_on_auto_eoi)
+                       s->priority_add = (irq + 1) & 7;
+               pic_clear_isr(s, irq);
+       }
+
 }
 
-int kvm_pic_read_irq(struct kvm_pic *s)
+int kvm_pic_read_irq(struct kvm *kvm)
 {
        int irq, irq2, intno;
+       struct kvm_pic *s = pic_irqchip(kvm);
 
+       spin_lock(&s->lock);
        irq = pic_get_irq(&s->pics[0]);
        if (irq >= 0) {
                pic_intack(&s->pics[0], irq);
@@ -181,16 +228,23 @@ int kvm_pic_read_irq(struct kvm_pic *s)
                intno = s->pics[0].irq_base + irq;
        }
        pic_update_irq(s);
+       spin_unlock(&s->lock);
 
        return intno;
 }
 
 void kvm_pic_reset(struct kvm_kpic_state *s)
 {
+       int irq;
+       struct kvm *kvm = s->pics_state->irq_request_opaque;
+       struct kvm_vcpu *vcpu0 = kvm->bsp_vcpu;
+       u8 irr = s->irr, isr = s->imr;
+
        s->last_irr = 0;
        s->irr = 0;
        s->imr = 0;
        s->isr = 0;
+       s->isr_ack = 0xff;
        s->priority_add = 0;
        s->irq_base = 0;
        s->read_reg_select = 0;
@@ -201,6 +255,13 @@ void kvm_pic_reset(struct kvm_kpic_state *s)
        s->rotate_on_auto_eoi = 0;
        s->special_fully_nested_mode = 0;
        s->init4 = 0;
+
+       for (irq = 0; irq < PIC_NUM_PINS/2; irq++) {
+               if (vcpu0 && kvm_apic_accept_pic_intr(vcpu0))
+                       if (irr & (1 << irq) || isr & (1 << irq)) {
+                               pic_clear_isr(s, irq);
+                       }
+       }
 }
 
 static void pic_ioport_write(void *opaque, u32 addr, u32 val)
@@ -243,15 +304,15 @@ static void pic_ioport_write(void *opaque, u32 addr, u32 val)
                                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_clear_isr(s, irq);
                                        pic_update_irq(s->pics_state);
                                }
                                break;
                        case 3:
                                irq = val & 7;
-                               s->isr &= ~(1 << irq);
+                               pic_clear_isr(s, irq);
                                pic_update_irq(s->pics_state);
                                break;
                        case 6:
@@ -260,8 +321,8 @@ static void pic_ioport_write(void *opaque, u32 addr, u32 val)
                                break;
                        case 7:
                                irq = val & 7;
-                               s->isr &= ~(1 << irq);
                                s->priority_add = (irq + 1) & 7;
+                               pic_clear_isr(s, irq);
                                pic_update_irq(s->pics_state);
                                break;
                        default:
@@ -303,7 +364,7 @@ static u32 pic_poll_read(struct kvm_kpic_state *s, u32 addr1)
                        s->pics_state->pics[0].irr &= ~(1 << 2);
                }
                s->irr &= ~(1 << ret);
-               s->isr &= ~(1 << ret);
+               pic_clear_isr(s, ret);
                if (addr1 >> 7 || ret != 2)
                        pic_update_irq(s->pics_state);
        } else {
@@ -348,8 +409,7 @@ static u32 elcr_ioport_read(void *opaque, u32 addr1)
        return s->elcr;
 }
 
-static int picdev_in_range(struct kvm_io_device *this, gpa_t addr,
-                          int len, int is_write)
+static int picdev_in_range(gpa_t addr)
 {
        switch (addr) {
        case 0x20:
@@ -364,17 +424,25 @@ static int picdev_in_range(struct kvm_io_device *this, gpa_t addr,
        }
 }
 
-static void picdev_write(struct kvm_io_device *this,
+static inline struct kvm_pic *to_pic(struct kvm_io_device *dev)
+{
+       return container_of(dev, struct kvm_pic, dev);
+}
+
+static int picdev_write(struct kvm_io_device *this,
                         gpa_t addr, int len, const void *val)
 {
-       struct kvm_pic *s = this->private;
+       struct kvm_pic *s = to_pic(this);
        unsigned char data = *(unsigned char *)val;
+       if (!picdev_in_range(addr))
+               return -EOPNOTSUPP;
 
        if (len != 1) {
                if (printk_ratelimit())
                        printk(KERN_ERR "PIC: non byte write\n");
-               return;
+               return 0;
        }
+       spin_lock(&s->lock);
        switch (addr) {
        case 0x20:
        case 0x21:
@@ -387,19 +455,24 @@ static void picdev_write(struct kvm_io_device *this,
                elcr_ioport_write(&s->pics[addr & 1], addr, data);
                break;
        }
+       spin_unlock(&s->lock);
+       return 0;
 }
 
-static void picdev_read(struct kvm_io_device *this,
-                       gpa_t addr, int len, void *val)
+static int picdev_read(struct kvm_io_device *this,
+                      gpa_t addr, int len, void *val)
 {
-       struct kvm_pic *s = this->private;
+       struct kvm_pic *s = to_pic(this);
        unsigned char data = 0;
+       if (!picdev_in_range(addr))
+               return -EOPNOTSUPP;
 
        if (len != 1) {
                if (printk_ratelimit())
                        printk(KERN_ERR "PIC: non byte read\n");
-               return;
+               return 0;
        }
+       spin_lock(&s->lock);
        switch (addr) {
        case 0x20:
        case 0x21:
@@ -413,6 +486,8 @@ static void picdev_read(struct kvm_io_device *this,
                break;
        }
        *(unsigned char *)val = data;
+       spin_unlock(&s->lock);
+       return 0;
 }
 
 /*
@@ -421,19 +496,32 @@ static void picdev_read(struct kvm_io_device *this,
 static void pic_irq_request(void *opaque, int level)
 {
        struct kvm *kvm = opaque;
-       struct kvm_vcpu *vcpu = kvm->vcpus[0];
+       struct kvm_vcpu *vcpu = kvm->bsp_vcpu;
+       struct kvm_pic *s = pic_irqchip(kvm);
+       int irq = pic_get_irq(&s->pics[0]);
 
-       pic_irqchip(kvm)->output = level;
-       if (vcpu)
+       s->output = level;
+       if (vcpu && level && (s->pics[0].isr_ack & (1 << irq))) {
+               s->pics[0].isr_ack &= ~(1 << irq);
                kvm_vcpu_kick(vcpu);
+       }
 }
 
+static const struct kvm_io_device_ops picdev_ops = {
+       .read     = picdev_read,
+       .write    = picdev_write,
+};
+
 struct kvm_pic *kvm_create_pic(struct kvm *kvm)
 {
        struct kvm_pic *s;
+       int ret;
+
        s = kzalloc(sizeof(struct kvm_pic), GFP_KERNEL);
        if (!s)
                return NULL;
+       spin_lock_init(&s->lock);
+       s->kvm = kvm;
        s->pics[0].elcr_mask = 0xf8;
        s->pics[1].elcr_mask = 0xde;
        s->irq_request = pic_irq_request;
@@ -444,10 +532,12 @@ struct kvm_pic *kvm_create_pic(struct kvm *kvm)
        /*
         * Initialize PIO device
         */
-       s->dev.read = picdev_read;
-       s->dev.write = picdev_write;
-       s->dev.in_range = picdev_in_range;
-       s->dev.private = s;
-       kvm_io_bus_register_dev(&kvm->pio_bus, &s->dev);
+       kvm_iodevice_init(&s->dev, &picdev_ops);
+       ret = kvm_io_bus_register_dev(kvm, &kvm->pio_bus, &s->dev);
+       if (ret < 0) {
+               kfree(s);
+               return NULL;
+       }
+
        return s;
 }