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/module.h> 15d7d5b05fSDeng-Cheng Zhu #include <linux/vmalloc.h> 16d7d5b05fSDeng-Cheng Zhu #include <linux/fs.h> 17d7d5b05fSDeng-Cheng Zhu #include <linux/bootmem.h> 18d7d5b05fSDeng-Cheng Zhu #include <asm/page.h> 19d7d5b05fSDeng-Cheng Zhu #include <asm/cacheflush.h> 20d7d5b05fSDeng-Cheng Zhu 21d7d5b05fSDeng-Cheng Zhu #include <linux/kvm_host.h> 22d7d5b05fSDeng-Cheng Zhu 23d7d5b05fSDeng-Cheng Zhu #include "interrupt.h" 24d7d5b05fSDeng-Cheng Zhu 25d7d5b05fSDeng-Cheng Zhu void kvm_mips_queue_irq(struct kvm_vcpu *vcpu, uint32_t priority) 26d7d5b05fSDeng-Cheng Zhu { 27d7d5b05fSDeng-Cheng Zhu set_bit(priority, &vcpu->arch.pending_exceptions); 28d7d5b05fSDeng-Cheng Zhu } 29d7d5b05fSDeng-Cheng Zhu 30d7d5b05fSDeng-Cheng Zhu void kvm_mips_dequeue_irq(struct kvm_vcpu *vcpu, uint32_t priority) 31d7d5b05fSDeng-Cheng Zhu { 32d7d5b05fSDeng-Cheng Zhu clear_bit(priority, &vcpu->arch.pending_exceptions); 33d7d5b05fSDeng-Cheng Zhu } 34d7d5b05fSDeng-Cheng Zhu 35d7d5b05fSDeng-Cheng Zhu void kvm_mips_queue_timer_int_cb(struct kvm_vcpu *vcpu) 36d7d5b05fSDeng-Cheng Zhu { 37d7d5b05fSDeng-Cheng Zhu /* 38d7d5b05fSDeng-Cheng Zhu * Cause bits to reflect the pending timer interrupt, 39d7d5b05fSDeng-Cheng Zhu * the EXC code will be set when we are actually 40d7d5b05fSDeng-Cheng Zhu * delivering the interrupt: 41d7d5b05fSDeng-Cheng Zhu */ 42d7d5b05fSDeng-Cheng Zhu kvm_set_c0_guest_cause(vcpu->arch.cop0, (C_IRQ5 | C_TI)); 43d7d5b05fSDeng-Cheng Zhu 44d7d5b05fSDeng-Cheng Zhu /* Queue up an INT exception for the core */ 45d7d5b05fSDeng-Cheng Zhu kvm_mips_queue_irq(vcpu, MIPS_EXC_INT_TIMER); 46d7d5b05fSDeng-Cheng Zhu 47d7d5b05fSDeng-Cheng Zhu } 48d7d5b05fSDeng-Cheng Zhu 49d7d5b05fSDeng-Cheng Zhu void kvm_mips_dequeue_timer_int_cb(struct kvm_vcpu *vcpu) 50d7d5b05fSDeng-Cheng Zhu { 51d7d5b05fSDeng-Cheng Zhu kvm_clear_c0_guest_cause(vcpu->arch.cop0, (C_IRQ5 | C_TI)); 52d7d5b05fSDeng-Cheng Zhu kvm_mips_dequeue_irq(vcpu, MIPS_EXC_INT_TIMER); 53d7d5b05fSDeng-Cheng Zhu } 54d7d5b05fSDeng-Cheng Zhu 55d7d5b05fSDeng-Cheng Zhu void kvm_mips_queue_io_int_cb(struct kvm_vcpu *vcpu, 56d7d5b05fSDeng-Cheng Zhu struct kvm_mips_interrupt *irq) 57d7d5b05fSDeng-Cheng Zhu { 58d7d5b05fSDeng-Cheng Zhu int intr = (int)irq->irq; 59d7d5b05fSDeng-Cheng Zhu 60d7d5b05fSDeng-Cheng Zhu /* 61d7d5b05fSDeng-Cheng Zhu * Cause bits to reflect the pending IO interrupt, 62d7d5b05fSDeng-Cheng Zhu * the EXC code will be set when we are actually 63d7d5b05fSDeng-Cheng Zhu * delivering the interrupt: 64d7d5b05fSDeng-Cheng Zhu */ 65d7d5b05fSDeng-Cheng Zhu switch (intr) { 66d7d5b05fSDeng-Cheng Zhu case 2: 67d7d5b05fSDeng-Cheng Zhu kvm_set_c0_guest_cause(vcpu->arch.cop0, (C_IRQ0)); 68d7d5b05fSDeng-Cheng Zhu /* Queue up an INT exception for the core */ 69d7d5b05fSDeng-Cheng Zhu kvm_mips_queue_irq(vcpu, MIPS_EXC_INT_IO); 70d7d5b05fSDeng-Cheng Zhu break; 71d7d5b05fSDeng-Cheng Zhu 72d7d5b05fSDeng-Cheng Zhu case 3: 73d7d5b05fSDeng-Cheng Zhu kvm_set_c0_guest_cause(vcpu->arch.cop0, (C_IRQ1)); 74d7d5b05fSDeng-Cheng Zhu kvm_mips_queue_irq(vcpu, MIPS_EXC_INT_IPI_1); 75d7d5b05fSDeng-Cheng Zhu break; 76d7d5b05fSDeng-Cheng Zhu 77d7d5b05fSDeng-Cheng Zhu case 4: 78d7d5b05fSDeng-Cheng Zhu kvm_set_c0_guest_cause(vcpu->arch.cop0, (C_IRQ2)); 79d7d5b05fSDeng-Cheng Zhu kvm_mips_queue_irq(vcpu, MIPS_EXC_INT_IPI_2); 80d7d5b05fSDeng-Cheng Zhu break; 81d7d5b05fSDeng-Cheng Zhu 82d7d5b05fSDeng-Cheng Zhu default: 83d7d5b05fSDeng-Cheng Zhu break; 84d7d5b05fSDeng-Cheng Zhu } 85d7d5b05fSDeng-Cheng Zhu 86d7d5b05fSDeng-Cheng Zhu } 87d7d5b05fSDeng-Cheng Zhu 88d7d5b05fSDeng-Cheng Zhu void kvm_mips_dequeue_io_int_cb(struct kvm_vcpu *vcpu, 89d7d5b05fSDeng-Cheng Zhu struct kvm_mips_interrupt *irq) 90d7d5b05fSDeng-Cheng Zhu { 91d7d5b05fSDeng-Cheng Zhu int intr = (int)irq->irq; 92d7d5b05fSDeng-Cheng Zhu 93d7d5b05fSDeng-Cheng Zhu switch (intr) { 94d7d5b05fSDeng-Cheng Zhu case -2: 95d7d5b05fSDeng-Cheng Zhu kvm_clear_c0_guest_cause(vcpu->arch.cop0, (C_IRQ0)); 96d7d5b05fSDeng-Cheng Zhu kvm_mips_dequeue_irq(vcpu, MIPS_EXC_INT_IO); 97d7d5b05fSDeng-Cheng Zhu break; 98d7d5b05fSDeng-Cheng Zhu 99d7d5b05fSDeng-Cheng Zhu case -3: 100d7d5b05fSDeng-Cheng Zhu kvm_clear_c0_guest_cause(vcpu->arch.cop0, (C_IRQ1)); 101d7d5b05fSDeng-Cheng Zhu kvm_mips_dequeue_irq(vcpu, MIPS_EXC_INT_IPI_1); 102d7d5b05fSDeng-Cheng Zhu break; 103d7d5b05fSDeng-Cheng Zhu 104d7d5b05fSDeng-Cheng Zhu case -4: 105d7d5b05fSDeng-Cheng Zhu kvm_clear_c0_guest_cause(vcpu->arch.cop0, (C_IRQ2)); 106d7d5b05fSDeng-Cheng Zhu kvm_mips_dequeue_irq(vcpu, MIPS_EXC_INT_IPI_2); 107d7d5b05fSDeng-Cheng Zhu break; 108d7d5b05fSDeng-Cheng Zhu 109d7d5b05fSDeng-Cheng Zhu default: 110d7d5b05fSDeng-Cheng Zhu break; 111d7d5b05fSDeng-Cheng Zhu } 112d7d5b05fSDeng-Cheng Zhu 113d7d5b05fSDeng-Cheng Zhu } 114d7d5b05fSDeng-Cheng Zhu 115d7d5b05fSDeng-Cheng Zhu /* Deliver the interrupt of the corresponding priority, if possible. */ 116d7d5b05fSDeng-Cheng Zhu int kvm_mips_irq_deliver_cb(struct kvm_vcpu *vcpu, unsigned int priority, 117d7d5b05fSDeng-Cheng Zhu uint32_t cause) 118d7d5b05fSDeng-Cheng Zhu { 119d7d5b05fSDeng-Cheng Zhu int allowed = 0; 120d7d5b05fSDeng-Cheng Zhu uint32_t exccode; 121d7d5b05fSDeng-Cheng Zhu 122d7d5b05fSDeng-Cheng Zhu struct kvm_vcpu_arch *arch = &vcpu->arch; 123d7d5b05fSDeng-Cheng Zhu struct mips_coproc *cop0 = vcpu->arch.cop0; 124d7d5b05fSDeng-Cheng Zhu 125d7d5b05fSDeng-Cheng Zhu switch (priority) { 126d7d5b05fSDeng-Cheng Zhu case MIPS_EXC_INT_TIMER: 127d7d5b05fSDeng-Cheng Zhu if ((kvm_read_c0_guest_status(cop0) & ST0_IE) 128d7d5b05fSDeng-Cheng Zhu && (!(kvm_read_c0_guest_status(cop0) & (ST0_EXL | ST0_ERL))) 129d7d5b05fSDeng-Cheng Zhu && (kvm_read_c0_guest_status(cop0) & IE_IRQ5)) { 130d7d5b05fSDeng-Cheng Zhu allowed = 1; 131*16d100dbSJames Hogan exccode = EXCCODE_INT; 132d7d5b05fSDeng-Cheng Zhu } 133d7d5b05fSDeng-Cheng Zhu break; 134d7d5b05fSDeng-Cheng Zhu 135d7d5b05fSDeng-Cheng Zhu case MIPS_EXC_INT_IO: 136d7d5b05fSDeng-Cheng Zhu if ((kvm_read_c0_guest_status(cop0) & ST0_IE) 137d7d5b05fSDeng-Cheng Zhu && (!(kvm_read_c0_guest_status(cop0) & (ST0_EXL | ST0_ERL))) 138d7d5b05fSDeng-Cheng Zhu && (kvm_read_c0_guest_status(cop0) & IE_IRQ0)) { 139d7d5b05fSDeng-Cheng Zhu allowed = 1; 140*16d100dbSJames Hogan exccode = EXCCODE_INT; 141d7d5b05fSDeng-Cheng Zhu } 142d7d5b05fSDeng-Cheng Zhu break; 143d7d5b05fSDeng-Cheng Zhu 144d7d5b05fSDeng-Cheng Zhu case MIPS_EXC_INT_IPI_1: 145d7d5b05fSDeng-Cheng Zhu if ((kvm_read_c0_guest_status(cop0) & ST0_IE) 146d7d5b05fSDeng-Cheng Zhu && (!(kvm_read_c0_guest_status(cop0) & (ST0_EXL | ST0_ERL))) 147d7d5b05fSDeng-Cheng Zhu && (kvm_read_c0_guest_status(cop0) & IE_IRQ1)) { 148d7d5b05fSDeng-Cheng Zhu allowed = 1; 149*16d100dbSJames Hogan exccode = EXCCODE_INT; 150d7d5b05fSDeng-Cheng Zhu } 151d7d5b05fSDeng-Cheng Zhu break; 152d7d5b05fSDeng-Cheng Zhu 153d7d5b05fSDeng-Cheng Zhu case MIPS_EXC_INT_IPI_2: 154d7d5b05fSDeng-Cheng Zhu if ((kvm_read_c0_guest_status(cop0) & ST0_IE) 155d7d5b05fSDeng-Cheng Zhu && (!(kvm_read_c0_guest_status(cop0) & (ST0_EXL | ST0_ERL))) 156d7d5b05fSDeng-Cheng Zhu && (kvm_read_c0_guest_status(cop0) & IE_IRQ2)) { 157d7d5b05fSDeng-Cheng Zhu allowed = 1; 158*16d100dbSJames Hogan exccode = EXCCODE_INT; 159d7d5b05fSDeng-Cheng Zhu } 160d7d5b05fSDeng-Cheng Zhu break; 161d7d5b05fSDeng-Cheng Zhu 162d7d5b05fSDeng-Cheng Zhu default: 163d7d5b05fSDeng-Cheng Zhu break; 164d7d5b05fSDeng-Cheng Zhu } 165d7d5b05fSDeng-Cheng Zhu 166d7d5b05fSDeng-Cheng Zhu /* Are we allowed to deliver the interrupt ??? */ 167d7d5b05fSDeng-Cheng Zhu if (allowed) { 168d7d5b05fSDeng-Cheng Zhu if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { 169d7d5b05fSDeng-Cheng Zhu /* save old pc */ 170d7d5b05fSDeng-Cheng Zhu kvm_write_c0_guest_epc(cop0, arch->pc); 171d7d5b05fSDeng-Cheng Zhu kvm_set_c0_guest_status(cop0, ST0_EXL); 172d7d5b05fSDeng-Cheng Zhu 173d7d5b05fSDeng-Cheng Zhu if (cause & CAUSEF_BD) 174d7d5b05fSDeng-Cheng Zhu kvm_set_c0_guest_cause(cop0, CAUSEF_BD); 175d7d5b05fSDeng-Cheng Zhu else 176d7d5b05fSDeng-Cheng Zhu kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); 177d7d5b05fSDeng-Cheng Zhu 178d7d5b05fSDeng-Cheng Zhu kvm_debug("Delivering INT @ pc %#lx\n", arch->pc); 179d7d5b05fSDeng-Cheng Zhu 180d7d5b05fSDeng-Cheng Zhu } else 181d7d5b05fSDeng-Cheng Zhu kvm_err("Trying to deliver interrupt when EXL is already set\n"); 182d7d5b05fSDeng-Cheng Zhu 183d7d5b05fSDeng-Cheng Zhu kvm_change_c0_guest_cause(cop0, CAUSEF_EXCCODE, 184d7d5b05fSDeng-Cheng Zhu (exccode << CAUSEB_EXCCODE)); 185d7d5b05fSDeng-Cheng Zhu 186d7d5b05fSDeng-Cheng Zhu /* XXXSL Set PC to the interrupt exception entry point */ 187d7d5b05fSDeng-Cheng Zhu if (kvm_read_c0_guest_cause(cop0) & CAUSEF_IV) 188d7d5b05fSDeng-Cheng Zhu arch->pc = KVM_GUEST_KSEG0 + 0x200; 189d7d5b05fSDeng-Cheng Zhu else 190d7d5b05fSDeng-Cheng Zhu arch->pc = KVM_GUEST_KSEG0 + 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, 199d7d5b05fSDeng-Cheng Zhu uint32_t cause) 200d7d5b05fSDeng-Cheng Zhu { 201d7d5b05fSDeng-Cheng Zhu return 1; 202d7d5b05fSDeng-Cheng Zhu } 203d7d5b05fSDeng-Cheng Zhu 204d7d5b05fSDeng-Cheng Zhu void kvm_mips_deliver_interrupts(struct kvm_vcpu *vcpu, uint32_t 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