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