diff mbox series

[01/11] LoongArch: KVM: Add iocsr and mmio bus simulation in kernel

Message ID 20240705023854.1005258-2-lixianglai@loongson.cn (mailing list archive)
State New, archived
Headers show
Series Added Interrupt controller emulation for loongarch kvm | expand

Commit Message

Xianglai Li July 5, 2024, 2:38 a.m. UTC
Add iocsr and mmio memory read and write simulation to the kernel.
When the VM accesses the device address space through iocsr
instructions or mmio, it does not need to return to the qemu
user mode but directly completes the access in the kernel mode.

Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn>
Signed-off-by: Xianglai Li <lixianglai@loongson.cn>
---
Cc: Bibo Mao <maobibo@loongson.cn> 
Cc: Huacai Chen <chenhuacai@kernel.org> 
Cc: kvm@vger.kernel.org 
Cc: loongarch@lists.linux.dev 
Cc: Min Zhou <zhoumin@loongson.cn> 
Cc: Paolo Bonzini <pbonzini@redhat.com> 
Cc: Tianrui Zhao <zhaotianrui@loongson.cn> 
Cc: WANG Xuerui <kernel@xen0n.name> 
Cc: Xianglai li <lixianglai@loongson.cn> 

 arch/loongarch/kvm/exit.c | 69 ++++++++++++++++++++++++++++-----------
 include/linux/kvm_host.h  |  1 +
 2 files changed, 51 insertions(+), 19 deletions(-)

Comments

Bibo Mao July 12, 2024, 6:55 a.m. UTC | #1
On 2024/7/5 上午10:38, Xianglai Li wrote:
> Add iocsr and mmio memory read and write simulation to the kernel.
> When the VM accesses the device address space through iocsr
> instructions or mmio, it does not need to return to the qemu
> user mode but directly completes the access in the kernel mode.
> 
> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn>
> Signed-off-by: Xianglai Li <lixianglai@loongson.cn>
> ---
> Cc: Bibo Mao <maobibo@loongson.cn>
> Cc: Huacai Chen <chenhuacai@kernel.org>
> Cc: kvm@vger.kernel.org
> Cc: loongarch@lists.linux.dev
> Cc: Min Zhou <zhoumin@loongson.cn>
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Cc: Tianrui Zhao <zhaotianrui@loongson.cn>
> Cc: WANG Xuerui <kernel@xen0n.name>
> Cc: Xianglai li <lixianglai@loongson.cn>
> 
>   arch/loongarch/kvm/exit.c | 69 ++++++++++++++++++++++++++++-----------
>   include/linux/kvm_host.h  |  1 +
>   2 files changed, 51 insertions(+), 19 deletions(-)
> 
> diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c
> index a68573e091c0..e8e37e135dd1 100644
> --- a/arch/loongarch/kvm/exit.c
> +++ b/arch/loongarch/kvm/exit.c
> @@ -148,7 +148,7 @@ static int kvm_handle_csr(struct kvm_vcpu *vcpu, larch_inst inst)
>   int kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu)
>   {
>   	int ret;
> -	unsigned long val;
> +	unsigned long *val;
>   	u32 addr, rd, rj, opcode;
>   
>   	/*
> @@ -161,6 +161,7 @@ int kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu)
>   	ret = EMULATE_DO_IOCSR;
>   	run->iocsr_io.phys_addr = addr;
>   	run->iocsr_io.is_write = 0;
> +	val = &vcpu->arch.gprs[rd];
>   
>   	/* LoongArch is Little endian */
>   	switch (opcode) {
> @@ -194,15 +195,21 @@ int kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu)
>   		break;
>   	default:
>   		ret = EMULATE_FAIL;
> -		break;
> +		return ret;
How about directly return such as *return EMULATE_FAIL;* ?

>   	}
>   
> -	if (ret == EMULATE_DO_IOCSR) {
> -		if (run->iocsr_io.is_write) {
> -			val = vcpu->arch.gprs[rd];
> -			memcpy(run->iocsr_io.data, &val, run->iocsr_io.len);
> -		}
> -		vcpu->arch.io_gpr = rd;
> +	if (run->iocsr_io.is_write) {
> +		if (!kvm_io_bus_write(vcpu, KVM_IOCSR_BUS, addr, run->iocsr_io.len, val))
It exceeds 80 chars, it will be better if line wrapper is added.

> +			ret = EMULATE_DONE;
> +		else
> +			/* Save data and let user space to write it */
> +			memcpy(run->iocsr_io.data, val, run->iocsr_io.len);
> +	} else {
> +		if (!kvm_io_bus_read(vcpu, KVM_IOCSR_BUS, addr, run->iocsr_io.len, val))
Ditto.
> +			ret = EMULATE_DONE;
> +		else
> +			/* Save register id for iocsr read completion */
> +			vcpu->arch.io_gpr = rd;
>   	}
>   
>   	return ret;
> @@ -438,19 +445,33 @@ int kvm_emu_mmio_read(struct kvm_vcpu *vcpu, larch_inst inst)
>   	}
>   
>   	if (ret == EMULATE_DO_MMIO) {
> +		/*
> +		 * if mmio device such as pch pic is emulated in KVM,
> +		 * it need not return to user space to handle the mmio
> +		 * exception.
> +		 */
> +		ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, vcpu->arch.badv,
> +				run->mmio.len, &vcpu->arch.gprs[rd]);
> +		if (!ret) {
> +			update_pc(&vcpu->arch);
> +			vcpu->mmio_needed = 0;
> +			return EMULATE_DONE;
> +		}
> +
>   		/* Set for kvm_complete_mmio_read() use */
>   		vcpu->arch.io_gpr = rd;
>   		run->mmio.is_write = 0;
>   		vcpu->mmio_is_write = 0;
>   		trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, run->mmio.len,
>   				run->mmio.phys_addr, NULL);
Should the trace function be called for KVM_MMIO_BUS also? I think the 
trace function should be called before kvm_io_bus_read.

> -	} else {
> -		kvm_err("Read not supported Inst=0x%08x @%lx BadVaddr:%#lx\n",
> -			inst.word, vcpu->arch.pc, vcpu->arch.badv);
> -		kvm_arch_vcpu_dump_regs(vcpu);
> -		vcpu->mmio_needed = 0;
> +		return EMULATE_DO_MMIO;
>   	}
>   
> +	kvm_err("Read not supported Inst=0x%08x @%lx BadVaddr:%#lx\n",
> +			inst.word, vcpu->arch.pc, vcpu->arch.badv);
> +	kvm_arch_vcpu_dump_regs(vcpu);
> +	vcpu->mmio_needed = 0;
> +
Empty line is not necessary from my view :)
>   	return ret;
>   }
>   
> @@ -591,19 +612,29 @@ int kvm_emu_mmio_write(struct kvm_vcpu *vcpu, larch_inst inst)
>   	}
>   
>   	if (ret == EMULATE_DO_MMIO) {
> +		/*
> +		 * if mmio device such as pch pic is emulated in KVM,
> +		 * it need not return to user space to handle the mmio
> +		 * exception.
> +		 */
> +		ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, vcpu->arch.badv,
> +				run->mmio.len, data);
> +		if (!ret)
> +			return EMULATE_DONE;
> +
>   		run->mmio.is_write = 1;
>   		vcpu->mmio_needed = 1;
>   		vcpu->mmio_is_write = 1;
>   		trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, run->mmio.len,
>   				run->mmio.phys_addr, data);
Ditto, trace function should be put before kvm_io_bus_write.

> -	} else {
> -		vcpu->arch.pc = curr_pc;
> -		kvm_err("Write not supported Inst=0x%08x @%lx BadVaddr:%#lx\n",
> -			inst.word, vcpu->arch.pc, vcpu->arch.badv);
> -		kvm_arch_vcpu_dump_regs(vcpu);
> -		/* Rollback PC if emulation was unsuccessful */
> +		return EMULATE_DO_MMIO;
>   	}
>   
> +	vcpu->arch.pc = curr_pc;
> +	kvm_err("Write not supported Inst=0x%08x @%lx BadVaddr:%#lx\n",
> +			inst.word, vcpu->arch.pc, vcpu->arch.badv);
> +	kvm_arch_vcpu_dump_regs(vcpu);
> +	/* Rollback PC if emulation was unsuccessful */
>   	return ret;
>   }
>   
> diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
> index 692c01e41a18..f51b2e53d81c 100644
> --- a/include/linux/kvm_host.h
> +++ b/include/linux/kvm_host.h
> @@ -219,6 +219,7 @@ enum kvm_bus {
>   	KVM_PIO_BUS,
>   	KVM_VIRTIO_CCW_NOTIFY_BUS,
>   	KVM_FAST_MMIO_BUS,
> +	KVM_IOCSR_BUS,
>   	KVM_NR_BUSES
>   };
I just think this patch should be after PCH/EXTIOI/IPI is created and 
IOCSR/MMIO space is registered in kernel space.It is only my points, I 
am not good at this:(

Regards
Bibo Mao
>   
>
diff mbox series

Patch

diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c
index a68573e091c0..e8e37e135dd1 100644
--- a/arch/loongarch/kvm/exit.c
+++ b/arch/loongarch/kvm/exit.c
@@ -148,7 +148,7 @@  static int kvm_handle_csr(struct kvm_vcpu *vcpu, larch_inst inst)
 int kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu)
 {
 	int ret;
-	unsigned long val;
+	unsigned long *val;
 	u32 addr, rd, rj, opcode;
 
 	/*
@@ -161,6 +161,7 @@  int kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu)
 	ret = EMULATE_DO_IOCSR;
 	run->iocsr_io.phys_addr = addr;
 	run->iocsr_io.is_write = 0;
+	val = &vcpu->arch.gprs[rd];
 
 	/* LoongArch is Little endian */
 	switch (opcode) {
@@ -194,15 +195,21 @@  int kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu)
 		break;
 	default:
 		ret = EMULATE_FAIL;
-		break;
+		return ret;
 	}
 
-	if (ret == EMULATE_DO_IOCSR) {
-		if (run->iocsr_io.is_write) {
-			val = vcpu->arch.gprs[rd];
-			memcpy(run->iocsr_io.data, &val, run->iocsr_io.len);
-		}
-		vcpu->arch.io_gpr = rd;
+	if (run->iocsr_io.is_write) {
+		if (!kvm_io_bus_write(vcpu, KVM_IOCSR_BUS, addr, run->iocsr_io.len, val))
+			ret = EMULATE_DONE;
+		else
+			/* Save data and let user space to write it */
+			memcpy(run->iocsr_io.data, val, run->iocsr_io.len);
+	} else {
+		if (!kvm_io_bus_read(vcpu, KVM_IOCSR_BUS, addr, run->iocsr_io.len, val))
+			ret = EMULATE_DONE;
+		else
+			/* Save register id for iocsr read completion */
+			vcpu->arch.io_gpr = rd;
 	}
 
 	return ret;
@@ -438,19 +445,33 @@  int kvm_emu_mmio_read(struct kvm_vcpu *vcpu, larch_inst inst)
 	}
 
 	if (ret == EMULATE_DO_MMIO) {
+		/*
+		 * if mmio device such as pch pic is emulated in KVM,
+		 * it need not return to user space to handle the mmio
+		 * exception.
+		 */
+		ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, vcpu->arch.badv,
+				run->mmio.len, &vcpu->arch.gprs[rd]);
+		if (!ret) {
+			update_pc(&vcpu->arch);
+			vcpu->mmio_needed = 0;
+			return EMULATE_DONE;
+		}
+
 		/* Set for kvm_complete_mmio_read() use */
 		vcpu->arch.io_gpr = rd;
 		run->mmio.is_write = 0;
 		vcpu->mmio_is_write = 0;
 		trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, run->mmio.len,
 				run->mmio.phys_addr, NULL);
-	} else {
-		kvm_err("Read not supported Inst=0x%08x @%lx BadVaddr:%#lx\n",
-			inst.word, vcpu->arch.pc, vcpu->arch.badv);
-		kvm_arch_vcpu_dump_regs(vcpu);
-		vcpu->mmio_needed = 0;
+		return EMULATE_DO_MMIO;
 	}
 
+	kvm_err("Read not supported Inst=0x%08x @%lx BadVaddr:%#lx\n",
+			inst.word, vcpu->arch.pc, vcpu->arch.badv);
+	kvm_arch_vcpu_dump_regs(vcpu);
+	vcpu->mmio_needed = 0;
+
 	return ret;
 }
 
@@ -591,19 +612,29 @@  int kvm_emu_mmio_write(struct kvm_vcpu *vcpu, larch_inst inst)
 	}
 
 	if (ret == EMULATE_DO_MMIO) {
+		/*
+		 * if mmio device such as pch pic is emulated in KVM,
+		 * it need not return to user space to handle the mmio
+		 * exception.
+		 */
+		ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, vcpu->arch.badv,
+				run->mmio.len, data);
+		if (!ret)
+			return EMULATE_DONE;
+
 		run->mmio.is_write = 1;
 		vcpu->mmio_needed = 1;
 		vcpu->mmio_is_write = 1;
 		trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, run->mmio.len,
 				run->mmio.phys_addr, data);
-	} else {
-		vcpu->arch.pc = curr_pc;
-		kvm_err("Write not supported Inst=0x%08x @%lx BadVaddr:%#lx\n",
-			inst.word, vcpu->arch.pc, vcpu->arch.badv);
-		kvm_arch_vcpu_dump_regs(vcpu);
-		/* Rollback PC if emulation was unsuccessful */
+		return EMULATE_DO_MMIO;
 	}
 
+	vcpu->arch.pc = curr_pc;
+	kvm_err("Write not supported Inst=0x%08x @%lx BadVaddr:%#lx\n",
+			inst.word, vcpu->arch.pc, vcpu->arch.badv);
+	kvm_arch_vcpu_dump_regs(vcpu);
+	/* Rollback PC if emulation was unsuccessful */
 	return ret;
 }
 
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 692c01e41a18..f51b2e53d81c 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -219,6 +219,7 @@  enum kvm_bus {
 	KVM_PIO_BUS,
 	KVM_VIRTIO_CCW_NOTIFY_BUS,
 	KVM_FAST_MMIO_BUS,
+	KVM_IOCSR_BUS,
 	KVM_NR_BUSES
 };