1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
7 #include <linux/errno.h>
8 #include <asm/kvm_csr.h>
9 #include <asm/kvm_vcpu.h>
11 static unsigned int priority_to_irq
[EXCCODE_INT_NUM
] = {
14 [INT_SWI0
] = CPU_SIP0
,
15 [INT_SWI1
] = CPU_SIP1
,
26 static int kvm_irq_deliver(struct kvm_vcpu
*vcpu
, unsigned int priority
)
30 clear_bit(priority
, &vcpu
->arch
.irq_pending
);
31 if (priority
< EXCCODE_INT_NUM
)
32 irq
= priority_to_irq
[priority
];
42 case INT_HWI0
... INT_HWI7
:
53 static int kvm_irq_clear(struct kvm_vcpu
*vcpu
, unsigned int priority
)
57 clear_bit(priority
, &vcpu
->arch
.irq_clear
);
58 if (priority
< EXCCODE_INT_NUM
)
59 irq
= priority_to_irq
[priority
];
66 clear_gcsr_estat(irq
);
69 case INT_HWI0
... INT_HWI7
:
80 void kvm_deliver_intr(struct kvm_vcpu
*vcpu
)
82 unsigned int priority
;
83 unsigned long *pending
= &vcpu
->arch
.irq_pending
;
84 unsigned long *pending_clr
= &vcpu
->arch
.irq_clear
;
86 if (!(*pending
) && !(*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
),
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
),
110 int kvm_pending_timer(struct kvm_vcpu
*vcpu
)
112 return test_bit(INT_TI
, &vcpu
->arch
.irq_pending
);
116 * Only support illegal instruction or illegal Address Error exception,
117 * Other exceptions are injected by hardware in kvm mode
119 static void _kvm_deliver_exception(struct kvm_vcpu
*vcpu
,
120 unsigned int code
, unsigned int subcode
)
122 unsigned long val
, vec_size
;
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
129 if (code
== EXCCODE_ADE
) {
130 if (subcode
== EXSUBCODE_ADEF
)
133 val
= vcpu
->arch
.badv
;
134 kvm_write_hw_gcsr(LOONGARCH_CSR_BADV
, val
);
137 /* Set exception instruction */
138 kvm_write_hw_gcsr(LOONGARCH_CSR_BADI
, vcpu
->arch
.badi
);
142 * Set IRQ disabled and PLV0 with CRMD
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
);
149 /* Set exception PC address */
150 kvm_write_hw_gcsr(LOONGARCH_CSR_ERA
, vcpu
->arch
.pc
);
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]
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
);
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
;
167 vec_size
= (1 << vec_size
) * 4;
168 val
= kvm_read_hw_gcsr(LOONGARCH_CSR_EENTRY
);
169 vcpu
->arch
.pc
= val
+ code
* vec_size
;
172 void kvm_deliver_exception(struct kvm_vcpu
*vcpu
)
175 unsigned long *pending
= &vcpu
->arch
.exception_pending
;
178 code
= __ffs(*pending
);
179 _kvm_deliver_exception(vcpu
, code
, vcpu
->arch
.esubcode
);
181 vcpu
->arch
.esubcode
= 0;