mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
f45ad5b8aa
Implement vcpu interrupt operations such as vcpu set irq and vcpu clear irq, using set_gcsr_estat() to set irq which is parsed by the irq bitmap. Reviewed-by: Bibo Mao <maobibo@loongson.cn> Tested-by: Huacai Chen <chenhuacai@loongson.cn> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
184 lines
4.1 KiB
C
184 lines
4.1 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>
|
|
|
|
static unsigned int priority_to_irq[EXCCODE_INT_NUM] = {
|
|
[INT_TI] = CPU_TIMER,
|
|
[INT_IPI] = CPU_IPI,
|
|
[INT_SWI0] = CPU_SIP0,
|
|
[INT_SWI1] = CPU_SIP1,
|
|
[INT_HWI0] = CPU_IP0,
|
|
[INT_HWI1] = CPU_IP1,
|
|
[INT_HWI2] = CPU_IP2,
|
|
[INT_HWI3] = CPU_IP3,
|
|
[INT_HWI4] = CPU_IP4,
|
|
[INT_HWI5] = CPU_IP5,
|
|
[INT_HWI6] = CPU_IP6,
|
|
[INT_HWI7] = CPU_IP7,
|
|
};
|
|
|
|
static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
|
|
{
|
|
unsigned int irq = 0;
|
|
|
|
clear_bit(priority, &vcpu->arch.irq_pending);
|
|
if (priority < EXCCODE_INT_NUM)
|
|
irq = priority_to_irq[priority];
|
|
|
|
switch (priority) {
|
|
case INT_TI:
|
|
case INT_IPI:
|
|
case INT_SWI0:
|
|
case INT_SWI1:
|
|
set_gcsr_estat(irq);
|
|
break;
|
|
|
|
case INT_HWI0 ... INT_HWI7:
|
|
set_csr_gintc(irq);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority)
|
|
{
|
|
unsigned int irq = 0;
|
|
|
|
clear_bit(priority, &vcpu->arch.irq_clear);
|
|
if (priority < EXCCODE_INT_NUM)
|
|
irq = priority_to_irq[priority];
|
|
|
|
switch (priority) {
|
|
case INT_TI:
|
|
case INT_IPI:
|
|
case INT_SWI0:
|
|
case INT_SWI1:
|
|
clear_gcsr_estat(irq);
|
|
break;
|
|
|
|
case INT_HWI0 ... INT_HWI7:
|
|
clear_csr_gintc(irq);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void kvm_deliver_intr(struct kvm_vcpu *vcpu)
|
|
{
|
|
unsigned int priority;
|
|
unsigned long *pending = &vcpu->arch.irq_pending;
|
|
unsigned long *pending_clr = &vcpu->arch.irq_clear;
|
|
|
|
if (!(*pending) && !(*pending_clr))
|
|
return;
|
|
|
|
if (*pending_clr) {
|
|
priority = __ffs(*pending_clr);
|
|
while (priority <= INT_IPI) {
|
|
kvm_irq_clear(vcpu, priority);
|
|
priority = find_next_bit(pending_clr,
|
|
BITS_PER_BYTE * sizeof(*pending_clr),
|
|
priority + 1);
|
|
}
|
|
}
|
|
|
|
if (*pending) {
|
|
priority = __ffs(*pending);
|
|
while (priority <= INT_IPI) {
|
|
kvm_irq_deliver(vcpu, priority);
|
|
priority = find_next_bit(pending,
|
|
BITS_PER_BYTE * sizeof(*pending),
|
|
priority + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|