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