162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * kvm guest debug support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2014 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author(s): David Hildenbrand <dahi@linux.vnet.ibm.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/kvm_host.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include "kvm-s390.h" 1262306a36Sopenharmony_ci#include "gaccess.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * Extends the address range given by *start and *stop to include the address 1662306a36Sopenharmony_ci * range starting with estart and the length len. Takes care of overflowing 1762306a36Sopenharmony_ci * intervals and tries to minimize the overall interval size. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_cistatic void extend_address_range(u64 *start, u64 *stop, u64 estart, int len) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci u64 estop; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci if (len > 0) 2462306a36Sopenharmony_ci len--; 2562306a36Sopenharmony_ci else 2662306a36Sopenharmony_ci len = 0; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci estop = estart + len; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci /* 0-0 range represents "not set" */ 3162306a36Sopenharmony_ci if ((*start == 0) && (*stop == 0)) { 3262306a36Sopenharmony_ci *start = estart; 3362306a36Sopenharmony_ci *stop = estop; 3462306a36Sopenharmony_ci } else if (*start <= *stop) { 3562306a36Sopenharmony_ci /* increase the existing range */ 3662306a36Sopenharmony_ci if (estart < *start) 3762306a36Sopenharmony_ci *start = estart; 3862306a36Sopenharmony_ci if (estop > *stop) 3962306a36Sopenharmony_ci *stop = estop; 4062306a36Sopenharmony_ci } else { 4162306a36Sopenharmony_ci /* "overflowing" interval, whereby *stop > *start */ 4262306a36Sopenharmony_ci if (estart <= *stop) { 4362306a36Sopenharmony_ci if (estop > *stop) 4462306a36Sopenharmony_ci *stop = estop; 4562306a36Sopenharmony_ci } else if (estop > *start) { 4662306a36Sopenharmony_ci if (estart < *start) 4762306a36Sopenharmony_ci *start = estart; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci /* minimize the range */ 5062306a36Sopenharmony_ci else if ((estop - *stop) < (*start - estart)) 5162306a36Sopenharmony_ci *stop = estop; 5262306a36Sopenharmony_ci else 5362306a36Sopenharmony_ci *start = estart; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define MAX_INST_SIZE 6 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void enable_all_hw_bp(struct kvm_vcpu *vcpu) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci unsigned long start, len; 6262306a36Sopenharmony_ci u64 *cr9 = &vcpu->arch.sie_block->gcr[9]; 6362306a36Sopenharmony_ci u64 *cr10 = &vcpu->arch.sie_block->gcr[10]; 6462306a36Sopenharmony_ci u64 *cr11 = &vcpu->arch.sie_block->gcr[11]; 6562306a36Sopenharmony_ci int i; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (vcpu->arch.guestdbg.nr_hw_bp <= 0 || 6862306a36Sopenharmony_ci vcpu->arch.guestdbg.hw_bp_info == NULL) 6962306a36Sopenharmony_ci return; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* 7262306a36Sopenharmony_ci * If the guest is not interested in branching events, we can safely 7362306a36Sopenharmony_ci * limit them to the PER address range. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci if (!(*cr9 & PER_EVENT_BRANCH)) 7662306a36Sopenharmony_ci *cr9 |= PER_CONTROL_BRANCH_ADDRESS; 7762306a36Sopenharmony_ci *cr9 |= PER_EVENT_IFETCH | PER_EVENT_BRANCH; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci for (i = 0; i < vcpu->arch.guestdbg.nr_hw_bp; i++) { 8062306a36Sopenharmony_ci start = vcpu->arch.guestdbg.hw_bp_info[i].addr; 8162306a36Sopenharmony_ci len = vcpu->arch.guestdbg.hw_bp_info[i].len; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* 8462306a36Sopenharmony_ci * The instruction in front of the desired bp has to 8562306a36Sopenharmony_ci * report instruction-fetching events 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci if (start < MAX_INST_SIZE) { 8862306a36Sopenharmony_ci len += start; 8962306a36Sopenharmony_ci start = 0; 9062306a36Sopenharmony_ci } else { 9162306a36Sopenharmony_ci start -= MAX_INST_SIZE; 9262306a36Sopenharmony_ci len += MAX_INST_SIZE; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci extend_address_range(cr10, cr11, start, len); 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic void enable_all_hw_wp(struct kvm_vcpu *vcpu) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci unsigned long start, len; 10262306a36Sopenharmony_ci u64 *cr9 = &vcpu->arch.sie_block->gcr[9]; 10362306a36Sopenharmony_ci u64 *cr10 = &vcpu->arch.sie_block->gcr[10]; 10462306a36Sopenharmony_ci u64 *cr11 = &vcpu->arch.sie_block->gcr[11]; 10562306a36Sopenharmony_ci int i; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (vcpu->arch.guestdbg.nr_hw_wp <= 0 || 10862306a36Sopenharmony_ci vcpu->arch.guestdbg.hw_wp_info == NULL) 10962306a36Sopenharmony_ci return; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* if host uses storage alternation for special address 11262306a36Sopenharmony_ci * spaces, enable all events and give all to the guest */ 11362306a36Sopenharmony_ci if (*cr9 & PER_EVENT_STORE && *cr9 & PER_CONTROL_ALTERATION) { 11462306a36Sopenharmony_ci *cr9 &= ~PER_CONTROL_ALTERATION; 11562306a36Sopenharmony_ci *cr10 = 0; 11662306a36Sopenharmony_ci *cr11 = -1UL; 11762306a36Sopenharmony_ci } else { 11862306a36Sopenharmony_ci *cr9 &= ~PER_CONTROL_ALTERATION; 11962306a36Sopenharmony_ci *cr9 |= PER_EVENT_STORE; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci for (i = 0; i < vcpu->arch.guestdbg.nr_hw_wp; i++) { 12262306a36Sopenharmony_ci start = vcpu->arch.guestdbg.hw_wp_info[i].addr; 12362306a36Sopenharmony_ci len = vcpu->arch.guestdbg.hw_wp_info[i].len; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci extend_address_range(cr10, cr11, start, len); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_civoid kvm_s390_backup_guest_per_regs(struct kvm_vcpu *vcpu) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci vcpu->arch.guestdbg.cr0 = vcpu->arch.sie_block->gcr[0]; 13362306a36Sopenharmony_ci vcpu->arch.guestdbg.cr9 = vcpu->arch.sie_block->gcr[9]; 13462306a36Sopenharmony_ci vcpu->arch.guestdbg.cr10 = vcpu->arch.sie_block->gcr[10]; 13562306a36Sopenharmony_ci vcpu->arch.guestdbg.cr11 = vcpu->arch.sie_block->gcr[11]; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_civoid kvm_s390_restore_guest_per_regs(struct kvm_vcpu *vcpu) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci vcpu->arch.sie_block->gcr[0] = vcpu->arch.guestdbg.cr0; 14162306a36Sopenharmony_ci vcpu->arch.sie_block->gcr[9] = vcpu->arch.guestdbg.cr9; 14262306a36Sopenharmony_ci vcpu->arch.sie_block->gcr[10] = vcpu->arch.guestdbg.cr10; 14362306a36Sopenharmony_ci vcpu->arch.sie_block->gcr[11] = vcpu->arch.guestdbg.cr11; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_civoid kvm_s390_patch_guest_per_regs(struct kvm_vcpu *vcpu) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci /* 14962306a36Sopenharmony_ci * TODO: if guest psw has per enabled, otherwise 0s! 15062306a36Sopenharmony_ci * This reduces the amount of reported events. 15162306a36Sopenharmony_ci * Need to intercept all psw changes! 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (guestdbg_sstep_enabled(vcpu)) { 15562306a36Sopenharmony_ci /* disable timer (clock-comparator) interrupts */ 15662306a36Sopenharmony_ci vcpu->arch.sie_block->gcr[0] &= ~CR0_CLOCK_COMPARATOR_SUBMASK; 15762306a36Sopenharmony_ci vcpu->arch.sie_block->gcr[9] |= PER_EVENT_IFETCH; 15862306a36Sopenharmony_ci vcpu->arch.sie_block->gcr[10] = 0; 15962306a36Sopenharmony_ci vcpu->arch.sie_block->gcr[11] = -1UL; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (guestdbg_hw_bp_enabled(vcpu)) { 16362306a36Sopenharmony_ci enable_all_hw_bp(vcpu); 16462306a36Sopenharmony_ci enable_all_hw_wp(vcpu); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* TODO: Instruction-fetching-nullification not allowed for now */ 16862306a36Sopenharmony_ci if (vcpu->arch.sie_block->gcr[9] & PER_EVENT_NULLIFICATION) 16962306a36Sopenharmony_ci vcpu->arch.sie_block->gcr[9] &= ~PER_EVENT_NULLIFICATION; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci#define MAX_WP_SIZE 100 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int __import_wp_info(struct kvm_vcpu *vcpu, 17562306a36Sopenharmony_ci struct kvm_hw_breakpoint *bp_data, 17662306a36Sopenharmony_ci struct kvm_hw_wp_info_arch *wp_info) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci int ret = 0; 17962306a36Sopenharmony_ci wp_info->len = bp_data->len; 18062306a36Sopenharmony_ci wp_info->addr = bp_data->addr; 18162306a36Sopenharmony_ci wp_info->phys_addr = bp_data->phys_addr; 18262306a36Sopenharmony_ci wp_info->old_data = NULL; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (wp_info->len < 0 || wp_info->len > MAX_WP_SIZE) 18562306a36Sopenharmony_ci return -EINVAL; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci wp_info->old_data = kmalloc(bp_data->len, GFP_KERNEL_ACCOUNT); 18862306a36Sopenharmony_ci if (!wp_info->old_data) 18962306a36Sopenharmony_ci return -ENOMEM; 19062306a36Sopenharmony_ci /* try to backup the original value */ 19162306a36Sopenharmony_ci ret = read_guest_abs(vcpu, wp_info->phys_addr, wp_info->old_data, 19262306a36Sopenharmony_ci wp_info->len); 19362306a36Sopenharmony_ci if (ret) { 19462306a36Sopenharmony_ci kfree(wp_info->old_data); 19562306a36Sopenharmony_ci wp_info->old_data = NULL; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return ret; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci#define MAX_BP_COUNT 50 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ciint kvm_s390_import_bp_data(struct kvm_vcpu *vcpu, 20462306a36Sopenharmony_ci struct kvm_guest_debug *dbg) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci int ret = 0, nr_wp = 0, nr_bp = 0, i; 20762306a36Sopenharmony_ci struct kvm_hw_breakpoint *bp_data = NULL; 20862306a36Sopenharmony_ci struct kvm_hw_wp_info_arch *wp_info = NULL; 20962306a36Sopenharmony_ci struct kvm_hw_bp_info_arch *bp_info = NULL; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (dbg->arch.nr_hw_bp <= 0 || !dbg->arch.hw_bp) 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci else if (dbg->arch.nr_hw_bp > MAX_BP_COUNT) 21462306a36Sopenharmony_ci return -EINVAL; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci bp_data = memdup_user(dbg->arch.hw_bp, 21762306a36Sopenharmony_ci sizeof(*bp_data) * dbg->arch.nr_hw_bp); 21862306a36Sopenharmony_ci if (IS_ERR(bp_data)) 21962306a36Sopenharmony_ci return PTR_ERR(bp_data); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci for (i = 0; i < dbg->arch.nr_hw_bp; i++) { 22262306a36Sopenharmony_ci switch (bp_data[i].type) { 22362306a36Sopenharmony_ci case KVM_HW_WP_WRITE: 22462306a36Sopenharmony_ci nr_wp++; 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci case KVM_HW_BP: 22762306a36Sopenharmony_ci nr_bp++; 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci default: 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (nr_wp > 0) { 23562306a36Sopenharmony_ci wp_info = kmalloc_array(nr_wp, 23662306a36Sopenharmony_ci sizeof(*wp_info), 23762306a36Sopenharmony_ci GFP_KERNEL_ACCOUNT); 23862306a36Sopenharmony_ci if (!wp_info) { 23962306a36Sopenharmony_ci ret = -ENOMEM; 24062306a36Sopenharmony_ci goto error; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci if (nr_bp > 0) { 24462306a36Sopenharmony_ci bp_info = kmalloc_array(nr_bp, 24562306a36Sopenharmony_ci sizeof(*bp_info), 24662306a36Sopenharmony_ci GFP_KERNEL_ACCOUNT); 24762306a36Sopenharmony_ci if (!bp_info) { 24862306a36Sopenharmony_ci ret = -ENOMEM; 24962306a36Sopenharmony_ci goto error; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci for (nr_wp = 0, nr_bp = 0, i = 0; i < dbg->arch.nr_hw_bp; i++) { 25462306a36Sopenharmony_ci switch (bp_data[i].type) { 25562306a36Sopenharmony_ci case KVM_HW_WP_WRITE: 25662306a36Sopenharmony_ci ret = __import_wp_info(vcpu, &bp_data[i], 25762306a36Sopenharmony_ci &wp_info[nr_wp]); 25862306a36Sopenharmony_ci if (ret) 25962306a36Sopenharmony_ci goto error; 26062306a36Sopenharmony_ci nr_wp++; 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci case KVM_HW_BP: 26362306a36Sopenharmony_ci bp_info[nr_bp].len = bp_data[i].len; 26462306a36Sopenharmony_ci bp_info[nr_bp].addr = bp_data[i].addr; 26562306a36Sopenharmony_ci nr_bp++; 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci vcpu->arch.guestdbg.nr_hw_bp = nr_bp; 27162306a36Sopenharmony_ci vcpu->arch.guestdbg.hw_bp_info = bp_info; 27262306a36Sopenharmony_ci vcpu->arch.guestdbg.nr_hw_wp = nr_wp; 27362306a36Sopenharmony_ci vcpu->arch.guestdbg.hw_wp_info = wp_info; 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_cierror: 27662306a36Sopenharmony_ci kfree(bp_data); 27762306a36Sopenharmony_ci kfree(wp_info); 27862306a36Sopenharmony_ci kfree(bp_info); 27962306a36Sopenharmony_ci return ret; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_civoid kvm_s390_clear_bp_data(struct kvm_vcpu *vcpu) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci int i; 28562306a36Sopenharmony_ci struct kvm_hw_wp_info_arch *hw_wp_info = NULL; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci for (i = 0; i < vcpu->arch.guestdbg.nr_hw_wp; i++) { 28862306a36Sopenharmony_ci hw_wp_info = &vcpu->arch.guestdbg.hw_wp_info[i]; 28962306a36Sopenharmony_ci kfree(hw_wp_info->old_data); 29062306a36Sopenharmony_ci hw_wp_info->old_data = NULL; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci kfree(vcpu->arch.guestdbg.hw_wp_info); 29362306a36Sopenharmony_ci vcpu->arch.guestdbg.hw_wp_info = NULL; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci kfree(vcpu->arch.guestdbg.hw_bp_info); 29662306a36Sopenharmony_ci vcpu->arch.guestdbg.hw_bp_info = NULL; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci vcpu->arch.guestdbg.nr_hw_wp = 0; 29962306a36Sopenharmony_ci vcpu->arch.guestdbg.nr_hw_bp = 0; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic inline int in_addr_range(u64 addr, u64 a, u64 b) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci if (a <= b) 30562306a36Sopenharmony_ci return (addr >= a) && (addr <= b); 30662306a36Sopenharmony_ci else 30762306a36Sopenharmony_ci /* "overflowing" interval */ 30862306a36Sopenharmony_ci return (addr >= a) || (addr <= b); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci#define end_of_range(bp_info) (bp_info->addr + bp_info->len - 1) 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic struct kvm_hw_bp_info_arch *find_hw_bp(struct kvm_vcpu *vcpu, 31462306a36Sopenharmony_ci unsigned long addr) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct kvm_hw_bp_info_arch *bp_info = vcpu->arch.guestdbg.hw_bp_info; 31762306a36Sopenharmony_ci int i; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (vcpu->arch.guestdbg.nr_hw_bp == 0) 32062306a36Sopenharmony_ci return NULL; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci for (i = 0; i < vcpu->arch.guestdbg.nr_hw_bp; i++) { 32362306a36Sopenharmony_ci /* addr is directly the start or in the range of a bp */ 32462306a36Sopenharmony_ci if (addr == bp_info->addr) 32562306a36Sopenharmony_ci goto found; 32662306a36Sopenharmony_ci if (bp_info->len > 0 && 32762306a36Sopenharmony_ci in_addr_range(addr, bp_info->addr, end_of_range(bp_info))) 32862306a36Sopenharmony_ci goto found; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci bp_info++; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci return NULL; 33462306a36Sopenharmony_cifound: 33562306a36Sopenharmony_ci return bp_info; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic struct kvm_hw_wp_info_arch *any_wp_changed(struct kvm_vcpu *vcpu) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci int i; 34162306a36Sopenharmony_ci struct kvm_hw_wp_info_arch *wp_info = NULL; 34262306a36Sopenharmony_ci void *temp = NULL; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (vcpu->arch.guestdbg.nr_hw_wp == 0) 34562306a36Sopenharmony_ci return NULL; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci for (i = 0; i < vcpu->arch.guestdbg.nr_hw_wp; i++) { 34862306a36Sopenharmony_ci wp_info = &vcpu->arch.guestdbg.hw_wp_info[i]; 34962306a36Sopenharmony_ci if (!wp_info || !wp_info->old_data || wp_info->len <= 0) 35062306a36Sopenharmony_ci continue; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci temp = kmalloc(wp_info->len, GFP_KERNEL_ACCOUNT); 35362306a36Sopenharmony_ci if (!temp) 35462306a36Sopenharmony_ci continue; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* refetch the wp data and compare it to the old value */ 35762306a36Sopenharmony_ci if (!read_guest_abs(vcpu, wp_info->phys_addr, temp, 35862306a36Sopenharmony_ci wp_info->len)) { 35962306a36Sopenharmony_ci if (memcmp(temp, wp_info->old_data, wp_info->len)) { 36062306a36Sopenharmony_ci kfree(temp); 36162306a36Sopenharmony_ci return wp_info; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci kfree(temp); 36562306a36Sopenharmony_ci temp = NULL; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci return NULL; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_civoid kvm_s390_prepare_debug_exit(struct kvm_vcpu *vcpu) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_DEBUG; 37462306a36Sopenharmony_ci vcpu->guest_debug &= ~KVM_GUESTDBG_EXIT_PENDING; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci#define PER_CODE_MASK (PER_EVENT_MASK >> 24) 37862306a36Sopenharmony_ci#define PER_CODE_BRANCH (PER_EVENT_BRANCH >> 24) 37962306a36Sopenharmony_ci#define PER_CODE_IFETCH (PER_EVENT_IFETCH >> 24) 38062306a36Sopenharmony_ci#define PER_CODE_STORE (PER_EVENT_STORE >> 24) 38162306a36Sopenharmony_ci#define PER_CODE_STORE_REAL (PER_EVENT_STORE_REAL >> 24) 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci#define per_bp_event(code) \ 38462306a36Sopenharmony_ci (code & (PER_CODE_IFETCH | PER_CODE_BRANCH)) 38562306a36Sopenharmony_ci#define per_write_wp_event(code) \ 38662306a36Sopenharmony_ci (code & (PER_CODE_STORE | PER_CODE_STORE_REAL)) 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int debug_exit_required(struct kvm_vcpu *vcpu, u8 perc, 38962306a36Sopenharmony_ci unsigned long peraddr) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct kvm_debug_exit_arch *debug_exit = &vcpu->run->debug.arch; 39262306a36Sopenharmony_ci struct kvm_hw_wp_info_arch *wp_info = NULL; 39362306a36Sopenharmony_ci struct kvm_hw_bp_info_arch *bp_info = NULL; 39462306a36Sopenharmony_ci unsigned long addr = vcpu->arch.sie_block->gpsw.addr; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (guestdbg_hw_bp_enabled(vcpu)) { 39762306a36Sopenharmony_ci if (per_write_wp_event(perc) && 39862306a36Sopenharmony_ci vcpu->arch.guestdbg.nr_hw_wp > 0) { 39962306a36Sopenharmony_ci wp_info = any_wp_changed(vcpu); 40062306a36Sopenharmony_ci if (wp_info) { 40162306a36Sopenharmony_ci debug_exit->addr = wp_info->addr; 40262306a36Sopenharmony_ci debug_exit->type = KVM_HW_WP_WRITE; 40362306a36Sopenharmony_ci goto exit_required; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci if (per_bp_event(perc) && 40762306a36Sopenharmony_ci vcpu->arch.guestdbg.nr_hw_bp > 0) { 40862306a36Sopenharmony_ci bp_info = find_hw_bp(vcpu, addr); 40962306a36Sopenharmony_ci /* remove duplicate events if PC==PER address */ 41062306a36Sopenharmony_ci if (bp_info && (addr != peraddr)) { 41162306a36Sopenharmony_ci debug_exit->addr = addr; 41262306a36Sopenharmony_ci debug_exit->type = KVM_HW_BP; 41362306a36Sopenharmony_ci vcpu->arch.guestdbg.last_bp = addr; 41462306a36Sopenharmony_ci goto exit_required; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci /* breakpoint missed */ 41762306a36Sopenharmony_ci bp_info = find_hw_bp(vcpu, peraddr); 41862306a36Sopenharmony_ci if (bp_info && vcpu->arch.guestdbg.last_bp != peraddr) { 41962306a36Sopenharmony_ci debug_exit->addr = peraddr; 42062306a36Sopenharmony_ci debug_exit->type = KVM_HW_BP; 42162306a36Sopenharmony_ci goto exit_required; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci if (guestdbg_sstep_enabled(vcpu) && per_bp_event(perc)) { 42662306a36Sopenharmony_ci debug_exit->addr = addr; 42762306a36Sopenharmony_ci debug_exit->type = KVM_SINGLESTEP; 42862306a36Sopenharmony_ci goto exit_required; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ciexit_required: 43362306a36Sopenharmony_ci return 1; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic int per_fetched_addr(struct kvm_vcpu *vcpu, unsigned long *addr) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci u8 exec_ilen = 0; 43962306a36Sopenharmony_ci u16 opcode[3]; 44062306a36Sopenharmony_ci int rc; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (vcpu->arch.sie_block->icptcode == ICPT_PROGI) { 44362306a36Sopenharmony_ci /* PER address references the fetched or the execute instr */ 44462306a36Sopenharmony_ci *addr = vcpu->arch.sie_block->peraddr; 44562306a36Sopenharmony_ci /* 44662306a36Sopenharmony_ci * Manually detect if we have an EXECUTE instruction. As 44762306a36Sopenharmony_ci * instructions are always 2 byte aligned we can read the 44862306a36Sopenharmony_ci * first two bytes unconditionally 44962306a36Sopenharmony_ci */ 45062306a36Sopenharmony_ci rc = read_guest_instr(vcpu, *addr, &opcode, 2); 45162306a36Sopenharmony_ci if (rc) 45262306a36Sopenharmony_ci return rc; 45362306a36Sopenharmony_ci if (opcode[0] >> 8 == 0x44) 45462306a36Sopenharmony_ci exec_ilen = 4; 45562306a36Sopenharmony_ci if ((opcode[0] & 0xff0f) == 0xc600) 45662306a36Sopenharmony_ci exec_ilen = 6; 45762306a36Sopenharmony_ci } else { 45862306a36Sopenharmony_ci /* instr was suppressed, calculate the responsible instr */ 45962306a36Sopenharmony_ci *addr = __rewind_psw(vcpu->arch.sie_block->gpsw, 46062306a36Sopenharmony_ci kvm_s390_get_ilen(vcpu)); 46162306a36Sopenharmony_ci if (vcpu->arch.sie_block->icptstatus & 0x01) { 46262306a36Sopenharmony_ci exec_ilen = (vcpu->arch.sie_block->icptstatus & 0x60) >> 4; 46362306a36Sopenharmony_ci if (!exec_ilen) 46462306a36Sopenharmony_ci exec_ilen = 4; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (exec_ilen) { 46962306a36Sopenharmony_ci /* read the complete EXECUTE instr to detect the fetched addr */ 47062306a36Sopenharmony_ci rc = read_guest_instr(vcpu, *addr, &opcode, exec_ilen); 47162306a36Sopenharmony_ci if (rc) 47262306a36Sopenharmony_ci return rc; 47362306a36Sopenharmony_ci if (exec_ilen == 6) { 47462306a36Sopenharmony_ci /* EXECUTE RELATIVE LONG - RIL-b format */ 47562306a36Sopenharmony_ci s32 rl = *((s32 *) (opcode + 1)); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* rl is a _signed_ 32 bit value specifying halfwords */ 47862306a36Sopenharmony_ci *addr += (u64)(s64) rl * 2; 47962306a36Sopenharmony_ci } else { 48062306a36Sopenharmony_ci /* EXECUTE - RX-a format */ 48162306a36Sopenharmony_ci u32 base = (opcode[1] & 0xf000) >> 12; 48262306a36Sopenharmony_ci u32 disp = opcode[1] & 0x0fff; 48362306a36Sopenharmony_ci u32 index = opcode[0] & 0x000f; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci *addr = base ? vcpu->run->s.regs.gprs[base] : 0; 48662306a36Sopenharmony_ci *addr += index ? vcpu->run->s.regs.gprs[index] : 0; 48762306a36Sopenharmony_ci *addr += disp; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci *addr = kvm_s390_logical_to_effective(vcpu, *addr); 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci return 0; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci#define guest_per_enabled(vcpu) \ 49562306a36Sopenharmony_ci (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ciint kvm_s390_handle_per_ifetch_icpt(struct kvm_vcpu *vcpu) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci const u64 cr10 = vcpu->arch.sie_block->gcr[10]; 50062306a36Sopenharmony_ci const u64 cr11 = vcpu->arch.sie_block->gcr[11]; 50162306a36Sopenharmony_ci const u8 ilen = kvm_s390_get_ilen(vcpu); 50262306a36Sopenharmony_ci struct kvm_s390_pgm_info pgm_info = { 50362306a36Sopenharmony_ci .code = PGM_PER, 50462306a36Sopenharmony_ci .per_code = PER_CODE_IFETCH, 50562306a36Sopenharmony_ci .per_address = __rewind_psw(vcpu->arch.sie_block->gpsw, ilen), 50662306a36Sopenharmony_ci }; 50762306a36Sopenharmony_ci unsigned long fetched_addr; 50862306a36Sopenharmony_ci int rc; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* 51162306a36Sopenharmony_ci * The PSW points to the next instruction, therefore the intercepted 51262306a36Sopenharmony_ci * instruction generated a PER i-fetch event. PER address therefore 51362306a36Sopenharmony_ci * points at the previous PSW address (could be an EXECUTE function). 51462306a36Sopenharmony_ci */ 51562306a36Sopenharmony_ci if (!guestdbg_enabled(vcpu)) 51662306a36Sopenharmony_ci return kvm_s390_inject_prog_irq(vcpu, &pgm_info); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (debug_exit_required(vcpu, pgm_info.per_code, pgm_info.per_address)) 51962306a36Sopenharmony_ci vcpu->guest_debug |= KVM_GUESTDBG_EXIT_PENDING; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (!guest_per_enabled(vcpu) || 52262306a36Sopenharmony_ci !(vcpu->arch.sie_block->gcr[9] & PER_EVENT_IFETCH)) 52362306a36Sopenharmony_ci return 0; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci rc = per_fetched_addr(vcpu, &fetched_addr); 52662306a36Sopenharmony_ci if (rc < 0) 52762306a36Sopenharmony_ci return rc; 52862306a36Sopenharmony_ci if (rc) 52962306a36Sopenharmony_ci /* instruction-fetching exceptions */ 53062306a36Sopenharmony_ci return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (in_addr_range(fetched_addr, cr10, cr11)) 53362306a36Sopenharmony_ci return kvm_s390_inject_prog_irq(vcpu, &pgm_info); 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic int filter_guest_per_event(struct kvm_vcpu *vcpu) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci const u8 perc = vcpu->arch.sie_block->perc; 54062306a36Sopenharmony_ci u64 addr = vcpu->arch.sie_block->gpsw.addr; 54162306a36Sopenharmony_ci u64 cr9 = vcpu->arch.sie_block->gcr[9]; 54262306a36Sopenharmony_ci u64 cr10 = vcpu->arch.sie_block->gcr[10]; 54362306a36Sopenharmony_ci u64 cr11 = vcpu->arch.sie_block->gcr[11]; 54462306a36Sopenharmony_ci /* filter all events, demanded by the guest */ 54562306a36Sopenharmony_ci u8 guest_perc = perc & (cr9 >> 24) & PER_CODE_MASK; 54662306a36Sopenharmony_ci unsigned long fetched_addr; 54762306a36Sopenharmony_ci int rc; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (!guest_per_enabled(vcpu)) 55062306a36Sopenharmony_ci guest_perc = 0; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* filter "successful-branching" events */ 55362306a36Sopenharmony_ci if (guest_perc & PER_CODE_BRANCH && 55462306a36Sopenharmony_ci cr9 & PER_CONTROL_BRANCH_ADDRESS && 55562306a36Sopenharmony_ci !in_addr_range(addr, cr10, cr11)) 55662306a36Sopenharmony_ci guest_perc &= ~PER_CODE_BRANCH; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* filter "instruction-fetching" events */ 55962306a36Sopenharmony_ci if (guest_perc & PER_CODE_IFETCH) { 56062306a36Sopenharmony_ci rc = per_fetched_addr(vcpu, &fetched_addr); 56162306a36Sopenharmony_ci if (rc < 0) 56262306a36Sopenharmony_ci return rc; 56362306a36Sopenharmony_ci /* 56462306a36Sopenharmony_ci * Don't inject an irq on exceptions. This would make handling 56562306a36Sopenharmony_ci * on icpt code 8 very complex (as PSW was already rewound). 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci if (rc || !in_addr_range(fetched_addr, cr10, cr11)) 56862306a36Sopenharmony_ci guest_perc &= ~PER_CODE_IFETCH; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* All other PER events will be given to the guest */ 57262306a36Sopenharmony_ci /* TODO: Check altered address/address space */ 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci vcpu->arch.sie_block->perc = guest_perc; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (!guest_perc) 57762306a36Sopenharmony_ci vcpu->arch.sie_block->iprcc &= ~PGM_PER; 57862306a36Sopenharmony_ci return 0; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci#define pssec(vcpu) (vcpu->arch.sie_block->gcr[1] & _ASCE_SPACE_SWITCH) 58262306a36Sopenharmony_ci#define hssec(vcpu) (vcpu->arch.sie_block->gcr[13] & _ASCE_SPACE_SWITCH) 58362306a36Sopenharmony_ci#define old_ssec(vcpu) ((vcpu->arch.sie_block->tecmc >> 31) & 0x1) 58462306a36Sopenharmony_ci#define old_as_is_home(vcpu) !(vcpu->arch.sie_block->tecmc & 0xffff) 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ciint kvm_s390_handle_per_event(struct kvm_vcpu *vcpu) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci int rc, new_as; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (debug_exit_required(vcpu, vcpu->arch.sie_block->perc, 59162306a36Sopenharmony_ci vcpu->arch.sie_block->peraddr)) 59262306a36Sopenharmony_ci vcpu->guest_debug |= KVM_GUESTDBG_EXIT_PENDING; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci rc = filter_guest_per_event(vcpu); 59562306a36Sopenharmony_ci if (rc) 59662306a36Sopenharmony_ci return rc; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* 59962306a36Sopenharmony_ci * Only RP, SAC, SACF, PT, PTI, PR, PC instructions can trigger 60062306a36Sopenharmony_ci * a space-switch event. PER events enforce space-switch events 60162306a36Sopenharmony_ci * for these instructions. So if no PER event for the guest is left, 60262306a36Sopenharmony_ci * we might have to filter the space-switch element out, too. 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_ci if (vcpu->arch.sie_block->iprcc == PGM_SPACE_SWITCH) { 60562306a36Sopenharmony_ci vcpu->arch.sie_block->iprcc = 0; 60662306a36Sopenharmony_ci new_as = psw_bits(vcpu->arch.sie_block->gpsw).as; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* 60962306a36Sopenharmony_ci * If the AS changed from / to home, we had RP, SAC or SACF 61062306a36Sopenharmony_ci * instruction. Check primary and home space-switch-event 61162306a36Sopenharmony_ci * controls. (theoretically home -> home produced no event) 61262306a36Sopenharmony_ci */ 61362306a36Sopenharmony_ci if (((new_as == PSW_BITS_AS_HOME) ^ old_as_is_home(vcpu)) && 61462306a36Sopenharmony_ci (pssec(vcpu) || hssec(vcpu))) 61562306a36Sopenharmony_ci vcpu->arch.sie_block->iprcc = PGM_SPACE_SWITCH; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci /* 61862306a36Sopenharmony_ci * PT, PTI, PR, PC instruction operate on primary AS only. Check 61962306a36Sopenharmony_ci * if the primary-space-switch-event control was or got set. 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_ci if (new_as == PSW_BITS_AS_PRIMARY && !old_as_is_home(vcpu) && 62262306a36Sopenharmony_ci (pssec(vcpu) || old_ssec(vcpu))) 62362306a36Sopenharmony_ci vcpu->arch.sie_block->iprcc = PGM_SPACE_SWITCH; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci return 0; 62662306a36Sopenharmony_ci} 627