Commit 35da19c3 authored by Xianglai Li's avatar Xianglai Li
Browse files

LoongArch: KVM: Add PCHPIC read and write functions

LoongArch inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/IB8FOC



------------------------------------------

Implementation of IPI interrupt controller address
space read and write function simulation.

Implement interrupt injection interface under loongarch.

Signed-off-by: default avatarTianrui Zhao <zhaotianrui@loongson.cn>
Signed-off-by: default avatarXianglai Li <lixianglai@loongson.cn>
parent 9b2552d1
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -35,6 +35,22 @@
#define KVM_HALT_POLL_NS_DEFAULT	500000
#define KVM_REQ_RECORD_STEAL		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_VALID_MASK		(KVM_GUESTDBG_ENABLE | \
			KVM_GUESTDBG_USE_SW_BP | KVM_GUESTDBG_SINGLESTEP)
struct kvm_vm_stat {
@@ -45,6 +61,8 @@ struct kvm_vm_stat {
	u64 ipi_write_exits;
	u64 extioi_read_exits;
	u64 extioi_write_exits;
	u64 pch_pic_read_exits;
	u64 pch_pic_write_exits;
};

struct kvm_vcpu_stat {
+31 −0
Original line number Diff line number Diff line
@@ -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 */
+1 −0
Original line number Diff line number Diff line
@@ -19,6 +19,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
/*
+29 −16
Original line number Diff line number Diff line
@@ -223,9 +223,9 @@ static int kvm_loongarch_ipi_read(struct kvm_vcpu *vcpu,
	return ret;
}

static void send_ipi_data(struct kvm_vcpu *vcpu, gpa_t addr, uint64_t data)
static int send_ipi_data(struct kvm_vcpu *vcpu, gpa_t addr, uint64_t data)
{
	int i;
	int i, ret;
	uint32_t val = 0, mask = 0;
	/*
	 * Bit 27-30 is mask for byte writing.
@@ -233,8 +233,11 @@ static void send_ipi_data(struct kvm_vcpu *vcpu, gpa_t addr, uint64_t data)
	 */
	if ((data >> 27) & 0xf) {
		/* Read the old val */
		kvm_io_bus_read(vcpu, KVM_IOCSR_BUS, addr, sizeof(val), &val);

		ret = kvm_io_bus_read(vcpu, KVM_IOCSR_BUS, addr, sizeof(val), &val);
		if (unlikely(ret)) {
			kvm_err("%s: : read date from addr %llx failed\n", __func__, addr);
			return ret;
		}
		/* Construct the mask by scanning the bit 27-30 */
		for (i = 0; i < 4; i++) {
			if (data & (0x1 << (27 + i)))
@@ -245,41 +248,48 @@ static void send_ipi_data(struct kvm_vcpu *vcpu, gpa_t addr, uint64_t data)
	}

	val |= ((uint32_t)(data >> 32) & ~mask);
	kvm_io_bus_write(vcpu, KVM_IOCSR_BUS, addr, sizeof(val), &val);
	ret = kvm_io_bus_write(vcpu, KVM_IOCSR_BUS, addr, sizeof(val), &val);
	if (unlikely(ret))
		kvm_err("%s: : write date to addr %llx failed\n", __func__, addr);

	return ret;
}

static void mail_send(struct kvm *kvm, uint64_t data)
static int mail_send(struct kvm *kvm, uint64_t data)
{
	struct kvm_vcpu *vcpu;
	int cpu, mailbox;
	int offset;
	int offset, ret;

	cpu = ((data & 0xffffffff) >> 16) & 0x3ff;
	vcpu = kvm_get_vcpu_by_cpuid(kvm, cpu);
	if (unlikely(vcpu == NULL)) {
		kvm_err("%s: invalid target cpu: %d\n", __func__, cpu);
		return;
		return -EINVAL;
	}

	mailbox = ((data & 0xffffffff) >> 2) & 0x7;
	offset = SMP_MAILBOX + CORE_BUF_20 + mailbox * 4;
	send_ipi_data(vcpu, offset, data);
	ret = send_ipi_data(vcpu, offset, data);

	return ret;
}

static void any_send(struct kvm *kvm, uint64_t data)
static int any_send(struct kvm *kvm, uint64_t data)
{
	struct kvm_vcpu *vcpu;
	int cpu, offset;
	int cpu, offset, ret;

	cpu = ((data & 0xffffffff) >> 16) & 0x3ff;
	vcpu = kvm_get_vcpu_by_cpuid(kvm, cpu);
	if (unlikely(vcpu == NULL)) {
		kvm_err("%s: invalid target cpu: %d\n", __func__, cpu);
		return;
		return -EINVAL;
	}

	offset = data & 0xffff;
	send_ipi_data(vcpu, offset, data);
	ret = send_ipi_data(vcpu, offset, data);
	return ret;
}

static int kvm_loongarch_mail_write(struct kvm_vcpu *vcpu,
@@ -287,6 +297,7 @@ static int kvm_loongarch_mail_write(struct kvm_vcpu *vcpu,
			gpa_t addr, int len, const void *val)
{
	struct loongarch_ipi *ipi;
	int ret;

	ipi = vcpu->kvm->arch.ipi;
	if (!ipi) {
@@ -299,16 +310,18 @@ static int kvm_loongarch_mail_write(struct kvm_vcpu *vcpu,

	switch (addr) {
	case MAIL_SEND_OFFSET:
		mail_send(vcpu->kvm, *(uint64_t *)val);
		ret = mail_send(vcpu->kvm, *(uint64_t *)val);
		break;
	case ANY_SEND_OFFSET:
		any_send(vcpu->kvm, *(uint64_t *)val);
		ret = any_send(vcpu->kvm, *(uint64_t *)val);
		break;
	default:
		kvm_err("%s: invalid addr %llx!\n", __func__, addr);
		ret = -EINVAL;
		break;
	}

	return 0;
	return ret;
}

static const struct kvm_io_device_ops kvm_loongarch_ipi_ops = {
+290 −2
Original line number Diff line number Diff line
@@ -8,18 +8,306 @@
#include <asm/kvm_vcpu.h>
#include <linux/count_zeros.h>

/* update the isr according to irq level and route irq to extioi */
static void pch_pic_update_irq(struct loongarch_pch_pic *s, int irq, int level)
{
	u64 mask = (1 << irq);

	/*
	 * set isr and route irq to extioi and
	 * the route table is in htmsi_vector[]
	 */
	if (level) {
		if (mask & s->irr & ~s->mask) {
			s->isr |= mask;
			irq = s->htmsi_vector[irq];
			extioi_set_irq(s->kvm->arch.extioi, irq, level);
		}
	} else {
		if (mask & s->isr & ~s->irr) {
			s->isr &= ~mask;
			irq = s->htmsi_vector[irq];
			extioi_set_irq(s->kvm->arch.extioi, irq, level);
		}
	}
}

/* msi irq handler */
void pch_msi_set_irq(struct kvm *kvm, int irq, int level)
{
	extioi_set_irq(kvm->arch.extioi, 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 = (1 << 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: extioi */
		s->route_entry[offset] = 1;
		break;
	case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END:
		/* route table to extioi */
		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_loongarch_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: extioi */
		*(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_loongarch_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_loongarch_pch_pic_ops = {
Loading