xref: /linux/arch/loongarch/kvm/interrupt.c (revision bba2c3615bd6cfee7456d1130f2e6b01b3f4e9ba)
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 #include <asm/kvm_dmsintc.h>
11 
12 static void kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned long mask)
13 {
14 	unsigned long irq;
15 	unsigned long old, new;
16 
17 	if (mask & CPU_AVEC)
18 		dmsintc_inject_irq(vcpu);
19 
20 	irq = mask & KVM_ESTAT_INTI_MASK;
21 	if (irq) {
22 		old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
23 		set_gcsr_estat(irq);
24 		new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
25 
26 		/* Inject TI if TVAL inverted */
27 		if (new > old)
28 			set_gcsr_estat(CPU_TIMER);
29 	}
30 
31 	irq = (mask >> VIP_DELTA) & KVM_GINTC_IRQ_MASK;
32 	if (irq)
33 		set_csr_gintc(irq);
34 }
35 
36 static void kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned long mask)
37 {
38 	unsigned long irq;
39 	unsigned long old, new;
40 
41 	irq = mask & KVM_ESTAT_INTI_MASK;
42 	if (irq) {
43 		old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
44 		clear_gcsr_estat(irq);
45 		new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
46 
47 		/* Inject TI if TVAL inverted */
48 		if (new > old)
49 			set_gcsr_estat(CPU_TIMER);
50 	}
51 
52 	irq = (mask >> VIP_DELTA) & KVM_GINTC_IRQ_MASK;
53 	if (irq)
54 		clear_csr_gintc(irq);
55 }
56 
57 void kvm_deliver_intr(struct kvm_vcpu *vcpu)
58 {
59 	unsigned long mask;
60 
61 	mask = READ_ONCE(vcpu->arch.irq_clear);
62 	if (mask) {
63 		mask = xchg_relaxed(&vcpu->arch.irq_clear, 0);
64 		kvm_irq_clear(vcpu, mask);
65 	}
66 
67 	mask = READ_ONCE(vcpu->arch.irq_pending);
68 	if (mask) {
69 		mask = xchg_relaxed(&vcpu->arch.irq_pending, 0);
70 		kvm_irq_deliver(vcpu, mask);
71 	}
72 }
73 
74 int kvm_pending_timer(struct kvm_vcpu *vcpu)
75 {
76 	return test_bit(INT_TI, &vcpu->arch.irq_pending);
77 }
78 
79 /*
80  * Only support illegal instruction or illegal Address Error exception,
81  * Other exceptions are injected by hardware in kvm mode
82  */
83 static void _kvm_deliver_exception(struct kvm_vcpu *vcpu,
84 				unsigned int code, unsigned int subcode)
85 {
86 	unsigned long val, vec_size;
87 
88 	/*
89 	 * BADV is added for EXCCODE_ADE exception
90 	 *  Use PC register (GVA address) if it is instruction exeception
91 	 *  Else use BADV from host side (GPA address) for data exeception
92 	 */
93 	if (code == EXCCODE_ADE) {
94 		if (subcode == EXSUBCODE_ADEF)
95 			val = vcpu->arch.pc;
96 		else
97 			val = vcpu->arch.badv;
98 		kvm_write_hw_gcsr(LOONGARCH_CSR_BADV, val);
99 	}
100 
101 	/* Set exception instruction */
102 	kvm_write_hw_gcsr(LOONGARCH_CSR_BADI, vcpu->arch.badi);
103 
104 	/*
105 	 * Save CRMD in PRMD
106 	 * Set IRQ disabled and PLV0 with CRMD
107 	 */
108 	val = kvm_read_hw_gcsr(LOONGARCH_CSR_CRMD);
109 	kvm_write_hw_gcsr(LOONGARCH_CSR_PRMD, val);
110 	val = val & ~(CSR_CRMD_PLV | CSR_CRMD_IE);
111 	kvm_write_hw_gcsr(LOONGARCH_CSR_CRMD, val);
112 
113 	/* Set exception PC address */
114 	kvm_write_hw_gcsr(LOONGARCH_CSR_ERA, vcpu->arch.pc);
115 
116 	/*
117 	 * Set exception code
118 	 * Exception and interrupt can be inject at the same time
119 	 * Hardware will handle exception first and then extern interrupt
120 	 * Exception code is Ecode in ESTAT[16:21]
121 	 * Interrupt code in ESTAT[0:12]
122 	 */
123 	val = kvm_read_hw_gcsr(LOONGARCH_CSR_ESTAT);
124 	val = (val & ~CSR_ESTAT_EXC) | code;
125 	kvm_write_hw_gcsr(LOONGARCH_CSR_ESTAT, val);
126 
127 	/* Calculate expcetion entry address */
128 	val = kvm_read_hw_gcsr(LOONGARCH_CSR_ECFG);
129 	vec_size = (val & CSR_ECFG_VS) >> CSR_ECFG_VS_SHIFT;
130 	if (vec_size)
131 		vec_size = (1 << vec_size) * 4;
132 	val =  kvm_read_hw_gcsr(LOONGARCH_CSR_EENTRY);
133 	vcpu->arch.pc = val + code * vec_size;
134 }
135 
136 void kvm_deliver_exception(struct kvm_vcpu *vcpu)
137 {
138 	unsigned int code;
139 	unsigned long *pending = &vcpu->arch.exception_pending;
140 
141 	if (*pending) {
142 		code = __ffs(*pending);
143 		_kvm_deliver_exception(vcpu, code, vcpu->arch.esubcode);
144 		*pending = 0;
145 		vcpu->arch.esubcode = 0;
146 	}
147 }
148