static int __write_machine_check()

in kvm/interrupt.c [566:676]


static int __write_machine_check(struct kvm_vcpu *vcpu,
				 struct kvm_s390_mchk_info *mchk)
{
	unsigned long ext_sa_addr;
	unsigned long lc;
	freg_t fprs[NUM_FPRS];
	union mci mci;
	int rc;

	/*
	 * All other possible payload for a machine check (e.g. the register
	 * contents in the save area) will be handled by the ultravisor, as
	 * the hypervisor does not not have the needed information for
	 * protected guests.
	 */
	if (kvm_s390_pv_cpu_is_protected(vcpu)) {
		vcpu->arch.sie_block->iictl = IICTL_CODE_MCHK;
		vcpu->arch.sie_block->mcic = mchk->mcic;
		vcpu->arch.sie_block->faddr = mchk->failing_storage_address;
		vcpu->arch.sie_block->edc = mchk->ext_damage_code;
		return 0;
	}

	mci.val = mchk->mcic;
	/* take care of lazy register loading */
	save_fpu_regs();
	save_access_regs(vcpu->run->s.regs.acrs);
	if (MACHINE_HAS_GS && vcpu->arch.gs_enabled)
		save_gs_cb(current->thread.gs_cb);

	/* Extended save area */
	rc = read_guest_lc(vcpu, __LC_MCESAD, &ext_sa_addr,
			   sizeof(unsigned long));
	/* Only bits 0 through 63-LC are used for address formation */
	lc = ext_sa_addr & MCESA_LC_MASK;
	if (test_kvm_facility(vcpu->kvm, 133)) {
		switch (lc) {
		case 0:
		case 10:
			ext_sa_addr &= ~0x3ffUL;
			break;
		case 11:
			ext_sa_addr &= ~0x7ffUL;
			break;
		case 12:
			ext_sa_addr &= ~0xfffUL;
			break;
		default:
			ext_sa_addr = 0;
			break;
		}
	} else {
		ext_sa_addr &= ~0x3ffUL;
	}

	if (!rc && mci.vr && ext_sa_addr && test_kvm_facility(vcpu->kvm, 129)) {
		if (write_guest_abs(vcpu, ext_sa_addr, vcpu->run->s.regs.vrs,
				    512))
			mci.vr = 0;
	} else {
		mci.vr = 0;
	}
	if (!rc && mci.gs && ext_sa_addr && test_kvm_facility(vcpu->kvm, 133)
	    && (lc == 11 || lc == 12)) {
		if (write_guest_abs(vcpu, ext_sa_addr + 1024,
				    &vcpu->run->s.regs.gscb, 32))
			mci.gs = 0;
	} else {
		mci.gs = 0;
	}

	/* General interruption information */
	rc |= put_guest_lc(vcpu, 1, (u8 __user *) __LC_AR_MODE_ID);
	rc |= write_guest_lc(vcpu, __LC_MCK_OLD_PSW,
			     &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
	rc |= read_guest_lc(vcpu, __LC_MCK_NEW_PSW,
			    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
	rc |= put_guest_lc(vcpu, mci.val, (u64 __user *) __LC_MCCK_CODE);

	/* Register-save areas */
	if (MACHINE_HAS_VX) {
		convert_vx_to_fp(fprs, (__vector128 *) vcpu->run->s.regs.vrs);
		rc |= write_guest_lc(vcpu, __LC_FPREGS_SAVE_AREA, fprs, 128);
	} else {
		rc |= write_guest_lc(vcpu, __LC_FPREGS_SAVE_AREA,
				     vcpu->run->s.regs.fprs, 128);
	}
	rc |= write_guest_lc(vcpu, __LC_GPREGS_SAVE_AREA,
			     vcpu->run->s.regs.gprs, 128);
	rc |= put_guest_lc(vcpu, current->thread.fpu.fpc,
			   (u32 __user *) __LC_FP_CREG_SAVE_AREA);
	rc |= put_guest_lc(vcpu, vcpu->arch.sie_block->todpr,
			   (u32 __user *) __LC_TOD_PROGREG_SAVE_AREA);
	rc |= put_guest_lc(vcpu, kvm_s390_get_cpu_timer(vcpu),
			   (u64 __user *) __LC_CPU_TIMER_SAVE_AREA);
	rc |= put_guest_lc(vcpu, vcpu->arch.sie_block->ckc >> 8,
			   (u64 __user *) __LC_CLOCK_COMP_SAVE_AREA);
	rc |= write_guest_lc(vcpu, __LC_AREGS_SAVE_AREA,
			     &vcpu->run->s.regs.acrs, 64);
	rc |= write_guest_lc(vcpu, __LC_CREGS_SAVE_AREA,
			     &vcpu->arch.sie_block->gcr, 128);

	/* Extended interruption information */
	rc |= put_guest_lc(vcpu, mchk->ext_damage_code,
			   (u32 __user *) __LC_EXT_DAMAGE_CODE);
	rc |= put_guest_lc(vcpu, mchk->failing_storage_address,
			   (u64 __user *) __LC_MCCK_FAIL_STOR_ADDR);
	rc |= write_guest_lc(vcpu, __LC_PSW_SAVE_AREA, &mchk->fixed_logout,
			     sizeof(mchk->fixed_logout));
	return rc ? -EFAULT : 0;
}