Commit 2e8b9df8 authored by Xianglai Li's avatar Xianglai Li Committed by Huacai Chen
Browse files

LoongArch: KVM: Add EIOINTC device support



Add device model for EIOINTC interrupt controller, implement basic
create & destroy interfaces, and register device model to kvm device
table.

Signed-off-by: default avatarTianrui Zhao <zhaotianrui@loongson.cn>
Signed-off-by: default avatarXianglai Li <lixianglai@loongson.cn>
Signed-off-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
parent 8e305426
Loading
Loading
Loading
Loading
+93 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (C) 2024 Loongson Technology Corporation Limited
 */

#ifndef __ASM_KVM_EIOINTC_H
#define __ASM_KVM_EIOINTC_H

#include <kvm/iodev.h>

#define EIOINTC_IRQS			256
#define EIOINTC_ROUTE_MAX_VCPUS		256
#define EIOINTC_IRQS_U8_NUMS		(EIOINTC_IRQS / 8)
#define EIOINTC_IRQS_U16_NUMS		(EIOINTC_IRQS_U8_NUMS / 2)
#define EIOINTC_IRQS_U32_NUMS		(EIOINTC_IRQS_U8_NUMS / 4)
#define EIOINTC_IRQS_U64_NUMS		(EIOINTC_IRQS_U8_NUMS / 8)
/* map to ipnum per 32 irqs */
#define EIOINTC_IRQS_NODETYPE_COUNT	16

#define EIOINTC_BASE			0x1400
#define EIOINTC_SIZE			0x900

#define EIOINTC_VIRT_BASE		(0x40000000)
#define EIOINTC_VIRT_SIZE		(0x1000)

#define LOONGSON_IP_NUM			8

struct loongarch_eiointc {
	spinlock_t lock;
	struct kvm *kvm;
	struct kvm_io_device device;
	struct kvm_io_device device_vext;
	uint32_t num_cpu;
	uint32_t features;
	uint32_t status;

	/* hardware state */
	union nodetype {
		u64 reg_u64[EIOINTC_IRQS_NODETYPE_COUNT / 4];
		u32 reg_u32[EIOINTC_IRQS_NODETYPE_COUNT / 2];
		u16 reg_u16[EIOINTC_IRQS_NODETYPE_COUNT];
		u8 reg_u8[EIOINTC_IRQS_NODETYPE_COUNT * 2];
	} nodetype;

	/* one bit shows the state of one irq */
	union bounce {
		u64 reg_u64[EIOINTC_IRQS_U64_NUMS];
		u32 reg_u32[EIOINTC_IRQS_U32_NUMS];
		u16 reg_u16[EIOINTC_IRQS_U16_NUMS];
		u8 reg_u8[EIOINTC_IRQS_U8_NUMS];
	} bounce;

	union isr {
		u64 reg_u64[EIOINTC_IRQS_U64_NUMS];
		u32 reg_u32[EIOINTC_IRQS_U32_NUMS];
		u16 reg_u16[EIOINTC_IRQS_U16_NUMS];
		u8 reg_u8[EIOINTC_IRQS_U8_NUMS];
	} isr;
	union coreisr {
		u64 reg_u64[EIOINTC_ROUTE_MAX_VCPUS][EIOINTC_IRQS_U64_NUMS];
		u32 reg_u32[EIOINTC_ROUTE_MAX_VCPUS][EIOINTC_IRQS_U32_NUMS];
		u16 reg_u16[EIOINTC_ROUTE_MAX_VCPUS][EIOINTC_IRQS_U16_NUMS];
		u8 reg_u8[EIOINTC_ROUTE_MAX_VCPUS][EIOINTC_IRQS_U8_NUMS];
	} coreisr;
	union enable {
		u64 reg_u64[EIOINTC_IRQS_U64_NUMS];
		u32 reg_u32[EIOINTC_IRQS_U32_NUMS];
		u16 reg_u16[EIOINTC_IRQS_U16_NUMS];
		u8 reg_u8[EIOINTC_IRQS_U8_NUMS];
	} enable;

	/* use one byte to config ipmap for 32 irqs at once */
	union ipmap {
		u64 reg_u64;
		u32 reg_u32[EIOINTC_IRQS_U32_NUMS / 4];
		u16 reg_u16[EIOINTC_IRQS_U16_NUMS / 4];
		u8 reg_u8[EIOINTC_IRQS_U8_NUMS / 4];
	} ipmap;
	/* use one byte to config coremap for one irq */
	union coremap {
		u64 reg_u64[EIOINTC_IRQS / 8];
		u32 reg_u32[EIOINTC_IRQS / 4];
		u16 reg_u16[EIOINTC_IRQS / 2];
		u8 reg_u8[EIOINTC_IRQS];
	} coremap;

	DECLARE_BITMAP(sw_coreisr[EIOINTC_ROUTE_MAX_VCPUS][LOONGSON_IP_NUM], EIOINTC_IRQS);
	uint8_t  sw_coremap[EIOINTC_IRQS];
};

int kvm_loongarch_register_eiointc_device(void);

#endif /* __ASM_KVM_EIOINTC_H */
+3 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <asm/inst.h>
#include <asm/kvm_mmu.h>
#include <asm/kvm_ipi.h>
#include <asm/kvm_eiointc.h>
#include <asm/loongarch.h>

/* Loongarch KVM register ids */
@@ -87,7 +88,7 @@ struct kvm_world_switch {
 *
 *  For LOONGARCH_CSR_CPUID register, max CPUID size if 512
 *  For IPI hardware, max destination CPUID size 1024
 *  For extioi interrupt controller, max destination CPUID size is 256
 *  For eiointc interrupt controller, max destination CPUID size is 256
 *  For msgint interrupt controller, max supported CPUID size is 65536
 *
 * Currently max CPUID is defined as 256 for KVM hypervisor, in future
@@ -121,6 +122,7 @@ struct kvm_arch {
	s64 time_offset;
	struct kvm_context __percpu *vmcs;
	struct loongarch_ipi *ipi;
	struct loongarch_eiointc *eiointc;
};

#define CSR_MAX_NUMS		0x800
+1 −0
Original line number Diff line number Diff line
@@ -19,5 +19,6 @@ kvm-y += tlb.o
kvm-y += vcpu.o
kvm-y += vm.o
kvm-y += intc/ipi.o
kvm-y += intc/eiointc.o

CFLAGS_exit.o	+= $(call cc-option,-Wno-override-init,)
+132 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2024 Loongson Technology Corporation Limited
 */

#include <asm/kvm_eiointc.h>
#include <asm/kvm_vcpu.h>
#include <linux/count_zeros.h>

static int kvm_eiointc_read(struct kvm_vcpu *vcpu,
			struct kvm_io_device *dev,
			gpa_t addr, int len, void *val)
{
	return 0;
}

static int kvm_eiointc_write(struct kvm_vcpu *vcpu,
			struct kvm_io_device *dev,
			gpa_t addr, int len, const void *val)
{
	return 0;
}

static const struct kvm_io_device_ops kvm_eiointc_ops = {
	.read	= kvm_eiointc_read,
	.write	= kvm_eiointc_write,
};

static int kvm_eiointc_virt_read(struct kvm_vcpu *vcpu,
				struct kvm_io_device *dev,
				gpa_t addr, int len, void *val)
{
	return 0;
}

static int kvm_eiointc_virt_write(struct kvm_vcpu *vcpu,
				struct kvm_io_device *dev,
				gpa_t addr, int len, const void *val)
{
	return 0;
}

static const struct kvm_io_device_ops kvm_eiointc_virt_ops = {
	.read	= kvm_eiointc_virt_read,
	.write	= kvm_eiointc_virt_write,
};

static int kvm_eiointc_get_attr(struct kvm_device *dev,
				struct kvm_device_attr *attr)
{
	return 0;
}

static int kvm_eiointc_set_attr(struct kvm_device *dev,
				struct kvm_device_attr *attr)
{
	return 0;
}

static int kvm_eiointc_create(struct kvm_device *dev, u32 type)
{
	int ret;
	struct loongarch_eiointc *s;
	struct kvm_io_device *device, *device1;
	struct kvm *kvm = dev->kvm;

	/* eiointc has been created */
	if (kvm->arch.eiointc)
		return -EINVAL;

	s = kzalloc(sizeof(struct loongarch_eiointc), GFP_KERNEL);
	if (!s)
		return -ENOMEM;

	spin_lock_init(&s->lock);
	s->kvm = kvm;

	/*
	 * Initialize IOCSR device
	 */
	device = &s->device;
	kvm_iodevice_init(device, &kvm_eiointc_ops);
	mutex_lock(&kvm->slots_lock);
	ret = kvm_io_bus_register_dev(kvm, KVM_IOCSR_BUS,
			EIOINTC_BASE, EIOINTC_SIZE, device);
	mutex_unlock(&kvm->slots_lock);
	if (ret < 0) {
		kfree(s);
		return ret;
	}

	device1 = &s->device_vext;
	kvm_iodevice_init(device1, &kvm_eiointc_virt_ops);
	ret = kvm_io_bus_register_dev(kvm, KVM_IOCSR_BUS,
			EIOINTC_VIRT_BASE, EIOINTC_VIRT_SIZE, device1);
	if (ret < 0) {
		kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &s->device);
		kfree(s);
		return ret;
	}
	kvm->arch.eiointc = s;

	return 0;
}

static void kvm_eiointc_destroy(struct kvm_device *dev)
{
	struct kvm *kvm;
	struct loongarch_eiointc *eiointc;

	if (!dev || !dev->kvm || !dev->kvm->arch.eiointc)
		return;

	kvm = dev->kvm;
	eiointc = kvm->arch.eiointc;
	kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device);
	kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device_vext);
	kfree(eiointc);
}

static struct kvm_device_ops kvm_eiointc_dev_ops = {
	.name = "kvm-loongarch-eiointc",
	.create = kvm_eiointc_create,
	.destroy = kvm_eiointc_destroy,
	.set_attr = kvm_eiointc_set_attr,
	.get_attr = kvm_eiointc_get_attr,
};

int kvm_loongarch_register_eiointc_device(void)
{
	return kvm_register_device_ops(&kvm_eiointc_dev_ops, KVM_DEV_TYPE_LOONGARCH_EIOINTC);
}
+6 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include <asm/cacheflush.h>
#include <asm/cpufeature.h>
#include <asm/kvm_csr.h>
#include <asm/kvm_eiointc.h>
#include "trace.h"

unsigned long vpid_mask;
@@ -370,6 +371,11 @@ static int kvm_loongarch_env_init(void)

	/* Register LoongArch IPI interrupt controller interface. */
	ret = kvm_loongarch_register_ipi_device();
	if (ret)
		return ret;

	/* Register LoongArch EIOINTC interrupt controller interface. */
	ret = kvm_loongarch_register_eiointc_device();

	return ret;
}
Loading