Skip to content

Commit

Permalink
LoongArch: KVM: Add IPI read and write function
Browse files Browse the repository at this point in the history
Add implementation of IPI interrupt controller's address space read and
write function simulation.

Signed-off-by: Min Zhou <zhoumin@loongson.cn>
Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn>
Signed-off-by: Xianglai Li <lixianglai@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
  • Loading branch information
Xianglai Li authored and Huacai Chen committed Nov 13, 2024
1 parent c532de5 commit daee2f9
Show file tree
Hide file tree
Showing 3 changed files with 291 additions and 2 deletions.
2 changes: 2 additions & 0 deletions arch/loongarch/include/asm/kvm_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ struct kvm_vm_stat {
struct kvm_vm_stat_generic generic;
u64 pages;
u64 hugepages;
u64 ipi_read_exits;
u64 ipi_write_exits;
};

struct kvm_vcpu_stat {
Expand Down
12 changes: 12 additions & 0 deletions arch/loongarch/include/asm/kvm_ipi.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ struct ipi_state {
#define IOCSR_IPI_BASE 0x1000
#define IOCSR_IPI_SIZE 0x160

#define IOCSR_IPI_STATUS 0x000
#define IOCSR_IPI_EN 0x004
#define IOCSR_IPI_SET 0x008
#define IOCSR_IPI_CLEAR 0x00c
#define IOCSR_IPI_BUF_20 0x020
#define IOCSR_IPI_BUF_28 0x028
#define IOCSR_IPI_BUF_30 0x030
#define IOCSR_IPI_BUF_38 0x038
#define IOCSR_IPI_SEND 0x040
#define IOCSR_MAIL_SEND 0x048
#define IOCSR_ANY_SEND 0x158

int kvm_loongarch_register_ipi_device(void);

#endif
279 changes: 277 additions & 2 deletions arch/loongarch/kvm/intc/ipi.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,293 @@
#include <asm/kvm_ipi.h>
#include <asm/kvm_vcpu.h>

static void ipi_send(struct kvm *kvm, uint64_t data)
{
int cpu, action;
uint32_t status;
struct kvm_vcpu *vcpu;
struct kvm_interrupt irq;

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;
}

action = BIT(data & 0x1f);
spin_lock(&vcpu->arch.ipi_state.lock);
status = vcpu->arch.ipi_state.status;
vcpu->arch.ipi_state.status |= action;
spin_unlock(&vcpu->arch.ipi_state.lock);
if (status == 0) {
irq.irq = LARCH_INT_IPI;
kvm_vcpu_ioctl_interrupt(vcpu, &irq);
}
}

static void ipi_clear(struct kvm_vcpu *vcpu, uint64_t data)
{
uint32_t status;
struct kvm_interrupt irq;

spin_lock(&vcpu->arch.ipi_state.lock);
vcpu->arch.ipi_state.status &= ~data;
status = vcpu->arch.ipi_state.status;
spin_unlock(&vcpu->arch.ipi_state.lock);
if (status == 0) {
irq.irq = -LARCH_INT_IPI;
kvm_vcpu_ioctl_interrupt(vcpu, &irq);
}
}

static uint64_t read_mailbox(struct kvm_vcpu *vcpu, int offset, int len)
{
uint64_t data = 0;

spin_lock(&vcpu->arch.ipi_state.lock);
data = *(ulong *)((void *)vcpu->arch.ipi_state.buf + (offset - 0x20));
spin_unlock(&vcpu->arch.ipi_state.lock);

switch (len) {
case 1:
return data & 0xff;
case 2:
return data & 0xffff;
case 4:
return data & 0xffffffff;
case 8:
return data;
default:
kvm_err("%s: unknown data len: %d\n", __func__, len);
return 0;
}
}

static void write_mailbox(struct kvm_vcpu *vcpu, int offset, uint64_t data, int len)
{
void *pbuf;

spin_lock(&vcpu->arch.ipi_state.lock);
pbuf = (void *)vcpu->arch.ipi_state.buf + (offset - 0x20);

switch (len) {
case 1:
*(unsigned char *)pbuf = (unsigned char)data;
break;
case 2:
*(unsigned short *)pbuf = (unsigned short)data;
break;
case 4:
*(unsigned int *)pbuf = (unsigned int)data;
break;
case 8:
*(unsigned long *)pbuf = (unsigned long)data;
break;
default:
kvm_err("%s: unknown data len: %d\n", __func__, len);
}
spin_unlock(&vcpu->arch.ipi_state.lock);
}

static int send_ipi_data(struct kvm_vcpu *vcpu, gpa_t addr, uint64_t data)
{
int i, ret;
uint32_t val = 0, mask = 0;

/*
* Bit 27-30 is mask for byte writing.
* If the mask is 0, we need not to do anything.
*/
if ((data >> 27) & 0xf) {
/* Read the old 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 & (BIT(27 + i)))
mask |= (0xff << (i * 8));
}
/* Save the old part of val */
val &= mask;
}
val |= ((uint32_t)(data >> 32) & ~mask);
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 int mail_send(struct kvm *kvm, uint64_t data)
{
int cpu, mailbox, offset;
struct kvm_vcpu *vcpu;

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 -EINVAL;
}
mailbox = ((data & 0xffffffff) >> 2) & 0x7;
offset = IOCSR_IPI_BASE + IOCSR_IPI_BUF_20 + mailbox * 4;

return send_ipi_data(vcpu, offset, data);
}

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

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 -EINVAL;
}
offset = data & 0xffff;

return send_ipi_data(vcpu, offset, data);
}

static int loongarch_ipi_readl(struct kvm_vcpu *vcpu, gpa_t addr, int len, void *val)
{
int ret = 0;
uint32_t offset;
uint64_t res = 0;

offset = (uint32_t)(addr & 0x1ff);
WARN_ON_ONCE(offset & (len - 1));

switch (offset) {
case IOCSR_IPI_STATUS:
spin_lock(&vcpu->arch.ipi_state.lock);
res = vcpu->arch.ipi_state.status;
spin_unlock(&vcpu->arch.ipi_state.lock);
break;
case IOCSR_IPI_EN:
spin_lock(&vcpu->arch.ipi_state.lock);
res = vcpu->arch.ipi_state.en;
spin_unlock(&vcpu->arch.ipi_state.lock);
break;
case IOCSR_IPI_SET:
res = 0;
break;
case IOCSR_IPI_CLEAR:
res = 0;
break;
case IOCSR_IPI_BUF_20 ... IOCSR_IPI_BUF_38 + 7:
if (offset + len > IOCSR_IPI_BUF_38 + 8) {
kvm_err("%s: invalid offset or len: offset = %d, len = %d\n",
__func__, offset, len);
ret = -EINVAL;
break;
}
res = read_mailbox(vcpu, offset, len);
break;
default:
kvm_err("%s: unknown addr: %llx\n", __func__, addr);
ret = -EINVAL;
break;
}
*(uint64_t *)val = res;

return ret;
}

static int loongarch_ipi_writel(struct kvm_vcpu *vcpu, gpa_t addr, int len, const void *val)
{
int ret = 0;
uint64_t data;
uint32_t offset;

data = *(uint64_t *)val;

offset = (uint32_t)(addr & 0x1ff);
WARN_ON_ONCE(offset & (len - 1));

switch (offset) {
case IOCSR_IPI_STATUS:
ret = -EINVAL;
break;
case IOCSR_IPI_EN:
spin_lock(&vcpu->arch.ipi_state.lock);
vcpu->arch.ipi_state.en = data;
spin_unlock(&vcpu->arch.ipi_state.lock);
break;
case IOCSR_IPI_SET:
ret = -EINVAL;
break;
case IOCSR_IPI_CLEAR:
/* Just clear the status of the current vcpu */
ipi_clear(vcpu, data);
break;
case IOCSR_IPI_BUF_20 ... IOCSR_IPI_BUF_38 + 7:
if (offset + len > IOCSR_IPI_BUF_38 + 8) {
kvm_err("%s: invalid offset or len: offset = %d, len = %d\n",
__func__, offset, len);
ret = -EINVAL;
break;
}
write_mailbox(vcpu, offset, data, len);
break;
case IOCSR_IPI_SEND:
ipi_send(vcpu->kvm, data);
break;
case IOCSR_MAIL_SEND:
ret = mail_send(vcpu->kvm, *(uint64_t *)val);
break;
case IOCSR_ANY_SEND:
ret = any_send(vcpu->kvm, *(uint64_t *)val);
break;
default:
kvm_err("%s: unknown addr: %llx\n", __func__, addr);
ret = -EINVAL;
break;
}

return ret;
}

static int kvm_ipi_read(struct kvm_vcpu *vcpu,
struct kvm_io_device *dev,
gpa_t addr, int len, void *val)
{
return 0;
int ret;
struct loongarch_ipi *ipi;

ipi = vcpu->kvm->arch.ipi;
if (!ipi) {
kvm_err("%s: ipi irqchip not valid!\n", __func__);
return -EINVAL;
}
ipi->kvm->stat.ipi_read_exits++;
ret = loongarch_ipi_readl(vcpu, addr, len, val);

return ret;
}

static int kvm_ipi_write(struct kvm_vcpu *vcpu,
struct kvm_io_device *dev,
gpa_t addr, int len, const void *val)
{
return 0;
int ret;
struct loongarch_ipi *ipi;

ipi = vcpu->kvm->arch.ipi;
if (!ipi) {
kvm_err("%s: ipi irqchip not valid!\n", __func__);
return -EINVAL;
}
ipi->kvm->stat.ipi_write_exits++;
ret = loongarch_ipi_writel(vcpu, addr, len, val);

return ret;
}

static const struct kvm_io_device_ops kvm_ipi_ops = {
Expand Down

0 comments on commit daee2f9

Please sign in to comment.