intercept.c 6.7 KB
Newer Older
1
/*
2
 * in-kernel handling for sie intercepts
3
 *
4
 * Copyright IBM Corp. 2008, 2009
5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (version 2 only)
 * as published by the Free Software Foundation.
 *
 *    Author(s): Carsten Otte <cotte@de.ibm.com>
 *               Christian Borntraeger <borntraeger@de.ibm.com>
 */

#include <linux/kvm_host.h>
#include <linux/errno.h>
#include <linux/pagemap.h>

#include <asm/kvm_host.h>
19
#include <asm/asm-offsets.h>
20 21

#include "kvm-s390.h"
22
#include "gaccess.h"
23
#include "trace.h"
24
#include "trace-s390.h"
25

26

27
static const intercept_handler_t instruction_handlers[256] = {
28
	[0x01] = kvm_s390_handle_01,
29
	[0x82] = kvm_s390_handle_lpsw,
30
	[0x83] = kvm_s390_handle_diag,
31
	[0xae] = kvm_s390_handle_sigp,
32
	[0xb2] = kvm_s390_handle_b2,
33
	[0xb6] = kvm_s390_handle_stctl,
34
	[0xb7] = kvm_s390_handle_lctl,
35
	[0xb9] = kvm_s390_handle_b9,
36
	[0xe5] = kvm_s390_handle_e5,
37
	[0xeb] = kvm_s390_handle_eb,
38
};
39 40 41 42

static int handle_noop(struct kvm_vcpu *vcpu)
{
	switch (vcpu->arch.sie_block->icptcode) {
43 44 45
	case 0x0:
		vcpu->stat.exit_null++;
		break;
46 47 48 49 50 51 52 53 54 55 56 57 58 59
	case 0x10:
		vcpu->stat.exit_external_request++;
		break;
	case 0x14:
		vcpu->stat.exit_external_interrupt++;
		break;
	default:
		break; /* nothing */
	}
	return 0;
}

static int handle_stop(struct kvm_vcpu *vcpu)
{
60
	int rc = 0;
61

62
	vcpu->stat.exit_stop_request++;
63 64
	spin_lock_bh(&vcpu->arch.local_int.lock);

65 66
	trace_kvm_s390_stop_request(vcpu->arch.local_int.action_bits);

67
	if (vcpu->arch.local_int.action_bits & ACTION_STOP_ON_STOP) {
68 69
		atomic_set_mask(CPUSTAT_STOPPED,
				&vcpu->arch.sie_block->cpuflags);
70 71
		vcpu->arch.local_int.action_bits &= ~ACTION_STOP_ON_STOP;
		VCPU_EVENT(vcpu, 3, "%s", "cpu stopped");
72
		rc = -EOPNOTSUPP;
73 74
	}

75 76 77 78 79 80 81 82 83 84 85 86
	if (vcpu->arch.local_int.action_bits & ACTION_STORE_ON_STOP) {
		vcpu->arch.local_int.action_bits &= ~ACTION_STORE_ON_STOP;
		/* store status must be called unlocked. Since local_int.lock
		 * only protects local_int.* and not guest memory we can give
		 * up the lock here */
		spin_unlock_bh(&vcpu->arch.local_int.lock);
		rc = kvm_s390_vcpu_store_status(vcpu,
						KVM_S390_STORE_STATUS_NOADDR);
		if (rc >= 0)
			rc = -EOPNOTSUPP;
	} else
		spin_unlock_bh(&vcpu->arch.local_int.lock);
87
	return rc;
88 89 90 91 92
}

static int handle_validity(struct kvm_vcpu *vcpu)
{
	int viwhy = vcpu->arch.sie_block->ipb >> 16;
93

94
	vcpu->stat.exit_validity++;
95
	trace_kvm_s390_intercept_validity(vcpu, viwhy);
96 97
	WARN_ONCE(true, "kvm: unhandled validity intercept 0x%x\n", viwhy);
	return -EOPNOTSUPP;
98 99
}

100 101 102 103 104
static int handle_instruction(struct kvm_vcpu *vcpu)
{
	intercept_handler_t handler;

	vcpu->stat.exit_instruction++;
105 106 107
	trace_kvm_s390_intercept_instruction(vcpu,
					     vcpu->arch.sie_block->ipa,
					     vcpu->arch.sie_block->ipb);
108 109 110
	handler = instruction_handlers[vcpu->arch.sie_block->ipa >> 8];
	if (handler)
		return handler(vcpu);
111
	return -EOPNOTSUPP;
112 113
}

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
static void __extract_prog_irq(struct kvm_vcpu *vcpu,
			       struct kvm_s390_pgm_info *pgm_info)
{
	memset(pgm_info, 0, sizeof(struct kvm_s390_pgm_info));
	pgm_info->code = vcpu->arch.sie_block->iprcc;

	switch (vcpu->arch.sie_block->iprcc & ~PGM_PER) {
	case PGM_AFX_TRANSLATION:
	case PGM_ASX_TRANSLATION:
	case PGM_EX_TRANSLATION:
	case PGM_LFX_TRANSLATION:
	case PGM_LSTE_SEQUENCE:
	case PGM_LSX_TRANSLATION:
	case PGM_LX_TRANSLATION:
	case PGM_PRIMARY_AUTHORITY:
	case PGM_SECONDARY_AUTHORITY:
	case PGM_SPACE_SWITCH:
		pgm_info->trans_exc_code = vcpu->arch.sie_block->tecmc;
		break;
	case PGM_ALEN_TRANSLATION:
	case PGM_ALE_SEQUENCE:
	case PGM_ASTE_INSTANCE:
	case PGM_ASTE_SEQUENCE:
	case PGM_ASTE_VALIDITY:
	case PGM_EXTENDED_AUTHORITY:
		pgm_info->exc_access_id = vcpu->arch.sie_block->eai;
		break;
	case PGM_ASCE_TYPE:
	case PGM_PAGE_TRANSLATION:
	case PGM_REGION_FIRST_TRANS:
	case PGM_REGION_SECOND_TRANS:
	case PGM_REGION_THIRD_TRANS:
	case PGM_SEGMENT_TRANSLATION:
		pgm_info->trans_exc_code = vcpu->arch.sie_block->tecmc;
		pgm_info->exc_access_id  = vcpu->arch.sie_block->eai;
		pgm_info->op_access_id  = vcpu->arch.sie_block->oai;
		break;
	case PGM_MONITOR:
		pgm_info->mon_class_nr = vcpu->arch.sie_block->mcn;
		pgm_info->mon_code = vcpu->arch.sie_block->tecmc;
		break;
	case PGM_DATA:
		pgm_info->data_exc_code = vcpu->arch.sie_block->dxc;
		break;
	case PGM_PROTECTION:
		pgm_info->trans_exc_code = vcpu->arch.sie_block->tecmc;
		pgm_info->exc_access_id  = vcpu->arch.sie_block->eai;
		break;
	default:
		break;
	}

	if (vcpu->arch.sie_block->iprcc & PGM_PER) {
		pgm_info->per_code = vcpu->arch.sie_block->perc;
		pgm_info->per_atmid = vcpu->arch.sie_block->peratmid;
		pgm_info->per_address = vcpu->arch.sie_block->peraddr;
		pgm_info->per_access_id = vcpu->arch.sie_block->peraid;
	}
}

174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
/*
 * restore ITDB to program-interruption TDB in guest lowcore
 * and set TX abort indication if required
*/
static int handle_itdb(struct kvm_vcpu *vcpu)
{
	struct kvm_s390_itdb *itdb;
	int rc;

	if (!IS_TE_ENABLED(vcpu) || !IS_ITDB_VALID(vcpu))
		return 0;
	if (current->thread.per_flags & PER_FLAG_NO_TE)
		return 0;
	itdb = (struct kvm_s390_itdb *)vcpu->arch.sie_block->itdba;
	rc = write_guest_lc(vcpu, __LC_PGM_TDB, itdb, sizeof(*itdb));
	if (rc)
		return rc;
	memset(itdb, 0, sizeof(*itdb));

	return 0;
}

196 197
#define per_event(vcpu) (vcpu->arch.sie_block->iprcc & PGM_PER)

198 199
static int handle_prog(struct kvm_vcpu *vcpu)
{
200
	struct kvm_s390_pgm_info pgm_info;
201 202
	int rc;

203
	vcpu->stat.exit_program_interruption++;
204

205 206 207 208 209 210 211
	if (guestdbg_enabled(vcpu) && per_event(vcpu)) {
		kvm_s390_handle_per_event(vcpu);
		/* the interrupt might have been filtered out completely */
		if (vcpu->arch.sie_block->iprcc == 0)
			return 0;
	}

212 213 214
	trace_kvm_s390_intercept_prog(vcpu, vcpu->arch.sie_block->iprcc);

	rc = handle_itdb(vcpu);
215 216
	if (rc)
		return rc;
217

218
	__extract_prog_irq(vcpu, &pgm_info);
219
	return kvm_s390_inject_prog_irq(vcpu, &pgm_info);
220 221 222 223 224 225 226 227 228 229
}

static int handle_instruction_and_prog(struct kvm_vcpu *vcpu)
{
	int rc, rc2;

	vcpu->stat.exit_instr_and_program++;
	rc = handle_instruction(vcpu);
	rc2 = handle_prog(vcpu);

230
	if (rc == -EOPNOTSUPP)
231 232 233 234 235 236
		vcpu->arch.sie_block->icptcode = 0x04;
	if (rc)
		return rc;
	return rc2;
}

237
static const intercept_handler_t intercept_funcs[] = {
238
	[0x00 >> 2] = handle_noop,
239 240 241
	[0x04 >> 2] = handle_instruction,
	[0x08 >> 2] = handle_prog,
	[0x0C >> 2] = handle_instruction_and_prog,
242 243
	[0x10 >> 2] = handle_noop,
	[0x14 >> 2] = handle_noop,
244
	[0x18 >> 2] = handle_noop,
245
	[0x1C >> 2] = kvm_s390_handle_wait,
246 247 248 249 250 251 252 253 254
	[0x20 >> 2] = handle_validity,
	[0x28 >> 2] = handle_stop,
};

int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu)
{
	intercept_handler_t func;
	u8 code = vcpu->arch.sie_block->icptcode;

255
	if (code & 3 || (code >> 2) >= ARRAY_SIZE(intercept_funcs))
256
		return -EOPNOTSUPP;
257 258 259
	func = intercept_funcs[code >> 2];
	if (func)
		return func(vcpu);
260
	return -EOPNOTSUPP;
261
}