Commit 81efe043 authored by Tianrui Zhao's avatar Tianrui Zhao Committed by Huacai Chen
Browse files

LoongArch: KVM: Implement handle iocsr exception



Implement kvm handle vcpu iocsr exception, setting the iocsr info into
vcpu_run and return to user space to handle it.

Reviewed-by: default avatarBibo Mao <maobibo@loongson.cn>
Tested-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
Signed-off-by: default avatarTianrui Zhao <zhaotianrui@loongson.cn>
Signed-off-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
parent da50f5a6
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -65,6 +65,14 @@ enum reg2_op {
	revbd_op	= 0x0f,
	revh2w_op	= 0x10,
	revhd_op	= 0x11,
	iocsrrdb_op     = 0x19200,
	iocsrrdh_op     = 0x19201,
	iocsrrdw_op     = 0x19202,
	iocsrrdd_op     = 0x19203,
	iocsrwrb_op     = 0x19204,
	iocsrwrh_op     = 0x19205,
	iocsrwrw_op     = 0x19206,
	iocsrwrd_op     = 0x19207,
};

enum reg2i5_op {
@@ -318,6 +326,13 @@ struct reg2bstrd_format {
	unsigned int opcode : 10;
};

struct reg2csr_format {
	unsigned int rd : 5;
	unsigned int rj : 5;
	unsigned int csr : 14;
	unsigned int opcode : 8;
};

struct reg3_format {
	unsigned int rd : 5;
	unsigned int rj : 5;
@@ -346,6 +361,7 @@ union loongarch_instruction {
	struct reg2i14_format	reg2i14_format;
	struct reg2i16_format	reg2i16_format;
	struct reg2bstrd_format	reg2bstrd_format;
	struct reg2csr_format   reg2csr_format;
	struct reg3_format	reg3_format;
	struct reg3sa2_format	reg3sa2_format;
};
+91 −0
Original line number Diff line number Diff line
@@ -103,3 +103,94 @@ static int kvm_handle_csr(struct kvm_vcpu *vcpu, larch_inst inst)

	return EMULATE_DONE;
}

int kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu)
{
	int ret;
	unsigned long val;
	u32 addr, rd, rj, opcode;

	/*
	 * Each IOCSR with different opcode
	 */
	rd = inst.reg2_format.rd;
	rj = inst.reg2_format.rj;
	opcode = inst.reg2_format.opcode;
	addr = vcpu->arch.gprs[rj];
	ret = EMULATE_DO_IOCSR;
	run->iocsr_io.phys_addr = addr;
	run->iocsr_io.is_write = 0;

	/* LoongArch is Little endian */
	switch (opcode) {
	case iocsrrdb_op:
		run->iocsr_io.len = 1;
		break;
	case iocsrrdh_op:
		run->iocsr_io.len = 2;
		break;
	case iocsrrdw_op:
		run->iocsr_io.len = 4;
		break;
	case iocsrrdd_op:
		run->iocsr_io.len = 8;
		break;
	case iocsrwrb_op:
		run->iocsr_io.len = 1;
		run->iocsr_io.is_write = 1;
		break;
	case iocsrwrh_op:
		run->iocsr_io.len = 2;
		run->iocsr_io.is_write = 1;
		break;
	case iocsrwrw_op:
		run->iocsr_io.len = 4;
		run->iocsr_io.is_write = 1;
		break;
	case iocsrwrd_op:
		run->iocsr_io.len = 8;
		run->iocsr_io.is_write = 1;
		break;
	default:
		ret = EMULATE_FAIL;
		break;
	}

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

	return ret;
}

int kvm_complete_iocsr_read(struct kvm_vcpu *vcpu, struct kvm_run *run)
{
	enum emulation_result er = EMULATE_DONE;
	unsigned long *gpr = &vcpu->arch.gprs[vcpu->arch.io_gpr];

	switch (run->iocsr_io.len) {
	case 1:
		*gpr = *(s8 *)run->iocsr_io.data;
		break;
	case 2:
		*gpr = *(s16 *)run->iocsr_io.data;
		break;
	case 4:
		*gpr = *(s32 *)run->iocsr_io.data;
		break;
	case 8:
		*gpr = *(s64 *)run->iocsr_io.data;
		break;
	default:
		kvm_err("Bad IOCSR length: %d, addr is 0x%lx\n",
				run->iocsr_io.len, vcpu->arch.badv);
		er = EMULATE_FAIL;
		break;
	}

	return er;
}