Commit dba6c8ba authored by Bibo Mao's avatar Bibo Mao Committed by Xianglai Li
Browse files

LoongArch: KVM: Add PV IPI support on host side

mainline inclusion
from mainline-v6.10-rc1
commit e33bda7ee50c3c20d80f5ca6dc5ca2cd37863518
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/IAZJDO


CVE: NA

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

On LoongArch system, IPI hw uses iocsr registers. There are one iocsr
register access on IPI sending, and two iocsr access on IPI receiving
for the IPI interrupt handler. In VM mode all iocsr accessing will cause
VM to trap into hypervisor. So with one IPI hw notification there will
be three times of trap.

In this patch PV IPI is added for VM, hypercall instruction is used for
IPI sender, and hypervisor will inject an SWI to the destination vcpu.
During the SWI interrupt handler, only CSR.ESTAT register is written to
clear irq. CSR.ESTAT register access will not trap into hypervisor, so
with PV IPI supported, there is one trap with IPI sender, and no trap
with IPI receiver, there is only one trap with IPI notification.

Also this patch adds IPI multicast support, the method is similar with
x86. With IPI multicast support, IPI notification can be sent to at
most 128 vcpus at one time. It greatly reduces the times of trapping
into hypervisor.

Signed-off-by: default avatarBibo Mao <maobibo@loongson.cn>
Signed-off-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
Signed-off-by: default avatarXianglai Li <lixianglai@loongson.cn>
parent bd254ad9
Loading
Loading
Loading
Loading
+33 −29
Original line number Diff line number Diff line
@@ -7,23 +7,27 @@
 */
#define HYPERVISOR_KVM			1
#define HYPERVISOR_VENDOR_SHIFT		8
#define HYPERCALL_CODE(vendor, code)	((vendor << HYPERVISOR_VENDOR_SHIFT) + code)
#define KVM_HCALL_CODE_PV_SERVICE	0
#define HYPERCALL_ENCODE(vendor, code)	((vendor << HYPERVISOR_VENDOR_SHIFT) + code)

#define KVM_HCALL_CODE_SERVICE		0
#define KVM_HCALL_CODE_SWDBG		1
#define KVM_HCALL_PV_SERVICE		HYPERCALL_CODE(HYPERVISOR_KVM, KVM_HCALL_CODE_PV_SERVICE)
#define  KVM_HCALL_FUNC_PV_IPI		1

#define KVM_HCALL_SERVICE		HYPERCALL_ENCODE(HYPERVISOR_KVM, KVM_HCALL_CODE_SERVICE)
#define  KVM_HCALL_FUNC_IPI		1
#define  KVM_HCALL_FUNC_NOTIFY		2
#define KVM_HCALL_SWDBG			HYPERCALL_CODE(HYPERVISOR_KVM, KVM_HCALL_CODE_SWDBG)

#define KVM_HCALL_SWDBG			HYPERCALL_ENCODE(HYPERVISOR_KVM, KVM_HCALL_CODE_SWDBG)

/*
 * LoongArch hypercall return code
 */
#define KVM_HCALL_STATUS_SUCCESS	0
#define KVM_HCALL_SUCCESS		0
#define KVM_HCALL_INVALID_CODE		-1UL
#define KVM_HCALL_INVALID_PARAMETER	-2UL

#define KVM_STEAL_PHYS_VALID		BIT_ULL(0)
#define KVM_STEAL_PHYS_MASK		GENMASK_ULL(63, 6)

struct kvm_steal_time {
	__u64 steal;
	__u32 version;
@@ -36,16 +40,16 @@ struct kvm_steal_time {
 *
 * a0: function identifier
 * a1-a6: args
 * Return value will be placed in v0.
 * Return value will be placed in a0.
 * Up to 6 arguments are passed in a1, a2, a3, a4, a5, a6.
 */
static __always_inline long kvm_hypercall(u64 fid)
static __always_inline long kvm_hypercall0(u64 fid)
{
	register long ret asm("v0");
	register long ret asm("a0");
	register unsigned long fun asm("a0") = fid;

	__asm__ __volatile__(
		"hvcl "__stringify(KVM_HCALL_PV_SERVICE)
		"hvcl "__stringify(KVM_HCALL_SERVICE)
		: "=r" (ret)
		: "r" (fun)
		: "memory"
@@ -56,12 +60,12 @@ static __always_inline long kvm_hypercall(u64 fid)

static __always_inline long kvm_hypercall1(u64 fid, unsigned long arg0)
{
	register long ret asm("v0");
	register long ret asm("a0");
	register unsigned long fun asm("a0") = fid;
	register unsigned long a1  asm("a1") = arg0;

	__asm__ __volatile__(
		"hvcl "__stringify(KVM_HCALL_PV_SERVICE)
		"hvcl "__stringify(KVM_HCALL_SERVICE)
		: "=r" (ret)
		: "r" (fun), "r" (a1)
		: "memory"
@@ -73,13 +77,13 @@ static __always_inline long kvm_hypercall1(u64 fid, unsigned long arg0)
static __always_inline long kvm_hypercall2(u64 fid,
		unsigned long arg0, unsigned long arg1)
{
	register long ret asm("v0");
	register long ret asm("a0");
	register unsigned long fun asm("a0") = fid;
	register unsigned long a1  asm("a1") = arg0;
	register unsigned long a2  asm("a2") = arg1;

	__asm__ __volatile__(
			"hvcl "__stringify(KVM_HCALL_PV_SERVICE)
		"hvcl "__stringify(KVM_HCALL_SERVICE)
		: "=r" (ret)
		: "r" (fun), "r" (a1), "r" (a2)
		: "memory"
@@ -91,14 +95,14 @@ static __always_inline long kvm_hypercall2(u64 fid,
static __always_inline long kvm_hypercall3(u64 fid,
	unsigned long arg0, unsigned long arg1, unsigned long arg2)
{
	register long ret asm("v0");
	register long ret asm("a0");
	register unsigned long fun asm("a0") = fid;
	register unsigned long a1  asm("a1") = arg0;
	register unsigned long a2  asm("a2") = arg1;
	register unsigned long a3  asm("a3") = arg2;

	__asm__ __volatile__(
		"hvcl "__stringify(KVM_HCALL_PV_SERVICE)
		"hvcl "__stringify(KVM_HCALL_SERVICE)
		: "=r" (ret)
		: "r" (fun), "r" (a1), "r" (a2), "r" (a3)
		: "memory"
@@ -108,10 +112,10 @@ static __always_inline long kvm_hypercall3(u64 fid,
}

static __always_inline long kvm_hypercall4(u64 fid,
		unsigned long arg0, unsigned long arg1, unsigned long arg2,
		unsigned long arg3)
		unsigned long arg0, unsigned long arg1,
		unsigned long arg2, unsigned long arg3)
{
	register long ret asm("v0");
	register long ret asm("a0");
	register unsigned long fun asm("a0") = fid;
	register unsigned long a1  asm("a1") = arg0;
	register unsigned long a2  asm("a2") = arg1;
@@ -119,7 +123,7 @@ static __always_inline long kvm_hypercall4(u64 fid,
	register unsigned long a4  asm("a4") = arg3;

	__asm__ __volatile__(
		"hvcl "__stringify(KVM_HCALL_PV_SERVICE)
		"hvcl "__stringify(KVM_HCALL_SERVICE)
		: "=r" (ret)
		: "r"(fun), "r" (a1), "r" (a2), "r" (a3), "r" (a4)
		: "memory"
@@ -129,10 +133,10 @@ static __always_inline long kvm_hypercall4(u64 fid,
}

static __always_inline long kvm_hypercall5(u64 fid,
		unsigned long arg0, unsigned long arg1, unsigned long arg2,
		unsigned long arg3, unsigned long arg4)
		unsigned long arg0, unsigned long arg1,
		unsigned long arg2, unsigned long arg3, unsigned long arg4)
{
	register long ret asm("v0");
	register long ret asm("a0");
	register unsigned long fun asm("a0") = fid;
	register unsigned long a1  asm("a1") = arg0;
	register unsigned long a2  asm("a2") = arg1;
@@ -141,7 +145,7 @@ static __always_inline long kvm_hypercall5(u64 fid,
	register unsigned long a5  asm("a5") = arg4;

	__asm__ __volatile__(
		"hvcl "__stringify(KVM_HCALL_PV_SERVICE)
		"hvcl "__stringify(KVM_HCALL_SERVICE)
		: "=r" (ret)
		: "r"(fun), "r" (a1), "r" (a2), "r" (a3), "r" (a4), "r" (a5)
		: "memory"
@@ -150,7 +154,6 @@ static __always_inline long kvm_hypercall5(u64 fid,
	return ret;
}


static inline unsigned int kvm_arch_para_features(void)
{
	return 0;
@@ -165,4 +168,5 @@ static inline bool kvm_check_and_clear_guest_paused(void)
{
	return false;
}

#endif /* _ASM_LOONGARCH_KVM_PARA_H */
+10 −0
Original line number Diff line number Diff line
@@ -110,4 +110,14 @@ static inline int kvm_queue_exception(struct kvm_vcpu *vcpu,
		return -1;
}

static inline unsigned long kvm_read_reg(struct kvm_vcpu *vcpu, int num)
{
	return vcpu->arch.gprs[num];
}

static inline void kvm_write_reg(struct kvm_vcpu *vcpu, int num, unsigned long val)
{
	vcpu->arch.gprs[num] = val;
}

#endif /* __ASM_LOONGARCH_KVM_VCPU_H__ */
+32 −35
Original line number Diff line number Diff line
@@ -160,6 +160,9 @@ int kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu)
		run->iocsr_io.len = 8;
		run->iocsr_io.is_write = 1;
		break;
	case CPUCFG_KVM_FEATURE:
		vcpu->arch.gprs[rd] = KVM_FEATURE_IPI;
		break;
	default:
		ret = EMULATE_FAIL;
		return ret;
@@ -761,29 +764,26 @@ static int kvm_handle_lasx_disabled(struct kvm_vcpu *vcpu)
	return RESUME_GUEST;
}

static int kvm_pv_send_ipi(struct kvm_vcpu *vcpu)
static int kvm_send_pv_ipi(struct kvm_vcpu *vcpu)
{
	unsigned long ipi_bitmap;
	unsigned int min, cpu, i;
	unsigned long ipi_bitmap;
	struct kvm_vcpu *dest;

	min = vcpu->arch.gprs[LOONGARCH_GPR_A3];
	min = kvm_read_reg(vcpu, LOONGARCH_GPR_A3);
	for (i = 0; i < 2; i++, min += BITS_PER_LONG) {
		ipi_bitmap = vcpu->arch.gprs[LOONGARCH_GPR_A1 + i];
		ipi_bitmap = kvm_read_reg(vcpu, LOONGARCH_GPR_A1 + i);
		if (!ipi_bitmap)
			continue;

		cpu = find_first_bit((void *)&ipi_bitmap, BITS_PER_LONG);
		while (cpu < BITS_PER_LONG) {
			dest = kvm_get_vcpu_by_cpuid(vcpu->kvm, cpu + min);
			cpu = find_next_bit((void *)&ipi_bitmap, BITS_PER_LONG,
					cpu + 1);
			cpu = find_next_bit((void *)&ipi_bitmap, BITS_PER_LONG, cpu + 1);
			if (!dest)
				continue;

			/*
			 * Send SWI0 to dest vcpu to emulate IPI interrupt
			 */
			/* Send SWI0 to dest vcpu to emulate IPI interrupt */
			kvm_queue_irq(dest, INT_SWI0);
			kvm_vcpu_kick(dest);
		}
@@ -792,6 +792,27 @@ static int kvm_pv_send_ipi(struct kvm_vcpu *vcpu)
	return 0;
}

/*
 * Hypercall emulation always return to guest, Caller should check retval.
 */
static void kvm_handle_service(struct kvm_vcpu *vcpu)
{
	unsigned long func = kvm_read_reg(vcpu, LOONGARCH_GPR_A0);
	long ret;

	switch (func) {
	case KVM_HCALL_FUNC_IPI:
		kvm_send_pv_ipi(vcpu);
		ret = KVM_HCALL_SUCCESS;
		break;
	default:
		ret = KVM_HCALL_INVALID_CODE;
		break;
	};

	kvm_write_reg(vcpu, LOONGARCH_GPR_A0, ret);
}

static int kvm_save_notify(struct kvm_vcpu *vcpu)
{
	unsigned long id, data;
@@ -811,30 +832,6 @@ static int kvm_save_notify(struct kvm_vcpu *vcpu)
	return 0;
};

/*
 * hypercall emulation always return to guest, Caller should check retval.
 */
static void kvm_handle_pv_service(struct kvm_vcpu *vcpu)
{
	unsigned long func = vcpu->arch.gprs[LOONGARCH_GPR_A0];
	long ret;

	switch (func) {
	case KVM_HCALL_FUNC_PV_IPI:
		kvm_pv_send_ipi(vcpu);
		ret = KVM_HCALL_STATUS_SUCCESS;
		break;
	case KVM_HCALL_FUNC_NOTIFY:
		ret = kvm_save_notify(vcpu);
		break;
	default:
		ret = KVM_HCALL_INVALID_CODE;
		break;
	};

	vcpu->arch.gprs[LOONGARCH_GPR_A0] = ret;
}

static int kvm_handle_hypercall(struct kvm_vcpu *vcpu)
{
	larch_inst inst;
@@ -846,9 +843,9 @@ static int kvm_handle_hypercall(struct kvm_vcpu *vcpu)
	ret = RESUME_GUEST;

	switch (code) {
	case KVM_HCALL_PV_SERVICE:
	case KVM_HCALL_SERVICE:
		vcpu->stat.hypercall_exits++;
		kvm_handle_pv_service(vcpu);
		kvm_handle_service(vcpu);
		break;
	case KVM_HCALL_SWDBG:
		/* KVM_HC_SWDBG only in effective when SW_BP is enabled */