xref: /kernel/linux/linux-5.10/arch/s390/kvm/priv.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * handling privileged instructions
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2008, 2020
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *    Author(s): Carsten Otte <cotte@de.ibm.com>
88c2ecf20Sopenharmony_ci *               Christian Borntraeger <borntraeger@de.ibm.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kvm.h>
128c2ecf20Sopenharmony_ci#include <linux/gfp.h>
138c2ecf20Sopenharmony_ci#include <linux/errno.h>
148c2ecf20Sopenharmony_ci#include <linux/compat.h>
158c2ecf20Sopenharmony_ci#include <linux/mm_types.h>
168c2ecf20Sopenharmony_ci#include <linux/pgtable.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <asm/asm-offsets.h>
198c2ecf20Sopenharmony_ci#include <asm/facility.h>
208c2ecf20Sopenharmony_ci#include <asm/current.h>
218c2ecf20Sopenharmony_ci#include <asm/debug.h>
228c2ecf20Sopenharmony_ci#include <asm/ebcdic.h>
238c2ecf20Sopenharmony_ci#include <asm/sysinfo.h>
248c2ecf20Sopenharmony_ci#include <asm/page-states.h>
258c2ecf20Sopenharmony_ci#include <asm/gmap.h>
268c2ecf20Sopenharmony_ci#include <asm/io.h>
278c2ecf20Sopenharmony_ci#include <asm/ptrace.h>
288c2ecf20Sopenharmony_ci#include <asm/sclp.h>
298c2ecf20Sopenharmony_ci#include <asm/ap.h>
308c2ecf20Sopenharmony_ci#include "gaccess.h"
318c2ecf20Sopenharmony_ci#include "kvm-s390.h"
328c2ecf20Sopenharmony_ci#include "trace.h"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic int handle_ri(struct kvm_vcpu *vcpu)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	vcpu->stat.instruction_ri++;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	if (test_kvm_facility(vcpu->kvm, 64)) {
398c2ecf20Sopenharmony_ci		VCPU_EVENT(vcpu, 3, "%s", "ENABLE: RI (lazy)");
408c2ecf20Sopenharmony_ci		vcpu->arch.sie_block->ecb3 |= ECB3_RI;
418c2ecf20Sopenharmony_ci		kvm_s390_retry_instr(vcpu);
428c2ecf20Sopenharmony_ci		return 0;
438c2ecf20Sopenharmony_ci	} else
448c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ciint kvm_s390_handle_aa(struct kvm_vcpu *vcpu)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	if ((vcpu->arch.sie_block->ipa & 0xf) <= 4)
508c2ecf20Sopenharmony_ci		return handle_ri(vcpu);
518c2ecf20Sopenharmony_ci	else
528c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int handle_gs(struct kvm_vcpu *vcpu)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	vcpu->stat.instruction_gs++;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (test_kvm_facility(vcpu->kvm, 133)) {
608c2ecf20Sopenharmony_ci		VCPU_EVENT(vcpu, 3, "%s", "ENABLE: GS (lazy)");
618c2ecf20Sopenharmony_ci		preempt_disable();
628c2ecf20Sopenharmony_ci		__ctl_set_bit(2, 4);
638c2ecf20Sopenharmony_ci		current->thread.gs_cb = (struct gs_cb *)&vcpu->run->s.regs.gscb;
648c2ecf20Sopenharmony_ci		restore_gs_cb(current->thread.gs_cb);
658c2ecf20Sopenharmony_ci		preempt_enable();
668c2ecf20Sopenharmony_ci		vcpu->arch.sie_block->ecb |= ECB_GS;
678c2ecf20Sopenharmony_ci		vcpu->arch.sie_block->ecd |= ECD_HOSTREGMGMT;
688c2ecf20Sopenharmony_ci		vcpu->arch.gs_enabled = 1;
698c2ecf20Sopenharmony_ci		kvm_s390_retry_instr(vcpu);
708c2ecf20Sopenharmony_ci		return 0;
718c2ecf20Sopenharmony_ci	} else
728c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ciint kvm_s390_handle_e3(struct kvm_vcpu *vcpu)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	int code = vcpu->arch.sie_block->ipb & 0xff;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (code == 0x49 || code == 0x4d)
808c2ecf20Sopenharmony_ci		return handle_gs(vcpu);
818c2ecf20Sopenharmony_ci	else
828c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci/* Handle SCK (SET CLOCK) interception */
858c2ecf20Sopenharmony_cistatic int handle_set_clock(struct kvm_vcpu *vcpu)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct kvm_s390_vm_tod_clock gtod = { 0 };
888c2ecf20Sopenharmony_ci	int rc;
898c2ecf20Sopenharmony_ci	u8 ar;
908c2ecf20Sopenharmony_ci	u64 op2;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	vcpu->stat.instruction_sck++;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
958c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	op2 = kvm_s390_get_base_disp_s(vcpu, &ar);
988c2ecf20Sopenharmony_ci	if (op2 & 7)	/* Operand must be on a doubleword boundary */
998c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
1008c2ecf20Sopenharmony_ci	rc = read_guest(vcpu, op2, ar, &gtod.tod, sizeof(gtod.tod));
1018c2ecf20Sopenharmony_ci	if (rc)
1028c2ecf20Sopenharmony_ci		return kvm_s390_inject_prog_cond(vcpu, rc);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	VCPU_EVENT(vcpu, 3, "SCK: setting guest TOD to 0x%llx", gtod.tod);
1058c2ecf20Sopenharmony_ci	/*
1068c2ecf20Sopenharmony_ci	 * To set the TOD clock the kvm lock must be taken, but the vcpu lock
1078c2ecf20Sopenharmony_ci	 * is already held in handle_set_clock. The usual lock order is the
1088c2ecf20Sopenharmony_ci	 * opposite.  As SCK is deprecated and should not be used in several
1098c2ecf20Sopenharmony_ci	 * cases, for example when the multiple epoch facility or TOD clock
1108c2ecf20Sopenharmony_ci	 * steering facility is installed (see Principles of Operation),  a
1118c2ecf20Sopenharmony_ci	 * slow path can be used.  If the lock can not be taken via try_lock,
1128c2ecf20Sopenharmony_ci	 * the instruction will be retried via -EAGAIN at a later point in
1138c2ecf20Sopenharmony_ci	 * time.
1148c2ecf20Sopenharmony_ci	 */
1158c2ecf20Sopenharmony_ci	if (!kvm_s390_try_set_tod_clock(vcpu->kvm, &gtod)) {
1168c2ecf20Sopenharmony_ci		kvm_s390_retry_instr(vcpu);
1178c2ecf20Sopenharmony_ci		return -EAGAIN;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	kvm_s390_set_psw_cc(vcpu, 0);
1218c2ecf20Sopenharmony_ci	return 0;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int handle_set_prefix(struct kvm_vcpu *vcpu)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	u64 operand2;
1278c2ecf20Sopenharmony_ci	u32 address;
1288c2ecf20Sopenharmony_ci	int rc;
1298c2ecf20Sopenharmony_ci	u8 ar;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	vcpu->stat.instruction_spx++;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
1348c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	operand2 = kvm_s390_get_base_disp_s(vcpu, &ar);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/* must be word boundary */
1398c2ecf20Sopenharmony_ci	if (operand2 & 3)
1408c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* get the value */
1438c2ecf20Sopenharmony_ci	rc = read_guest(vcpu, operand2, ar, &address, sizeof(address));
1448c2ecf20Sopenharmony_ci	if (rc)
1458c2ecf20Sopenharmony_ci		return kvm_s390_inject_prog_cond(vcpu, rc);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	address &= 0x7fffe000u;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/*
1508c2ecf20Sopenharmony_ci	 * Make sure the new value is valid memory. We only need to check the
1518c2ecf20Sopenharmony_ci	 * first page, since address is 8k aligned and memory pieces are always
1528c2ecf20Sopenharmony_ci	 * at least 1MB aligned and have at least a size of 1MB.
1538c2ecf20Sopenharmony_ci	 */
1548c2ecf20Sopenharmony_ci	if (kvm_is_error_gpa(vcpu->kvm, address))
1558c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	kvm_s390_set_prefix(vcpu, address);
1588c2ecf20Sopenharmony_ci	trace_kvm_s390_handle_prefix(vcpu, 1, address);
1598c2ecf20Sopenharmony_ci	return 0;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int handle_store_prefix(struct kvm_vcpu *vcpu)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	u64 operand2;
1658c2ecf20Sopenharmony_ci	u32 address;
1668c2ecf20Sopenharmony_ci	int rc;
1678c2ecf20Sopenharmony_ci	u8 ar;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	vcpu->stat.instruction_stpx++;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
1728c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	operand2 = kvm_s390_get_base_disp_s(vcpu, &ar);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/* must be word boundary */
1778c2ecf20Sopenharmony_ci	if (operand2 & 3)
1788c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	address = kvm_s390_get_prefix(vcpu);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	/* get the value */
1838c2ecf20Sopenharmony_ci	rc = write_guest(vcpu, operand2, ar, &address, sizeof(address));
1848c2ecf20Sopenharmony_ci	if (rc)
1858c2ecf20Sopenharmony_ci		return kvm_s390_inject_prog_cond(vcpu, rc);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	VCPU_EVENT(vcpu, 3, "STPX: storing prefix 0x%x into 0x%llx", address, operand2);
1888c2ecf20Sopenharmony_ci	trace_kvm_s390_handle_prefix(vcpu, 0, address);
1898c2ecf20Sopenharmony_ci	return 0;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int handle_store_cpu_address(struct kvm_vcpu *vcpu)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	u16 vcpu_id = vcpu->vcpu_id;
1958c2ecf20Sopenharmony_ci	u64 ga;
1968c2ecf20Sopenharmony_ci	int rc;
1978c2ecf20Sopenharmony_ci	u8 ar;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	vcpu->stat.instruction_stap++;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
2028c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	ga = kvm_s390_get_base_disp_s(vcpu, &ar);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (ga & 1)
2078c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	rc = write_guest(vcpu, ga, ar, &vcpu_id, sizeof(vcpu_id));
2108c2ecf20Sopenharmony_ci	if (rc)
2118c2ecf20Sopenharmony_ci		return kvm_s390_inject_prog_cond(vcpu, rc);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	VCPU_EVENT(vcpu, 3, "STAP: storing cpu address (%u) to 0x%llx", vcpu_id, ga);
2148c2ecf20Sopenharmony_ci	trace_kvm_s390_handle_stap(vcpu, ga);
2158c2ecf20Sopenharmony_ci	return 0;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ciint kvm_s390_skey_check_enable(struct kvm_vcpu *vcpu)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	int rc;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	trace_kvm_s390_skey_related_inst(vcpu);
2238c2ecf20Sopenharmony_ci	/* Already enabled? */
2248c2ecf20Sopenharmony_ci	if (vcpu->arch.skey_enabled)
2258c2ecf20Sopenharmony_ci		return 0;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	rc = s390_enable_skey();
2288c2ecf20Sopenharmony_ci	VCPU_EVENT(vcpu, 3, "enabling storage keys for guest: %d", rc);
2298c2ecf20Sopenharmony_ci	if (rc)
2308c2ecf20Sopenharmony_ci		return rc;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (kvm_s390_test_cpuflags(vcpu, CPUSTAT_KSS))
2338c2ecf20Sopenharmony_ci		kvm_s390_clear_cpuflags(vcpu, CPUSTAT_KSS);
2348c2ecf20Sopenharmony_ci	if (!vcpu->kvm->arch.use_skf)
2358c2ecf20Sopenharmony_ci		vcpu->arch.sie_block->ictl |= ICTL_ISKE | ICTL_SSKE | ICTL_RRBE;
2368c2ecf20Sopenharmony_ci	else
2378c2ecf20Sopenharmony_ci		vcpu->arch.sie_block->ictl &= ~(ICTL_ISKE | ICTL_SSKE | ICTL_RRBE);
2388c2ecf20Sopenharmony_ci	vcpu->arch.skey_enabled = true;
2398c2ecf20Sopenharmony_ci	return 0;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic int try_handle_skey(struct kvm_vcpu *vcpu)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	int rc;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	rc = kvm_s390_skey_check_enable(vcpu);
2478c2ecf20Sopenharmony_ci	if (rc)
2488c2ecf20Sopenharmony_ci		return rc;
2498c2ecf20Sopenharmony_ci	if (vcpu->kvm->arch.use_skf) {
2508c2ecf20Sopenharmony_ci		/* with storage-key facility, SIE interprets it for us */
2518c2ecf20Sopenharmony_ci		kvm_s390_retry_instr(vcpu);
2528c2ecf20Sopenharmony_ci		VCPU_EVENT(vcpu, 4, "%s", "retrying storage key operation");
2538c2ecf20Sopenharmony_ci		return -EAGAIN;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci	return 0;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic int handle_iske(struct kvm_vcpu *vcpu)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	unsigned long gaddr, vmaddr;
2618c2ecf20Sopenharmony_ci	unsigned char key;
2628c2ecf20Sopenharmony_ci	int reg1, reg2;
2638c2ecf20Sopenharmony_ci	bool unlocked;
2648c2ecf20Sopenharmony_ci	int rc;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	vcpu->stat.instruction_iske++;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
2698c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	rc = try_handle_skey(vcpu);
2728c2ecf20Sopenharmony_ci	if (rc)
2738c2ecf20Sopenharmony_ci		return rc != -EAGAIN ? rc : 0;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	gaddr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
2788c2ecf20Sopenharmony_ci	gaddr = kvm_s390_logical_to_effective(vcpu, gaddr);
2798c2ecf20Sopenharmony_ci	gaddr = kvm_s390_real_to_abs(vcpu, gaddr);
2808c2ecf20Sopenharmony_ci	vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(gaddr));
2818c2ecf20Sopenharmony_ci	if (kvm_is_error_hva(vmaddr))
2828c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
2838c2ecf20Sopenharmony_ciretry:
2848c2ecf20Sopenharmony_ci	unlocked = false;
2858c2ecf20Sopenharmony_ci	mmap_read_lock(current->mm);
2868c2ecf20Sopenharmony_ci	rc = get_guest_storage_key(current->mm, vmaddr, &key);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	if (rc) {
2898c2ecf20Sopenharmony_ci		rc = fixup_user_fault(current->mm, vmaddr,
2908c2ecf20Sopenharmony_ci				      FAULT_FLAG_WRITE, &unlocked);
2918c2ecf20Sopenharmony_ci		if (!rc) {
2928c2ecf20Sopenharmony_ci			mmap_read_unlock(current->mm);
2938c2ecf20Sopenharmony_ci			goto retry;
2948c2ecf20Sopenharmony_ci		}
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci	mmap_read_unlock(current->mm);
2978c2ecf20Sopenharmony_ci	if (rc == -EFAULT)
2988c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
2998c2ecf20Sopenharmony_ci	if (rc < 0)
3008c2ecf20Sopenharmony_ci		return rc;
3018c2ecf20Sopenharmony_ci	vcpu->run->s.regs.gprs[reg1] &= ~0xff;
3028c2ecf20Sopenharmony_ci	vcpu->run->s.regs.gprs[reg1] |= key;
3038c2ecf20Sopenharmony_ci	return 0;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic int handle_rrbe(struct kvm_vcpu *vcpu)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	unsigned long vmaddr, gaddr;
3098c2ecf20Sopenharmony_ci	int reg1, reg2;
3108c2ecf20Sopenharmony_ci	bool unlocked;
3118c2ecf20Sopenharmony_ci	int rc;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	vcpu->stat.instruction_rrbe++;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
3168c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	rc = try_handle_skey(vcpu);
3198c2ecf20Sopenharmony_ci	if (rc)
3208c2ecf20Sopenharmony_ci		return rc != -EAGAIN ? rc : 0;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	gaddr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
3258c2ecf20Sopenharmony_ci	gaddr = kvm_s390_logical_to_effective(vcpu, gaddr);
3268c2ecf20Sopenharmony_ci	gaddr = kvm_s390_real_to_abs(vcpu, gaddr);
3278c2ecf20Sopenharmony_ci	vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(gaddr));
3288c2ecf20Sopenharmony_ci	if (kvm_is_error_hva(vmaddr))
3298c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
3308c2ecf20Sopenharmony_ciretry:
3318c2ecf20Sopenharmony_ci	unlocked = false;
3328c2ecf20Sopenharmony_ci	mmap_read_lock(current->mm);
3338c2ecf20Sopenharmony_ci	rc = reset_guest_reference_bit(current->mm, vmaddr);
3348c2ecf20Sopenharmony_ci	if (rc < 0) {
3358c2ecf20Sopenharmony_ci		rc = fixup_user_fault(current->mm, vmaddr,
3368c2ecf20Sopenharmony_ci				      FAULT_FLAG_WRITE, &unlocked);
3378c2ecf20Sopenharmony_ci		if (!rc) {
3388c2ecf20Sopenharmony_ci			mmap_read_unlock(current->mm);
3398c2ecf20Sopenharmony_ci			goto retry;
3408c2ecf20Sopenharmony_ci		}
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci	mmap_read_unlock(current->mm);
3438c2ecf20Sopenharmony_ci	if (rc == -EFAULT)
3448c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
3458c2ecf20Sopenharmony_ci	if (rc < 0)
3468c2ecf20Sopenharmony_ci		return rc;
3478c2ecf20Sopenharmony_ci	kvm_s390_set_psw_cc(vcpu, rc);
3488c2ecf20Sopenharmony_ci	return 0;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci#define SSKE_NQ 0x8
3528c2ecf20Sopenharmony_ci#define SSKE_MR 0x4
3538c2ecf20Sopenharmony_ci#define SSKE_MC 0x2
3548c2ecf20Sopenharmony_ci#define SSKE_MB 0x1
3558c2ecf20Sopenharmony_cistatic int handle_sske(struct kvm_vcpu *vcpu)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	unsigned char m3 = vcpu->arch.sie_block->ipb >> 28;
3588c2ecf20Sopenharmony_ci	unsigned long start, end;
3598c2ecf20Sopenharmony_ci	unsigned char key, oldkey;
3608c2ecf20Sopenharmony_ci	int reg1, reg2;
3618c2ecf20Sopenharmony_ci	bool unlocked;
3628c2ecf20Sopenharmony_ci	int rc;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	vcpu->stat.instruction_sske++;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
3678c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	rc = try_handle_skey(vcpu);
3708c2ecf20Sopenharmony_ci	if (rc)
3718c2ecf20Sopenharmony_ci		return rc != -EAGAIN ? rc : 0;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	if (!test_kvm_facility(vcpu->kvm, 8))
3748c2ecf20Sopenharmony_ci		m3 &= ~SSKE_MB;
3758c2ecf20Sopenharmony_ci	if (!test_kvm_facility(vcpu->kvm, 10))
3768c2ecf20Sopenharmony_ci		m3 &= ~(SSKE_MC | SSKE_MR);
3778c2ecf20Sopenharmony_ci	if (!test_kvm_facility(vcpu->kvm, 14))
3788c2ecf20Sopenharmony_ci		m3 &= ~SSKE_NQ;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	key = vcpu->run->s.regs.gprs[reg1] & 0xfe;
3838c2ecf20Sopenharmony_ci	start = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
3848c2ecf20Sopenharmony_ci	start = kvm_s390_logical_to_effective(vcpu, start);
3858c2ecf20Sopenharmony_ci	if (m3 & SSKE_MB) {
3868c2ecf20Sopenharmony_ci		/* start already designates an absolute address */
3878c2ecf20Sopenharmony_ci		end = (start + _SEGMENT_SIZE) & ~(_SEGMENT_SIZE - 1);
3888c2ecf20Sopenharmony_ci	} else {
3898c2ecf20Sopenharmony_ci		start = kvm_s390_real_to_abs(vcpu, start);
3908c2ecf20Sopenharmony_ci		end = start + PAGE_SIZE;
3918c2ecf20Sopenharmony_ci	}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	while (start != end) {
3948c2ecf20Sopenharmony_ci		unsigned long vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(start));
3958c2ecf20Sopenharmony_ci		unlocked = false;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci		if (kvm_is_error_hva(vmaddr))
3988c2ecf20Sopenharmony_ci			return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci		mmap_read_lock(current->mm);
4018c2ecf20Sopenharmony_ci		rc = cond_set_guest_storage_key(current->mm, vmaddr, key, &oldkey,
4028c2ecf20Sopenharmony_ci						m3 & SSKE_NQ, m3 & SSKE_MR,
4038c2ecf20Sopenharmony_ci						m3 & SSKE_MC);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		if (rc < 0) {
4068c2ecf20Sopenharmony_ci			rc = fixup_user_fault(current->mm, vmaddr,
4078c2ecf20Sopenharmony_ci					      FAULT_FLAG_WRITE, &unlocked);
4088c2ecf20Sopenharmony_ci			rc = !rc ? -EAGAIN : rc;
4098c2ecf20Sopenharmony_ci		}
4108c2ecf20Sopenharmony_ci		mmap_read_unlock(current->mm);
4118c2ecf20Sopenharmony_ci		if (rc == -EFAULT)
4128c2ecf20Sopenharmony_ci			return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
4138c2ecf20Sopenharmony_ci		if (rc == -EAGAIN)
4148c2ecf20Sopenharmony_ci			continue;
4158c2ecf20Sopenharmony_ci		if (rc < 0)
4168c2ecf20Sopenharmony_ci			return rc;
4178c2ecf20Sopenharmony_ci		start += PAGE_SIZE;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (m3 & (SSKE_MC | SSKE_MR)) {
4218c2ecf20Sopenharmony_ci		if (m3 & SSKE_MB) {
4228c2ecf20Sopenharmony_ci			/* skey in reg1 is unpredictable */
4238c2ecf20Sopenharmony_ci			kvm_s390_set_psw_cc(vcpu, 3);
4248c2ecf20Sopenharmony_ci		} else {
4258c2ecf20Sopenharmony_ci			kvm_s390_set_psw_cc(vcpu, rc);
4268c2ecf20Sopenharmony_ci			vcpu->run->s.regs.gprs[reg1] &= ~0xff00UL;
4278c2ecf20Sopenharmony_ci			vcpu->run->s.regs.gprs[reg1] |= (u64) oldkey << 8;
4288c2ecf20Sopenharmony_ci		}
4298c2ecf20Sopenharmony_ci	}
4308c2ecf20Sopenharmony_ci	if (m3 & SSKE_MB) {
4318c2ecf20Sopenharmony_ci		if (psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_64BIT)
4328c2ecf20Sopenharmony_ci			vcpu->run->s.regs.gprs[reg2] &= ~PAGE_MASK;
4338c2ecf20Sopenharmony_ci		else
4348c2ecf20Sopenharmony_ci			vcpu->run->s.regs.gprs[reg2] &= ~0xfffff000UL;
4358c2ecf20Sopenharmony_ci		end = kvm_s390_logical_to_effective(vcpu, end);
4368c2ecf20Sopenharmony_ci		vcpu->run->s.regs.gprs[reg2] |= end;
4378c2ecf20Sopenharmony_ci	}
4388c2ecf20Sopenharmony_ci	return 0;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cistatic int handle_ipte_interlock(struct kvm_vcpu *vcpu)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	vcpu->stat.instruction_ipte_interlock++;
4448c2ecf20Sopenharmony_ci	if (psw_bits(vcpu->arch.sie_block->gpsw).pstate)
4458c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
4468c2ecf20Sopenharmony_ci	wait_event(vcpu->kvm->arch.ipte_wq, !ipte_lock_held(vcpu));
4478c2ecf20Sopenharmony_ci	kvm_s390_retry_instr(vcpu);
4488c2ecf20Sopenharmony_ci	VCPU_EVENT(vcpu, 4, "%s", "retrying ipte interlock operation");
4498c2ecf20Sopenharmony_ci	return 0;
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic int handle_test_block(struct kvm_vcpu *vcpu)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	gpa_t addr;
4558c2ecf20Sopenharmony_ci	int reg2;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	vcpu->stat.instruction_tb++;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
4608c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	kvm_s390_get_regs_rre(vcpu, NULL, &reg2);
4638c2ecf20Sopenharmony_ci	addr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
4648c2ecf20Sopenharmony_ci	addr = kvm_s390_logical_to_effective(vcpu, addr);
4658c2ecf20Sopenharmony_ci	if (kvm_s390_check_low_addr_prot_real(vcpu, addr))
4668c2ecf20Sopenharmony_ci		return kvm_s390_inject_prog_irq(vcpu, &vcpu->arch.pgm);
4678c2ecf20Sopenharmony_ci	addr = kvm_s390_real_to_abs(vcpu, addr);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	if (kvm_is_error_gpa(vcpu->kvm, addr))
4708c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
4718c2ecf20Sopenharmony_ci	/*
4728c2ecf20Sopenharmony_ci	 * We don't expect errors on modern systems, and do not care
4738c2ecf20Sopenharmony_ci	 * about storage keys (yet), so let's just clear the page.
4748c2ecf20Sopenharmony_ci	 */
4758c2ecf20Sopenharmony_ci	if (kvm_clear_guest(vcpu->kvm, addr, PAGE_SIZE))
4768c2ecf20Sopenharmony_ci		return -EFAULT;
4778c2ecf20Sopenharmony_ci	kvm_s390_set_psw_cc(vcpu, 0);
4788c2ecf20Sopenharmony_ci	vcpu->run->s.regs.gprs[0] = 0;
4798c2ecf20Sopenharmony_ci	return 0;
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic int handle_tpi(struct kvm_vcpu *vcpu)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	struct kvm_s390_interrupt_info *inti;
4858c2ecf20Sopenharmony_ci	unsigned long len;
4868c2ecf20Sopenharmony_ci	u32 tpi_data[3];
4878c2ecf20Sopenharmony_ci	int rc;
4888c2ecf20Sopenharmony_ci	u64 addr;
4898c2ecf20Sopenharmony_ci	u8 ar;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	vcpu->stat.instruction_tpi++;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	addr = kvm_s390_get_base_disp_s(vcpu, &ar);
4948c2ecf20Sopenharmony_ci	if (addr & 3)
4958c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	inti = kvm_s390_get_io_int(vcpu->kvm, vcpu->arch.sie_block->gcr[6], 0);
4988c2ecf20Sopenharmony_ci	if (!inti) {
4998c2ecf20Sopenharmony_ci		kvm_s390_set_psw_cc(vcpu, 0);
5008c2ecf20Sopenharmony_ci		return 0;
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	tpi_data[0] = inti->io.subchannel_id << 16 | inti->io.subchannel_nr;
5048c2ecf20Sopenharmony_ci	tpi_data[1] = inti->io.io_int_parm;
5058c2ecf20Sopenharmony_ci	tpi_data[2] = inti->io.io_int_word;
5068c2ecf20Sopenharmony_ci	if (addr) {
5078c2ecf20Sopenharmony_ci		/*
5088c2ecf20Sopenharmony_ci		 * Store the two-word I/O interruption code into the
5098c2ecf20Sopenharmony_ci		 * provided area.
5108c2ecf20Sopenharmony_ci		 */
5118c2ecf20Sopenharmony_ci		len = sizeof(tpi_data) - 4;
5128c2ecf20Sopenharmony_ci		rc = write_guest(vcpu, addr, ar, &tpi_data, len);
5138c2ecf20Sopenharmony_ci		if (rc) {
5148c2ecf20Sopenharmony_ci			rc = kvm_s390_inject_prog_cond(vcpu, rc);
5158c2ecf20Sopenharmony_ci			goto reinject_interrupt;
5168c2ecf20Sopenharmony_ci		}
5178c2ecf20Sopenharmony_ci	} else {
5188c2ecf20Sopenharmony_ci		/*
5198c2ecf20Sopenharmony_ci		 * Store the three-word I/O interruption code into
5208c2ecf20Sopenharmony_ci		 * the appropriate lowcore area.
5218c2ecf20Sopenharmony_ci		 */
5228c2ecf20Sopenharmony_ci		len = sizeof(tpi_data);
5238c2ecf20Sopenharmony_ci		if (write_guest_lc(vcpu, __LC_SUBCHANNEL_ID, &tpi_data, len)) {
5248c2ecf20Sopenharmony_ci			/* failed writes to the low core are not recoverable */
5258c2ecf20Sopenharmony_ci			rc = -EFAULT;
5268c2ecf20Sopenharmony_ci			goto reinject_interrupt;
5278c2ecf20Sopenharmony_ci		}
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	/* irq was successfully handed to the guest */
5318c2ecf20Sopenharmony_ci	kfree(inti);
5328c2ecf20Sopenharmony_ci	kvm_s390_set_psw_cc(vcpu, 1);
5338c2ecf20Sopenharmony_ci	return 0;
5348c2ecf20Sopenharmony_cireinject_interrupt:
5358c2ecf20Sopenharmony_ci	/*
5368c2ecf20Sopenharmony_ci	 * If we encounter a problem storing the interruption code, the
5378c2ecf20Sopenharmony_ci	 * instruction is suppressed from the guest's view: reinject the
5388c2ecf20Sopenharmony_ci	 * interrupt.
5398c2ecf20Sopenharmony_ci	 */
5408c2ecf20Sopenharmony_ci	if (kvm_s390_reinject_io_int(vcpu->kvm, inti)) {
5418c2ecf20Sopenharmony_ci		kfree(inti);
5428c2ecf20Sopenharmony_ci		rc = -EFAULT;
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci	/* don't set the cc, a pgm irq was injected or we drop to user space */
5458c2ecf20Sopenharmony_ci	return rc ? -EFAULT : 0;
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_cistatic int handle_tsch(struct kvm_vcpu *vcpu)
5498c2ecf20Sopenharmony_ci{
5508c2ecf20Sopenharmony_ci	struct kvm_s390_interrupt_info *inti = NULL;
5518c2ecf20Sopenharmony_ci	const u64 isc_mask = 0xffUL << 24; /* all iscs set */
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	vcpu->stat.instruction_tsch++;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	/* a valid schid has at least one bit set */
5568c2ecf20Sopenharmony_ci	if (vcpu->run->s.regs.gprs[1])
5578c2ecf20Sopenharmony_ci		inti = kvm_s390_get_io_int(vcpu->kvm, isc_mask,
5588c2ecf20Sopenharmony_ci					   vcpu->run->s.regs.gprs[1]);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	/*
5618c2ecf20Sopenharmony_ci	 * Prepare exit to userspace.
5628c2ecf20Sopenharmony_ci	 * We indicate whether we dequeued a pending I/O interrupt
5638c2ecf20Sopenharmony_ci	 * so that userspace can re-inject it if the instruction gets
5648c2ecf20Sopenharmony_ci	 * a program check. While this may re-order the pending I/O
5658c2ecf20Sopenharmony_ci	 * interrupts, this is no problem since the priority is kept
5668c2ecf20Sopenharmony_ci	 * intact.
5678c2ecf20Sopenharmony_ci	 */
5688c2ecf20Sopenharmony_ci	vcpu->run->exit_reason = KVM_EXIT_S390_TSCH;
5698c2ecf20Sopenharmony_ci	vcpu->run->s390_tsch.dequeued = !!inti;
5708c2ecf20Sopenharmony_ci	if (inti) {
5718c2ecf20Sopenharmony_ci		vcpu->run->s390_tsch.subchannel_id = inti->io.subchannel_id;
5728c2ecf20Sopenharmony_ci		vcpu->run->s390_tsch.subchannel_nr = inti->io.subchannel_nr;
5738c2ecf20Sopenharmony_ci		vcpu->run->s390_tsch.io_int_parm = inti->io.io_int_parm;
5748c2ecf20Sopenharmony_ci		vcpu->run->s390_tsch.io_int_word = inti->io.io_int_word;
5758c2ecf20Sopenharmony_ci	}
5768c2ecf20Sopenharmony_ci	vcpu->run->s390_tsch.ipb = vcpu->arch.sie_block->ipb;
5778c2ecf20Sopenharmony_ci	kfree(inti);
5788c2ecf20Sopenharmony_ci	return -EREMOTE;
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cistatic int handle_io_inst(struct kvm_vcpu *vcpu)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	VCPU_EVENT(vcpu, 4, "%s", "I/O instruction");
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
5868c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	if (vcpu->kvm->arch.css_support) {
5898c2ecf20Sopenharmony_ci		/*
5908c2ecf20Sopenharmony_ci		 * Most I/O instructions will be handled by userspace.
5918c2ecf20Sopenharmony_ci		 * Exceptions are tpi and the interrupt portion of tsch.
5928c2ecf20Sopenharmony_ci		 */
5938c2ecf20Sopenharmony_ci		if (vcpu->arch.sie_block->ipa == 0xb236)
5948c2ecf20Sopenharmony_ci			return handle_tpi(vcpu);
5958c2ecf20Sopenharmony_ci		if (vcpu->arch.sie_block->ipa == 0xb235)
5968c2ecf20Sopenharmony_ci			return handle_tsch(vcpu);
5978c2ecf20Sopenharmony_ci		/* Handle in userspace. */
5988c2ecf20Sopenharmony_ci		vcpu->stat.instruction_io_other++;
5998c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
6008c2ecf20Sopenharmony_ci	} else {
6018c2ecf20Sopenharmony_ci		/*
6028c2ecf20Sopenharmony_ci		 * Set condition code 3 to stop the guest from issuing channel
6038c2ecf20Sopenharmony_ci		 * I/O instructions.
6048c2ecf20Sopenharmony_ci		 */
6058c2ecf20Sopenharmony_ci		kvm_s390_set_psw_cc(vcpu, 3);
6068c2ecf20Sopenharmony_ci		return 0;
6078c2ecf20Sopenharmony_ci	}
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci/*
6118c2ecf20Sopenharmony_ci * handle_pqap: Handling pqap interception
6128c2ecf20Sopenharmony_ci * @vcpu: the vcpu having issue the pqap instruction
6138c2ecf20Sopenharmony_ci *
6148c2ecf20Sopenharmony_ci * We now support PQAP/AQIC instructions and we need to correctly
6158c2ecf20Sopenharmony_ci * answer the guest even if no dedicated driver's hook is available.
6168c2ecf20Sopenharmony_ci *
6178c2ecf20Sopenharmony_ci * The intercepting code calls a dedicated callback for this instruction
6188c2ecf20Sopenharmony_ci * if a driver did register one in the CRYPTO satellite of the
6198c2ecf20Sopenharmony_ci * SIE block.
6208c2ecf20Sopenharmony_ci *
6218c2ecf20Sopenharmony_ci * If no callback is available, the queues are not available, return this
6228c2ecf20Sopenharmony_ci * response code to the caller and set CC to 3.
6238c2ecf20Sopenharmony_ci * Else return the response code returned by the callback.
6248c2ecf20Sopenharmony_ci */
6258c2ecf20Sopenharmony_cistatic int handle_pqap(struct kvm_vcpu *vcpu)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	struct ap_queue_status status = {};
6288c2ecf20Sopenharmony_ci	unsigned long reg0;
6298c2ecf20Sopenharmony_ci	int ret;
6308c2ecf20Sopenharmony_ci	uint8_t fc;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	/* Verify that the AP instruction are available */
6338c2ecf20Sopenharmony_ci	if (!ap_instructions_available())
6348c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
6358c2ecf20Sopenharmony_ci	/* Verify that the guest is allowed to use AP instructions */
6368c2ecf20Sopenharmony_ci	if (!(vcpu->arch.sie_block->eca & ECA_APIE))
6378c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
6388c2ecf20Sopenharmony_ci	/*
6398c2ecf20Sopenharmony_ci	 * The only possibly intercepted functions when AP instructions are
6408c2ecf20Sopenharmony_ci	 * available for the guest are AQIC and TAPQ with the t bit set
6418c2ecf20Sopenharmony_ci	 * since we do not set IC.3 (FIII) we currently will only intercept
6428c2ecf20Sopenharmony_ci	 * the AQIC function code.
6438c2ecf20Sopenharmony_ci	 * Note: running nested under z/VM can result in intercepts for other
6448c2ecf20Sopenharmony_ci	 * function codes, e.g. PQAP(QCI). We do not support this and bail out.
6458c2ecf20Sopenharmony_ci	 */
6468c2ecf20Sopenharmony_ci	reg0 = vcpu->run->s.regs.gprs[0];
6478c2ecf20Sopenharmony_ci	fc = (reg0 >> 24) & 0xff;
6488c2ecf20Sopenharmony_ci	if (fc != 0x03)
6498c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	/* PQAP instruction is allowed for guest kernel only */
6528c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
6538c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	/* Common PQAP instruction specification exceptions */
6568c2ecf20Sopenharmony_ci	/* bits 41-47 must all be zeros */
6578c2ecf20Sopenharmony_ci	if (reg0 & 0x007f0000UL)
6588c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
6598c2ecf20Sopenharmony_ci	/* APFT not install and T bit set */
6608c2ecf20Sopenharmony_ci	if (!test_kvm_facility(vcpu->kvm, 15) && (reg0 & 0x00800000UL))
6618c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
6628c2ecf20Sopenharmony_ci	/* APXA not installed and APID greater 64 or APQI greater 16 */
6638c2ecf20Sopenharmony_ci	if (!(vcpu->kvm->arch.crypto.crycbd & 0x02) && (reg0 & 0x0000c0f0UL))
6648c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	/* AQIC function code specific exception */
6678c2ecf20Sopenharmony_ci	/* facility 65 not present for AQIC function code */
6688c2ecf20Sopenharmony_ci	if (!test_kvm_facility(vcpu->kvm, 65))
6698c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	/*
6728c2ecf20Sopenharmony_ci	 * Verify that the hook callback is registered, lock the owner
6738c2ecf20Sopenharmony_ci	 * and call the hook.
6748c2ecf20Sopenharmony_ci	 */
6758c2ecf20Sopenharmony_ci	if (vcpu->kvm->arch.crypto.pqap_hook) {
6768c2ecf20Sopenharmony_ci		if (!try_module_get(vcpu->kvm->arch.crypto.pqap_hook->owner))
6778c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
6788c2ecf20Sopenharmony_ci		ret = vcpu->kvm->arch.crypto.pqap_hook->hook(vcpu);
6798c2ecf20Sopenharmony_ci		module_put(vcpu->kvm->arch.crypto.pqap_hook->owner);
6808c2ecf20Sopenharmony_ci		if (!ret && vcpu->run->s.regs.gprs[1] & 0x00ff0000)
6818c2ecf20Sopenharmony_ci			kvm_s390_set_psw_cc(vcpu, 3);
6828c2ecf20Sopenharmony_ci		return ret;
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci	/*
6858c2ecf20Sopenharmony_ci	 * A vfio_driver must register a hook.
6868c2ecf20Sopenharmony_ci	 * No hook means no driver to enable the SIE CRYCB and no queues.
6878c2ecf20Sopenharmony_ci	 * We send this response to the guest.
6888c2ecf20Sopenharmony_ci	 */
6898c2ecf20Sopenharmony_ci	status.response_code = 0x01;
6908c2ecf20Sopenharmony_ci	memcpy(&vcpu->run->s.regs.gprs[1], &status, sizeof(status));
6918c2ecf20Sopenharmony_ci	kvm_s390_set_psw_cc(vcpu, 3);
6928c2ecf20Sopenharmony_ci	return 0;
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic int handle_stfl(struct kvm_vcpu *vcpu)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	int rc;
6988c2ecf20Sopenharmony_ci	unsigned int fac;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	vcpu->stat.instruction_stfl++;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
7038c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	/*
7068c2ecf20Sopenharmony_ci	 * We need to shift the lower 32 facility bits (bit 0-31) from a u64
7078c2ecf20Sopenharmony_ci	 * into a u32 memory representation. They will remain bits 0-31.
7088c2ecf20Sopenharmony_ci	 */
7098c2ecf20Sopenharmony_ci	fac = *vcpu->kvm->arch.model.fac_list >> 32;
7108c2ecf20Sopenharmony_ci	rc = write_guest_lc(vcpu, offsetof(struct lowcore, stfl_fac_list),
7118c2ecf20Sopenharmony_ci			    &fac, sizeof(fac));
7128c2ecf20Sopenharmony_ci	if (rc)
7138c2ecf20Sopenharmony_ci		return rc;
7148c2ecf20Sopenharmony_ci	VCPU_EVENT(vcpu, 3, "STFL: store facility list 0x%x", fac);
7158c2ecf20Sopenharmony_ci	trace_kvm_s390_handle_stfl(vcpu, fac);
7168c2ecf20Sopenharmony_ci	return 0;
7178c2ecf20Sopenharmony_ci}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci#define PSW_MASK_ADDR_MODE (PSW_MASK_EA | PSW_MASK_BA)
7208c2ecf20Sopenharmony_ci#define PSW_MASK_UNASSIGNED 0xb80800fe7fffffffUL
7218c2ecf20Sopenharmony_ci#define PSW_ADDR_24 0x0000000000ffffffUL
7228c2ecf20Sopenharmony_ci#define PSW_ADDR_31 0x000000007fffffffUL
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ciint is_valid_psw(psw_t *psw)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	if (psw->mask & PSW_MASK_UNASSIGNED)
7278c2ecf20Sopenharmony_ci		return 0;
7288c2ecf20Sopenharmony_ci	if ((psw->mask & PSW_MASK_ADDR_MODE) == PSW_MASK_BA) {
7298c2ecf20Sopenharmony_ci		if (psw->addr & ~PSW_ADDR_31)
7308c2ecf20Sopenharmony_ci			return 0;
7318c2ecf20Sopenharmony_ci	}
7328c2ecf20Sopenharmony_ci	if (!(psw->mask & PSW_MASK_ADDR_MODE) && (psw->addr & ~PSW_ADDR_24))
7338c2ecf20Sopenharmony_ci		return 0;
7348c2ecf20Sopenharmony_ci	if ((psw->mask & PSW_MASK_ADDR_MODE) ==  PSW_MASK_EA)
7358c2ecf20Sopenharmony_ci		return 0;
7368c2ecf20Sopenharmony_ci	if (psw->addr & 1)
7378c2ecf20Sopenharmony_ci		return 0;
7388c2ecf20Sopenharmony_ci	return 1;
7398c2ecf20Sopenharmony_ci}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ciint kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu)
7428c2ecf20Sopenharmony_ci{
7438c2ecf20Sopenharmony_ci	psw_t *gpsw = &vcpu->arch.sie_block->gpsw;
7448c2ecf20Sopenharmony_ci	psw_compat_t new_psw;
7458c2ecf20Sopenharmony_ci	u64 addr;
7468c2ecf20Sopenharmony_ci	int rc;
7478c2ecf20Sopenharmony_ci	u8 ar;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	vcpu->stat.instruction_lpsw++;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	if (gpsw->mask & PSW_MASK_PSTATE)
7528c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	addr = kvm_s390_get_base_disp_s(vcpu, &ar);
7558c2ecf20Sopenharmony_ci	if (addr & 7)
7568c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	rc = read_guest(vcpu, addr, ar, &new_psw, sizeof(new_psw));
7598c2ecf20Sopenharmony_ci	if (rc)
7608c2ecf20Sopenharmony_ci		return kvm_s390_inject_prog_cond(vcpu, rc);
7618c2ecf20Sopenharmony_ci	if (!(new_psw.mask & PSW32_MASK_BASE))
7628c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
7638c2ecf20Sopenharmony_ci	gpsw->mask = (new_psw.mask & ~PSW32_MASK_BASE) << 32;
7648c2ecf20Sopenharmony_ci	gpsw->mask |= new_psw.addr & PSW32_ADDR_AMODE;
7658c2ecf20Sopenharmony_ci	gpsw->addr = new_psw.addr & ~PSW32_ADDR_AMODE;
7668c2ecf20Sopenharmony_ci	if (!is_valid_psw(gpsw))
7678c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
7688c2ecf20Sopenharmony_ci	return 0;
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_cistatic int handle_lpswe(struct kvm_vcpu *vcpu)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	psw_t new_psw;
7748c2ecf20Sopenharmony_ci	u64 addr;
7758c2ecf20Sopenharmony_ci	int rc;
7768c2ecf20Sopenharmony_ci	u8 ar;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	vcpu->stat.instruction_lpswe++;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
7818c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	addr = kvm_s390_get_base_disp_s(vcpu, &ar);
7848c2ecf20Sopenharmony_ci	if (addr & 7)
7858c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
7868c2ecf20Sopenharmony_ci	rc = read_guest(vcpu, addr, ar, &new_psw, sizeof(new_psw));
7878c2ecf20Sopenharmony_ci	if (rc)
7888c2ecf20Sopenharmony_ci		return kvm_s390_inject_prog_cond(vcpu, rc);
7898c2ecf20Sopenharmony_ci	vcpu->arch.sie_block->gpsw = new_psw;
7908c2ecf20Sopenharmony_ci	if (!is_valid_psw(&vcpu->arch.sie_block->gpsw))
7918c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
7928c2ecf20Sopenharmony_ci	return 0;
7938c2ecf20Sopenharmony_ci}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_cistatic int handle_stidp(struct kvm_vcpu *vcpu)
7968c2ecf20Sopenharmony_ci{
7978c2ecf20Sopenharmony_ci	u64 stidp_data = vcpu->kvm->arch.model.cpuid;
7988c2ecf20Sopenharmony_ci	u64 operand2;
7998c2ecf20Sopenharmony_ci	int rc;
8008c2ecf20Sopenharmony_ci	u8 ar;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	vcpu->stat.instruction_stidp++;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
8058c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	operand2 = kvm_s390_get_base_disp_s(vcpu, &ar);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	if (operand2 & 7)
8108c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	rc = write_guest(vcpu, operand2, ar, &stidp_data, sizeof(stidp_data));
8138c2ecf20Sopenharmony_ci	if (rc)
8148c2ecf20Sopenharmony_ci		return kvm_s390_inject_prog_cond(vcpu, rc);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	VCPU_EVENT(vcpu, 3, "STIDP: store cpu id 0x%llx", stidp_data);
8178c2ecf20Sopenharmony_ci	return 0;
8188c2ecf20Sopenharmony_ci}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_cistatic void handle_stsi_3_2_2(struct kvm_vcpu *vcpu, struct sysinfo_3_2_2 *mem)
8218c2ecf20Sopenharmony_ci{
8228c2ecf20Sopenharmony_ci	int cpus = 0;
8238c2ecf20Sopenharmony_ci	int n;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	cpus = atomic_read(&vcpu->kvm->online_vcpus);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	/* deal with other level 3 hypervisors */
8288c2ecf20Sopenharmony_ci	if (stsi(mem, 3, 2, 2))
8298c2ecf20Sopenharmony_ci		mem->count = 0;
8308c2ecf20Sopenharmony_ci	if (mem->count < 8)
8318c2ecf20Sopenharmony_ci		mem->count++;
8328c2ecf20Sopenharmony_ci	for (n = mem->count - 1; n > 0 ; n--)
8338c2ecf20Sopenharmony_ci		memcpy(&mem->vm[n], &mem->vm[n - 1], sizeof(mem->vm[0]));
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	memset(&mem->vm[0], 0, sizeof(mem->vm[0]));
8368c2ecf20Sopenharmony_ci	mem->vm[0].cpus_total = cpus;
8378c2ecf20Sopenharmony_ci	mem->vm[0].cpus_configured = cpus;
8388c2ecf20Sopenharmony_ci	mem->vm[0].cpus_standby = 0;
8398c2ecf20Sopenharmony_ci	mem->vm[0].cpus_reserved = 0;
8408c2ecf20Sopenharmony_ci	mem->vm[0].caf = 1000;
8418c2ecf20Sopenharmony_ci	memcpy(mem->vm[0].name, "KVMguest", 8);
8428c2ecf20Sopenharmony_ci	ASCEBC(mem->vm[0].name, 8);
8438c2ecf20Sopenharmony_ci	memcpy(mem->vm[0].cpi, "KVM/Linux       ", 16);
8448c2ecf20Sopenharmony_ci	ASCEBC(mem->vm[0].cpi, 16);
8458c2ecf20Sopenharmony_ci}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_cistatic void insert_stsi_usr_data(struct kvm_vcpu *vcpu, u64 addr, u8 ar,
8488c2ecf20Sopenharmony_ci				 u8 fc, u8 sel1, u16 sel2)
8498c2ecf20Sopenharmony_ci{
8508c2ecf20Sopenharmony_ci	vcpu->run->exit_reason = KVM_EXIT_S390_STSI;
8518c2ecf20Sopenharmony_ci	vcpu->run->s390_stsi.addr = addr;
8528c2ecf20Sopenharmony_ci	vcpu->run->s390_stsi.ar = ar;
8538c2ecf20Sopenharmony_ci	vcpu->run->s390_stsi.fc = fc;
8548c2ecf20Sopenharmony_ci	vcpu->run->s390_stsi.sel1 = sel1;
8558c2ecf20Sopenharmony_ci	vcpu->run->s390_stsi.sel2 = sel2;
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_cistatic int handle_stsi(struct kvm_vcpu *vcpu)
8598c2ecf20Sopenharmony_ci{
8608c2ecf20Sopenharmony_ci	int fc = (vcpu->run->s.regs.gprs[0] & 0xf0000000) >> 28;
8618c2ecf20Sopenharmony_ci	int sel1 = vcpu->run->s.regs.gprs[0] & 0xff;
8628c2ecf20Sopenharmony_ci	int sel2 = vcpu->run->s.regs.gprs[1] & 0xffff;
8638c2ecf20Sopenharmony_ci	unsigned long mem = 0;
8648c2ecf20Sopenharmony_ci	u64 operand2;
8658c2ecf20Sopenharmony_ci	int rc = 0;
8668c2ecf20Sopenharmony_ci	u8 ar;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	vcpu->stat.instruction_stsi++;
8698c2ecf20Sopenharmony_ci	VCPU_EVENT(vcpu, 3, "STSI: fc: %u sel1: %u sel2: %u", fc, sel1, sel2);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
8728c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	if (fc > 3) {
8758c2ecf20Sopenharmony_ci		kvm_s390_set_psw_cc(vcpu, 3);
8768c2ecf20Sopenharmony_ci		return 0;
8778c2ecf20Sopenharmony_ci	}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	if (vcpu->run->s.regs.gprs[0] & 0x0fffff00
8808c2ecf20Sopenharmony_ci	    || vcpu->run->s.regs.gprs[1] & 0xffff0000)
8818c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	if (fc == 0) {
8848c2ecf20Sopenharmony_ci		vcpu->run->s.regs.gprs[0] = 3 << 28;
8858c2ecf20Sopenharmony_ci		kvm_s390_set_psw_cc(vcpu, 0);
8868c2ecf20Sopenharmony_ci		return 0;
8878c2ecf20Sopenharmony_ci	}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	operand2 = kvm_s390_get_base_disp_s(vcpu, &ar);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	if (!kvm_s390_pv_cpu_is_protected(vcpu) && (operand2 & 0xfff))
8928c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	switch (fc) {
8958c2ecf20Sopenharmony_ci	case 1: /* same handling for 1 and 2 */
8968c2ecf20Sopenharmony_ci	case 2:
8978c2ecf20Sopenharmony_ci		mem = get_zeroed_page(GFP_KERNEL);
8988c2ecf20Sopenharmony_ci		if (!mem)
8998c2ecf20Sopenharmony_ci			goto out_no_data;
9008c2ecf20Sopenharmony_ci		if (stsi((void *) mem, fc, sel1, sel2))
9018c2ecf20Sopenharmony_ci			goto out_no_data;
9028c2ecf20Sopenharmony_ci		break;
9038c2ecf20Sopenharmony_ci	case 3:
9048c2ecf20Sopenharmony_ci		if (sel1 != 2 || sel2 != 2)
9058c2ecf20Sopenharmony_ci			goto out_no_data;
9068c2ecf20Sopenharmony_ci		mem = get_zeroed_page(GFP_KERNEL);
9078c2ecf20Sopenharmony_ci		if (!mem)
9088c2ecf20Sopenharmony_ci			goto out_no_data;
9098c2ecf20Sopenharmony_ci		handle_stsi_3_2_2(vcpu, (void *) mem);
9108c2ecf20Sopenharmony_ci		break;
9118c2ecf20Sopenharmony_ci	}
9128c2ecf20Sopenharmony_ci	if (kvm_s390_pv_cpu_is_protected(vcpu)) {
9138c2ecf20Sopenharmony_ci		memcpy((void *)sida_origin(vcpu->arch.sie_block), (void *)mem,
9148c2ecf20Sopenharmony_ci		       PAGE_SIZE);
9158c2ecf20Sopenharmony_ci		rc = 0;
9168c2ecf20Sopenharmony_ci	} else {
9178c2ecf20Sopenharmony_ci		rc = write_guest(vcpu, operand2, ar, (void *)mem, PAGE_SIZE);
9188c2ecf20Sopenharmony_ci	}
9198c2ecf20Sopenharmony_ci	if (rc) {
9208c2ecf20Sopenharmony_ci		rc = kvm_s390_inject_prog_cond(vcpu, rc);
9218c2ecf20Sopenharmony_ci		goto out;
9228c2ecf20Sopenharmony_ci	}
9238c2ecf20Sopenharmony_ci	if (vcpu->kvm->arch.user_stsi) {
9248c2ecf20Sopenharmony_ci		insert_stsi_usr_data(vcpu, operand2, ar, fc, sel1, sel2);
9258c2ecf20Sopenharmony_ci		rc = -EREMOTE;
9268c2ecf20Sopenharmony_ci	}
9278c2ecf20Sopenharmony_ci	trace_kvm_s390_handle_stsi(vcpu, fc, sel1, sel2, operand2);
9288c2ecf20Sopenharmony_ci	free_page(mem);
9298c2ecf20Sopenharmony_ci	kvm_s390_set_psw_cc(vcpu, 0);
9308c2ecf20Sopenharmony_ci	vcpu->run->s.regs.gprs[0] = 0;
9318c2ecf20Sopenharmony_ci	return rc;
9328c2ecf20Sopenharmony_ciout_no_data:
9338c2ecf20Sopenharmony_ci	kvm_s390_set_psw_cc(vcpu, 3);
9348c2ecf20Sopenharmony_ciout:
9358c2ecf20Sopenharmony_ci	free_page(mem);
9368c2ecf20Sopenharmony_ci	return rc;
9378c2ecf20Sopenharmony_ci}
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ciint kvm_s390_handle_b2(struct kvm_vcpu *vcpu)
9408c2ecf20Sopenharmony_ci{
9418c2ecf20Sopenharmony_ci	switch (vcpu->arch.sie_block->ipa & 0x00ff) {
9428c2ecf20Sopenharmony_ci	case 0x02:
9438c2ecf20Sopenharmony_ci		return handle_stidp(vcpu);
9448c2ecf20Sopenharmony_ci	case 0x04:
9458c2ecf20Sopenharmony_ci		return handle_set_clock(vcpu);
9468c2ecf20Sopenharmony_ci	case 0x10:
9478c2ecf20Sopenharmony_ci		return handle_set_prefix(vcpu);
9488c2ecf20Sopenharmony_ci	case 0x11:
9498c2ecf20Sopenharmony_ci		return handle_store_prefix(vcpu);
9508c2ecf20Sopenharmony_ci	case 0x12:
9518c2ecf20Sopenharmony_ci		return handle_store_cpu_address(vcpu);
9528c2ecf20Sopenharmony_ci	case 0x14:
9538c2ecf20Sopenharmony_ci		return kvm_s390_handle_vsie(vcpu);
9548c2ecf20Sopenharmony_ci	case 0x21:
9558c2ecf20Sopenharmony_ci	case 0x50:
9568c2ecf20Sopenharmony_ci		return handle_ipte_interlock(vcpu);
9578c2ecf20Sopenharmony_ci	case 0x29:
9588c2ecf20Sopenharmony_ci		return handle_iske(vcpu);
9598c2ecf20Sopenharmony_ci	case 0x2a:
9608c2ecf20Sopenharmony_ci		return handle_rrbe(vcpu);
9618c2ecf20Sopenharmony_ci	case 0x2b:
9628c2ecf20Sopenharmony_ci		return handle_sske(vcpu);
9638c2ecf20Sopenharmony_ci	case 0x2c:
9648c2ecf20Sopenharmony_ci		return handle_test_block(vcpu);
9658c2ecf20Sopenharmony_ci	case 0x30:
9668c2ecf20Sopenharmony_ci	case 0x31:
9678c2ecf20Sopenharmony_ci	case 0x32:
9688c2ecf20Sopenharmony_ci	case 0x33:
9698c2ecf20Sopenharmony_ci	case 0x34:
9708c2ecf20Sopenharmony_ci	case 0x35:
9718c2ecf20Sopenharmony_ci	case 0x36:
9728c2ecf20Sopenharmony_ci	case 0x37:
9738c2ecf20Sopenharmony_ci	case 0x38:
9748c2ecf20Sopenharmony_ci	case 0x39:
9758c2ecf20Sopenharmony_ci	case 0x3a:
9768c2ecf20Sopenharmony_ci	case 0x3b:
9778c2ecf20Sopenharmony_ci	case 0x3c:
9788c2ecf20Sopenharmony_ci	case 0x5f:
9798c2ecf20Sopenharmony_ci	case 0x74:
9808c2ecf20Sopenharmony_ci	case 0x76:
9818c2ecf20Sopenharmony_ci		return handle_io_inst(vcpu);
9828c2ecf20Sopenharmony_ci	case 0x56:
9838c2ecf20Sopenharmony_ci		return handle_sthyi(vcpu);
9848c2ecf20Sopenharmony_ci	case 0x7d:
9858c2ecf20Sopenharmony_ci		return handle_stsi(vcpu);
9868c2ecf20Sopenharmony_ci	case 0xaf:
9878c2ecf20Sopenharmony_ci		return handle_pqap(vcpu);
9888c2ecf20Sopenharmony_ci	case 0xb1:
9898c2ecf20Sopenharmony_ci		return handle_stfl(vcpu);
9908c2ecf20Sopenharmony_ci	case 0xb2:
9918c2ecf20Sopenharmony_ci		return handle_lpswe(vcpu);
9928c2ecf20Sopenharmony_ci	default:
9938c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
9948c2ecf20Sopenharmony_ci	}
9958c2ecf20Sopenharmony_ci}
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_cistatic int handle_epsw(struct kvm_vcpu *vcpu)
9988c2ecf20Sopenharmony_ci{
9998c2ecf20Sopenharmony_ci	int reg1, reg2;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	vcpu->stat.instruction_epsw++;
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	/* This basically extracts the mask half of the psw. */
10068c2ecf20Sopenharmony_ci	vcpu->run->s.regs.gprs[reg1] &= 0xffffffff00000000UL;
10078c2ecf20Sopenharmony_ci	vcpu->run->s.regs.gprs[reg1] |= vcpu->arch.sie_block->gpsw.mask >> 32;
10088c2ecf20Sopenharmony_ci	if (reg2) {
10098c2ecf20Sopenharmony_ci		vcpu->run->s.regs.gprs[reg2] &= 0xffffffff00000000UL;
10108c2ecf20Sopenharmony_ci		vcpu->run->s.regs.gprs[reg2] |=
10118c2ecf20Sopenharmony_ci			vcpu->arch.sie_block->gpsw.mask & 0x00000000ffffffffUL;
10128c2ecf20Sopenharmony_ci	}
10138c2ecf20Sopenharmony_ci	return 0;
10148c2ecf20Sopenharmony_ci}
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci#define PFMF_RESERVED   0xfffc0101UL
10178c2ecf20Sopenharmony_ci#define PFMF_SK         0x00020000UL
10188c2ecf20Sopenharmony_ci#define PFMF_CF         0x00010000UL
10198c2ecf20Sopenharmony_ci#define PFMF_UI         0x00008000UL
10208c2ecf20Sopenharmony_ci#define PFMF_FSC        0x00007000UL
10218c2ecf20Sopenharmony_ci#define PFMF_NQ         0x00000800UL
10228c2ecf20Sopenharmony_ci#define PFMF_MR         0x00000400UL
10238c2ecf20Sopenharmony_ci#define PFMF_MC         0x00000200UL
10248c2ecf20Sopenharmony_ci#define PFMF_KEY        0x000000feUL
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_cistatic int handle_pfmf(struct kvm_vcpu *vcpu)
10278c2ecf20Sopenharmony_ci{
10288c2ecf20Sopenharmony_ci	bool mr = false, mc = false, nq;
10298c2ecf20Sopenharmony_ci	int reg1, reg2;
10308c2ecf20Sopenharmony_ci	unsigned long start, end;
10318c2ecf20Sopenharmony_ci	unsigned char key;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	vcpu->stat.instruction_pfmf++;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	if (!test_kvm_facility(vcpu->kvm, 8))
10388c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
10418c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	if (vcpu->run->s.regs.gprs[reg1] & PFMF_RESERVED)
10448c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	/* Only provide non-quiescing support if enabled for the guest */
10478c2ecf20Sopenharmony_ci	if (vcpu->run->s.regs.gprs[reg1] & PFMF_NQ &&
10488c2ecf20Sopenharmony_ci	    !test_kvm_facility(vcpu->kvm, 14))
10498c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	/* Only provide conditional-SSKE support if enabled for the guest */
10528c2ecf20Sopenharmony_ci	if (vcpu->run->s.regs.gprs[reg1] & PFMF_SK &&
10538c2ecf20Sopenharmony_ci	    test_kvm_facility(vcpu->kvm, 10)) {
10548c2ecf20Sopenharmony_ci		mr = vcpu->run->s.regs.gprs[reg1] & PFMF_MR;
10558c2ecf20Sopenharmony_ci		mc = vcpu->run->s.regs.gprs[reg1] & PFMF_MC;
10568c2ecf20Sopenharmony_ci	}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	nq = vcpu->run->s.regs.gprs[reg1] & PFMF_NQ;
10598c2ecf20Sopenharmony_ci	key = vcpu->run->s.regs.gprs[reg1] & PFMF_KEY;
10608c2ecf20Sopenharmony_ci	start = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
10618c2ecf20Sopenharmony_ci	start = kvm_s390_logical_to_effective(vcpu, start);
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	if (vcpu->run->s.regs.gprs[reg1] & PFMF_CF) {
10648c2ecf20Sopenharmony_ci		if (kvm_s390_check_low_addr_prot_real(vcpu, start))
10658c2ecf20Sopenharmony_ci			return kvm_s390_inject_prog_irq(vcpu, &vcpu->arch.pgm);
10668c2ecf20Sopenharmony_ci	}
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	switch (vcpu->run->s.regs.gprs[reg1] & PFMF_FSC) {
10698c2ecf20Sopenharmony_ci	case 0x00000000:
10708c2ecf20Sopenharmony_ci		/* only 4k frames specify a real address */
10718c2ecf20Sopenharmony_ci		start = kvm_s390_real_to_abs(vcpu, start);
10728c2ecf20Sopenharmony_ci		end = (start + PAGE_SIZE) & ~(PAGE_SIZE - 1);
10738c2ecf20Sopenharmony_ci		break;
10748c2ecf20Sopenharmony_ci	case 0x00001000:
10758c2ecf20Sopenharmony_ci		end = (start + _SEGMENT_SIZE) & ~(_SEGMENT_SIZE - 1);
10768c2ecf20Sopenharmony_ci		break;
10778c2ecf20Sopenharmony_ci	case 0x00002000:
10788c2ecf20Sopenharmony_ci		/* only support 2G frame size if EDAT2 is available and we are
10798c2ecf20Sopenharmony_ci		   not in 24-bit addressing mode */
10808c2ecf20Sopenharmony_ci		if (!test_kvm_facility(vcpu->kvm, 78) ||
10818c2ecf20Sopenharmony_ci		    psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_24BIT)
10828c2ecf20Sopenharmony_ci			return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
10838c2ecf20Sopenharmony_ci		end = (start + _REGION3_SIZE) & ~(_REGION3_SIZE - 1);
10848c2ecf20Sopenharmony_ci		break;
10858c2ecf20Sopenharmony_ci	default:
10868c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
10878c2ecf20Sopenharmony_ci	}
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	while (start != end) {
10908c2ecf20Sopenharmony_ci		unsigned long vmaddr;
10918c2ecf20Sopenharmony_ci		bool unlocked = false;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci		/* Translate guest address to host address */
10948c2ecf20Sopenharmony_ci		vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(start));
10958c2ecf20Sopenharmony_ci		if (kvm_is_error_hva(vmaddr))
10968c2ecf20Sopenharmony_ci			return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci		if (vcpu->run->s.regs.gprs[reg1] & PFMF_CF) {
10998c2ecf20Sopenharmony_ci			if (kvm_clear_guest(vcpu->kvm, start, PAGE_SIZE))
11008c2ecf20Sopenharmony_ci				return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
11018c2ecf20Sopenharmony_ci		}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci		if (vcpu->run->s.regs.gprs[reg1] & PFMF_SK) {
11048c2ecf20Sopenharmony_ci			int rc = kvm_s390_skey_check_enable(vcpu);
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci			if (rc)
11078c2ecf20Sopenharmony_ci				return rc;
11088c2ecf20Sopenharmony_ci			mmap_read_lock(current->mm);
11098c2ecf20Sopenharmony_ci			rc = cond_set_guest_storage_key(current->mm, vmaddr,
11108c2ecf20Sopenharmony_ci							key, NULL, nq, mr, mc);
11118c2ecf20Sopenharmony_ci			if (rc < 0) {
11128c2ecf20Sopenharmony_ci				rc = fixup_user_fault(current->mm, vmaddr,
11138c2ecf20Sopenharmony_ci						      FAULT_FLAG_WRITE, &unlocked);
11148c2ecf20Sopenharmony_ci				rc = !rc ? -EAGAIN : rc;
11158c2ecf20Sopenharmony_ci			}
11168c2ecf20Sopenharmony_ci			mmap_read_unlock(current->mm);
11178c2ecf20Sopenharmony_ci			if (rc == -EFAULT)
11188c2ecf20Sopenharmony_ci				return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
11198c2ecf20Sopenharmony_ci			if (rc == -EAGAIN)
11208c2ecf20Sopenharmony_ci				continue;
11218c2ecf20Sopenharmony_ci			if (rc < 0)
11228c2ecf20Sopenharmony_ci				return rc;
11238c2ecf20Sopenharmony_ci		}
11248c2ecf20Sopenharmony_ci		start += PAGE_SIZE;
11258c2ecf20Sopenharmony_ci	}
11268c2ecf20Sopenharmony_ci	if (vcpu->run->s.regs.gprs[reg1] & PFMF_FSC) {
11278c2ecf20Sopenharmony_ci		if (psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_64BIT) {
11288c2ecf20Sopenharmony_ci			vcpu->run->s.regs.gprs[reg2] = end;
11298c2ecf20Sopenharmony_ci		} else {
11308c2ecf20Sopenharmony_ci			vcpu->run->s.regs.gprs[reg2] &= ~0xffffffffUL;
11318c2ecf20Sopenharmony_ci			end = kvm_s390_logical_to_effective(vcpu, end);
11328c2ecf20Sopenharmony_ci			vcpu->run->s.regs.gprs[reg2] |= end;
11338c2ecf20Sopenharmony_ci		}
11348c2ecf20Sopenharmony_ci	}
11358c2ecf20Sopenharmony_ci	return 0;
11368c2ecf20Sopenharmony_ci}
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci/*
11398c2ecf20Sopenharmony_ci * Must be called with relevant read locks held (kvm->mm->mmap_lock, kvm->srcu)
11408c2ecf20Sopenharmony_ci */
11418c2ecf20Sopenharmony_cistatic inline int __do_essa(struct kvm_vcpu *vcpu, const int orc)
11428c2ecf20Sopenharmony_ci{
11438c2ecf20Sopenharmony_ci	int r1, r2, nappended, entries;
11448c2ecf20Sopenharmony_ci	unsigned long gfn, hva, res, pgstev, ptev;
11458c2ecf20Sopenharmony_ci	unsigned long *cbrlo;
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	/*
11488c2ecf20Sopenharmony_ci	 * We don't need to set SD.FPF.SK to 1 here, because if we have a
11498c2ecf20Sopenharmony_ci	 * machine check here we either handle it or crash
11508c2ecf20Sopenharmony_ci	 */
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	kvm_s390_get_regs_rre(vcpu, &r1, &r2);
11538c2ecf20Sopenharmony_ci	gfn = vcpu->run->s.regs.gprs[r2] >> PAGE_SHIFT;
11548c2ecf20Sopenharmony_ci	hva = gfn_to_hva(vcpu->kvm, gfn);
11558c2ecf20Sopenharmony_ci	entries = (vcpu->arch.sie_block->cbrlo & ~PAGE_MASK) >> 3;
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	if (kvm_is_error_hva(hva))
11588c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	nappended = pgste_perform_essa(vcpu->kvm->mm, hva, orc, &ptev, &pgstev);
11618c2ecf20Sopenharmony_ci	if (nappended < 0) {
11628c2ecf20Sopenharmony_ci		res = orc ? 0x10 : 0;
11638c2ecf20Sopenharmony_ci		vcpu->run->s.regs.gprs[r1] = res; /* Exception Indication */
11648c2ecf20Sopenharmony_ci		return 0;
11658c2ecf20Sopenharmony_ci	}
11668c2ecf20Sopenharmony_ci	res = (pgstev & _PGSTE_GPS_USAGE_MASK) >> 22;
11678c2ecf20Sopenharmony_ci	/*
11688c2ecf20Sopenharmony_ci	 * Set the block-content state part of the result. 0 means resident, so
11698c2ecf20Sopenharmony_ci	 * nothing to do if the page is valid. 2 is for preserved pages
11708c2ecf20Sopenharmony_ci	 * (non-present and non-zero), and 3 for zero pages (non-present and
11718c2ecf20Sopenharmony_ci	 * zero).
11728c2ecf20Sopenharmony_ci	 */
11738c2ecf20Sopenharmony_ci	if (ptev & _PAGE_INVALID) {
11748c2ecf20Sopenharmony_ci		res |= 2;
11758c2ecf20Sopenharmony_ci		if (pgstev & _PGSTE_GPS_ZERO)
11768c2ecf20Sopenharmony_ci			res |= 1;
11778c2ecf20Sopenharmony_ci	}
11788c2ecf20Sopenharmony_ci	if (pgstev & _PGSTE_GPS_NODAT)
11798c2ecf20Sopenharmony_ci		res |= 0x20;
11808c2ecf20Sopenharmony_ci	vcpu->run->s.regs.gprs[r1] = res;
11818c2ecf20Sopenharmony_ci	/*
11828c2ecf20Sopenharmony_ci	 * It is possible that all the normal 511 slots were full, in which case
11838c2ecf20Sopenharmony_ci	 * we will now write in the 512th slot, which is reserved for host use.
11848c2ecf20Sopenharmony_ci	 * In both cases we let the normal essa handling code process all the
11858c2ecf20Sopenharmony_ci	 * slots, including the reserved one, if needed.
11868c2ecf20Sopenharmony_ci	 */
11878c2ecf20Sopenharmony_ci	if (nappended > 0) {
11888c2ecf20Sopenharmony_ci		cbrlo = phys_to_virt(vcpu->arch.sie_block->cbrlo & PAGE_MASK);
11898c2ecf20Sopenharmony_ci		cbrlo[entries] = gfn << PAGE_SHIFT;
11908c2ecf20Sopenharmony_ci	}
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	if (orc) {
11938c2ecf20Sopenharmony_ci		struct kvm_memory_slot *ms = gfn_to_memslot(vcpu->kvm, gfn);
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci		/* Increment only if we are really flipping the bit */
11968c2ecf20Sopenharmony_ci		if (ms && !test_and_set_bit(gfn - ms->base_gfn, kvm_second_dirty_bitmap(ms)))
11978c2ecf20Sopenharmony_ci			atomic64_inc(&vcpu->kvm->arch.cmma_dirty_pages);
11988c2ecf20Sopenharmony_ci	}
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	return nappended;
12018c2ecf20Sopenharmony_ci}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_cistatic int handle_essa(struct kvm_vcpu *vcpu)
12048c2ecf20Sopenharmony_ci{
12058c2ecf20Sopenharmony_ci	/* entries expected to be 1FF */
12068c2ecf20Sopenharmony_ci	int entries = (vcpu->arch.sie_block->cbrlo & ~PAGE_MASK) >> 3;
12078c2ecf20Sopenharmony_ci	unsigned long *cbrlo;
12088c2ecf20Sopenharmony_ci	struct gmap *gmap;
12098c2ecf20Sopenharmony_ci	int i, orc;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	VCPU_EVENT(vcpu, 4, "ESSA: release %d pages", entries);
12128c2ecf20Sopenharmony_ci	gmap = vcpu->arch.gmap;
12138c2ecf20Sopenharmony_ci	vcpu->stat.instruction_essa++;
12148c2ecf20Sopenharmony_ci	if (!vcpu->kvm->arch.use_cmma)
12158c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
12188c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
12198c2ecf20Sopenharmony_ci	/* Check for invalid operation request code */
12208c2ecf20Sopenharmony_ci	orc = (vcpu->arch.sie_block->ipb & 0xf0000000) >> 28;
12218c2ecf20Sopenharmony_ci	/* ORCs 0-6 are always valid */
12228c2ecf20Sopenharmony_ci	if (orc > (test_kvm_facility(vcpu->kvm, 147) ? ESSA_SET_STABLE_NODAT
12238c2ecf20Sopenharmony_ci						: ESSA_SET_STABLE_IF_RESIDENT))
12248c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	if (!vcpu->kvm->arch.migration_mode) {
12278c2ecf20Sopenharmony_ci		/*
12288c2ecf20Sopenharmony_ci		 * CMMA is enabled in the KVM settings, but is disabled in
12298c2ecf20Sopenharmony_ci		 * the SIE block and in the mm_context, and we are not doing
12308c2ecf20Sopenharmony_ci		 * a migration. Enable CMMA in the mm_context.
12318c2ecf20Sopenharmony_ci		 * Since we need to take a write lock to write to the context
12328c2ecf20Sopenharmony_ci		 * to avoid races with storage keys handling, we check if the
12338c2ecf20Sopenharmony_ci		 * value really needs to be written to; if the value is
12348c2ecf20Sopenharmony_ci		 * already correct, we do nothing and avoid the lock.
12358c2ecf20Sopenharmony_ci		 */
12368c2ecf20Sopenharmony_ci		if (vcpu->kvm->mm->context.uses_cmm == 0) {
12378c2ecf20Sopenharmony_ci			mmap_write_lock(vcpu->kvm->mm);
12388c2ecf20Sopenharmony_ci			vcpu->kvm->mm->context.uses_cmm = 1;
12398c2ecf20Sopenharmony_ci			mmap_write_unlock(vcpu->kvm->mm);
12408c2ecf20Sopenharmony_ci		}
12418c2ecf20Sopenharmony_ci		/*
12428c2ecf20Sopenharmony_ci		 * If we are here, we are supposed to have CMMA enabled in
12438c2ecf20Sopenharmony_ci		 * the SIE block. Enabling CMMA works on a per-CPU basis,
12448c2ecf20Sopenharmony_ci		 * while the context use_cmma flag is per process.
12458c2ecf20Sopenharmony_ci		 * It's possible that the context flag is enabled and the
12468c2ecf20Sopenharmony_ci		 * SIE flag is not, so we set the flag always; if it was
12478c2ecf20Sopenharmony_ci		 * already set, nothing changes, otherwise we enable it
12488c2ecf20Sopenharmony_ci		 * on this CPU too.
12498c2ecf20Sopenharmony_ci		 */
12508c2ecf20Sopenharmony_ci		vcpu->arch.sie_block->ecb2 |= ECB2_CMMA;
12518c2ecf20Sopenharmony_ci		/* Retry the ESSA instruction */
12528c2ecf20Sopenharmony_ci		kvm_s390_retry_instr(vcpu);
12538c2ecf20Sopenharmony_ci	} else {
12548c2ecf20Sopenharmony_ci		int srcu_idx;
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci		mmap_read_lock(vcpu->kvm->mm);
12578c2ecf20Sopenharmony_ci		srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
12588c2ecf20Sopenharmony_ci		i = __do_essa(vcpu, orc);
12598c2ecf20Sopenharmony_ci		srcu_read_unlock(&vcpu->kvm->srcu, srcu_idx);
12608c2ecf20Sopenharmony_ci		mmap_read_unlock(vcpu->kvm->mm);
12618c2ecf20Sopenharmony_ci		if (i < 0)
12628c2ecf20Sopenharmony_ci			return i;
12638c2ecf20Sopenharmony_ci		/* Account for the possible extra cbrl entry */
12648c2ecf20Sopenharmony_ci		entries += i;
12658c2ecf20Sopenharmony_ci	}
12668c2ecf20Sopenharmony_ci	vcpu->arch.sie_block->cbrlo &= PAGE_MASK;	/* reset nceo */
12678c2ecf20Sopenharmony_ci	cbrlo = phys_to_virt(vcpu->arch.sie_block->cbrlo);
12688c2ecf20Sopenharmony_ci	mmap_read_lock(gmap->mm);
12698c2ecf20Sopenharmony_ci	for (i = 0; i < entries; ++i)
12708c2ecf20Sopenharmony_ci		__gmap_zap(gmap, cbrlo[i]);
12718c2ecf20Sopenharmony_ci	mmap_read_unlock(gmap->mm);
12728c2ecf20Sopenharmony_ci	return 0;
12738c2ecf20Sopenharmony_ci}
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ciint kvm_s390_handle_b9(struct kvm_vcpu *vcpu)
12768c2ecf20Sopenharmony_ci{
12778c2ecf20Sopenharmony_ci	switch (vcpu->arch.sie_block->ipa & 0x00ff) {
12788c2ecf20Sopenharmony_ci	case 0x8a:
12798c2ecf20Sopenharmony_ci	case 0x8e:
12808c2ecf20Sopenharmony_ci	case 0x8f:
12818c2ecf20Sopenharmony_ci		return handle_ipte_interlock(vcpu);
12828c2ecf20Sopenharmony_ci	case 0x8d:
12838c2ecf20Sopenharmony_ci		return handle_epsw(vcpu);
12848c2ecf20Sopenharmony_ci	case 0xab:
12858c2ecf20Sopenharmony_ci		return handle_essa(vcpu);
12868c2ecf20Sopenharmony_ci	case 0xaf:
12878c2ecf20Sopenharmony_ci		return handle_pfmf(vcpu);
12888c2ecf20Sopenharmony_ci	default:
12898c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
12908c2ecf20Sopenharmony_ci	}
12918c2ecf20Sopenharmony_ci}
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ciint kvm_s390_handle_lctl(struct kvm_vcpu *vcpu)
12948c2ecf20Sopenharmony_ci{
12958c2ecf20Sopenharmony_ci	int reg1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4;
12968c2ecf20Sopenharmony_ci	int reg3 = vcpu->arch.sie_block->ipa & 0x000f;
12978c2ecf20Sopenharmony_ci	int reg, rc, nr_regs;
12988c2ecf20Sopenharmony_ci	u32 ctl_array[16];
12998c2ecf20Sopenharmony_ci	u64 ga;
13008c2ecf20Sopenharmony_ci	u8 ar;
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	vcpu->stat.instruction_lctl++;
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
13058c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	ga = kvm_s390_get_base_disp_rs(vcpu, &ar);
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	if (ga & 3)
13108c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	VCPU_EVENT(vcpu, 4, "LCTL: r1:%d, r3:%d, addr: 0x%llx", reg1, reg3, ga);
13138c2ecf20Sopenharmony_ci	trace_kvm_s390_handle_lctl(vcpu, 0, reg1, reg3, ga);
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	nr_regs = ((reg3 - reg1) & 0xf) + 1;
13168c2ecf20Sopenharmony_ci	rc = read_guest(vcpu, ga, ar, ctl_array, nr_regs * sizeof(u32));
13178c2ecf20Sopenharmony_ci	if (rc)
13188c2ecf20Sopenharmony_ci		return kvm_s390_inject_prog_cond(vcpu, rc);
13198c2ecf20Sopenharmony_ci	reg = reg1;
13208c2ecf20Sopenharmony_ci	nr_regs = 0;
13218c2ecf20Sopenharmony_ci	do {
13228c2ecf20Sopenharmony_ci		vcpu->arch.sie_block->gcr[reg] &= 0xffffffff00000000ul;
13238c2ecf20Sopenharmony_ci		vcpu->arch.sie_block->gcr[reg] |= ctl_array[nr_regs++];
13248c2ecf20Sopenharmony_ci		if (reg == reg3)
13258c2ecf20Sopenharmony_ci			break;
13268c2ecf20Sopenharmony_ci		reg = (reg + 1) % 16;
13278c2ecf20Sopenharmony_ci	} while (1);
13288c2ecf20Sopenharmony_ci	kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);
13298c2ecf20Sopenharmony_ci	return 0;
13308c2ecf20Sopenharmony_ci}
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ciint kvm_s390_handle_stctl(struct kvm_vcpu *vcpu)
13338c2ecf20Sopenharmony_ci{
13348c2ecf20Sopenharmony_ci	int reg1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4;
13358c2ecf20Sopenharmony_ci	int reg3 = vcpu->arch.sie_block->ipa & 0x000f;
13368c2ecf20Sopenharmony_ci	int reg, rc, nr_regs;
13378c2ecf20Sopenharmony_ci	u32 ctl_array[16];
13388c2ecf20Sopenharmony_ci	u64 ga;
13398c2ecf20Sopenharmony_ci	u8 ar;
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	vcpu->stat.instruction_stctl++;
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
13448c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	ga = kvm_s390_get_base_disp_rs(vcpu, &ar);
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	if (ga & 3)
13498c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	VCPU_EVENT(vcpu, 4, "STCTL r1:%d, r3:%d, addr: 0x%llx", reg1, reg3, ga);
13528c2ecf20Sopenharmony_ci	trace_kvm_s390_handle_stctl(vcpu, 0, reg1, reg3, ga);
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	reg = reg1;
13558c2ecf20Sopenharmony_ci	nr_regs = 0;
13568c2ecf20Sopenharmony_ci	do {
13578c2ecf20Sopenharmony_ci		ctl_array[nr_regs++] = vcpu->arch.sie_block->gcr[reg];
13588c2ecf20Sopenharmony_ci		if (reg == reg3)
13598c2ecf20Sopenharmony_ci			break;
13608c2ecf20Sopenharmony_ci		reg = (reg + 1) % 16;
13618c2ecf20Sopenharmony_ci	} while (1);
13628c2ecf20Sopenharmony_ci	rc = write_guest(vcpu, ga, ar, ctl_array, nr_regs * sizeof(u32));
13638c2ecf20Sopenharmony_ci	return rc ? kvm_s390_inject_prog_cond(vcpu, rc) : 0;
13648c2ecf20Sopenharmony_ci}
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_cistatic int handle_lctlg(struct kvm_vcpu *vcpu)
13678c2ecf20Sopenharmony_ci{
13688c2ecf20Sopenharmony_ci	int reg1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4;
13698c2ecf20Sopenharmony_ci	int reg3 = vcpu->arch.sie_block->ipa & 0x000f;
13708c2ecf20Sopenharmony_ci	int reg, rc, nr_regs;
13718c2ecf20Sopenharmony_ci	u64 ctl_array[16];
13728c2ecf20Sopenharmony_ci	u64 ga;
13738c2ecf20Sopenharmony_ci	u8 ar;
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	vcpu->stat.instruction_lctlg++;
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
13788c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	ga = kvm_s390_get_base_disp_rsy(vcpu, &ar);
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	if (ga & 7)
13838c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	VCPU_EVENT(vcpu, 4, "LCTLG: r1:%d, r3:%d, addr: 0x%llx", reg1, reg3, ga);
13868c2ecf20Sopenharmony_ci	trace_kvm_s390_handle_lctl(vcpu, 1, reg1, reg3, ga);
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	nr_regs = ((reg3 - reg1) & 0xf) + 1;
13898c2ecf20Sopenharmony_ci	rc = read_guest(vcpu, ga, ar, ctl_array, nr_regs * sizeof(u64));
13908c2ecf20Sopenharmony_ci	if (rc)
13918c2ecf20Sopenharmony_ci		return kvm_s390_inject_prog_cond(vcpu, rc);
13928c2ecf20Sopenharmony_ci	reg = reg1;
13938c2ecf20Sopenharmony_ci	nr_regs = 0;
13948c2ecf20Sopenharmony_ci	do {
13958c2ecf20Sopenharmony_ci		vcpu->arch.sie_block->gcr[reg] = ctl_array[nr_regs++];
13968c2ecf20Sopenharmony_ci		if (reg == reg3)
13978c2ecf20Sopenharmony_ci			break;
13988c2ecf20Sopenharmony_ci		reg = (reg + 1) % 16;
13998c2ecf20Sopenharmony_ci	} while (1);
14008c2ecf20Sopenharmony_ci	kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);
14018c2ecf20Sopenharmony_ci	return 0;
14028c2ecf20Sopenharmony_ci}
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_cistatic int handle_stctg(struct kvm_vcpu *vcpu)
14058c2ecf20Sopenharmony_ci{
14068c2ecf20Sopenharmony_ci	int reg1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4;
14078c2ecf20Sopenharmony_ci	int reg3 = vcpu->arch.sie_block->ipa & 0x000f;
14088c2ecf20Sopenharmony_ci	int reg, rc, nr_regs;
14098c2ecf20Sopenharmony_ci	u64 ctl_array[16];
14108c2ecf20Sopenharmony_ci	u64 ga;
14118c2ecf20Sopenharmony_ci	u8 ar;
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	vcpu->stat.instruction_stctg++;
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
14168c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	ga = kvm_s390_get_base_disp_rsy(vcpu, &ar);
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	if (ga & 7)
14218c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	VCPU_EVENT(vcpu, 4, "STCTG r1:%d, r3:%d, addr: 0x%llx", reg1, reg3, ga);
14248c2ecf20Sopenharmony_ci	trace_kvm_s390_handle_stctl(vcpu, 1, reg1, reg3, ga);
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	reg = reg1;
14278c2ecf20Sopenharmony_ci	nr_regs = 0;
14288c2ecf20Sopenharmony_ci	do {
14298c2ecf20Sopenharmony_ci		ctl_array[nr_regs++] = vcpu->arch.sie_block->gcr[reg];
14308c2ecf20Sopenharmony_ci		if (reg == reg3)
14318c2ecf20Sopenharmony_ci			break;
14328c2ecf20Sopenharmony_ci		reg = (reg + 1) % 16;
14338c2ecf20Sopenharmony_ci	} while (1);
14348c2ecf20Sopenharmony_ci	rc = write_guest(vcpu, ga, ar, ctl_array, nr_regs * sizeof(u64));
14358c2ecf20Sopenharmony_ci	return rc ? kvm_s390_inject_prog_cond(vcpu, rc) : 0;
14368c2ecf20Sopenharmony_ci}
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ciint kvm_s390_handle_eb(struct kvm_vcpu *vcpu)
14398c2ecf20Sopenharmony_ci{
14408c2ecf20Sopenharmony_ci	switch (vcpu->arch.sie_block->ipb & 0x000000ff) {
14418c2ecf20Sopenharmony_ci	case 0x25:
14428c2ecf20Sopenharmony_ci		return handle_stctg(vcpu);
14438c2ecf20Sopenharmony_ci	case 0x2f:
14448c2ecf20Sopenharmony_ci		return handle_lctlg(vcpu);
14458c2ecf20Sopenharmony_ci	case 0x60:
14468c2ecf20Sopenharmony_ci	case 0x61:
14478c2ecf20Sopenharmony_ci	case 0x62:
14488c2ecf20Sopenharmony_ci		return handle_ri(vcpu);
14498c2ecf20Sopenharmony_ci	default:
14508c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
14518c2ecf20Sopenharmony_ci	}
14528c2ecf20Sopenharmony_ci}
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_cistatic int handle_tprot(struct kvm_vcpu *vcpu)
14558c2ecf20Sopenharmony_ci{
14568c2ecf20Sopenharmony_ci	u64 address1, address2;
14578c2ecf20Sopenharmony_ci	unsigned long hva, gpa;
14588c2ecf20Sopenharmony_ci	int ret = 0, cc = 0;
14598c2ecf20Sopenharmony_ci	bool writable;
14608c2ecf20Sopenharmony_ci	u8 ar;
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	vcpu->stat.instruction_tprot++;
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
14658c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	kvm_s390_get_base_disp_sse(vcpu, &address1, &address2, &ar, NULL);
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	/* we only handle the Linux memory detection case:
14708c2ecf20Sopenharmony_ci	 * access key == 0
14718c2ecf20Sopenharmony_ci	 * everything else goes to userspace. */
14728c2ecf20Sopenharmony_ci	if (address2 & 0xf0)
14738c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
14748c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_DAT)
14758c2ecf20Sopenharmony_ci		ipte_lock(vcpu);
14768c2ecf20Sopenharmony_ci	ret = guest_translate_address(vcpu, address1, ar, &gpa, GACC_STORE);
14778c2ecf20Sopenharmony_ci	if (ret == PGM_PROTECTION) {
14788c2ecf20Sopenharmony_ci		/* Write protected? Try again with read-only... */
14798c2ecf20Sopenharmony_ci		cc = 1;
14808c2ecf20Sopenharmony_ci		ret = guest_translate_address(vcpu, address1, ar, &gpa,
14818c2ecf20Sopenharmony_ci					      GACC_FETCH);
14828c2ecf20Sopenharmony_ci	}
14838c2ecf20Sopenharmony_ci	if (ret) {
14848c2ecf20Sopenharmony_ci		if (ret == PGM_ADDRESSING || ret == PGM_TRANSLATION_SPEC) {
14858c2ecf20Sopenharmony_ci			ret = kvm_s390_inject_program_int(vcpu, ret);
14868c2ecf20Sopenharmony_ci		} else if (ret > 0) {
14878c2ecf20Sopenharmony_ci			/* Translation not available */
14888c2ecf20Sopenharmony_ci			kvm_s390_set_psw_cc(vcpu, 3);
14898c2ecf20Sopenharmony_ci			ret = 0;
14908c2ecf20Sopenharmony_ci		}
14918c2ecf20Sopenharmony_ci		goto out_unlock;
14928c2ecf20Sopenharmony_ci	}
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci	hva = gfn_to_hva_prot(vcpu->kvm, gpa_to_gfn(gpa), &writable);
14958c2ecf20Sopenharmony_ci	if (kvm_is_error_hva(hva)) {
14968c2ecf20Sopenharmony_ci		ret = kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
14978c2ecf20Sopenharmony_ci	} else {
14988c2ecf20Sopenharmony_ci		if (!writable)
14998c2ecf20Sopenharmony_ci			cc = 1;		/* Write not permitted ==> read-only */
15008c2ecf20Sopenharmony_ci		kvm_s390_set_psw_cc(vcpu, cc);
15018c2ecf20Sopenharmony_ci		/* Note: CC2 only occurs for storage keys (not supported yet) */
15028c2ecf20Sopenharmony_ci	}
15038c2ecf20Sopenharmony_ciout_unlock:
15048c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_DAT)
15058c2ecf20Sopenharmony_ci		ipte_unlock(vcpu);
15068c2ecf20Sopenharmony_ci	return ret;
15078c2ecf20Sopenharmony_ci}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ciint kvm_s390_handle_e5(struct kvm_vcpu *vcpu)
15108c2ecf20Sopenharmony_ci{
15118c2ecf20Sopenharmony_ci	switch (vcpu->arch.sie_block->ipa & 0x00ff) {
15128c2ecf20Sopenharmony_ci	case 0x01:
15138c2ecf20Sopenharmony_ci		return handle_tprot(vcpu);
15148c2ecf20Sopenharmony_ci	default:
15158c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
15168c2ecf20Sopenharmony_ci	}
15178c2ecf20Sopenharmony_ci}
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_cistatic int handle_sckpf(struct kvm_vcpu *vcpu)
15208c2ecf20Sopenharmony_ci{
15218c2ecf20Sopenharmony_ci	u32 value;
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	vcpu->stat.instruction_sckpf++;
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
15268c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	if (vcpu->run->s.regs.gprs[0] & 0x00000000ffff0000)
15298c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu,
15308c2ecf20Sopenharmony_ci						   PGM_SPECIFICATION);
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	value = vcpu->run->s.regs.gprs[0] & 0x000000000000ffff;
15338c2ecf20Sopenharmony_ci	vcpu->arch.sie_block->todpr = value;
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	return 0;
15368c2ecf20Sopenharmony_ci}
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_cistatic int handle_ptff(struct kvm_vcpu *vcpu)
15398c2ecf20Sopenharmony_ci{
15408c2ecf20Sopenharmony_ci	vcpu->stat.instruction_ptff++;
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	/* we don't emulate any control instructions yet */
15438c2ecf20Sopenharmony_ci	kvm_s390_set_psw_cc(vcpu, 3);
15448c2ecf20Sopenharmony_ci	return 0;
15458c2ecf20Sopenharmony_ci}
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_ciint kvm_s390_handle_01(struct kvm_vcpu *vcpu)
15488c2ecf20Sopenharmony_ci{
15498c2ecf20Sopenharmony_ci	switch (vcpu->arch.sie_block->ipa & 0x00ff) {
15508c2ecf20Sopenharmony_ci	case 0x04:
15518c2ecf20Sopenharmony_ci		return handle_ptff(vcpu);
15528c2ecf20Sopenharmony_ci	case 0x07:
15538c2ecf20Sopenharmony_ci		return handle_sckpf(vcpu);
15548c2ecf20Sopenharmony_ci	default:
15558c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
15568c2ecf20Sopenharmony_ci	}
15578c2ecf20Sopenharmony_ci}
1558