* [PATCH v6 1/6] LoongArch: KVM: Check irq validility in kvm_vcpu_ioctl_interrupt()
2026-06-11 6:58 [PATCH v6 0/6] LoongArch: KVM: Small enhancement about interrupt injection Bibo Mao
@ 2026-06-11 6:58 ` Bibo Mao
2026-06-11 7:08 ` sashiko-bot
2026-06-11 6:58 ` [PATCH v6 2/6] LoongArch: KVM: Check msgint feature in interrupt post Bibo Mao
` (4 subsequent siblings)
5 siblings, 1 reply; 14+ messages in thread
From: Bibo Mao @ 2026-06-11 6:58 UTC (permalink / raw)
To: Huacai Chen; +Cc: kernel, kvm, loongarch, linux-kernel, stable
Function kvm_vcpu_ioctl_interrupt() can be called from userspace, here
add irq validility cheking in kvm_vcpu_ioctl_interrupt().
Fixes: f45ad5b8aa93 ("LoongArch: KVM: Implement vcpu interrupt operations")
Cc: stable@vger.kernel.org
Signed-off-by: Bibo Mao <maobibo@loongson.cn>
---
arch/loongarch/kvm/vcpu.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index e28084c49e68..df5be9b265e8 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -1487,6 +1487,11 @@ void kvm_lose_fpu(struct kvm_vcpu *vcpu)
int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq)
{
int intr = (int)irq->irq;
+ unsigned int vector;
+
+ vector = abs(intr);
+ if (vector >= EXCCODE_INT_NUM)
+ return -EINVAL;
if (intr > 0)
kvm_queue_irq(vcpu, intr);
--
2.39.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v6 1/6] LoongArch: KVM: Check irq validility in kvm_vcpu_ioctl_interrupt()
2026-06-11 6:58 ` [PATCH v6 1/6] LoongArch: KVM: Check irq validility in kvm_vcpu_ioctl_interrupt() Bibo Mao
@ 2026-06-11 7:08 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-06-11 7:08 UTC (permalink / raw)
To: Bibo Mao; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Un-ratelimited `kvm_err` enables host log flooding DoS from unprivileged userspace, and valid IRQ 0 (`INT_SWI0`) cannot be queued or dequeued.
--
commit fd892c086057b77e1ae791d2e1126c1532b41bbb
Author: Bibo Mao <maobibo@loongson.cn>
LoongArch: KVM: Check irq validility in kvm_vcpu_ioctl_interrupt()
This commit adds validity checking for the interrupt vector in
kvm_vcpu_ioctl_interrupt(), returning an error if it exceeds
EXCCODE_INT_NUM.
> diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
> index e28084c49e682..df5be9b265e8d 100644
> --- a/arch/loongarch/kvm/vcpu.c
> +++ b/arch/loongarch/kvm/vcpu.c
> @@ -1487,6 +1487,11 @@ void kvm_lose_fpu(struct kvm_vcpu *vcpu)
> int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq)
> {
> int intr = (int)irq->irq;
> + unsigned int vector;
> +
> + vector = abs(intr);
> + if (vector >= EXCCODE_INT_NUM)
> + return -EINVAL;
>
> if (intr > 0)
> kvm_queue_irq(vcpu, intr);
[Severity: High]
This is a pre-existing issue, but looking at the rest of
kvm_vcpu_ioctl_interrupt(), if irq->irq is 0 (which corresponds to INT_SWI0),
both the intr > 0 and intr < 0 checks are skipped.
The code then falls through to the following else block:
else {
kvm_err("%s: invalid interrupt ioctl %d\n", __func__, irq->irq);
return -EINVAL;
}
Since kvm_err() is not rate-limited, can an unprivileged userspace process
holding a vCPU file descriptor call this ioctl in a tight loop with
irq->irq = 0 to rapidly flood the host kernel log?
Could this create a denial of service by locking up the system via a
serial console or exhausting disk space?
Also, does this logic fundamentally prevent userspace from correctly
queuing or dequeuing INT_SWI0?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260611065815.1439154-1-maobibo@loongson.cn?part=1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v6 2/6] LoongArch: KVM: Check msgint feature in interrupt post
2026-06-11 6:58 [PATCH v6 0/6] LoongArch: KVM: Small enhancement about interrupt injection Bibo Mao
2026-06-11 6:58 ` [PATCH v6 1/6] LoongArch: KVM: Check irq validility in kvm_vcpu_ioctl_interrupt() Bibo Mao
@ 2026-06-11 6:58 ` Bibo Mao
2026-06-11 7:13 ` sashiko-bot
2026-06-11 6:58 ` [PATCH v6 3/6] LoongArch: KVM: Use existing macro about interrupt bit mask Bibo Mao
` (3 subsequent siblings)
5 siblings, 1 reply; 14+ messages in thread
From: Bibo Mao @ 2026-06-11 6:58 UTC (permalink / raw)
To: Huacai Chen; +Cc: kernel, kvm, loongarch, linux-kernel
Interrupt AVEC is valid only if VM has msgint feature, and this feature
is checked in interrupt handling. Since interrupt handling is executing
in VM context switch, and it is hot path, here move the feature checking
in interrupt post rather than interrupt handling.
Signed-off-by: Bibo Mao <maobibo@loongson.cn>
---
arch/loongarch/kvm/interrupt.c | 5 -----
arch/loongarch/kvm/vcpu.c | 3 +++
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/arch/loongarch/kvm/interrupt.c b/arch/loongarch/kvm/interrupt.c
index a18c60dffbba..48dd56aa4dc5 100644
--- a/arch/loongarch/kvm/interrupt.c
+++ b/arch/loongarch/kvm/interrupt.c
@@ -36,8 +36,6 @@ static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
switch (priority) {
case INT_AVEC:
- if (!kvm_guest_has_msgint(&vcpu->arch))
- break;
dmsintc_inject_irq(vcpu);
fallthrough;
case INT_TI:
@@ -75,9 +73,6 @@ static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority)
switch (priority) {
case INT_AVEC:
- if (!kvm_guest_has_msgint(&vcpu->arch))
- break;
- fallthrough;
case INT_TI:
case INT_IPI:
case INT_SWI0:
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index df5be9b265e8..e986146d2272 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -1493,6 +1493,9 @@ int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq)
if (vector >= EXCCODE_INT_NUM)
return -EINVAL;
+ if (!kvm_guest_has_msgint(&vcpu->arch) && (vector == INT_AVEC))
+ return -EINVAL;
+
if (intr > 0)
kvm_queue_irq(vcpu, intr);
else if (intr < 0)
--
2.39.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v6 2/6] LoongArch: KVM: Check msgint feature in interrupt post
2026-06-11 6:58 ` [PATCH v6 2/6] LoongArch: KVM: Check msgint feature in interrupt post Bibo Mao
@ 2026-06-11 7:13 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-06-11 7:13 UTC (permalink / raw)
To: Bibo Mao; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Uninitialized variable `vector` array leads to host stack data leakage and guest state corruption.
- [High] Missing memory barriers between writing the MSI vector map and setting the interrupt pending flag causes lost interrupts.
--
commit 7cbebc4f6556c5cec6e10f7b92d02d8b3f478b95
Author: Bibo Mao <maobibo@loongson.cn>
LoongArch: KVM: Check msgint feature in interrupt post
Interrupt AVEC is valid only if VM has msgint feature, and this feature
is checked in interrupt handling. Since interrupt handling is executing
in VM context switch, and it is hot path, here move the feature checking
in interrupt post rather than interrupt handling.
> diff --git a/arch/loongarch/kvm/interrupt.c b/arch/loongarch/kvm/interrupt.c
> index a18c60dffbbae..48dd56aa4dc56 100644
> --- a/arch/loongarch/kvm/interrupt.c
> +++ b/arch/loongarch/kvm/interrupt.c
> @@ -36,8 +36,6 @@ static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
>
> switch (priority) {
> case INT_AVEC:
> - if (!kvm_guest_has_msgint(&vcpu->arch))
> - break;
> dmsintc_inject_irq(vcpu);
[Severity: Critical]
This is a pre-existing issue, but in dmsintc_inject_irq() the vector array
is declared uninitialized:
arch/loongarch/kvm/intc/dmsintc.c:dmsintc_inject_irq() {
unsigned long vector[4], old;
...
for (i = 0; i < 4; i++) {
old = atomic64_read(&(ds->vector_map[i]));
if (old)
vector[i] = atomic64_xchg(&(ds->vector_map[i]), 0);
}
if (vector[0]) {
...
}
If vector_map[i] is zero, vector[i] retains uninitialized stack memory.
Because the check if (vector[i]) evaluates this garbage, could this
unconditionally write uninitialized host stack data into the guest's ISR
registers and corrupt the guest's interrupt state?
> fallthrough;
> case INT_TI:
[ ... ]
> diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
> index df5be9b265e8d..e986146d22724 100644
> --- a/arch/loongarch/kvm/vcpu.c
> +++ b/arch/loongarch/kvm/vcpu.c
> @@ -1493,6 +1493,9 @@ int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq)
> if (vector >= EXCCODE_INT_NUM)
> return -EINVAL;
>
> + if (!kvm_guest_has_msgint(&vcpu->arch) && (vector == INT_AVEC))
> + return -EINVAL;
> +
> if (intr > 0)
> kvm_queue_irq(vcpu, intr);
[Severity: High]
This isn't a bug introduced by this patch, but is there a missing memory
barrier in the producer/consumer paths for MSI injection?
In dmsintc_deliver_msi_to_vcpu(), we update the vector map and then queue
the interrupt notification without a write barrier:
arch/loongarch/kvm/intc/dmsintc.c:dmsintc_deliver_msi_to_vcpu() {
...
set_bit(vector, (unsigned long *)&ds->vector_map);
kvm_vcpu_ioctl_interrupt(vcpu, &vcpu_irq); // calls kvm_queue_irq()
}
On the consumer side, kvm_deliver_intr() reads irq_pending and calls
kvm_irq_deliver(), which clears the pending bit and reads the vector map:
arch/loongarch/kvm/interrupt.c:kvm_deliver_intr() {
...
for_each_set_bit(priority, pending, EXCCODE_INT_NUM)
kvm_irq_deliver(vcpu, priority);
}
Since set_bit() doesn't imply a memory barrier, could the CPU reorder the
writes, causing the consumer to see a pending interrupt but an empty
vector_map, leading to permanently lost MSI interrupts?
> else if (intr < 0)
> kvm_dequeue_irq(vcpu, -intr);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260611065815.1439154-1-maobibo@loongson.cn?part=2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v6 3/6] LoongArch: KVM: Use existing macro about interrupt bit mask
2026-06-11 6:58 [PATCH v6 0/6] LoongArch: KVM: Small enhancement about interrupt injection Bibo Mao
2026-06-11 6:58 ` [PATCH v6 1/6] LoongArch: KVM: Check irq validility in kvm_vcpu_ioctl_interrupt() Bibo Mao
2026-06-11 6:58 ` [PATCH v6 2/6] LoongArch: KVM: Check msgint feature in interrupt post Bibo Mao
@ 2026-06-11 6:58 ` Bibo Mao
2026-06-11 6:58 ` [PATCH v6 4/6] LoongArch: KVM: Inject interrupt with batch method Bibo Mao
` (2 subsequent siblings)
5 siblings, 0 replies; 14+ messages in thread
From: Bibo Mao @ 2026-06-11 6:58 UTC (permalink / raw)
To: Huacai Chen; +Cc: kernel, kvm, loongarch, linux-kernel
With interrupt post, register CSR_GINTC and CSR_ESTAT is used, and
CSR_ESTAT is used for percpu interrupt injection and CSR_GINTC is for
external hardware interrupt injection.
Here use existing macro about interrupt bit of register CSR_GINTC and
CSR_ESTAT, rather than hard coded constant value.
Signed-off-by: Bibo Mao <maobibo@loongson.cn>
---
arch/loongarch/include/asm/kvm_vcpu.h | 43 ++++++++++++++++++---------
1 file changed, 29 insertions(+), 14 deletions(-)
diff --git a/arch/loongarch/include/asm/kvm_vcpu.h b/arch/loongarch/include/asm/kvm_vcpu.h
index 3784ab4ccdb5..a70a32103523 100644
--- a/arch/loongarch/include/asm/kvm_vcpu.h
+++ b/arch/loongarch/include/asm/kvm_vcpu.h
@@ -10,22 +10,37 @@
#include <asm/loongarch.h>
/* Controlled by 0x5 guest estat */
-#define CPU_SIP0 (_ULCAST_(1))
-#define CPU_SIP1 (_ULCAST_(1) << 1)
-#define CPU_PMU (_ULCAST_(1) << 10)
-#define CPU_TIMER (_ULCAST_(1) << 11)
-#define CPU_IPI (_ULCAST_(1) << 12)
-#define CPU_AVEC (_ULCAST_(1) << 14)
+#define CPU_SIP0 BIT(INT_SWI0)
+#define CPU_SIP1 BIT(INT_SWI1)
+#define CPU_HWI0 BIT(INT_HWI0)
+#define CPU_HWI1 BIT(INT_HWI1)
+#define CPU_HWI2 BIT(INT_HWI2)
+#define CPU_HWI3 BIT(INT_HWI3)
+#define CPU_HWI4 BIT(INT_HWI4)
+#define CPU_HWI5 BIT(INT_HWI5)
+#define CPU_HWI6 BIT(INT_HWI6)
+#define CPU_HWI7 BIT(INT_HWI7)
+#define CPU_PMU BIT(INT_PCOV)
+#define CPU_TIMER BIT(INT_TI)
+#define CPU_IPI BIT(INT_IPI)
+#define CPU_AVEC BIT(INT_AVEC)
+#define KVM_ESTAT_INTI_MASK (CPU_SIP0 | CPU_SIP1 | CPU_PMU | CPU_TIMER \
+ | CPU_IPI | CPU_AVEC)
+#define KVM_ESTAT_EXTI_MASK (CPU_HWI0 | CPU_HWI1 | CPU_HWI2 | CPU_HWI3 \
+ | CPU_HWI4 | CPU_HWI5 | CPU_HWI6 | CPU_HWI7)
/* Controlled by 0x52 guest exception VIP aligned to estat bit 5~12 */
-#define CPU_IP0 (_ULCAST_(1))
-#define CPU_IP1 (_ULCAST_(1) << 1)
-#define CPU_IP2 (_ULCAST_(1) << 2)
-#define CPU_IP3 (_ULCAST_(1) << 3)
-#define CPU_IP4 (_ULCAST_(1) << 4)
-#define CPU_IP5 (_ULCAST_(1) << 5)
-#define CPU_IP6 (_ULCAST_(1) << 6)
-#define CPU_IP7 (_ULCAST_(1) << 7)
+#define VIP_DELTA (INT_HWI0 - CSR_GINTC_VIP_SHIFT)
+#define CPU_IP0 BIT(INT_HWI0 - VIP_DELTA)
+#define CPU_IP1 BIT(INT_HWI1 - VIP_DELTA)
+#define CPU_IP2 BIT(INT_HWI2 - VIP_DELTA)
+#define CPU_IP3 BIT(INT_HWI3 - VIP_DELTA)
+#define CPU_IP4 BIT(INT_HWI4 - VIP_DELTA)
+#define CPU_IP5 BIT(INT_HWI5 - VIP_DELTA)
+#define CPU_IP6 BIT(INT_HWI6 - VIP_DELTA)
+#define CPU_IP7 BIT(INT_HWI7 - VIP_DELTA)
+#define KVM_GINTC_IRQ_MASK (CPU_IP0 | CPU_IP1 | CPU_IP2 | CPU_IP3 \
+ | CPU_IP4 | CPU_IP5 | CPU_IP6 | CPU_IP7)
#define MNSEC_PER_SEC (NSEC_PER_SEC >> 20)
--
2.39.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v6 4/6] LoongArch: KVM: Inject interrupt with batch method
2026-06-11 6:58 [PATCH v6 0/6] LoongArch: KVM: Small enhancement about interrupt injection Bibo Mao
` (2 preceding siblings ...)
2026-06-11 6:58 ` [PATCH v6 3/6] LoongArch: KVM: Use existing macro about interrupt bit mask Bibo Mao
@ 2026-06-11 6:58 ` Bibo Mao
2026-06-11 7:17 ` sashiko-bot
2026-06-11 6:58 ` [PATCH v6 5/6] LoongArch: KVM: Add valid bit check when set ESTAT CSR register Bibo Mao
2026-06-11 6:58 ` [PATCH v6 6/6] LoongArch: KVM: Deliver interrupt after IN_GUEST_MODE is set Bibo Mao
5 siblings, 1 reply; 14+ messages in thread
From: Bibo Mao @ 2026-06-11 6:58 UTC (permalink / raw)
To: Huacai Chen; +Cc: kernel, kvm, loongarch, linux-kernel
With bitmask method, interrupt can be injected with batch mode, rather
than one by one. Also remove unused array priority_to_irqp[] here.
Signed-off-by: Bibo Mao <maobibo@loongson.cn>
---
arch/loongarch/kvm/interrupt.c | 95 ++++++++++------------------------
1 file changed, 28 insertions(+), 67 deletions(-)
diff --git a/arch/loongarch/kvm/interrupt.c b/arch/loongarch/kvm/interrupt.c
index 48dd56aa4dc5..c1afeb058c5b 100644
--- a/arch/loongarch/kvm/interrupt.c
+++ b/arch/loongarch/kvm/interrupt.c
@@ -9,39 +9,16 @@
#include <asm/kvm_vcpu.h>
#include <asm/kvm_dmsintc.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,
- [INT_AVEC] = CPU_AVEC,
-};
-
-static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
+static void kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned long mask)
{
- unsigned int irq = 0;
+ unsigned long irq;
unsigned long old, new;
- clear_bit(priority, &vcpu->arch.irq_pending);
- if (priority < EXCCODE_INT_NUM)
- irq = priority_to_irq[priority];
-
- switch (priority) {
- case INT_AVEC:
- dmsintc_inject_irq(vcpu);
- fallthrough;
- case INT_TI:
- case INT_IPI:
- case INT_SWI0:
- case INT_SWI1:
+ irq = mask & KVM_ESTAT_INTI_MASK;
+ if (irq) {
+ if (irq & CPU_AVEC)
+ dmsintc_inject_irq(vcpu);
+
old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
set_gcsr_estat(irq);
new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
@@ -49,34 +26,20 @@ static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
/* Inject TI if TVAL inverted */
if (new > old)
set_gcsr_estat(CPU_TIMER);
- break;
-
- case INT_HWI0 ... INT_HWI7:
- set_csr_gintc(irq);
- break;
-
- default:
- break;
}
- return 1;
+ irq = (mask >> VIP_DELTA) & KVM_GINTC_IRQ_MASK;
+ if (irq)
+ set_csr_gintc(irq);
}
-static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority)
+static void kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned long mask)
{
- unsigned int irq = 0;
+ unsigned long irq;
unsigned long old, new;
- clear_bit(priority, &vcpu->arch.irq_clear);
- if (priority < EXCCODE_INT_NUM)
- irq = priority_to_irq[priority];
-
- switch (priority) {
- case INT_AVEC:
- case INT_TI:
- case INT_IPI:
- case INT_SWI0:
- case INT_SWI1:
+ 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);
@@ -84,30 +47,28 @@ static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority)
/* Inject TI if TVAL inverted */
if (new > old)
set_gcsr_estat(CPU_TIMER);
- break;
-
- case INT_HWI0 ... INT_HWI7:
- clear_csr_gintc(irq);
- break;
-
- default:
- break;
}
- return 1;
+ irq = (mask >> VIP_DELTA) & KVM_GINTC_IRQ_MASK;
+ if (irq)
+ clear_csr_gintc(irq);
}
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;
+ unsigned long mask;
- for_each_set_bit(priority, pending_clr, EXCCODE_INT_NUM)
- kvm_irq_clear(vcpu, priority);
+ mask = READ_ONCE(vcpu->arch.irq_clear);
+ if (mask) {
+ mask = xchg_relaxed(&vcpu->arch.irq_clear, 0);
+ kvm_irq_clear(vcpu, mask);
+ }
- for_each_set_bit(priority, pending, EXCCODE_INT_NUM)
- kvm_irq_deliver(vcpu, priority);
+ 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)
--
2.39.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v6 4/6] LoongArch: KVM: Inject interrupt with batch method
2026-06-11 6:58 ` [PATCH v6 4/6] LoongArch: KVM: Inject interrupt with batch method Bibo Mao
@ 2026-06-11 7:17 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-06-11 7:17 UTC (permalink / raw)
To: Bibo Mao; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Uninitialized stack array `vector` is accessed conditionally in `dmsintc_inject_irq`, potentially leading to hardware state corruption.
--
commit 1bbf844f12dc29aa2ddec056c816e1245e63e5fb
Author: Bibo Mao <maobibo@loongson.cn>
LoongArch: KVM: Inject interrupt with batch method
This commit converts interrupt injection and clearing to process multiple
interrupts simultaneously using a bitmask instead of one-by-one based on
priorities. It also removes the unused priority_to_irq array.
> diff --git a/arch/loongarch/kvm/interrupt.c b/arch/loongarch/kvm/interrupt.c
> index 48dd56aa4dc56..c1afeb058c5b3 100644
> --- a/arch/loongarch/kvm/interrupt.c
> +++ b/arch/loongarch/kvm/interrupt.c
[ ... ]
> -static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
> +static void kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned long mask)
> {
> - unsigned int irq = 0;
> + unsigned long irq;
> unsigned long old, new;
>
> - clear_bit(priority, &vcpu->arch.irq_pending);
> - if (priority < EXCCODE_INT_NUM)
> - irq = priority_to_irq[priority];
> -
> - switch (priority) {
> - case INT_AVEC:
> - dmsintc_inject_irq(vcpu);
> - fallthrough;
> - case INT_TI:
> - case INT_IPI:
> - case INT_SWI0:
> - case INT_SWI1:
> + irq = mask & KVM_ESTAT_INTI_MASK;
> + if (irq) {
> + if (irq & CPU_AVEC)
> + dmsintc_inject_irq(vcpu);
[Severity: High]
This is a pre-existing issue, but does dmsintc_inject_irq() use an
uninitialized stack array?
Looking at dmsintc_inject_irq() in arch/loongarch/kvm/intc/dmsintc.c, the
array vector is declared without initialization:
arch/loongarch/kvm/intc/dmsintc.c:dmsintc_inject_irq() {
unsigned int i;
unsigned long vector[4], old;
...
for (i = 0; i < 4; i++) {
old = atomic64_read(&(ds->vector_map[i]));
if (old)
vector[i] = atomic64_xchg(&(ds->vector_map[i]), 0);
}
If old is zero, vector[i] is left uninitialized. Then later the
uninitialized memory is evaluated:
if (vector[0]) {
old = kvm_read_hw_gcsr(LOONGARCH_CSR_ISR0);
kvm_write_hw_gcsr(LOONGARCH_CSR_ISR0, vector[0] | old);
}
...
Can this unpredictably corrupt the guest's interrupt state machine and
potentially cause guest kernel panics by writing garbage values to the
hardware CSR?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260611065815.1439154-1-maobibo@loongson.cn?part=4
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v6 5/6] LoongArch: KVM: Add valid bit check when set ESTAT CSR register
2026-06-11 6:58 [PATCH v6 0/6] LoongArch: KVM: Small enhancement about interrupt injection Bibo Mao
` (3 preceding siblings ...)
2026-06-11 6:58 ` [PATCH v6 4/6] LoongArch: KVM: Inject interrupt with batch method Bibo Mao
@ 2026-06-11 6:58 ` Bibo Mao
2026-06-11 10:10 ` Huacai Chen
2026-06-11 6:58 ` [PATCH v6 6/6] LoongArch: KVM: Deliver interrupt after IN_GUEST_MODE is set Bibo Mao
5 siblings, 1 reply; 14+ messages in thread
From: Bibo Mao @ 2026-06-11 6:58 UTC (permalink / raw)
To: Huacai Chen; +Cc: kernel, kvm, loongarch, linux-kernel
When set ESTAT CSR register in function _kvm_setcsr(), valid bit check
is added here. Also interrupt CPU_AVEC is checked by msgint feature.
Signed-off-by: Bibo Mao <maobibo@loongson.cn>
---
arch/loongarch/kvm/vcpu.c | 25 ++++++++++++++++---------
1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index e986146d2272..4f67eefbd4f1 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -602,7 +602,7 @@ struct kvm_vcpu *kvm_get_vcpu_by_cpuid(struct kvm *kvm, int cpuid)
static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
{
- unsigned long gintc;
+ unsigned long gintc, estat;
struct loongarch_csrs *csr = vcpu->arch.csr;
if (get_gcsr_flag(id) & INVALID_GCSR)
@@ -621,8 +621,9 @@ static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
preempt_enable();
/* ESTAT IP0~IP7 get from GINTC */
- gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & 0xff;
- *val = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT) | (gintc << 2);
+ gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & KVM_GINTC_IRQ_MASK;
+ estat = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT) & ~KVM_ESTAT_EXTI_MASK;
+ *val = estat | (gintc << VIP_DELTA);
return 0;
}
@@ -637,7 +638,8 @@ static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
static int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 val)
{
- int ret = 0, gintc;
+ int ret = 0;
+ unsigned long gintc, estat;
struct loongarch_csrs *csr = vcpu->arch.csr;
if (get_gcsr_flag(id) & INVALID_GCSR)
@@ -648,11 +650,16 @@ static int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 val)
if (id == LOONGARCH_CSR_ESTAT) {
/* ESTAT IP0~IP7 inject through GINTC */
- gintc = (val >> 2) & 0xff;
- kvm_set_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc);
-
- gintc = val & ~(0xffUL << 2);
- kvm_set_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, gintc);
+ gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & ~KVM_GINTC_IRQ_MASK;
+ gintc |= (val >> VIP_DELTA) & KVM_GINTC_IRQ_MASK;
+ kvm_write_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc);
+
+ /* only set valid ESTAT bits */
+ estat = val & ~KVM_ESTAT_EXTI_MASK;
+ estat &= CSR_ESTAT_IS | CSR_ESTAT_EXC | CSR_ESTAT_ESUBCODE;
+ if (!kvm_guest_has_msgint(&vcpu->arch))
+ estat &= ~CPU_AVEC;
+ kvm_write_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, estat);
return ret;
}
--
2.39.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v6 5/6] LoongArch: KVM: Add valid bit check when set ESTAT CSR register
2026-06-11 6:58 ` [PATCH v6 5/6] LoongArch: KVM: Add valid bit check when set ESTAT CSR register Bibo Mao
@ 2026-06-11 10:10 ` Huacai Chen
2026-06-11 12:52 ` Bibo Mao
0 siblings, 1 reply; 14+ messages in thread
From: Huacai Chen @ 2026-06-11 10:10 UTC (permalink / raw)
To: Bibo Mao; +Cc: kernel, kvm, loongarch, linux-kernel
Hi, Bibo,
On Thu, Jun 11, 2026 at 2:58 PM Bibo Mao <maobibo@loongson.cn> wrote:
>
> When set ESTAT CSR register in function _kvm_setcsr(), valid bit check
> is added here. Also interrupt CPU_AVEC is checked by msgint feature.
>
> Signed-off-by: Bibo Mao <maobibo@loongson.cn>
> ---
> arch/loongarch/kvm/vcpu.c | 25 ++++++++++++++++---------
> 1 file changed, 16 insertions(+), 9 deletions(-)
>
> diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
> index e986146d2272..4f67eefbd4f1 100644
> --- a/arch/loongarch/kvm/vcpu.c
> +++ b/arch/loongarch/kvm/vcpu.c
> @@ -602,7 +602,7 @@ struct kvm_vcpu *kvm_get_vcpu_by_cpuid(struct kvm *kvm, int cpuid)
>
> static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
> {
> - unsigned long gintc;
> + unsigned long gintc, estat;
> struct loongarch_csrs *csr = vcpu->arch.csr;
>
> if (get_gcsr_flag(id) & INVALID_GCSR)
> @@ -621,8 +621,9 @@ static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
> preempt_enable();
>
> /* ESTAT IP0~IP7 get from GINTC */
> - gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & 0xff;
> - *val = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT) | (gintc << 2);
> + gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & KVM_GINTC_IRQ_MASK;
> + estat = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT) & ~KVM_ESTAT_EXTI_MASK;
> + *val = estat | (gintc << VIP_DELTA);
> return 0;
> }
>
> @@ -637,7 +638,8 @@ static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
>
> static int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 val)
> {
> - int ret = 0, gintc;
> + int ret = 0;
> + unsigned long gintc, estat;
> struct loongarch_csrs *csr = vcpu->arch.csr;
>
> if (get_gcsr_flag(id) & INVALID_GCSR)
> @@ -648,11 +650,16 @@ static int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 val)
>
> if (id == LOONGARCH_CSR_ESTAT) {
> /* ESTAT IP0~IP7 inject through GINTC */
> - gintc = (val >> 2) & 0xff;
> - kvm_set_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc);
> -
> - gintc = val & ~(0xffUL << 2);
> - kvm_set_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, gintc);
> + gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & ~KVM_GINTC_IRQ_MASK;
> + gintc |= (val >> VIP_DELTA) & KVM_GINTC_IRQ_MASK;
> + kvm_write_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc);
I think read & write can be combined, which means it can be simplified as:
gintc = (val >> VIP_DELTA) & KVM_GINTC_IRQ_MASK;
kvm_set_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc);
> +
> + /* only set valid ESTAT bits */
> + estat = val & ~KVM_ESTAT_EXTI_MASK;
> + estat &= CSR_ESTAT_IS | CSR_ESTAT_EXC | CSR_ESTAT_ESUBCODE;
> + if (!kvm_guest_has_msgint(&vcpu->arch))
> + estat &= ~CPU_AVEC;
> + kvm_write_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, estat);
I think write csr may lose bits from the original value, so
kvm_write_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, estat)
should be
kvm_set_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, estat)
If you agree, I will change it when I apply because all others look good to me.
Huacai
>
> return ret;
> }
> --
> 2.39.3
>
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v6 5/6] LoongArch: KVM: Add valid bit check when set ESTAT CSR register
2026-06-11 10:10 ` Huacai Chen
@ 2026-06-11 12:52 ` Bibo Mao
2026-06-11 13:01 ` Huacai Chen
0 siblings, 1 reply; 14+ messages in thread
From: Bibo Mao @ 2026-06-11 12:52 UTC (permalink / raw)
To: Huacai Chen; +Cc: kernel, kvm, loongarch, linux-kernel
On 2026/6/11 下午6:10, Huacai Chen wrote:
> Hi, Bibo,
>
> On Thu, Jun 11, 2026 at 2:58 PM Bibo Mao <maobibo@loongson.cn> wrote:
>>
>> When set ESTAT CSR register in function _kvm_setcsr(), valid bit check
>> is added here. Also interrupt CPU_AVEC is checked by msgint feature.
>>
>> Signed-off-by: Bibo Mao <maobibo@loongson.cn>
>> ---
>> arch/loongarch/kvm/vcpu.c | 25 ++++++++++++++++---------
>> 1 file changed, 16 insertions(+), 9 deletions(-)
>>
>> diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
>> index e986146d2272..4f67eefbd4f1 100644
>> --- a/arch/loongarch/kvm/vcpu.c
>> +++ b/arch/loongarch/kvm/vcpu.c
>> @@ -602,7 +602,7 @@ struct kvm_vcpu *kvm_get_vcpu_by_cpuid(struct kvm *kvm, int cpuid)
>>
>> static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
>> {
>> - unsigned long gintc;
>> + unsigned long gintc, estat;
>> struct loongarch_csrs *csr = vcpu->arch.csr;
>>
>> if (get_gcsr_flag(id) & INVALID_GCSR)
>> @@ -621,8 +621,9 @@ static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
>> preempt_enable();
>>
>> /* ESTAT IP0~IP7 get from GINTC */
>> - gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & 0xff;
>> - *val = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT) | (gintc << 2);
>> + gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & KVM_GINTC_IRQ_MASK;
>> + estat = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT) & ~KVM_ESTAT_EXTI_MASK;
>> + *val = estat | (gintc << VIP_DELTA);
>> return 0;
>> }
>>
>> @@ -637,7 +638,8 @@ static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
>>
>> static int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 val)
>> {
>> - int ret = 0, gintc;
>> + int ret = 0;
>> + unsigned long gintc, estat;
>> struct loongarch_csrs *csr = vcpu->arch.csr;
>>
>> if (get_gcsr_flag(id) & INVALID_GCSR)
>> @@ -648,11 +650,16 @@ static int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 val)
>>
>> if (id == LOONGARCH_CSR_ESTAT) {
>> /* ESTAT IP0~IP7 inject through GINTC */
>> - gintc = (val >> 2) & 0xff;
>> - kvm_set_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc);
>> -
>> - gintc = val & ~(0xffUL << 2);
>> - kvm_set_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, gintc);
>> + gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & ~KVM_GINTC_IRQ_MASK;
>> + gintc |= (val >> VIP_DELTA) & KVM_GINTC_IRQ_MASK;
>> + kvm_write_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc);
> I think read & write can be combined, which means it can be simplified as:
> gintc = (val >> VIP_DELTA) & KVM_GINTC_IRQ_MASK;
> kvm_set_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc);
>
>> +
>> + /* only set valid ESTAT bits */
>> + estat = val & ~KVM_ESTAT_EXTI_MASK;
>> + estat &= CSR_ESTAT_IS | CSR_ESTAT_EXC | CSR_ESTAT_ESUBCODE;
>> + if (!kvm_guest_has_msgint(&vcpu->arch))
>> + estat &= ~CPU_AVEC;
>> + kvm_write_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, estat);
> I think write csr may lose bits from the original value, so
> kvm_write_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, estat)
> should be
> kvm_set_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, estat)
>
> If you agree, I will change it when I apply because all others look good to me.
At the beginning, it is kvm_set_sw_gcsr() with CSR_GINTC/CSR_ESTAT,
later I change it with kvm_write_sw_gcsr(). With kvm_set_sw_gcsr() API,
it is only set new interrupt, however does not clear the interrupt. So I
change it with kvm_write_sw_gcsr() which will overwrite the interrupt bits.
At the same time, bit field CSR_ESTAT_EXC | CSR_ESTAT_ESUBCODE is not
suitable for bit set, overwrite its value is more suitable.
Regards
Bibo Mao
>
> Huacai
>
>>
>> return ret;
>> }
>> --
>> 2.39.3
>>
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v6 5/6] LoongArch: KVM: Add valid bit check when set ESTAT CSR register
2026-06-11 12:52 ` Bibo Mao
@ 2026-06-11 13:01 ` Huacai Chen
2026-06-12 8:39 ` Huacai Chen
0 siblings, 1 reply; 14+ messages in thread
From: Huacai Chen @ 2026-06-11 13:01 UTC (permalink / raw)
To: Bibo Mao; +Cc: kernel, kvm, loongarch, linux-kernel
On Thu, Jun 11, 2026 at 8:55 PM Bibo Mao <maobibo@loongson.cn> wrote:
>
>
>
> On 2026/6/11 下午6:10, Huacai Chen wrote:
> > Hi, Bibo,
> >
> > On Thu, Jun 11, 2026 at 2:58 PM Bibo Mao <maobibo@loongson.cn> wrote:
> >>
> >> When set ESTAT CSR register in function _kvm_setcsr(), valid bit check
> >> is added here. Also interrupt CPU_AVEC is checked by msgint feature.
> >>
> >> Signed-off-by: Bibo Mao <maobibo@loongson.cn>
> >> ---
> >> arch/loongarch/kvm/vcpu.c | 25 ++++++++++++++++---------
> >> 1 file changed, 16 insertions(+), 9 deletions(-)
> >>
> >> diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
> >> index e986146d2272..4f67eefbd4f1 100644
> >> --- a/arch/loongarch/kvm/vcpu.c
> >> +++ b/arch/loongarch/kvm/vcpu.c
> >> @@ -602,7 +602,7 @@ struct kvm_vcpu *kvm_get_vcpu_by_cpuid(struct kvm *kvm, int cpuid)
> >>
> >> static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
> >> {
> >> - unsigned long gintc;
> >> + unsigned long gintc, estat;
> >> struct loongarch_csrs *csr = vcpu->arch.csr;
> >>
> >> if (get_gcsr_flag(id) & INVALID_GCSR)
> >> @@ -621,8 +621,9 @@ static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
> >> preempt_enable();
> >>
> >> /* ESTAT IP0~IP7 get from GINTC */
> >> - gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & 0xff;
> >> - *val = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT) | (gintc << 2);
> >> + gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & KVM_GINTC_IRQ_MASK;
> >> + estat = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT) & ~KVM_ESTAT_EXTI_MASK;
> >> + *val = estat | (gintc << VIP_DELTA);
> >> return 0;
> >> }
> >>
> >> @@ -637,7 +638,8 @@ static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
> >>
> >> static int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 val)
> >> {
> >> - int ret = 0, gintc;
> >> + int ret = 0;
> >> + unsigned long gintc, estat;
> >> struct loongarch_csrs *csr = vcpu->arch.csr;
> >>
> >> if (get_gcsr_flag(id) & INVALID_GCSR)
> >> @@ -648,11 +650,16 @@ static int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 val)
> >>
> >> if (id == LOONGARCH_CSR_ESTAT) {
> >> /* ESTAT IP0~IP7 inject through GINTC */
> >> - gintc = (val >> 2) & 0xff;
> >> - kvm_set_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc);
> >> -
> >> - gintc = val & ~(0xffUL << 2);
> >> - kvm_set_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, gintc);
> >> + gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & ~KVM_GINTC_IRQ_MASK;
> >> + gintc |= (val >> VIP_DELTA) & KVM_GINTC_IRQ_MASK;
> >> + kvm_write_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc);
> > I think read & write can be combined, which means it can be simplified as:
> > gintc = (val >> VIP_DELTA) & KVM_GINTC_IRQ_MASK;
> > kvm_set_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc);
> >
> >> +
> >> + /* only set valid ESTAT bits */
> >> + estat = val & ~KVM_ESTAT_EXTI_MASK;
> >> + estat &= CSR_ESTAT_IS | CSR_ESTAT_EXC | CSR_ESTAT_ESUBCODE;
> >> + if (!kvm_guest_has_msgint(&vcpu->arch))
> >> + estat &= ~CPU_AVEC;
> >> + kvm_write_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, estat);
> > I think write csr may lose bits from the original value, so
> > kvm_write_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, estat)
> > should be
> > kvm_set_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, estat)
> >
> > If you agree, I will change it when I apply because all others look good to me.
> At the beginning, it is kvm_set_sw_gcsr() with CSR_GINTC/CSR_ESTAT,
> later I change it with kvm_write_sw_gcsr(). With kvm_set_sw_gcsr() API,
> it is only set new interrupt, however does not clear the interrupt. So I
> change it with kvm_write_sw_gcsr() which will overwrite the interrupt bits.
>
> At the same time, bit field CSR_ESTAT_EXC | CSR_ESTAT_ESUBCODE is not
> suitable for bit set, overwrite its value is more suitable.
OK, then I will keep using kvm_write_sw_gcsr(), and please update the
selftest series if needed.
Huacai
>
> Regards
> Bibo Mao
>
> >
> > Huacai
> >
> >>
> >> return ret;
> >> }
> >> --
> >> 2.39.3
> >>
>
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v6 5/6] LoongArch: KVM: Add valid bit check when set ESTAT CSR register
2026-06-11 13:01 ` Huacai Chen
@ 2026-06-12 8:39 ` Huacai Chen
0 siblings, 0 replies; 14+ messages in thread
From: Huacai Chen @ 2026-06-12 8:39 UTC (permalink / raw)
To: Bibo Mao; +Cc: kernel, kvm, loongarch, linux-kernel
On Thu, Jun 11, 2026 at 9:01 PM Huacai Chen <chenhuacai@kernel.org> wrote:
>
> On Thu, Jun 11, 2026 at 8:55 PM Bibo Mao <maobibo@loongson.cn> wrote:
> >
> >
> >
> > On 2026/6/11 下午6:10, Huacai Chen wrote:
> > > Hi, Bibo,
> > >
> > > On Thu, Jun 11, 2026 at 2:58 PM Bibo Mao <maobibo@loongson.cn> wrote:
> > >>
> > >> When set ESTAT CSR register in function _kvm_setcsr(), valid bit check
> > >> is added here. Also interrupt CPU_AVEC is checked by msgint feature.
> > >>
> > >> Signed-off-by: Bibo Mao <maobibo@loongson.cn>
> > >> ---
> > >> arch/loongarch/kvm/vcpu.c | 25 ++++++++++++++++---------
> > >> 1 file changed, 16 insertions(+), 9 deletions(-)
> > >>
> > >> diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
> > >> index e986146d2272..4f67eefbd4f1 100644
> > >> --- a/arch/loongarch/kvm/vcpu.c
> > >> +++ b/arch/loongarch/kvm/vcpu.c
> > >> @@ -602,7 +602,7 @@ struct kvm_vcpu *kvm_get_vcpu_by_cpuid(struct kvm *kvm, int cpuid)
> > >>
> > >> static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
> > >> {
> > >> - unsigned long gintc;
> > >> + unsigned long gintc, estat;
> > >> struct loongarch_csrs *csr = vcpu->arch.csr;
> > >>
> > >> if (get_gcsr_flag(id) & INVALID_GCSR)
> > >> @@ -621,8 +621,9 @@ static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
> > >> preempt_enable();
> > >>
> > >> /* ESTAT IP0~IP7 get from GINTC */
> > >> - gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & 0xff;
> > >> - *val = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT) | (gintc << 2);
> > >> + gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & KVM_GINTC_IRQ_MASK;
> > >> + estat = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT) & ~KVM_ESTAT_EXTI_MASK;
> > >> + *val = estat | (gintc << VIP_DELTA);
> > >> return 0;
> > >> }
> > >>
> > >> @@ -637,7 +638,8 @@ static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val)
> > >>
> > >> static int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 val)
> > >> {
> > >> - int ret = 0, gintc;
> > >> + int ret = 0;
> > >> + unsigned long gintc, estat;
> > >> struct loongarch_csrs *csr = vcpu->arch.csr;
> > >>
> > >> if (get_gcsr_flag(id) & INVALID_GCSR)
> > >> @@ -648,11 +650,16 @@ static int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 val)
> > >>
> > >> if (id == LOONGARCH_CSR_ESTAT) {
> > >> /* ESTAT IP0~IP7 inject through GINTC */
> > >> - gintc = (val >> 2) & 0xff;
> > >> - kvm_set_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc);
> > >> -
> > >> - gintc = val & ~(0xffUL << 2);
> > >> - kvm_set_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, gintc);
> > >> + gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & ~KVM_GINTC_IRQ_MASK;
> > >> + gintc |= (val >> VIP_DELTA) & KVM_GINTC_IRQ_MASK;
> > >> + kvm_write_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc);
> > > I think read & write can be combined, which means it can be simplified as:
> > > gintc = (val >> VIP_DELTA) & KVM_GINTC_IRQ_MASK;
> > > kvm_set_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc);
> > >
> > >> +
> > >> + /* only set valid ESTAT bits */
> > >> + estat = val & ~KVM_ESTAT_EXTI_MASK;
> > >> + estat &= CSR_ESTAT_IS | CSR_ESTAT_EXC | CSR_ESTAT_ESUBCODE;
> > >> + if (!kvm_guest_has_msgint(&vcpu->arch))
> > >> + estat &= ~CPU_AVEC;
> > >> + kvm_write_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, estat);
> > > I think write csr may lose bits from the original value, so
> > > kvm_write_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, estat)
> > > should be
> > > kvm_set_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, estat)
> > >
> > > If you agree, I will change it when I apply because all others look good to me.
> > At the beginning, it is kvm_set_sw_gcsr() with CSR_GINTC/CSR_ESTAT,
> > later I change it with kvm_write_sw_gcsr(). With kvm_set_sw_gcsr() API,
> > it is only set new interrupt, however does not clear the interrupt. So I
> > change it with kvm_write_sw_gcsr() which will overwrite the interrupt bits.
> >
> > At the same time, bit field CSR_ESTAT_EXC | CSR_ESTAT_ESUBCODE is not
> > suitable for bit set, overwrite its value is more suitable.
> OK, then I will keep using kvm_write_sw_gcsr(), and please update the
> selftest series if needed.
Now all KVM patches are applied, you can double check it here.
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson.git/log/?h=loongarch-kvm
Huacai
>
> Huacai
>
> >
> > Regards
> > Bibo Mao
> >
> > >
> > > Huacai
> > >
> > >>
> > >> return ret;
> > >> }
> > >> --
> > >> 2.39.3
> > >>
> >
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v6 6/6] LoongArch: KVM: Deliver interrupt after IN_GUEST_MODE is set
2026-06-11 6:58 [PATCH v6 0/6] LoongArch: KVM: Small enhancement about interrupt injection Bibo Mao
` (4 preceding siblings ...)
2026-06-11 6:58 ` [PATCH v6 5/6] LoongArch: KVM: Add valid bit check when set ESTAT CSR register Bibo Mao
@ 2026-06-11 6:58 ` Bibo Mao
5 siblings, 0 replies; 14+ messages in thread
From: Bibo Mao @ 2026-06-11 6:58 UTC (permalink / raw)
To: Huacai Chen; +Cc: kernel, kvm, loongarch, linux-kernel
Interrupt delivery should be called after IN_GUEST_MODE is set. Other
threads may be posting interrupt however does not send IPI to the
vCPU, since the vCPU is not in IN_GUEST_MODE.
here move function call with kvm_deliver_intr() after IN_GUEST_MODE
is set, and set mode with OUTSIDE_GUEST_MODE with atomic method.
Signed-off-by: Bibo Mao <maobibo@loongson.cn>
---
arch/loongarch/kvm/vcpu.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index 4f67eefbd4f1..265675e63269 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -308,10 +308,10 @@ static int kvm_pre_enter_guest(struct kvm_vcpu *vcpu)
* check vmid before vcpu enter guest
*/
local_irq_disable();
- kvm_deliver_intr(vcpu);
kvm_deliver_exception(vcpu);
/* Make sure the vcpu mode has been written */
smp_store_mb(vcpu->mode, IN_GUEST_MODE);
+ kvm_deliver_intr(vcpu);
kvm_check_vpid(vcpu);
/*
@@ -348,7 +348,7 @@ static int kvm_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu)
u32 intr = estat & CSR_ESTAT_IS;
u32 ecode = (estat & CSR_ESTAT_EXC) >> CSR_ESTAT_EXC_SHIFT;
- vcpu->mode = OUTSIDE_GUEST_MODE;
+ smp_store_mb(vcpu->mode, OUTSIDE_GUEST_MODE);
/* Set a default exit reason */
run->exit_reason = KVM_EXIT_UNKNOWN;
--
2.39.3
^ permalink raw reply related [flat|nested] 14+ messages in thread