18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 38c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 48c2ecf20Sopenharmony_ci * for more details. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * KVM/MIPS: Interrupt delivery 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. 98c2ecf20Sopenharmony_ci * Authors: Sanjay Lal <sanjayl@kymasys.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 158c2ecf20Sopenharmony_ci#include <linux/fs.h> 168c2ecf20Sopenharmony_ci#include <linux/memblock.h> 178c2ecf20Sopenharmony_ci#include <asm/page.h> 188c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/kvm_host.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "interrupt.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_civoid kvm_mips_queue_irq(struct kvm_vcpu *vcpu, unsigned int priority) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci set_bit(priority, &vcpu->arch.pending_exceptions); 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_civoid kvm_mips_dequeue_irq(struct kvm_vcpu *vcpu, unsigned int priority) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci clear_bit(priority, &vcpu->arch.pending_exceptions); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_civoid kvm_mips_queue_timer_int_cb(struct kvm_vcpu *vcpu) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci /* 378c2ecf20Sopenharmony_ci * Cause bits to reflect the pending timer interrupt, 388c2ecf20Sopenharmony_ci * the EXC code will be set when we are actually 398c2ecf20Sopenharmony_ci * delivering the interrupt: 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(vcpu->arch.cop0, (C_IRQ5 | C_TI)); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci /* Queue up an INT exception for the core */ 448c2ecf20Sopenharmony_ci kvm_mips_queue_irq(vcpu, MIPS_EXC_INT_TIMER); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_civoid kvm_mips_dequeue_timer_int_cb(struct kvm_vcpu *vcpu) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(vcpu->arch.cop0, (C_IRQ5 | C_TI)); 518c2ecf20Sopenharmony_ci kvm_mips_dequeue_irq(vcpu, MIPS_EXC_INT_TIMER); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_civoid kvm_mips_queue_io_int_cb(struct kvm_vcpu *vcpu, 558c2ecf20Sopenharmony_ci struct kvm_mips_interrupt *irq) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci int intr = (int)irq->irq; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* 608c2ecf20Sopenharmony_ci * Cause bits to reflect the pending IO interrupt, 618c2ecf20Sopenharmony_ci * the EXC code will be set when we are actually 628c2ecf20Sopenharmony_ci * delivering the interrupt: 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(vcpu->arch.cop0, 1 << (intr + 8)); 658c2ecf20Sopenharmony_ci kvm_mips_queue_irq(vcpu, kvm_irq_to_priority(intr)); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_civoid kvm_mips_dequeue_io_int_cb(struct kvm_vcpu *vcpu, 698c2ecf20Sopenharmony_ci struct kvm_mips_interrupt *irq) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci int intr = (int)irq->irq; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(vcpu->arch.cop0, 1 << (-intr + 8)); 748c2ecf20Sopenharmony_ci kvm_mips_dequeue_irq(vcpu, kvm_irq_to_priority(-intr)); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* Deliver the interrupt of the corresponding priority, if possible. */ 788c2ecf20Sopenharmony_ciint kvm_mips_irq_deliver_cb(struct kvm_vcpu *vcpu, unsigned int priority, 798c2ecf20Sopenharmony_ci u32 cause) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci int allowed = 0; 828c2ecf20Sopenharmony_ci u32 exccode, ie; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci struct kvm_vcpu_arch *arch = &vcpu->arch; 858c2ecf20Sopenharmony_ci struct mips_coproc *cop0 = vcpu->arch.cop0; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (priority == MIPS_EXC_MAX) 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci ie = 1 << (kvm_priority_to_irq[priority] + 8); 918c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_status(cop0) & ST0_IE) 928c2ecf20Sopenharmony_ci && (!(kvm_read_c0_guest_status(cop0) & (ST0_EXL | ST0_ERL))) 938c2ecf20Sopenharmony_ci && (kvm_read_c0_guest_status(cop0) & ie)) { 948c2ecf20Sopenharmony_ci allowed = 1; 958c2ecf20Sopenharmony_ci exccode = EXCCODE_INT; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* Are we allowed to deliver the interrupt ??? */ 998c2ecf20Sopenharmony_ci if (allowed) { 1008c2ecf20Sopenharmony_ci if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) { 1018c2ecf20Sopenharmony_ci /* save old pc */ 1028c2ecf20Sopenharmony_ci kvm_write_c0_guest_epc(cop0, arch->pc); 1038c2ecf20Sopenharmony_ci kvm_set_c0_guest_status(cop0, ST0_EXL); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (cause & CAUSEF_BD) 1068c2ecf20Sopenharmony_ci kvm_set_c0_guest_cause(cop0, CAUSEF_BD); 1078c2ecf20Sopenharmony_ci else 1088c2ecf20Sopenharmony_ci kvm_clear_c0_guest_cause(cop0, CAUSEF_BD); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci kvm_debug("Delivering INT @ pc %#lx\n", arch->pc); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci } else 1138c2ecf20Sopenharmony_ci kvm_err("Trying to deliver interrupt when EXL is already set\n"); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci kvm_change_c0_guest_cause(cop0, CAUSEF_EXCCODE, 1168c2ecf20Sopenharmony_ci (exccode << CAUSEB_EXCCODE)); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* XXXSL Set PC to the interrupt exception entry point */ 1198c2ecf20Sopenharmony_ci arch->pc = kvm_mips_guest_exception_base(vcpu); 1208c2ecf20Sopenharmony_ci if (kvm_read_c0_guest_cause(cop0) & CAUSEF_IV) 1218c2ecf20Sopenharmony_ci arch->pc += 0x200; 1228c2ecf20Sopenharmony_ci else 1238c2ecf20Sopenharmony_ci arch->pc += 0x180; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci clear_bit(priority, &vcpu->arch.pending_exceptions); 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return allowed; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ciint kvm_mips_irq_clear_cb(struct kvm_vcpu *vcpu, unsigned int priority, 1328c2ecf20Sopenharmony_ci u32 cause) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci return 1; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_civoid kvm_mips_deliver_interrupts(struct kvm_vcpu *vcpu, u32 cause) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci unsigned long *pending = &vcpu->arch.pending_exceptions; 1408c2ecf20Sopenharmony_ci unsigned long *pending_clr = &vcpu->arch.pending_exceptions_clr; 1418c2ecf20Sopenharmony_ci unsigned int priority; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (!(*pending) && !(*pending_clr)) 1448c2ecf20Sopenharmony_ci return; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci priority = __ffs(*pending_clr); 1478c2ecf20Sopenharmony_ci while (priority <= MIPS_EXC_MAX) { 1488c2ecf20Sopenharmony_ci if (kvm_mips_callbacks->irq_clear(vcpu, priority, cause)) { 1498c2ecf20Sopenharmony_ci if (!KVM_MIPS_IRQ_CLEAR_ALL_AT_ONCE) 1508c2ecf20Sopenharmony_ci break; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci priority = find_next_bit(pending_clr, 1548c2ecf20Sopenharmony_ci BITS_PER_BYTE * sizeof(*pending_clr), 1558c2ecf20Sopenharmony_ci priority + 1); 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci priority = __ffs(*pending); 1598c2ecf20Sopenharmony_ci while (priority <= MIPS_EXC_MAX) { 1608c2ecf20Sopenharmony_ci if (kvm_mips_callbacks->irq_deliver(vcpu, priority, cause)) { 1618c2ecf20Sopenharmony_ci if (!KVM_MIPS_IRQ_DELIVER_ALL_AT_ONCE) 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci priority = find_next_bit(pending, 1668c2ecf20Sopenharmony_ci BITS_PER_BYTE * sizeof(*pending), 1678c2ecf20Sopenharmony_ci priority + 1); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciint kvm_mips_pending_timer(struct kvm_vcpu *vcpu) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci return test_bit(MIPS_EXC_INT_TIMER, &vcpu->arch.pending_exceptions); 1758c2ecf20Sopenharmony_ci} 176