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, subcode: vcpu->arch.esubcode); |
180 | *pending = 0; |
181 | vcpu->arch.esubcode = 0; |
182 | } |
183 | } |
184 | |