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