[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <6013af5d-5d35-3ecf-5bd0-2c557ace3fe8@loongson.cn>
Date: Sat, 14 Sep 2024 11:42:41 +0800
From: maobibo <maobibo@...ngson.cn>
To: Xianglai Li <lixianglai@...ngson.cn>, linux-kernel@...r.kernel.org
Cc: Tianrui Zhao <zhaotianrui@...ngson.cn>,
Huacai Chen <chenhuacai@...nel.org>, kvm@...r.kernel.org,
loongarch@...ts.linux.dev, Paolo Bonzini <pbonzini@...hat.com>,
WANG Xuerui <kernel@...0n.name>
Subject: Re: [PATCH V3 09/11] LoongArch: KVM: Add PCHPIC read and write
functions
On 2024/9/10 下午7:44, Xianglai Li wrote:
> Implementation of IPI interrupt controller address
> space read and write function simulation.
>
> Implement interrupt injection interface under loongarch.
>
> Signed-off-by: Tianrui Zhao <zhaotianrui@...ngson.cn>
> Signed-off-by: Xianglai Li <lixianglai@...ngson.cn>
> ---
> Cc: Bibo Mao <maobibo@...ngson.cn>
> Cc: Huacai Chen <chenhuacai@...nel.org>
> Cc: kvm@...r.kernel.org
> Cc: loongarch@...ts.linux.dev
> Cc: Paolo Bonzini <pbonzini@...hat.com>
> Cc: Tianrui Zhao <zhaotianrui@...ngson.cn>
> Cc: WANG Xuerui <kernel@...0n.name>
> Cc: Xianglai li <lixianglai@...ngson.cn>
>
> arch/loongarch/include/asm/kvm_host.h | 18 ++
> arch/loongarch/include/asm/kvm_pch_pic.h | 31 +++
> arch/loongarch/include/uapi/asm/kvm.h | 1 +
> arch/loongarch/kvm/intc/pch_pic.c | 290 ++++++++++++++++++++++-
> arch/loongarch/kvm/vm.c | 34 +++
> 5 files changed, 372 insertions(+), 2 deletions(-)
>
> diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h
> index a4feb1b9c816..1259636d7ead 100644
> --- a/arch/loongarch/include/asm/kvm_host.h
> +++ b/arch/loongarch/include/asm/kvm_host.h
> @@ -34,6 +34,22 @@
> #define KVM_REQ_TLB_FLUSH_GPA KVM_ARCH_REQ(0)
> #define KVM_REQ_STEAL_UPDATE KVM_ARCH_REQ(1)
>
> +/* KVM_IRQ_LINE irq field index values */
> +#define KVM_LOONGARCH_IRQ_TYPE_SHIFT 24
> +#define KVM_LOONGARCH_IRQ_TYPE_MASK 0xff
> +#define KVM_LOONGARCH_IRQ_VCPU_SHIFT 16
> +#define KVM_LOONGARCH_IRQ_VCPU_MASK 0xff
> +#define KVM_LOONGARCH_IRQ_NUM_SHIFT 0
> +#define KVM_LOONGARCH_IRQ_NUM_MASK 0xffff
> +
> +/* irq_type field */
> +#define KVM_LOONGARCH_IRQ_TYPE_CPU_IP 0
> +#define KVM_LOONGARCH_IRQ_TYPE_CPU_IO 1
> +#define KVM_LOONGARCH_IRQ_TYPE_HT 2
> +#define KVM_LOONGARCH_IRQ_TYPE_MSI 3
> +#define KVM_LOONGARCH_IRQ_TYPE_IOAPIC 4
> +#define KVM_LOONGARCH_IRQ_TYPE_ROUTE 5
> +
> #define KVM_GUESTDBG_SW_BP_MASK \
> (KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP)
> #define KVM_GUESTDBG_VALID_MASK \
> @@ -50,6 +66,8 @@ struct kvm_vm_stat {
> u64 ipi_write_exits;
> u64 eiointc_read_exits;
> u64 eiointc_write_exits;
> + u64 pch_pic_read_exits;
> + u64 pch_pic_write_exits;
> };
>
> struct kvm_vcpu_stat {
> diff --git a/arch/loongarch/include/asm/kvm_pch_pic.h b/arch/loongarch/include/asm/kvm_pch_pic.h
> index c320f66c2004..7a6625fdeab9 100644
> --- a/arch/loongarch/include/asm/kvm_pch_pic.h
> +++ b/arch/loongarch/include/asm/kvm_pch_pic.h
> @@ -8,6 +8,35 @@
>
> #include <kvm/iodev.h>
>
> +#define PCH_PIC_SIZE 0x3e8
> +
> +#define PCH_PIC_INT_ID_START 0x0
> +#define PCH_PIC_INT_ID_END 0x7
> +#define PCH_PIC_MASK_START 0x20
> +#define PCH_PIC_MASK_END 0x27
> +#define PCH_PIC_HTMSI_EN_START 0x40
> +#define PCH_PIC_HTMSI_EN_END 0x47
> +#define PCH_PIC_EDGE_START 0x60
> +#define PCH_PIC_EDGE_END 0x67
> +#define PCH_PIC_CLEAR_START 0x80
> +#define PCH_PIC_CLEAR_END 0x87
> +#define PCH_PIC_AUTO_CTRL0_START 0xc0
> +#define PCH_PIC_AUTO_CTRL0_END 0xc7
> +#define PCH_PIC_AUTO_CTRL1_START 0xe0
> +#define PCH_PIC_AUTO_CTRL1_END 0xe7
> +#define PCH_PIC_ROUTE_ENTRY_START 0x100
> +#define PCH_PIC_ROUTE_ENTRY_END 0x13f
> +#define PCH_PIC_HTMSI_VEC_START 0x200
> +#define PCH_PIC_HTMSI_VEC_END 0x23f
> +#define PCH_PIC_INT_IRR_START 0x380
> +#define PCH_PIC_INT_IRR_END 0x38f
> +#define PCH_PIC_INT_ISR_START 0x3a0
> +#define PCH_PIC_INT_ISR_END 0x3af
> +#define PCH_PIC_POLARITY_START 0x3e0
> +#define PCH_PIC_POLARITY_END 0x3e7
> +#define PCH_PIC_INT_ID_VAL 0x7000000UL
> +#define PCH_PIC_INT_ID_VER 0x1UL
> +
> struct loongarch_pch_pic {
> spinlock_t lock;
> struct kvm *kvm;
> @@ -26,5 +55,7 @@ struct loongarch_pch_pic {
> uint64_t pch_pic_base;
> };
>
> +void pch_pic_set_irq(struct loongarch_pch_pic *s, int irq, int level);
> +void pch_msi_set_irq(struct kvm *kvm, int irq, int level);
> int kvm_loongarch_register_pch_pic_device(void);
> #endif /* LOONGARCH_PCH_PIC_H */
> diff --git a/arch/loongarch/include/uapi/asm/kvm.h b/arch/loongarch/include/uapi/asm/kvm.h
> index d019f88b6286..acf8db9e3dfb 100644
> --- a/arch/loongarch/include/uapi/asm/kvm.h
> +++ b/arch/loongarch/include/uapi/asm/kvm.h
> @@ -16,6 +16,7 @@
>
> #define KVM_COALESCED_MMIO_PAGE_OFFSET 1
> #define KVM_DIRTY_LOG_PAGE_OFFSET 64
> +#define __KVM_HAVE_IRQ_LINE
>
> #define KVM_GUESTDBG_USE_SW_BP 0x00010000
>
> diff --git a/arch/loongarch/kvm/intc/pch_pic.c b/arch/loongarch/kvm/intc/pch_pic.c
> index 1888be1c9a8e..25a10bc3fff0 100644
> --- a/arch/loongarch/kvm/intc/pch_pic.c
> +++ b/arch/loongarch/kvm/intc/pch_pic.c
> @@ -8,18 +8,304 @@
> #include <asm/kvm_vcpu.h>
> #include <linux/count_zeros.h>
>
> +/* update the isr according to irq level and route irq to eiointc */
> +static void pch_pic_update_irq(struct loongarch_pch_pic *s, int irq, int level)
> +{
> + u64 mask = BIT(irq);
> +
> + /*
> + * set isr and route irq to eiointc and
> + * the route table is in htmsi_vector[]
> + */
> + if (level) {
> + if (mask & s->irr & ~s->mask) {
> + s->isr |= mask;
> + irq = s->htmsi_vector[irq];
> + eiointc_set_irq(s->kvm->arch.eiointc, irq, level);
> + }
> + } else {
> + if (mask & s->isr & ~s->irr) {
> + s->isr &= ~mask;
> + irq = s->htmsi_vector[irq];
> + eiointc_set_irq(s->kvm->arch.eiointc, irq, level);
> + }
> + }
> +}
> +
> +/* msi irq handler */
> +void pch_msi_set_irq(struct kvm *kvm, int irq, int level)
> +{
> + eiointc_set_irq(kvm->arch.eiointc, irq, level);
> +}
> +
> +/* called when a irq is triggered in pch pic */
> +void pch_pic_set_irq(struct loongarch_pch_pic *s, int irq, int level)
> +{
> + u64 mask = BIT(irq);
> +
> + spin_lock(&s->lock);
> + if (level)
> + /* set irr */
> + s->irr |= mask;
> + else {
> + /* 0 level signal in edge triggered irq does not mean to clear irq
> + * The irr register variable is cleared when the cpu writes to the
> + * PCH_PIC_CLEAR_START address area
> + */
> + if (s->edge & mask) {
> + spin_unlock(&s->lock);
> + return;
> + }
> + s->irr &= ~mask;
> + }
> + pch_pic_update_irq(s, irq, level);
> + spin_unlock(&s->lock);
> +}
> +
> +/* update batch irqs, the irq_mask is a bitmap of irqs */
> +static void pch_pic_update_batch_irqs(struct loongarch_pch_pic *s, u64 irq_mask, int level)
> +{
> + int irq, bits;
> +
> + /* find each irq by irqs bitmap and update each irq */
> + bits = sizeof(irq_mask) * 8;
> + irq = find_first_bit((void *)&irq_mask, bits);
> + while (irq < bits) {
> + pch_pic_update_irq(s, irq, level);
> + bitmap_clear((void *)&irq_mask, irq, 1);
> + irq = find_first_bit((void *)&irq_mask, bits);
> + }
> +}
> +
> +/*
> + * pch pic register is 64-bit, but it is accessed by 32-bit,
> + * so we use high to get whether low or high 32 bits we want
> + * to read.
> + */
> +static u32 pch_pic_read_reg(u64 *s, int high)
> +{
> + u64 val = *s;
> +
> + /* read the high 32 bits when the high is 1 */
> + return high ? (u32)(val >> 32) : (u32)val;
> +}
> +
> +/*
> + * pch pic register is 64-bit, but it is accessed by 32-bit,
> + * so we use high to get whether low or high 32 bits we want
> + * to write.
> + */
> +static u32 pch_pic_write_reg(u64 *s, int high, u32 v)
> +{
> + u64 val = *s, data = v;
> +
> + if (high) {
> + /*
> + * Clear val high 32 bits
> + * write the high 32 bits when the high is 1
> + */
> + *s = (val << 32 >> 32) | (data << 32);
> + val >>= 32;
> + } else
> + /*
> + * Clear val low 32 bits
> + * write the low 32 bits when the high is 0
> + */
> + *s = (val >> 32 << 32) | v;
> +
> + return (u32)val;
> +}
> +
> +static int loongarch_pch_pic_write(struct loongarch_pch_pic *s, gpa_t addr,
> + int len, const void *val)
> +{
> + u32 old, data, offset, index;
> + u64 irq;
> + int ret;
> +
> + ret = 0;
> + data = *(u32 *)val;
> + offset = addr - s->pch_pic_base;
> +
> + spin_lock(&s->lock);
> + switch (offset) {
> + case PCH_PIC_MASK_START ... PCH_PIC_MASK_END:
> + offset -= PCH_PIC_MASK_START;
> + /* get whether high or low 32 bits we want to write */
> + index = offset >> 2;
> + old = pch_pic_write_reg(&s->mask, index, data);
> +
> + /* enable irq when mask value change to 0 */
> + irq = (old & ~data) << (32 * index);
> + pch_pic_update_batch_irqs(s, irq, 1);
> +
> + /* disable irq when mask value change to 1 */
> + irq = (~old & data) << (32 * index);
> + pch_pic_update_batch_irqs(s, irq, 0);
> + break;
> + case PCH_PIC_HTMSI_EN_START ... PCH_PIC_HTMSI_EN_END:
> + offset -= PCH_PIC_HTMSI_EN_START;
> + index = offset >> 2;
> + pch_pic_write_reg(&s->htmsi_en, index, data);
> + break;
> + case PCH_PIC_EDGE_START ... PCH_PIC_EDGE_END:
> + offset -= PCH_PIC_EDGE_START;
> + index = offset >> 2;
> + /* 1: edge triggered, 0: level triggered */
> + pch_pic_write_reg(&s->edge, index, data);
> + break;
> + case PCH_PIC_CLEAR_START ... PCH_PIC_CLEAR_END:
> + offset -= PCH_PIC_CLEAR_START;
> + index = offset >> 2;
> + /* write 1 to clear edge irq */
> + old = pch_pic_read_reg(&s->irr, index);
> + /*
> + * get the irq bitmap which is edge triggered and
> + * already set and to be cleared
> + */
> + irq = old & pch_pic_read_reg(&s->edge, index) & data;
> + /* write irr to the new state where irqs have been cleared */
> + pch_pic_write_reg(&s->irr, index, old & ~irq);
> + /* update cleared irqs */
> + pch_pic_update_batch_irqs(s, irq, 0);
> + break;
> + case PCH_PIC_AUTO_CTRL0_START ... PCH_PIC_AUTO_CTRL0_END:
> + offset -= PCH_PIC_AUTO_CTRL0_START;
> + index = offset >> 2;
> + /* we only use default mode: fixed interrupt distribution mode */
> + pch_pic_write_reg(&s->auto_ctrl0, index, 0);
> + break;
> + case PCH_PIC_AUTO_CTRL1_START ... PCH_PIC_AUTO_CTRL1_END:
> + offset -= PCH_PIC_AUTO_CTRL1_START;
> + index = offset >> 2;
> + /* we only use default mode: fixed interrupt distribution mode */
> + pch_pic_write_reg(&s->auto_ctrl1, index, 0);
> + break;
> + case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END:
> + offset -= PCH_PIC_ROUTE_ENTRY_START;
> + /* only route to int0: eiointc */
> + s->route_entry[offset] = 1;
> + break;
> + case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END:
> + /* route table to eiointc */
> + offset -= PCH_PIC_HTMSI_VEC_START;
> + s->htmsi_vector[offset] = (u8)data;
> + break;
> + case PCH_PIC_POLARITY_START ... PCH_PIC_POLARITY_END:
> + offset -= PCH_PIC_POLARITY_START;
> + index = offset >> 2;
> +
> + /* we only use defalut value 0: high level triggered */
> + pch_pic_write_reg(&s->polarity, index, 0);
> + break;
> + default:
> + ret = -EINVAL;
> + break;
> + }
> + spin_unlock(&s->lock);
> + return ret;
> +}
> +
> static int kvm_pch_pic_write(struct kvm_vcpu *vcpu,
> struct kvm_io_device *dev,
> gpa_t addr, int len, const void *val)
> {
> - return 0;
> + int ret;
> + struct loongarch_pch_pic *s = vcpu->kvm->arch.pch_pic;
> +
> + if (!s) {
> + kvm_err("%s: pch pic irqchip not valid!\n", __func__);
> + return -EINVAL;
> + }
> +
> + /* statistics of pch pic writing */
> + vcpu->kvm->stat.pch_pic_write_exits++;
> + ret = loongarch_pch_pic_write(s, addr, len, val);
> + return ret;
> +}
> +
> +static int loongarch_pch_pic_read(struct loongarch_pch_pic *s, gpa_t addr, int len, void *val)
> +{
> + int offset, index, ret = 0;
> + u32 data = 0;
> + u64 int_id = 0;
> +
> + offset = addr - s->pch_pic_base;
> +
> + spin_lock(&s->lock);
> + switch (offset) {
> + case PCH_PIC_INT_ID_START ... PCH_PIC_INT_ID_END:
> + /* int id version */
> + int_id |= (u64)PCH_PIC_INT_ID_VER << 32;
> + /* irq number */
> + int_id |= (u64)31 << (32 + 16);
> + /* int id value */
> + int_id |= PCH_PIC_INT_ID_VAL;
> + *(u64 *)val = int_id;
> + break;
> + case PCH_PIC_MASK_START ... PCH_PIC_MASK_END:
> + offset -= PCH_PIC_MASK_START;
> + index = offset >> 2;
> + /* read mask reg */
> + data = pch_pic_read_reg(&s->mask, index);
> + *(u32 *)val = data;
> + break;
> + case PCH_PIC_HTMSI_EN_START ... PCH_PIC_HTMSI_EN_END:
> + offset -= PCH_PIC_HTMSI_EN_START;
> + index = offset >> 2;
> + /* read htmsi enable reg */
> + data = pch_pic_read_reg(&s->htmsi_en, index);
> + *(u32 *)val = data;
> + break;
> + case PCH_PIC_EDGE_START ... PCH_PIC_EDGE_END:
> + offset -= PCH_PIC_EDGE_START;
> + index = offset >> 2;
> + /* read edge enable reg */
> + data = pch_pic_read_reg(&s->edge, index);
> + *(u32 *)val = data;
> + break;
> + case PCH_PIC_AUTO_CTRL0_START ... PCH_PIC_AUTO_CTRL0_END:
> + case PCH_PIC_AUTO_CTRL1_START ... PCH_PIC_AUTO_CTRL1_END:
> + /* we only use default mode: fixed interrupt distribution mode */
> + *(u32 *)val = 0;
> + break;
> + case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END:
> + /* only route to int0: eiointc */
> + *(u8 *)val = 1;
> + break;
> + case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END:
> + offset -= PCH_PIC_HTMSI_VEC_START;
> + /* read htmsi vector */
> + data = s->htmsi_vector[offset];
> + *(u8 *)val = data;
> + break;
> + case PCH_PIC_POLARITY_START ... PCH_PIC_POLARITY_END:
> + /* we only use defalut value 0: high level triggered */
> + *(u32 *)val = 0;
> + break;
> + default:
> + ret = -EINVAL;
> + }
> + spin_unlock(&s->lock);
> + return ret;
> }
>
> static int kvm_pch_pic_read(struct kvm_vcpu *vcpu,
> struct kvm_io_device *dev,
> gpa_t addr, int len, void *val)
> {
> - return 0;
> + int ret;
> + struct loongarch_pch_pic *s = vcpu->kvm->arch.pch_pic;
> +
> + if (!s) {
> + kvm_err("%s: pch pic irqchip not valid!\n", __func__);
> + return -EINVAL;
> + }
> +
> + /* statistics of pch pic reading */
> + vcpu->kvm->stat.pch_pic_read_exits++;
> + ret = loongarch_pch_pic_read(s, addr, len, val);
> + return ret;
> }
>
> static const struct kvm_io_device_ops kvm_pch_pic_ops = {
> diff --git a/arch/loongarch/kvm/vm.c b/arch/loongarch/kvm/vm.c
> index 6b2e4f66ad26..5a60474bb933 100644
> --- a/arch/loongarch/kvm/vm.c
> +++ b/arch/loongarch/kvm/vm.c
> @@ -5,6 +5,8 @@
>
> #include <linux/kvm_host.h>
> #include <asm/kvm_mmu.h>
> +#include <asm/kvm_eiointc.h>
> +#include <asm/kvm_pch_pic.h>
>
> const struct _kvm_stats_desc kvm_vm_stats_desc[] = {
> KVM_GENERIC_VM_STATS(),
> @@ -103,3 +105,35 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
> {
> return -ENOIOCTLCMD;
> }
> +
> +int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *data,
> + bool line_status)
> +{
> + bool level;
> + struct loongarch_pch_pic *s;
> + int type, vcpu, irq, vcpus, val, ret = 0;
> +
Had better check whether irqchip_in_kernel is enabled for ioctl
interface from userspace, such as:
if (!kvm_arch_irqchip_in_kernel(kvm))
return -ENXIO;
> + level = data->level;
> + val = data->irq;
> + s = kvm->arch.pch_pic;
> + vcpus = atomic_read(&kvm->online_vcpus);
> +
> + type = (val >> KVM_LOONGARCH_IRQ_TYPE_SHIFT) & KVM_LOONGARCH_IRQ_TYPE_MASK;
> + vcpu = (val >> KVM_LOONGARCH_IRQ_VCPU_SHIFT) & KVM_LOONGARCH_IRQ_VCPU_MASK;
> + irq = (val >> KVM_LOONGARCH_IRQ_NUM_SHIFT) & KVM_LOONGARCH_IRQ_NUM_MASK;
> +
> + switch (type) {
> + case KVM_LOONGARCH_IRQ_TYPE_IOAPIC:
> + if (irq < KVM_IRQCHIP_NUM_PINS)
> + pch_pic_set_irq(s, irq, level);
> + else if (irq < 256)
> + pch_msi_set_irq(kvm, irq, level);
Can we use interface kvm_set_irq() to inject msi or irqline interrupt here?
Regards
Bibo Mao
> + else
> + ret = -EINVAL;
> + break;
> + default:
> + ret = -EINVAL;
> + }
> +
> + return ret;
> +}
>
Powered by blists - more mailing lists