#include <linux/err.h>
#include <linux/errno.h>
#include <asm/kvm_csr.h>
#include <asm/kvm_vcpu.h>
#include <asm/kvm_dmsintc.h>
static void kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned long mask)
{
unsigned long irq;
unsigned long old, new;
if (mask & CPU_AVEC)
dmsintc_inject_irq(vcpu);
irq = mask & KVM_ESTAT_INTI_MASK;
if (irq) {
old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
set_gcsr_estat(irq);
new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
if (new > old)
set_gcsr_estat(CPU_TIMER);
}
irq = (mask >> VIP_DELTA) & KVM_GINTC_IRQ_MASK;
if (irq)
set_csr_gintc(irq);
}
static void kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned long mask)
{
unsigned long irq;
unsigned long old, new;
irq = mask & KVM_ESTAT_INTI_MASK;
if (irq) {
old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
clear_gcsr_estat(irq);
new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
if (new > old)
set_gcsr_estat(CPU_TIMER);
}
irq = (mask >> VIP_DELTA) & KVM_GINTC_IRQ_MASK;
if (irq)
clear_csr_gintc(irq);
}
void kvm_deliver_intr(struct kvm_vcpu *vcpu)
{
unsigned long mask;
mask = READ_ONCE(vcpu->arch.irq_clear);
if (mask) {
mask = xchg_relaxed(&vcpu->arch.irq_clear, 0);
kvm_irq_clear(vcpu, mask);
}
mask = READ_ONCE(vcpu->arch.irq_pending);
if (mask) {
mask = xchg_relaxed(&vcpu->arch.irq_pending, 0);
kvm_irq_deliver(vcpu, mask);
}
}
int kvm_pending_timer(struct kvm_vcpu *vcpu)
{
return test_bit(INT_TI, &vcpu->arch.irq_pending);
}
static void _kvm_deliver_exception(struct kvm_vcpu *vcpu,
unsigned int code, unsigned int subcode)
{
unsigned long val, vec_size;
if (code == EXCCODE_ADE) {
if (subcode == EXSUBCODE_ADEF)
val = vcpu->arch.pc;
else
val = vcpu->arch.badv;
kvm_write_hw_gcsr(LOONGARCH_CSR_BADV, val);
}
kvm_write_hw_gcsr(LOONGARCH_CSR_BADI, vcpu->arch.badi);
val = kvm_read_hw_gcsr(LOONGARCH_CSR_CRMD);
kvm_write_hw_gcsr(LOONGARCH_CSR_PRMD, val);
val = val & ~(CSR_CRMD_PLV | CSR_CRMD_IE);
kvm_write_hw_gcsr(LOONGARCH_CSR_CRMD, val);
kvm_write_hw_gcsr(LOONGARCH_CSR_ERA, vcpu->arch.pc);
val = kvm_read_hw_gcsr(LOONGARCH_CSR_ESTAT);
val = (val & ~CSR_ESTAT_EXC) | code;
kvm_write_hw_gcsr(LOONGARCH_CSR_ESTAT, val);
val = kvm_read_hw_gcsr(LOONGARCH_CSR_ECFG);
vec_size = (val & CSR_ECFG_VS) >> CSR_ECFG_VS_SHIFT;
if (vec_size)
vec_size = (1 << vec_size) * 4;
val = kvm_read_hw_gcsr(LOONGARCH_CSR_EENTRY);
vcpu->arch.pc = val + code * vec_size;
}
void kvm_deliver_exception(struct kvm_vcpu *vcpu)
{
unsigned int code;
unsigned long *pending = &vcpu->arch.exception_pending;
if (*pending) {
code = __ffs(*pending);
_kvm_deliver_exception(vcpu, code, vcpu->arch.esubcode);
*pending = 0;
vcpu->arch.esubcode = 0;
}
}