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;