Skip to content

Commit f45ad5b

Browse files
zhaotianrui-loongsonchenhuacai
authored andcommitted
LoongArch: KVM: Implement vcpu interrupt operations
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 <[email protected]> Tested-by: Huacai Chen <[email protected]> Signed-off-by: Tianrui Zhao <[email protected]> Signed-off-by: Huacai Chen <[email protected]>
1 parent 84be421 commit f45ad5b

File tree

2 files changed

+221
-0
lines changed

2 files changed

+221
-0
lines changed

arch/loongarch/kvm/interrupt.c

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (C) 2020-2023 Loongson Technology Corporation Limited
4+
*/
5+
6+
#include <linux/err.h>
7+
#include <linux/errno.h>
8+
#include <asm/kvm_csr.h>
9+
#include <asm/kvm_vcpu.h>
10+
11+
static unsigned int priority_to_irq[EXCCODE_INT_NUM] = {
12+
[INT_TI] = CPU_TIMER,
13+
[INT_IPI] = CPU_IPI,
14+
[INT_SWI0] = CPU_SIP0,
15+
[INT_SWI1] = CPU_SIP1,
16+
[INT_HWI0] = CPU_IP0,
17+
[INT_HWI1] = CPU_IP1,
18+
[INT_HWI2] = CPU_IP2,
19+
[INT_HWI3] = CPU_IP3,
20+
[INT_HWI4] = CPU_IP4,
21+
[INT_HWI5] = CPU_IP5,
22+
[INT_HWI6] = CPU_IP6,
23+
[INT_HWI7] = CPU_IP7,
24+
};
25+
26+
static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
27+
{
28+
unsigned int irq = 0;
29+
30+
clear_bit(priority, &vcpu->arch.irq_pending);
31+
if (priority < EXCCODE_INT_NUM)
32+
irq = priority_to_irq[priority];
33+
34+
switch (priority) {
35+
case INT_TI:
36+
case INT_IPI:
37+
case INT_SWI0:
38+
case INT_SWI1:
39+
set_gcsr_estat(irq);
40+
break;
41+
42+
case INT_HWI0 ... INT_HWI7:
43+
set_csr_gintc(irq);
44+
break;
45+
46+
default:
47+
break;
48+
}
49+
50+
return 1;
51+
}
52+
53+
static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority)
54+
{
55+
unsigned int irq = 0;
56+
57+
clear_bit(priority, &vcpu->arch.irq_clear);
58+
if (priority < EXCCODE_INT_NUM)
59+
irq = priority_to_irq[priority];
60+
61+
switch (priority) {
62+
case INT_TI:
63+
case INT_IPI:
64+
case INT_SWI0:
65+
case INT_SWI1:
66+
clear_gcsr_estat(irq);
67+
break;
68+
69+
case INT_HWI0 ... INT_HWI7:
70+
clear_csr_gintc(irq);
71+
break;
72+
73+
default:
74+
break;
75+
}
76+
77+
return 1;
78+
}
79+
80+
void kvm_deliver_intr(struct kvm_vcpu *vcpu)
81+
{
82+
unsigned int priority;
83+
unsigned long *pending = &vcpu->arch.irq_pending;
84+
unsigned long *pending_clr = &vcpu->arch.irq_clear;
85+
86+
if (!(*pending) && !(*pending_clr))
87+
return;
88+
89+
if (*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),
95+
priority + 1);
96+
}
97+
}
98+
99+
if (*pending) {
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),
105+
priority + 1);
106+
}
107+
}
108+
}
109+
110+
int kvm_pending_timer(struct kvm_vcpu *vcpu)
111+
{
112+
return test_bit(INT_TI, &vcpu->arch.irq_pending);
113+
}
114+
115+
/*
116+
* Only support illegal instruction or illegal Address Error exception,
117+
* Other exceptions are injected by hardware in kvm mode
118+
*/
119+
static void _kvm_deliver_exception(struct kvm_vcpu *vcpu,
120+
unsigned int code, unsigned int subcode)
121+
{
122+
unsigned long val, vec_size;
123+
124+
/*
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
128+
*/
129+
if (code == EXCCODE_ADE) {
130+
if (subcode == EXSUBCODE_ADEF)
131+
val = vcpu->arch.pc;
132+
else
133+
val = vcpu->arch.badv;
134+
kvm_write_hw_gcsr(LOONGARCH_CSR_BADV, val);
135+
}
136+
137+
/* Set exception instruction */
138+
kvm_write_hw_gcsr(LOONGARCH_CSR_BADI, vcpu->arch.badi);
139+
140+
/*
141+
* Save CRMD in PRMD
142+
* Set IRQ disabled and PLV0 with CRMD
143+
*/
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);
148+
149+
/* Set exception PC address */
150+
kvm_write_hw_gcsr(LOONGARCH_CSR_ERA, vcpu->arch.pc);
151+
152+
/*
153+
* Set exception code
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]
158+
*/
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);
162+
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;
166+
if (vec_size)
167+
vec_size = (1 << vec_size) * 4;
168+
val = kvm_read_hw_gcsr(LOONGARCH_CSR_EENTRY);
169+
vcpu->arch.pc = val + code * vec_size;
170+
}
171+
172+
void kvm_deliver_exception(struct kvm_vcpu *vcpu)
173+
{
174+
unsigned int code;
175+
unsigned long *pending = &vcpu->arch.exception_pending;
176+
177+
if (*pending) {
178+
code = __ffs(*pending);
179+
_kvm_deliver_exception(vcpu, code, vcpu->arch.esubcode);
180+
*pending = 0;
181+
vcpu->arch.esubcode = 0;
182+
}
183+
}

arch/loongarch/kvm/vcpu.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,44 @@ void kvm_lose_fpu(struct kvm_vcpu *vcpu)
458458
preempt_enable();
459459
}
460460

461+
int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq)
462+
{
463+
int intr = (int)irq->irq;
464+
465+
if (intr > 0)
466+
kvm_queue_irq(vcpu, intr);
467+
else if (intr < 0)
468+
kvm_dequeue_irq(vcpu, -intr);
469+
else {
470+
kvm_err("%s: invalid interrupt ioctl %d\n", __func__, irq->irq);
471+
return -EINVAL;
472+
}
473+
474+
kvm_vcpu_kick(vcpu);
475+
476+
return 0;
477+
}
478+
479+
long kvm_arch_vcpu_async_ioctl(struct file *filp,
480+
unsigned int ioctl, unsigned long arg)
481+
{
482+
void __user *argp = (void __user *)arg;
483+
struct kvm_vcpu *vcpu = filp->private_data;
484+
485+
if (ioctl == KVM_INTERRUPT) {
486+
struct kvm_interrupt irq;
487+
488+
if (copy_from_user(&irq, argp, sizeof(irq)))
489+
return -EFAULT;
490+
491+
kvm_debug("[%d] %s: irq: %d\n", vcpu->vcpu_id, __func__, irq.irq);
492+
493+
return kvm_vcpu_ioctl_interrupt(vcpu, &irq);
494+
}
495+
496+
return -ENOIOCTLCMD;
497+
}
498+
461499
int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
462500
{
463501
return 0;

0 commit comments

Comments
 (0)