Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/loongarch/kvm/interrupt.c
50619 views
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
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
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
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
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
*/
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
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
179