xref: /kernel/linux/linux-5.10/arch/s390/kvm/guestdbg.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * kvm guest debug support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2014
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *    Author(s): David Hildenbrand <dahi@linux.vnet.ibm.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/kvm_host.h>
108c2ecf20Sopenharmony_ci#include <linux/errno.h>
118c2ecf20Sopenharmony_ci#include "kvm-s390.h"
128c2ecf20Sopenharmony_ci#include "gaccess.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/*
158c2ecf20Sopenharmony_ci * Extends the address range given by *start and *stop to include the address
168c2ecf20Sopenharmony_ci * range starting with estart and the length len. Takes care of overflowing
178c2ecf20Sopenharmony_ci * intervals and tries to minimize the overall interval size.
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_cistatic void extend_address_range(u64 *start, u64 *stop, u64 estart, int len)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	u64 estop;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	if (len > 0)
248c2ecf20Sopenharmony_ci		len--;
258c2ecf20Sopenharmony_ci	else
268c2ecf20Sopenharmony_ci		len = 0;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	estop = estart + len;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	/* 0-0 range represents "not set" */
318c2ecf20Sopenharmony_ci	if ((*start == 0) && (*stop == 0)) {
328c2ecf20Sopenharmony_ci		*start = estart;
338c2ecf20Sopenharmony_ci		*stop = estop;
348c2ecf20Sopenharmony_ci	} else if (*start <= *stop) {
358c2ecf20Sopenharmony_ci		/* increase the existing range */
368c2ecf20Sopenharmony_ci		if (estart < *start)
378c2ecf20Sopenharmony_ci			*start = estart;
388c2ecf20Sopenharmony_ci		if (estop > *stop)
398c2ecf20Sopenharmony_ci			*stop = estop;
408c2ecf20Sopenharmony_ci	} else {
418c2ecf20Sopenharmony_ci		/* "overflowing" interval, whereby *stop > *start */
428c2ecf20Sopenharmony_ci		if (estart <= *stop) {
438c2ecf20Sopenharmony_ci			if (estop > *stop)
448c2ecf20Sopenharmony_ci				*stop = estop;
458c2ecf20Sopenharmony_ci		} else if (estop > *start) {
468c2ecf20Sopenharmony_ci			if (estart < *start)
478c2ecf20Sopenharmony_ci				*start = estart;
488c2ecf20Sopenharmony_ci		}
498c2ecf20Sopenharmony_ci		/* minimize the range */
508c2ecf20Sopenharmony_ci		else if ((estop - *stop) < (*start - estart))
518c2ecf20Sopenharmony_ci			*stop = estop;
528c2ecf20Sopenharmony_ci		else
538c2ecf20Sopenharmony_ci			*start = estart;
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define MAX_INST_SIZE 6
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic void enable_all_hw_bp(struct kvm_vcpu *vcpu)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	unsigned long start, len;
628c2ecf20Sopenharmony_ci	u64 *cr9 = &vcpu->arch.sie_block->gcr[9];
638c2ecf20Sopenharmony_ci	u64 *cr10 = &vcpu->arch.sie_block->gcr[10];
648c2ecf20Sopenharmony_ci	u64 *cr11 = &vcpu->arch.sie_block->gcr[11];
658c2ecf20Sopenharmony_ci	int i;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (vcpu->arch.guestdbg.nr_hw_bp <= 0 ||
688c2ecf20Sopenharmony_ci	    vcpu->arch.guestdbg.hw_bp_info == NULL)
698c2ecf20Sopenharmony_ci		return;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	/*
728c2ecf20Sopenharmony_ci	 * If the guest is not interested in branching events, we can safely
738c2ecf20Sopenharmony_ci	 * limit them to the PER address range.
748c2ecf20Sopenharmony_ci	 */
758c2ecf20Sopenharmony_ci	if (!(*cr9 & PER_EVENT_BRANCH))
768c2ecf20Sopenharmony_ci		*cr9 |= PER_CONTROL_BRANCH_ADDRESS;
778c2ecf20Sopenharmony_ci	*cr9 |= PER_EVENT_IFETCH | PER_EVENT_BRANCH;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	for (i = 0; i < vcpu->arch.guestdbg.nr_hw_bp; i++) {
808c2ecf20Sopenharmony_ci		start = vcpu->arch.guestdbg.hw_bp_info[i].addr;
818c2ecf20Sopenharmony_ci		len = vcpu->arch.guestdbg.hw_bp_info[i].len;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci		/*
848c2ecf20Sopenharmony_ci		 * The instruction in front of the desired bp has to
858c2ecf20Sopenharmony_ci		 * report instruction-fetching events
868c2ecf20Sopenharmony_ci		 */
878c2ecf20Sopenharmony_ci		if (start < MAX_INST_SIZE) {
888c2ecf20Sopenharmony_ci			len += start;
898c2ecf20Sopenharmony_ci			start = 0;
908c2ecf20Sopenharmony_ci		} else {
918c2ecf20Sopenharmony_ci			start -= MAX_INST_SIZE;
928c2ecf20Sopenharmony_ci			len += MAX_INST_SIZE;
938c2ecf20Sopenharmony_ci		}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci		extend_address_range(cr10, cr11, start, len);
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic void enable_all_hw_wp(struct kvm_vcpu *vcpu)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	unsigned long start, len;
1028c2ecf20Sopenharmony_ci	u64 *cr9 = &vcpu->arch.sie_block->gcr[9];
1038c2ecf20Sopenharmony_ci	u64 *cr10 = &vcpu->arch.sie_block->gcr[10];
1048c2ecf20Sopenharmony_ci	u64 *cr11 = &vcpu->arch.sie_block->gcr[11];
1058c2ecf20Sopenharmony_ci	int i;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (vcpu->arch.guestdbg.nr_hw_wp <= 0 ||
1088c2ecf20Sopenharmony_ci	    vcpu->arch.guestdbg.hw_wp_info == NULL)
1098c2ecf20Sopenharmony_ci		return;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* if host uses storage alternation for special address
1128c2ecf20Sopenharmony_ci	 * spaces, enable all events and give all to the guest */
1138c2ecf20Sopenharmony_ci	if (*cr9 & PER_EVENT_STORE && *cr9 & PER_CONTROL_ALTERATION) {
1148c2ecf20Sopenharmony_ci		*cr9 &= ~PER_CONTROL_ALTERATION;
1158c2ecf20Sopenharmony_ci		*cr10 = 0;
1168c2ecf20Sopenharmony_ci		*cr11 = -1UL;
1178c2ecf20Sopenharmony_ci	} else {
1188c2ecf20Sopenharmony_ci		*cr9 &= ~PER_CONTROL_ALTERATION;
1198c2ecf20Sopenharmony_ci		*cr9 |= PER_EVENT_STORE;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci		for (i = 0; i < vcpu->arch.guestdbg.nr_hw_wp; i++) {
1228c2ecf20Sopenharmony_ci			start = vcpu->arch.guestdbg.hw_wp_info[i].addr;
1238c2ecf20Sopenharmony_ci			len = vcpu->arch.guestdbg.hw_wp_info[i].len;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci			extend_address_range(cr10, cr11, start, len);
1268c2ecf20Sopenharmony_ci		}
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_civoid kvm_s390_backup_guest_per_regs(struct kvm_vcpu *vcpu)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	vcpu->arch.guestdbg.cr0 = vcpu->arch.sie_block->gcr[0];
1338c2ecf20Sopenharmony_ci	vcpu->arch.guestdbg.cr9 = vcpu->arch.sie_block->gcr[9];
1348c2ecf20Sopenharmony_ci	vcpu->arch.guestdbg.cr10 = vcpu->arch.sie_block->gcr[10];
1358c2ecf20Sopenharmony_ci	vcpu->arch.guestdbg.cr11 = vcpu->arch.sie_block->gcr[11];
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_civoid kvm_s390_restore_guest_per_regs(struct kvm_vcpu *vcpu)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	vcpu->arch.sie_block->gcr[0] = vcpu->arch.guestdbg.cr0;
1418c2ecf20Sopenharmony_ci	vcpu->arch.sie_block->gcr[9] = vcpu->arch.guestdbg.cr9;
1428c2ecf20Sopenharmony_ci	vcpu->arch.sie_block->gcr[10] = vcpu->arch.guestdbg.cr10;
1438c2ecf20Sopenharmony_ci	vcpu->arch.sie_block->gcr[11] = vcpu->arch.guestdbg.cr11;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_civoid kvm_s390_patch_guest_per_regs(struct kvm_vcpu *vcpu)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	/*
1498c2ecf20Sopenharmony_ci	 * TODO: if guest psw has per enabled, otherwise 0s!
1508c2ecf20Sopenharmony_ci	 * This reduces the amount of reported events.
1518c2ecf20Sopenharmony_ci	 * Need to intercept all psw changes!
1528c2ecf20Sopenharmony_ci	 */
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (guestdbg_sstep_enabled(vcpu)) {
1558c2ecf20Sopenharmony_ci		/* disable timer (clock-comparator) interrupts */
1568c2ecf20Sopenharmony_ci		vcpu->arch.sie_block->gcr[0] &= ~CR0_CLOCK_COMPARATOR_SUBMASK;
1578c2ecf20Sopenharmony_ci		vcpu->arch.sie_block->gcr[9] |= PER_EVENT_IFETCH;
1588c2ecf20Sopenharmony_ci		vcpu->arch.sie_block->gcr[10] = 0;
1598c2ecf20Sopenharmony_ci		vcpu->arch.sie_block->gcr[11] = -1UL;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (guestdbg_hw_bp_enabled(vcpu)) {
1638c2ecf20Sopenharmony_ci		enable_all_hw_bp(vcpu);
1648c2ecf20Sopenharmony_ci		enable_all_hw_wp(vcpu);
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/* TODO: Instruction-fetching-nullification not allowed for now */
1688c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->gcr[9] & PER_EVENT_NULLIFICATION)
1698c2ecf20Sopenharmony_ci		vcpu->arch.sie_block->gcr[9] &= ~PER_EVENT_NULLIFICATION;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci#define MAX_WP_SIZE 100
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic int __import_wp_info(struct kvm_vcpu *vcpu,
1758c2ecf20Sopenharmony_ci			    struct kvm_hw_breakpoint *bp_data,
1768c2ecf20Sopenharmony_ci			    struct kvm_hw_wp_info_arch *wp_info)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	int ret = 0;
1798c2ecf20Sopenharmony_ci	wp_info->len = bp_data->len;
1808c2ecf20Sopenharmony_ci	wp_info->addr = bp_data->addr;
1818c2ecf20Sopenharmony_ci	wp_info->phys_addr = bp_data->phys_addr;
1828c2ecf20Sopenharmony_ci	wp_info->old_data = NULL;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (wp_info->len < 0 || wp_info->len > MAX_WP_SIZE)
1858c2ecf20Sopenharmony_ci		return -EINVAL;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	wp_info->old_data = kmalloc(bp_data->len, GFP_KERNEL);
1888c2ecf20Sopenharmony_ci	if (!wp_info->old_data)
1898c2ecf20Sopenharmony_ci		return -ENOMEM;
1908c2ecf20Sopenharmony_ci	/* try to backup the original value */
1918c2ecf20Sopenharmony_ci	ret = read_guest_abs(vcpu, wp_info->phys_addr, wp_info->old_data,
1928c2ecf20Sopenharmony_ci			     wp_info->len);
1938c2ecf20Sopenharmony_ci	if (ret) {
1948c2ecf20Sopenharmony_ci		kfree(wp_info->old_data);
1958c2ecf20Sopenharmony_ci		wp_info->old_data = NULL;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	return ret;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci#define MAX_BP_COUNT 50
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ciint kvm_s390_import_bp_data(struct kvm_vcpu *vcpu,
2048c2ecf20Sopenharmony_ci			    struct kvm_guest_debug *dbg)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	int ret = 0, nr_wp = 0, nr_bp = 0, i;
2078c2ecf20Sopenharmony_ci	struct kvm_hw_breakpoint *bp_data = NULL;
2088c2ecf20Sopenharmony_ci	struct kvm_hw_wp_info_arch *wp_info = NULL;
2098c2ecf20Sopenharmony_ci	struct kvm_hw_bp_info_arch *bp_info = NULL;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (dbg->arch.nr_hw_bp <= 0 || !dbg->arch.hw_bp)
2128c2ecf20Sopenharmony_ci		return 0;
2138c2ecf20Sopenharmony_ci	else if (dbg->arch.nr_hw_bp > MAX_BP_COUNT)
2148c2ecf20Sopenharmony_ci		return -EINVAL;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	bp_data = memdup_user(dbg->arch.hw_bp,
2178c2ecf20Sopenharmony_ci			      sizeof(*bp_data) * dbg->arch.nr_hw_bp);
2188c2ecf20Sopenharmony_ci	if (IS_ERR(bp_data))
2198c2ecf20Sopenharmony_ci		return PTR_ERR(bp_data);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	for (i = 0; i < dbg->arch.nr_hw_bp; i++) {
2228c2ecf20Sopenharmony_ci		switch (bp_data[i].type) {
2238c2ecf20Sopenharmony_ci		case KVM_HW_WP_WRITE:
2248c2ecf20Sopenharmony_ci			nr_wp++;
2258c2ecf20Sopenharmony_ci			break;
2268c2ecf20Sopenharmony_ci		case KVM_HW_BP:
2278c2ecf20Sopenharmony_ci			nr_bp++;
2288c2ecf20Sopenharmony_ci			break;
2298c2ecf20Sopenharmony_ci		default:
2308c2ecf20Sopenharmony_ci			break;
2318c2ecf20Sopenharmony_ci		}
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (nr_wp > 0) {
2358c2ecf20Sopenharmony_ci		wp_info = kmalloc_array(nr_wp,
2368c2ecf20Sopenharmony_ci					sizeof(*wp_info),
2378c2ecf20Sopenharmony_ci					GFP_KERNEL);
2388c2ecf20Sopenharmony_ci		if (!wp_info) {
2398c2ecf20Sopenharmony_ci			ret = -ENOMEM;
2408c2ecf20Sopenharmony_ci			goto error;
2418c2ecf20Sopenharmony_ci		}
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci	if (nr_bp > 0) {
2448c2ecf20Sopenharmony_ci		bp_info = kmalloc_array(nr_bp,
2458c2ecf20Sopenharmony_ci					sizeof(*bp_info),
2468c2ecf20Sopenharmony_ci					GFP_KERNEL);
2478c2ecf20Sopenharmony_ci		if (!bp_info) {
2488c2ecf20Sopenharmony_ci			ret = -ENOMEM;
2498c2ecf20Sopenharmony_ci			goto error;
2508c2ecf20Sopenharmony_ci		}
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	for (nr_wp = 0, nr_bp = 0, i = 0; i < dbg->arch.nr_hw_bp; i++) {
2548c2ecf20Sopenharmony_ci		switch (bp_data[i].type) {
2558c2ecf20Sopenharmony_ci		case KVM_HW_WP_WRITE:
2568c2ecf20Sopenharmony_ci			ret = __import_wp_info(vcpu, &bp_data[i],
2578c2ecf20Sopenharmony_ci					       &wp_info[nr_wp]);
2588c2ecf20Sopenharmony_ci			if (ret)
2598c2ecf20Sopenharmony_ci				goto error;
2608c2ecf20Sopenharmony_ci			nr_wp++;
2618c2ecf20Sopenharmony_ci			break;
2628c2ecf20Sopenharmony_ci		case KVM_HW_BP:
2638c2ecf20Sopenharmony_ci			bp_info[nr_bp].len = bp_data[i].len;
2648c2ecf20Sopenharmony_ci			bp_info[nr_bp].addr = bp_data[i].addr;
2658c2ecf20Sopenharmony_ci			nr_bp++;
2668c2ecf20Sopenharmony_ci			break;
2678c2ecf20Sopenharmony_ci		}
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	vcpu->arch.guestdbg.nr_hw_bp = nr_bp;
2718c2ecf20Sopenharmony_ci	vcpu->arch.guestdbg.hw_bp_info = bp_info;
2728c2ecf20Sopenharmony_ci	vcpu->arch.guestdbg.nr_hw_wp = nr_wp;
2738c2ecf20Sopenharmony_ci	vcpu->arch.guestdbg.hw_wp_info = wp_info;
2748c2ecf20Sopenharmony_ci	return 0;
2758c2ecf20Sopenharmony_cierror:
2768c2ecf20Sopenharmony_ci	kfree(bp_data);
2778c2ecf20Sopenharmony_ci	kfree(wp_info);
2788c2ecf20Sopenharmony_ci	kfree(bp_info);
2798c2ecf20Sopenharmony_ci	return ret;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_civoid kvm_s390_clear_bp_data(struct kvm_vcpu *vcpu)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	int i;
2858c2ecf20Sopenharmony_ci	struct kvm_hw_wp_info_arch *hw_wp_info = NULL;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	for (i = 0; i < vcpu->arch.guestdbg.nr_hw_wp; i++) {
2888c2ecf20Sopenharmony_ci		hw_wp_info = &vcpu->arch.guestdbg.hw_wp_info[i];
2898c2ecf20Sopenharmony_ci		kfree(hw_wp_info->old_data);
2908c2ecf20Sopenharmony_ci		hw_wp_info->old_data = NULL;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci	kfree(vcpu->arch.guestdbg.hw_wp_info);
2938c2ecf20Sopenharmony_ci	vcpu->arch.guestdbg.hw_wp_info = NULL;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	kfree(vcpu->arch.guestdbg.hw_bp_info);
2968c2ecf20Sopenharmony_ci	vcpu->arch.guestdbg.hw_bp_info = NULL;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	vcpu->arch.guestdbg.nr_hw_wp = 0;
2998c2ecf20Sopenharmony_ci	vcpu->arch.guestdbg.nr_hw_bp = 0;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic inline int in_addr_range(u64 addr, u64 a, u64 b)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	if (a <= b)
3058c2ecf20Sopenharmony_ci		return (addr >= a) && (addr <= b);
3068c2ecf20Sopenharmony_ci	else
3078c2ecf20Sopenharmony_ci		/* "overflowing" interval */
3088c2ecf20Sopenharmony_ci		return (addr >= a) || (addr <= b);
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci#define end_of_range(bp_info) (bp_info->addr + bp_info->len - 1)
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic struct kvm_hw_bp_info_arch *find_hw_bp(struct kvm_vcpu *vcpu,
3148c2ecf20Sopenharmony_ci					      unsigned long addr)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	struct kvm_hw_bp_info_arch *bp_info = vcpu->arch.guestdbg.hw_bp_info;
3178c2ecf20Sopenharmony_ci	int i;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (vcpu->arch.guestdbg.nr_hw_bp == 0)
3208c2ecf20Sopenharmony_ci		return NULL;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	for (i = 0; i < vcpu->arch.guestdbg.nr_hw_bp; i++) {
3238c2ecf20Sopenharmony_ci		/* addr is directly the start or in the range of a bp */
3248c2ecf20Sopenharmony_ci		if (addr == bp_info->addr)
3258c2ecf20Sopenharmony_ci			goto found;
3268c2ecf20Sopenharmony_ci		if (bp_info->len > 0 &&
3278c2ecf20Sopenharmony_ci		    in_addr_range(addr, bp_info->addr, end_of_range(bp_info)))
3288c2ecf20Sopenharmony_ci			goto found;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci		bp_info++;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	return NULL;
3348c2ecf20Sopenharmony_cifound:
3358c2ecf20Sopenharmony_ci	return bp_info;
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic struct kvm_hw_wp_info_arch *any_wp_changed(struct kvm_vcpu *vcpu)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	int i;
3418c2ecf20Sopenharmony_ci	struct kvm_hw_wp_info_arch *wp_info = NULL;
3428c2ecf20Sopenharmony_ci	void *temp = NULL;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	if (vcpu->arch.guestdbg.nr_hw_wp == 0)
3458c2ecf20Sopenharmony_ci		return NULL;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	for (i = 0; i < vcpu->arch.guestdbg.nr_hw_wp; i++) {
3488c2ecf20Sopenharmony_ci		wp_info = &vcpu->arch.guestdbg.hw_wp_info[i];
3498c2ecf20Sopenharmony_ci		if (!wp_info || !wp_info->old_data || wp_info->len <= 0)
3508c2ecf20Sopenharmony_ci			continue;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci		temp = kmalloc(wp_info->len, GFP_KERNEL);
3538c2ecf20Sopenharmony_ci		if (!temp)
3548c2ecf20Sopenharmony_ci			continue;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci		/* refetch the wp data and compare it to the old value */
3578c2ecf20Sopenharmony_ci		if (!read_guest_abs(vcpu, wp_info->phys_addr, temp,
3588c2ecf20Sopenharmony_ci				    wp_info->len)) {
3598c2ecf20Sopenharmony_ci			if (memcmp(temp, wp_info->old_data, wp_info->len)) {
3608c2ecf20Sopenharmony_ci				kfree(temp);
3618c2ecf20Sopenharmony_ci				return wp_info;
3628c2ecf20Sopenharmony_ci			}
3638c2ecf20Sopenharmony_ci		}
3648c2ecf20Sopenharmony_ci		kfree(temp);
3658c2ecf20Sopenharmony_ci		temp = NULL;
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	return NULL;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_civoid kvm_s390_prepare_debug_exit(struct kvm_vcpu *vcpu)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	vcpu->run->exit_reason = KVM_EXIT_DEBUG;
3748c2ecf20Sopenharmony_ci	vcpu->guest_debug &= ~KVM_GUESTDBG_EXIT_PENDING;
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci#define PER_CODE_MASK		(PER_EVENT_MASK >> 24)
3788c2ecf20Sopenharmony_ci#define PER_CODE_BRANCH		(PER_EVENT_BRANCH >> 24)
3798c2ecf20Sopenharmony_ci#define PER_CODE_IFETCH		(PER_EVENT_IFETCH >> 24)
3808c2ecf20Sopenharmony_ci#define PER_CODE_STORE		(PER_EVENT_STORE >> 24)
3818c2ecf20Sopenharmony_ci#define PER_CODE_STORE_REAL	(PER_EVENT_STORE_REAL >> 24)
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci#define per_bp_event(code) \
3848c2ecf20Sopenharmony_ci			(code & (PER_CODE_IFETCH | PER_CODE_BRANCH))
3858c2ecf20Sopenharmony_ci#define per_write_wp_event(code) \
3868c2ecf20Sopenharmony_ci			(code & (PER_CODE_STORE | PER_CODE_STORE_REAL))
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic int debug_exit_required(struct kvm_vcpu *vcpu, u8 perc,
3898c2ecf20Sopenharmony_ci			       unsigned long peraddr)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct kvm_debug_exit_arch *debug_exit = &vcpu->run->debug.arch;
3928c2ecf20Sopenharmony_ci	struct kvm_hw_wp_info_arch *wp_info = NULL;
3938c2ecf20Sopenharmony_ci	struct kvm_hw_bp_info_arch *bp_info = NULL;
3948c2ecf20Sopenharmony_ci	unsigned long addr = vcpu->arch.sie_block->gpsw.addr;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	if (guestdbg_hw_bp_enabled(vcpu)) {
3978c2ecf20Sopenharmony_ci		if (per_write_wp_event(perc) &&
3988c2ecf20Sopenharmony_ci		    vcpu->arch.guestdbg.nr_hw_wp > 0) {
3998c2ecf20Sopenharmony_ci			wp_info = any_wp_changed(vcpu);
4008c2ecf20Sopenharmony_ci			if (wp_info) {
4018c2ecf20Sopenharmony_ci				debug_exit->addr = wp_info->addr;
4028c2ecf20Sopenharmony_ci				debug_exit->type = KVM_HW_WP_WRITE;
4038c2ecf20Sopenharmony_ci				goto exit_required;
4048c2ecf20Sopenharmony_ci			}
4058c2ecf20Sopenharmony_ci		}
4068c2ecf20Sopenharmony_ci		if (per_bp_event(perc) &&
4078c2ecf20Sopenharmony_ci			 vcpu->arch.guestdbg.nr_hw_bp > 0) {
4088c2ecf20Sopenharmony_ci			bp_info = find_hw_bp(vcpu, addr);
4098c2ecf20Sopenharmony_ci			/* remove duplicate events if PC==PER address */
4108c2ecf20Sopenharmony_ci			if (bp_info && (addr != peraddr)) {
4118c2ecf20Sopenharmony_ci				debug_exit->addr = addr;
4128c2ecf20Sopenharmony_ci				debug_exit->type = KVM_HW_BP;
4138c2ecf20Sopenharmony_ci				vcpu->arch.guestdbg.last_bp = addr;
4148c2ecf20Sopenharmony_ci				goto exit_required;
4158c2ecf20Sopenharmony_ci			}
4168c2ecf20Sopenharmony_ci			/* breakpoint missed */
4178c2ecf20Sopenharmony_ci			bp_info = find_hw_bp(vcpu, peraddr);
4188c2ecf20Sopenharmony_ci			if (bp_info && vcpu->arch.guestdbg.last_bp != peraddr) {
4198c2ecf20Sopenharmony_ci				debug_exit->addr = peraddr;
4208c2ecf20Sopenharmony_ci				debug_exit->type = KVM_HW_BP;
4218c2ecf20Sopenharmony_ci				goto exit_required;
4228c2ecf20Sopenharmony_ci			}
4238c2ecf20Sopenharmony_ci		}
4248c2ecf20Sopenharmony_ci	}
4258c2ecf20Sopenharmony_ci	if (guestdbg_sstep_enabled(vcpu) && per_bp_event(perc)) {
4268c2ecf20Sopenharmony_ci		debug_exit->addr = addr;
4278c2ecf20Sopenharmony_ci		debug_exit->type = KVM_SINGLESTEP;
4288c2ecf20Sopenharmony_ci		goto exit_required;
4298c2ecf20Sopenharmony_ci	}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	return 0;
4328c2ecf20Sopenharmony_ciexit_required:
4338c2ecf20Sopenharmony_ci	return 1;
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cistatic int per_fetched_addr(struct kvm_vcpu *vcpu, unsigned long *addr)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	u8 exec_ilen = 0;
4398c2ecf20Sopenharmony_ci	u16 opcode[3];
4408c2ecf20Sopenharmony_ci	int rc;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->icptcode == ICPT_PROGI) {
4438c2ecf20Sopenharmony_ci		/* PER address references the fetched or the execute instr */
4448c2ecf20Sopenharmony_ci		*addr = vcpu->arch.sie_block->peraddr;
4458c2ecf20Sopenharmony_ci		/*
4468c2ecf20Sopenharmony_ci		 * Manually detect if we have an EXECUTE instruction. As
4478c2ecf20Sopenharmony_ci		 * instructions are always 2 byte aligned we can read the
4488c2ecf20Sopenharmony_ci		 * first two bytes unconditionally
4498c2ecf20Sopenharmony_ci		 */
4508c2ecf20Sopenharmony_ci		rc = read_guest_instr(vcpu, *addr, &opcode, 2);
4518c2ecf20Sopenharmony_ci		if (rc)
4528c2ecf20Sopenharmony_ci			return rc;
4538c2ecf20Sopenharmony_ci		if (opcode[0] >> 8 == 0x44)
4548c2ecf20Sopenharmony_ci			exec_ilen = 4;
4558c2ecf20Sopenharmony_ci		if ((opcode[0] & 0xff0f) == 0xc600)
4568c2ecf20Sopenharmony_ci			exec_ilen = 6;
4578c2ecf20Sopenharmony_ci	} else {
4588c2ecf20Sopenharmony_ci		/* instr was suppressed, calculate the responsible instr */
4598c2ecf20Sopenharmony_ci		*addr = __rewind_psw(vcpu->arch.sie_block->gpsw,
4608c2ecf20Sopenharmony_ci				     kvm_s390_get_ilen(vcpu));
4618c2ecf20Sopenharmony_ci		if (vcpu->arch.sie_block->icptstatus & 0x01) {
4628c2ecf20Sopenharmony_ci			exec_ilen = (vcpu->arch.sie_block->icptstatus & 0x60) >> 4;
4638c2ecf20Sopenharmony_ci			if (!exec_ilen)
4648c2ecf20Sopenharmony_ci				exec_ilen = 4;
4658c2ecf20Sopenharmony_ci		}
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	if (exec_ilen) {
4698c2ecf20Sopenharmony_ci		/* read the complete EXECUTE instr to detect the fetched addr */
4708c2ecf20Sopenharmony_ci		rc = read_guest_instr(vcpu, *addr, &opcode, exec_ilen);
4718c2ecf20Sopenharmony_ci		if (rc)
4728c2ecf20Sopenharmony_ci			return rc;
4738c2ecf20Sopenharmony_ci		if (exec_ilen == 6) {
4748c2ecf20Sopenharmony_ci			/* EXECUTE RELATIVE LONG - RIL-b format */
4758c2ecf20Sopenharmony_ci			s32 rl = *((s32 *) (opcode + 1));
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci			/* rl is a _signed_ 32 bit value specifying halfwords */
4788c2ecf20Sopenharmony_ci			*addr += (u64)(s64) rl * 2;
4798c2ecf20Sopenharmony_ci		} else {
4808c2ecf20Sopenharmony_ci			/* EXECUTE - RX-a format */
4818c2ecf20Sopenharmony_ci			u32 base = (opcode[1] & 0xf000) >> 12;
4828c2ecf20Sopenharmony_ci			u32 disp = opcode[1] & 0x0fff;
4838c2ecf20Sopenharmony_ci			u32 index = opcode[0] & 0x000f;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci			*addr = base ? vcpu->run->s.regs.gprs[base] : 0;
4868c2ecf20Sopenharmony_ci			*addr += index ? vcpu->run->s.regs.gprs[index] : 0;
4878c2ecf20Sopenharmony_ci			*addr += disp;
4888c2ecf20Sopenharmony_ci		}
4898c2ecf20Sopenharmony_ci		*addr = kvm_s390_logical_to_effective(vcpu, *addr);
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci	return 0;
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci#define guest_per_enabled(vcpu) \
4958c2ecf20Sopenharmony_ci			     (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER)
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ciint kvm_s390_handle_per_ifetch_icpt(struct kvm_vcpu *vcpu)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	const u64 cr10 = vcpu->arch.sie_block->gcr[10];
5008c2ecf20Sopenharmony_ci	const u64 cr11 = vcpu->arch.sie_block->gcr[11];
5018c2ecf20Sopenharmony_ci	const u8 ilen = kvm_s390_get_ilen(vcpu);
5028c2ecf20Sopenharmony_ci	struct kvm_s390_pgm_info pgm_info = {
5038c2ecf20Sopenharmony_ci		.code = PGM_PER,
5048c2ecf20Sopenharmony_ci		.per_code = PER_CODE_IFETCH,
5058c2ecf20Sopenharmony_ci		.per_address = __rewind_psw(vcpu->arch.sie_block->gpsw, ilen),
5068c2ecf20Sopenharmony_ci	};
5078c2ecf20Sopenharmony_ci	unsigned long fetched_addr;
5088c2ecf20Sopenharmony_ci	int rc;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	/*
5118c2ecf20Sopenharmony_ci	 * The PSW points to the next instruction, therefore the intercepted
5128c2ecf20Sopenharmony_ci	 * instruction generated a PER i-fetch event. PER address therefore
5138c2ecf20Sopenharmony_ci	 * points at the previous PSW address (could be an EXECUTE function).
5148c2ecf20Sopenharmony_ci	 */
5158c2ecf20Sopenharmony_ci	if (!guestdbg_enabled(vcpu))
5168c2ecf20Sopenharmony_ci		return kvm_s390_inject_prog_irq(vcpu, &pgm_info);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	if (debug_exit_required(vcpu, pgm_info.per_code, pgm_info.per_address))
5198c2ecf20Sopenharmony_ci		vcpu->guest_debug |= KVM_GUESTDBG_EXIT_PENDING;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (!guest_per_enabled(vcpu) ||
5228c2ecf20Sopenharmony_ci	    !(vcpu->arch.sie_block->gcr[9] & PER_EVENT_IFETCH))
5238c2ecf20Sopenharmony_ci		return 0;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	rc = per_fetched_addr(vcpu, &fetched_addr);
5268c2ecf20Sopenharmony_ci	if (rc < 0)
5278c2ecf20Sopenharmony_ci		return rc;
5288c2ecf20Sopenharmony_ci	if (rc)
5298c2ecf20Sopenharmony_ci		/* instruction-fetching exceptions */
5308c2ecf20Sopenharmony_ci		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	if (in_addr_range(fetched_addr, cr10, cr11))
5338c2ecf20Sopenharmony_ci		return kvm_s390_inject_prog_irq(vcpu, &pgm_info);
5348c2ecf20Sopenharmony_ci	return 0;
5358c2ecf20Sopenharmony_ci}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cistatic int filter_guest_per_event(struct kvm_vcpu *vcpu)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	const u8 perc = vcpu->arch.sie_block->perc;
5408c2ecf20Sopenharmony_ci	u64 addr = vcpu->arch.sie_block->gpsw.addr;
5418c2ecf20Sopenharmony_ci	u64 cr9 = vcpu->arch.sie_block->gcr[9];
5428c2ecf20Sopenharmony_ci	u64 cr10 = vcpu->arch.sie_block->gcr[10];
5438c2ecf20Sopenharmony_ci	u64 cr11 = vcpu->arch.sie_block->gcr[11];
5448c2ecf20Sopenharmony_ci	/* filter all events, demanded by the guest */
5458c2ecf20Sopenharmony_ci	u8 guest_perc = perc & (cr9 >> 24) & PER_CODE_MASK;
5468c2ecf20Sopenharmony_ci	unsigned long fetched_addr;
5478c2ecf20Sopenharmony_ci	int rc;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	if (!guest_per_enabled(vcpu))
5508c2ecf20Sopenharmony_ci		guest_perc = 0;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	/* filter "successful-branching" events */
5538c2ecf20Sopenharmony_ci	if (guest_perc & PER_CODE_BRANCH &&
5548c2ecf20Sopenharmony_ci	    cr9 & PER_CONTROL_BRANCH_ADDRESS &&
5558c2ecf20Sopenharmony_ci	    !in_addr_range(addr, cr10, cr11))
5568c2ecf20Sopenharmony_ci		guest_perc &= ~PER_CODE_BRANCH;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	/* filter "instruction-fetching" events */
5598c2ecf20Sopenharmony_ci	if (guest_perc & PER_CODE_IFETCH) {
5608c2ecf20Sopenharmony_ci		rc = per_fetched_addr(vcpu, &fetched_addr);
5618c2ecf20Sopenharmony_ci		if (rc < 0)
5628c2ecf20Sopenharmony_ci			return rc;
5638c2ecf20Sopenharmony_ci		/*
5648c2ecf20Sopenharmony_ci		 * Don't inject an irq on exceptions. This would make handling
5658c2ecf20Sopenharmony_ci		 * on icpt code 8 very complex (as PSW was already rewound).
5668c2ecf20Sopenharmony_ci		 */
5678c2ecf20Sopenharmony_ci		if (rc || !in_addr_range(fetched_addr, cr10, cr11))
5688c2ecf20Sopenharmony_ci			guest_perc &= ~PER_CODE_IFETCH;
5698c2ecf20Sopenharmony_ci	}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	/* All other PER events will be given to the guest */
5728c2ecf20Sopenharmony_ci	/* TODO: Check altered address/address space */
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	vcpu->arch.sie_block->perc = guest_perc;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	if (!guest_perc)
5778c2ecf20Sopenharmony_ci		vcpu->arch.sie_block->iprcc &= ~PGM_PER;
5788c2ecf20Sopenharmony_ci	return 0;
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci#define pssec(vcpu) (vcpu->arch.sie_block->gcr[1] & _ASCE_SPACE_SWITCH)
5828c2ecf20Sopenharmony_ci#define hssec(vcpu) (vcpu->arch.sie_block->gcr[13] & _ASCE_SPACE_SWITCH)
5838c2ecf20Sopenharmony_ci#define old_ssec(vcpu) ((vcpu->arch.sie_block->tecmc >> 31) & 0x1)
5848c2ecf20Sopenharmony_ci#define old_as_is_home(vcpu) !(vcpu->arch.sie_block->tecmc & 0xffff)
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ciint kvm_s390_handle_per_event(struct kvm_vcpu *vcpu)
5878c2ecf20Sopenharmony_ci{
5888c2ecf20Sopenharmony_ci	int rc, new_as;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	if (debug_exit_required(vcpu, vcpu->arch.sie_block->perc,
5918c2ecf20Sopenharmony_ci				vcpu->arch.sie_block->peraddr))
5928c2ecf20Sopenharmony_ci		vcpu->guest_debug |= KVM_GUESTDBG_EXIT_PENDING;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	rc = filter_guest_per_event(vcpu);
5958c2ecf20Sopenharmony_ci	if (rc)
5968c2ecf20Sopenharmony_ci		return rc;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	/*
5998c2ecf20Sopenharmony_ci	 * Only RP, SAC, SACF, PT, PTI, PR, PC instructions can trigger
6008c2ecf20Sopenharmony_ci	 * a space-switch event. PER events enforce space-switch events
6018c2ecf20Sopenharmony_ci	 * for these instructions. So if no PER event for the guest is left,
6028c2ecf20Sopenharmony_ci	 * we might have to filter the space-switch element out, too.
6038c2ecf20Sopenharmony_ci	 */
6048c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->iprcc == PGM_SPACE_SWITCH) {
6058c2ecf20Sopenharmony_ci		vcpu->arch.sie_block->iprcc = 0;
6068c2ecf20Sopenharmony_ci		new_as = psw_bits(vcpu->arch.sie_block->gpsw).as;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci		/*
6098c2ecf20Sopenharmony_ci		 * If the AS changed from / to home, we had RP, SAC or SACF
6108c2ecf20Sopenharmony_ci		 * instruction. Check primary and home space-switch-event
6118c2ecf20Sopenharmony_ci		 * controls. (theoretically home -> home produced no event)
6128c2ecf20Sopenharmony_ci		 */
6138c2ecf20Sopenharmony_ci		if (((new_as == PSW_BITS_AS_HOME) ^ old_as_is_home(vcpu)) &&
6148c2ecf20Sopenharmony_ci		    (pssec(vcpu) || hssec(vcpu)))
6158c2ecf20Sopenharmony_ci			vcpu->arch.sie_block->iprcc = PGM_SPACE_SWITCH;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci		/*
6188c2ecf20Sopenharmony_ci		 * PT, PTI, PR, PC instruction operate on primary AS only. Check
6198c2ecf20Sopenharmony_ci		 * if the primary-space-switch-event control was or got set.
6208c2ecf20Sopenharmony_ci		 */
6218c2ecf20Sopenharmony_ci		if (new_as == PSW_BITS_AS_PRIMARY && !old_as_is_home(vcpu) &&
6228c2ecf20Sopenharmony_ci		    (pssec(vcpu) || old_ssec(vcpu)))
6238c2ecf20Sopenharmony_ci			vcpu->arch.sie_block->iprcc = PGM_SPACE_SWITCH;
6248c2ecf20Sopenharmony_ci	}
6258c2ecf20Sopenharmony_ci	return 0;
6268c2ecf20Sopenharmony_ci}
627