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