linux/arch/mips/kvm/kvm_mips_int.c
<<
>>
Prefs
   1/*
   2* This file is subject to the terms and conditions of the GNU General Public
   3* License.  See the file "COPYING" in the main directory of this archive
   4* for more details.
   5*
   6* KVM/MIPS: Interrupt delivery
   7*
   8* Copyright (C) 2012  MIPS Technologies, Inc.  All rights reserved.
   9* Authors: Sanjay Lal <sanjayl@kymasys.com>
  10*/
  11
  12#include <linux/errno.h>
  13#include <linux/err.h>
  14#include <linux/module.h>
  15#include <linux/vmalloc.h>
  16#include <linux/fs.h>
  17#include <linux/bootmem.h>
  18#include <asm/page.h>
  19#include <asm/cacheflush.h>
  20
  21#include <linux/kvm_host.h>
  22
  23#include "kvm_mips_int.h"
  24
  25void kvm_mips_queue_irq(struct kvm_vcpu *vcpu, uint32_t priority)
  26{
  27        set_bit(priority, &vcpu->arch.pending_exceptions);
  28}
  29
  30void kvm_mips_dequeue_irq(struct kvm_vcpu *vcpu, uint32_t priority)
  31{
  32        clear_bit(priority, &vcpu->arch.pending_exceptions);
  33}
  34
  35void kvm_mips_queue_timer_int_cb(struct kvm_vcpu *vcpu)
  36{
  37        /* Cause bits to reflect the pending timer interrupt,
  38         * the EXC code will be set when we are actually
  39         * delivering the interrupt:
  40         */
  41        kvm_set_c0_guest_cause(vcpu->arch.cop0, (C_IRQ5 | C_TI));
  42
  43        /* Queue up an INT exception for the core */
  44        kvm_mips_queue_irq(vcpu, MIPS_EXC_INT_TIMER);
  45
  46}
  47
  48void kvm_mips_dequeue_timer_int_cb(struct kvm_vcpu *vcpu)
  49{
  50        kvm_clear_c0_guest_cause(vcpu->arch.cop0, (C_IRQ5 | C_TI));
  51        kvm_mips_dequeue_irq(vcpu, MIPS_EXC_INT_TIMER);
  52}
  53
  54void
  55kvm_mips_queue_io_int_cb(struct kvm_vcpu *vcpu, struct kvm_mips_interrupt *irq)
  56{
  57        int intr = (int)irq->irq;
  58
  59        /* Cause bits to reflect the pending IO interrupt,
  60         * the EXC code will be set when we are actually
  61         * delivering the interrupt:
  62         */
  63        switch (intr) {
  64        case 2:
  65                kvm_set_c0_guest_cause(vcpu->arch.cop0, (C_IRQ0));
  66                /* Queue up an INT exception for the core */
  67                kvm_mips_queue_irq(vcpu, MIPS_EXC_INT_IO);
  68                break;
  69
  70        case 3:
  71                kvm_set_c0_guest_cause(vcpu->arch.cop0, (C_IRQ1));
  72                kvm_mips_queue_irq(vcpu, MIPS_EXC_INT_IPI_1);
  73                break;
  74
  75        case 4:
  76                kvm_set_c0_guest_cause(vcpu->arch.cop0, (C_IRQ2));
  77                kvm_mips_queue_irq(vcpu, MIPS_EXC_INT_IPI_2);
  78                break;
  79
  80        default:
  81                break;
  82        }
  83
  84}
  85
  86void
  87kvm_mips_dequeue_io_int_cb(struct kvm_vcpu *vcpu,
  88                           struct kvm_mips_interrupt *irq)
  89{
  90        int intr = (int)irq->irq;
  91        switch (intr) {
  92        case -2:
  93                kvm_clear_c0_guest_cause(vcpu->arch.cop0, (C_IRQ0));
  94                kvm_mips_dequeue_irq(vcpu, MIPS_EXC_INT_IO);
  95                break;
  96
  97        case -3:
  98                kvm_clear_c0_guest_cause(vcpu->arch.cop0, (C_IRQ1));
  99                kvm_mips_dequeue_irq(vcpu, MIPS_EXC_INT_IPI_1);
 100                break;
 101
 102        case -4:
 103                kvm_clear_c0_guest_cause(vcpu->arch.cop0, (C_IRQ2));
 104                kvm_mips_dequeue_irq(vcpu, MIPS_EXC_INT_IPI_2);
 105                break;
 106
 107        default:
 108                break;
 109        }
 110
 111}
 112
 113/* Deliver the interrupt of the corresponding priority, if possible. */
 114int
 115kvm_mips_irq_deliver_cb(struct kvm_vcpu *vcpu, unsigned int priority,
 116                        uint32_t cause)
 117{
 118        int allowed = 0;
 119        uint32_t exccode;
 120
 121        struct kvm_vcpu_arch *arch = &vcpu->arch;
 122        struct mips_coproc *cop0 = vcpu->arch.cop0;
 123
 124        switch (priority) {
 125        case MIPS_EXC_INT_TIMER:
 126                if ((kvm_read_c0_guest_status(cop0) & ST0_IE)
 127                    && (!(kvm_read_c0_guest_status(cop0) & (ST0_EXL | ST0_ERL)))
 128                    && (kvm_read_c0_guest_status(cop0) & IE_IRQ5)) {
 129                        allowed = 1;
 130                        exccode = T_INT;
 131                }
 132                break;
 133
 134        case MIPS_EXC_INT_IO:
 135                if ((kvm_read_c0_guest_status(cop0) & ST0_IE)
 136                    && (!(kvm_read_c0_guest_status(cop0) & (ST0_EXL | ST0_ERL)))
 137                    && (kvm_read_c0_guest_status(cop0) & IE_IRQ0)) {
 138                        allowed = 1;
 139                        exccode = T_INT;
 140                }
 141                break;
 142
 143        case MIPS_EXC_INT_IPI_1:
 144                if ((kvm_read_c0_guest_status(cop0) & ST0_IE)
 145                    && (!(kvm_read_c0_guest_status(cop0) & (ST0_EXL | ST0_ERL)))
 146                    && (kvm_read_c0_guest_status(cop0) & IE_IRQ1)) {
 147                        allowed = 1;
 148                        exccode = T_INT;
 149                }
 150                break;
 151
 152        case MIPS_EXC_INT_IPI_2:
 153                if ((kvm_read_c0_guest_status(cop0) & ST0_IE)
 154                    && (!(kvm_read_c0_guest_status(cop0) & (ST0_EXL | ST0_ERL)))
 155                    && (kvm_read_c0_guest_status(cop0) & IE_IRQ2)) {
 156                        allowed = 1;
 157                        exccode = T_INT;
 158                }
 159                break;
 160
 161        default:
 162                break;
 163        }
 164
 165        /* Are we allowed to deliver the interrupt ??? */
 166        if (allowed) {
 167
 168                if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) {
 169                        /* save old pc */
 170                        kvm_write_c0_guest_epc(cop0, arch->pc);
 171                        kvm_set_c0_guest_status(cop0, ST0_EXL);
 172
 173                        if (cause & CAUSEF_BD)
 174                                kvm_set_c0_guest_cause(cop0, CAUSEF_BD);
 175                        else
 176                                kvm_clear_c0_guest_cause(cop0, CAUSEF_BD);
 177
 178                        kvm_debug("Delivering INT @ pc %#lx\n", arch->pc);
 179
 180                } else
 181                        kvm_err("Trying to deliver interrupt when EXL is already set\n");
 182
 183                kvm_change_c0_guest_cause(cop0, CAUSEF_EXCCODE,
 184                                          (exccode << CAUSEB_EXCCODE));
 185
 186                /* XXXSL Set PC to the interrupt exception entry point */
 187                if (kvm_read_c0_guest_cause(cop0) & CAUSEF_IV)
 188                        arch->pc = KVM_GUEST_KSEG0 + 0x200;
 189                else
 190                        arch->pc = KVM_GUEST_KSEG0 + 0x180;
 191
 192                clear_bit(priority, &vcpu->arch.pending_exceptions);
 193        }
 194
 195        return allowed;
 196}
 197
 198int
 199kvm_mips_irq_clear_cb(struct kvm_vcpu *vcpu, unsigned int priority,
 200                      uint32_t cause)
 201{
 202        return 1;
 203}
 204
 205void kvm_mips_deliver_interrupts(struct kvm_vcpu *vcpu, uint32_t cause)
 206{
 207        unsigned long *pending = &vcpu->arch.pending_exceptions;
 208        unsigned long *pending_clr = &vcpu->arch.pending_exceptions_clr;
 209        unsigned int priority;
 210
 211        if (!(*pending) && !(*pending_clr))
 212                return;
 213
 214        priority = __ffs(*pending_clr);
 215        while (priority <= MIPS_EXC_MAX) {
 216                if (kvm_mips_callbacks->irq_clear(vcpu, priority, cause)) {
 217                        if (!KVM_MIPS_IRQ_CLEAR_ALL_AT_ONCE)
 218                                break;
 219                }
 220
 221                priority = find_next_bit(pending_clr,
 222                                         BITS_PER_BYTE * sizeof(*pending_clr),
 223                                         priority + 1);
 224        }
 225
 226        priority = __ffs(*pending);
 227        while (priority <= MIPS_EXC_MAX) {
 228                if (kvm_mips_callbacks->irq_deliver(vcpu, priority, cause)) {
 229                        if (!KVM_MIPS_IRQ_DELIVER_ALL_AT_ONCE)
 230                                break;
 231                }
 232
 233                priority = find_next_bit(pending,
 234                                         BITS_PER_BYTE * sizeof(*pending),
 235                                         priority + 1);
 236        }
 237
 238}
 239
 240int kvm_mips_pending_timer(struct kvm_vcpu *vcpu)
 241{
 242        return test_bit(MIPS_EXC_INT_TIMER, &vcpu->arch.pending_exceptions);
 243}
 244