enum emulation_result kvm_mips_emulate_load()

in kvm/emulate.c [1270:1509]


enum emulation_result kvm_mips_emulate_load(union mips_instruction inst,
					    u32 cause, struct kvm_vcpu *vcpu)
{
	struct kvm_run *run = vcpu->run;
	int r;
	enum emulation_result er;
	unsigned long curr_pc;
	u32 op, rt;
	unsigned int imme;

	rt = inst.i_format.rt;
	op = inst.i_format.opcode;

	/*
	 * Find the resume PC now while we have safe and easy access to the
	 * prior branch instruction, and save it for
	 * kvm_mips_complete_mmio_load() to restore later.
	 */
	curr_pc = vcpu->arch.pc;
	er = update_pc(vcpu, cause);
	if (er == EMULATE_FAIL)
		return er;
	vcpu->arch.io_pc = vcpu->arch.pc;
	vcpu->arch.pc = curr_pc;

	vcpu->arch.io_gpr = rt;

	run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
						vcpu->arch.host_cp0_badvaddr);
	if (run->mmio.phys_addr == KVM_INVALID_ADDR)
		return EMULATE_FAIL;

	vcpu->mmio_needed = 2;	/* signed */
	switch (op) {
#if defined(CONFIG_64BIT)
	case ld_op:
		run->mmio.len = 8;
		break;

	case lwu_op:
		vcpu->mmio_needed = 1;	/* unsigned */
		fallthrough;
#endif
	case lw_op:
		run->mmio.len = 4;
		break;

	case lhu_op:
		vcpu->mmio_needed = 1;	/* unsigned */
		fallthrough;
	case lh_op:
		run->mmio.len = 2;
		break;

	case lbu_op:
		vcpu->mmio_needed = 1;	/* unsigned */
		fallthrough;
	case lb_op:
		run->mmio.len = 1;
		break;

	case lwl_op:
		run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
					vcpu->arch.host_cp0_badvaddr) & (~0x3);

		run->mmio.len = 4;
		imme = vcpu->arch.host_cp0_badvaddr & 0x3;
		switch (imme) {
		case 0:
			vcpu->mmio_needed = 3;	/* 1 byte */
			break;
		case 1:
			vcpu->mmio_needed = 4;	/* 2 bytes */
			break;
		case 2:
			vcpu->mmio_needed = 5;	/* 3 bytes */
			break;
		case 3:
			vcpu->mmio_needed = 6;	/* 4 bytes */
			break;
		default:
			break;
		}
		break;

	case lwr_op:
		run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
					vcpu->arch.host_cp0_badvaddr) & (~0x3);

		run->mmio.len = 4;
		imme = vcpu->arch.host_cp0_badvaddr & 0x3;
		switch (imme) {
		case 0:
			vcpu->mmio_needed = 7;	/* 4 bytes */
			break;
		case 1:
			vcpu->mmio_needed = 8;	/* 3 bytes */
			break;
		case 2:
			vcpu->mmio_needed = 9;	/* 2 bytes */
			break;
		case 3:
			vcpu->mmio_needed = 10;	/* 1 byte */
			break;
		default:
			break;
		}
		break;

#if defined(CONFIG_64BIT)
	case ldl_op:
		run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
					vcpu->arch.host_cp0_badvaddr) & (~0x7);

		run->mmio.len = 8;
		imme = vcpu->arch.host_cp0_badvaddr & 0x7;
		switch (imme) {
		case 0:
			vcpu->mmio_needed = 11;	/* 1 byte */
			break;
		case 1:
			vcpu->mmio_needed = 12;	/* 2 bytes */
			break;
		case 2:
			vcpu->mmio_needed = 13;	/* 3 bytes */
			break;
		case 3:
			vcpu->mmio_needed = 14;	/* 4 bytes */
			break;
		case 4:
			vcpu->mmio_needed = 15;	/* 5 bytes */
			break;
		case 5:
			vcpu->mmio_needed = 16;	/* 6 bytes */
			break;
		case 6:
			vcpu->mmio_needed = 17;	/* 7 bytes */
			break;
		case 7:
			vcpu->mmio_needed = 18;	/* 8 bytes */
			break;
		default:
			break;
		}
		break;

	case ldr_op:
		run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
					vcpu->arch.host_cp0_badvaddr) & (~0x7);

		run->mmio.len = 8;
		imme = vcpu->arch.host_cp0_badvaddr & 0x7;
		switch (imme) {
		case 0:
			vcpu->mmio_needed = 19;	/* 8 bytes */
			break;
		case 1:
			vcpu->mmio_needed = 20;	/* 7 bytes */
			break;
		case 2:
			vcpu->mmio_needed = 21;	/* 6 bytes */
			break;
		case 3:
			vcpu->mmio_needed = 22;	/* 5 bytes */
			break;
		case 4:
			vcpu->mmio_needed = 23;	/* 4 bytes */
			break;
		case 5:
			vcpu->mmio_needed = 24;	/* 3 bytes */
			break;
		case 6:
			vcpu->mmio_needed = 25;	/* 2 bytes */
			break;
		case 7:
			vcpu->mmio_needed = 26;	/* 1 byte */
			break;
		default:
			break;
		}
		break;
#endif

#ifdef CONFIG_CPU_LOONGSON64
	case ldc2_op:
		rt = inst.loongson3_lsdc2_format.rt;
		switch (inst.loongson3_lsdc2_format.opcode1) {
		/*
		 * Loongson-3 overridden ldc2 instructions.
		 * opcode1              instruction
		 *   0x0          gslbx: store 1 bytes from GPR
		 *   0x1          gslhx: store 2 bytes from GPR
		 *   0x2          gslwx: store 4 bytes from GPR
		 *   0x3          gsldx: store 8 bytes from GPR
		 */
		case 0x0:
			run->mmio.len = 1;
			vcpu->mmio_needed = 27;	/* signed */
			break;
		case 0x1:
			run->mmio.len = 2;
			vcpu->mmio_needed = 28;	/* signed */
			break;
		case 0x2:
			run->mmio.len = 4;
			vcpu->mmio_needed = 29;	/* signed */
			break;
		case 0x3:
			run->mmio.len = 8;
			vcpu->mmio_needed = 30;	/* signed */
			break;
		default:
			kvm_err("Godson Extended GS-Load for float not yet supported (inst=0x%08x)\n",
				inst.word);
			break;
		}
		break;
#endif

	default:
		kvm_err("Load not yet supported (inst=0x%08x)\n",
			inst.word);
		vcpu->mmio_needed = 0;
		return EMULATE_FAIL;
	}

	run->mmio.is_write = 0;
	vcpu->mmio_is_write = 0;

	r = kvm_io_bus_read(vcpu, KVM_MMIO_BUS,
			run->mmio.phys_addr, run->mmio.len, run->mmio.data);

	if (!r) {
		kvm_mips_complete_mmio_load(vcpu);
		vcpu->mmio_needed = 0;
		return EMULATE_DONE;
	}

	return EMULATE_DO_MMIO;
}