mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-08-09 06:14:34 +02:00
With bitmask method, interrupts can be injected with batch mode rather than one by one. Also remove unused array priority_to_irq[] here. Signed-off-by: Bibo Mao <maobibo@loongson.cn> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
148 lines
3.5 KiB
C
148 lines
3.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2020-2023 Loongson Technology Corporation Limited
|
|
*/
|
|
|
|
#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);
|
|
|
|
/* Inject TI if TVAL inverted */
|
|
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);
|
|
|
|
/* Inject TI if TVAL inverted */
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* Only support illegal instruction or illegal Address Error exception,
|
|
* Other exceptions are injected by hardware in kvm mode
|
|
*/
|
|
static void _kvm_deliver_exception(struct kvm_vcpu *vcpu,
|
|
unsigned int code, unsigned int subcode)
|
|
{
|
|
unsigned long val, vec_size;
|
|
|
|
/*
|
|
* BADV is added for EXCCODE_ADE exception
|
|
* Use PC register (GVA address) if it is instruction exeception
|
|
* Else use BADV from host side (GPA address) for data exeception
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/* Set exception instruction */
|
|
kvm_write_hw_gcsr(LOONGARCH_CSR_BADI, vcpu->arch.badi);
|
|
|
|
/*
|
|
* Save CRMD in PRMD
|
|
* Set IRQ disabled and PLV0 with CRMD
|
|
*/
|
|
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);
|
|
|
|
/* Set exception PC address */
|
|
kvm_write_hw_gcsr(LOONGARCH_CSR_ERA, vcpu->arch.pc);
|
|
|
|
/*
|
|
* Set exception code
|
|
* Exception and interrupt can be inject at the same time
|
|
* Hardware will handle exception first and then extern interrupt
|
|
* Exception code is Ecode in ESTAT[16:21]
|
|
* Interrupt code in ESTAT[0:12]
|
|
*/
|
|
val = kvm_read_hw_gcsr(LOONGARCH_CSR_ESTAT);
|
|
val = (val & ~CSR_ESTAT_EXC) | code;
|
|
kvm_write_hw_gcsr(LOONGARCH_CSR_ESTAT, val);
|
|
|
|
/* Calculate expcetion entry address */
|
|
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;
|
|
}
|
|
}
|