xref: /linux/arch/loongarch/kvm/interrupt.c (revision 51d90a15fedf8366cb96ef68d0ea2d0bf15417d2)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
4  */
5 
6 #include <linux/err.h>
7 #include <linux/errno.h>
8 #include <asm/kvm_csr.h>
9 #include <asm/kvm_vcpu.h>
10 
11 static unsigned int priority_to_irq[EXCCODE_INT_NUM] = {
12 	[INT_TI]	= CPU_TIMER,
13 	[INT_IPI]	= CPU_IPI,
14 	[INT_SWI0]	= CPU_SIP0,
15 	[INT_SWI1]	= CPU_SIP1,
16 	[INT_HWI0]	= CPU_IP0,
17 	[INT_HWI1]	= CPU_IP1,
18 	[INT_HWI2]	= CPU_IP2,
19 	[INT_HWI3]	= CPU_IP3,
20 	[INT_HWI4]	= CPU_IP4,
21 	[INT_HWI5]	= CPU_IP5,
22 	[INT_HWI6]	= CPU_IP6,
23 	[INT_HWI7]	= CPU_IP7,
24 	[INT_AVEC]	= CPU_AVEC,
25 };
26 
kvm_irq_deliver(struct kvm_vcpu * vcpu,unsigned int priority)27 static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
28 {
29 	unsigned int irq = 0;
30 
31 	clear_bit(priority, &vcpu->arch.irq_pending);
32 	if (priority < EXCCODE_INT_NUM)
33 		irq = priority_to_irq[priority];
34 
35 	if (cpu_has_msgint && (priority == INT_AVEC)) {
36 		set_gcsr_estat(irq);
37 		return 1;
38 	}
39 
40 	switch (priority) {
41 	case INT_TI:
42 	case INT_IPI:
43 	case INT_SWI0:
44 	case INT_SWI1:
45 		set_gcsr_estat(irq);
46 		break;
47 
48 	case INT_HWI0 ... INT_HWI7:
49 		set_csr_gintc(irq);
50 		break;
51 
52 	default:
53 		break;
54 	}
55 
56 	return 1;
57 }
58 
kvm_irq_clear(struct kvm_vcpu * vcpu,unsigned int priority)59 static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority)
60 {
61 	unsigned int irq = 0;
62 
63 	clear_bit(priority, &vcpu->arch.irq_clear);
64 	if (priority < EXCCODE_INT_NUM)
65 		irq = priority_to_irq[priority];
66 
67 	if (cpu_has_msgint && (priority == INT_AVEC)) {
68 		clear_gcsr_estat(irq);
69 		return 1;
70 	}
71 
72 	switch (priority) {
73 	case INT_TI:
74 	case INT_IPI:
75 	case INT_SWI0:
76 	case INT_SWI1:
77 		clear_gcsr_estat(irq);
78 		break;
79 
80 	case INT_HWI0 ... INT_HWI7:
81 		clear_csr_gintc(irq);
82 		break;
83 
84 	default:
85 		break;
86 	}
87 
88 	return 1;
89 }
90 
kvm_deliver_intr(struct kvm_vcpu * vcpu)91 void kvm_deliver_intr(struct kvm_vcpu *vcpu)
92 {
93 	unsigned int priority;
94 	unsigned long *pending = &vcpu->arch.irq_pending;
95 	unsigned long *pending_clr = &vcpu->arch.irq_clear;
96 
97 	for_each_set_bit(priority, pending_clr, EXCCODE_INT_NUM)
98 		kvm_irq_clear(vcpu, priority);
99 
100 	for_each_set_bit(priority, pending, EXCCODE_INT_NUM)
101 		kvm_irq_deliver(vcpu, priority);
102 }
103 
kvm_pending_timer(struct kvm_vcpu * vcpu)104 int kvm_pending_timer(struct kvm_vcpu *vcpu)
105 {
106 	return test_bit(INT_TI, &vcpu->arch.irq_pending);
107 }
108 
109 /*
110  * Only support illegal instruction or illegal Address Error exception,
111  * Other exceptions are injected by hardware in kvm mode
112  */
_kvm_deliver_exception(struct kvm_vcpu * vcpu,unsigned int code,unsigned int subcode)113 static void _kvm_deliver_exception(struct kvm_vcpu *vcpu,
114 				unsigned int code, unsigned int subcode)
115 {
116 	unsigned long val, vec_size;
117 
118 	/*
119 	 * BADV is added for EXCCODE_ADE exception
120 	 *  Use PC register (GVA address) if it is instruction exeception
121 	 *  Else use BADV from host side (GPA address) for data exeception
122 	 */
123 	if (code == EXCCODE_ADE) {
124 		if (subcode == EXSUBCODE_ADEF)
125 			val = vcpu->arch.pc;
126 		else
127 			val = vcpu->arch.badv;
128 		kvm_write_hw_gcsr(LOONGARCH_CSR_BADV, val);
129 	}
130 
131 	/* Set exception instruction */
132 	kvm_write_hw_gcsr(LOONGARCH_CSR_BADI, vcpu->arch.badi);
133 
134 	/*
135 	 * Save CRMD in PRMD
136 	 * Set IRQ disabled and PLV0 with CRMD
137 	 */
138 	val = kvm_read_hw_gcsr(LOONGARCH_CSR_CRMD);
139 	kvm_write_hw_gcsr(LOONGARCH_CSR_PRMD, val);
140 	val = val & ~(CSR_CRMD_PLV | CSR_CRMD_IE);
141 	kvm_write_hw_gcsr(LOONGARCH_CSR_CRMD, val);
142 
143 	/* Set exception PC address */
144 	kvm_write_hw_gcsr(LOONGARCH_CSR_ERA, vcpu->arch.pc);
145 
146 	/*
147 	 * Set exception code
148 	 * Exception and interrupt can be inject at the same time
149 	 * Hardware will handle exception first and then extern interrupt
150 	 * Exception code is Ecode in ESTAT[16:21]
151 	 * Interrupt code in ESTAT[0:12]
152 	 */
153 	val = kvm_read_hw_gcsr(LOONGARCH_CSR_ESTAT);
154 	val = (val & ~CSR_ESTAT_EXC) | code;
155 	kvm_write_hw_gcsr(LOONGARCH_CSR_ESTAT, val);
156 
157 	/* Calculate expcetion entry address */
158 	val = kvm_read_hw_gcsr(LOONGARCH_CSR_ECFG);
159 	vec_size = (val & CSR_ECFG_VS) >> CSR_ECFG_VS_SHIFT;
160 	if (vec_size)
161 		vec_size = (1 << vec_size) * 4;
162 	val =  kvm_read_hw_gcsr(LOONGARCH_CSR_EENTRY);
163 	vcpu->arch.pc = val + code * vec_size;
164 }
165 
kvm_deliver_exception(struct kvm_vcpu * vcpu)166 void kvm_deliver_exception(struct kvm_vcpu *vcpu)
167 {
168 	unsigned int code;
169 	unsigned long *pending = &vcpu->arch.exception_pending;
170 
171 	if (*pending) {
172 		code = __ffs(*pending);
173 		_kvm_deliver_exception(vcpu, code, vcpu->arch.esubcode);
174 		*pending = 0;
175 		vcpu->arch.esubcode = 0;
176 	}
177 }
178