intercept.c 9.5 KB
Newer Older
1
/*
2
 * in-kernel handling for sie intercepts
3
 *
4
 * Copyright IBM Corp. 2008, 2014
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
#include <asm/irq.h>
21 22

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

27

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

41 42 43 44 45 46 47 48 49 50 51 52 53
void kvm_s390_rewind_psw(struct kvm_vcpu *vcpu, int ilc)
{
	struct kvm_s390_sie_block *sie_block = vcpu->arch.sie_block;

	/* Use the length of the EXECUTE instruction if necessary */
	if (sie_block->icptstatus & 1) {
		ilc = (sie_block->icptstatus >> 4) & 0x6;
		if (!ilc)
			ilc = 4;
	}
	sie_block->gpsw.addr = __rewind_psw(sie_block->gpsw, ilc);
}

54 55 56
static int handle_noop(struct kvm_vcpu *vcpu)
{
	switch (vcpu->arch.sie_block->icptcode) {
57 58 59
	case 0x0:
		vcpu->stat.exit_null++;
		break;
60 61 62 63 64 65 66 67 68 69 70
	case 0x10:
		vcpu->stat.exit_external_request++;
		break;
	default:
		break; /* nothing */
	}
	return 0;
}

static int handle_stop(struct kvm_vcpu *vcpu)
{
71
	int rc = 0;
72
	unsigned int action_bits;
73

74
	vcpu->stat.exit_stop_request++;
75 76
	trace_kvm_s390_stop_request(vcpu->arch.local_int.action_bits);

77 78 79 80
	action_bits = vcpu->arch.local_int.action_bits;

	if (!(action_bits & ACTION_STOP_ON_STOP))
		return 0;
81

82
	if (action_bits & ACTION_STORE_ON_STOP) {
83 84
		rc = kvm_s390_vcpu_store_status(vcpu,
						KVM_S390_STORE_STATUS_NOADDR);
85 86 87 88
		if (rc)
			return rc;
	}

89 90
	if (!kvm_s390_user_cpu_state_ctrl(vcpu->kvm))
		kvm_s390_vcpu_stop(vcpu);
91
	return -EOPNOTSUPP;
92 93 94 95 96
}

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

98
	vcpu->stat.exit_validity++;
99
	trace_kvm_s390_intercept_validity(vcpu, viwhy);
100 101
	WARN_ONCE(true, "kvm: unhandled validity intercept 0x%x\n", viwhy);
	return -EOPNOTSUPP;
102 103
}

104 105 106 107 108
static int handle_instruction(struct kvm_vcpu *vcpu)
{
	intercept_handler_t handler;

	vcpu->stat.exit_instruction++;
109 110 111
	trace_kvm_s390_intercept_instruction(vcpu,
					     vcpu->arch.sie_block->ipa,
					     vcpu->arch.sie_block->ipb);
112 113 114
	handler = instruction_handlers[vcpu->arch.sie_block->ipa >> 8];
	if (handler)
		return handler(vcpu);
115
	return -EOPNOTSUPP;
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 174 175 176 177
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;
	}
}

178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
/*
 * 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;
}

200 201
#define per_event(vcpu) (vcpu->arch.sie_block->iprcc & PGM_PER)

202 203
static int handle_prog(struct kvm_vcpu *vcpu)
{
204
	struct kvm_s390_pgm_info pgm_info;
205
	psw_t psw;
206 207
	int rc;

208
	vcpu->stat.exit_program_interruption++;
209

210 211 212 213 214 215 216
	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;
	}

217
	trace_kvm_s390_intercept_prog(vcpu, vcpu->arch.sie_block->iprcc);
218 219 220 221 222 223 224 225
	if (vcpu->arch.sie_block->iprcc == PGM_SPECIFICATION) {
		rc = read_guest_lc(vcpu, __LC_PGM_NEW_PSW, &psw, sizeof(psw_t));
		if (rc)
			return rc;
		/* Avoid endless loops of specification exceptions */
		if (!is_valid_psw(&psw))
			return -EOPNOTSUPP;
	}
226
	rc = handle_itdb(vcpu);
227 228
	if (rc)
		return rc;
229

230
	__extract_prog_irq(vcpu, &pgm_info);
231
	return kvm_s390_inject_prog_irq(vcpu, &pgm_info);
232 233 234 235 236 237 238 239 240 241
}

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);

242
	if (rc == -EOPNOTSUPP)
243 244 245 246 247 248
		vcpu->arch.sie_block->icptcode = 0x04;
	if (rc)
		return rc;
	return rc2;
}

249 250 251 252 253 254 255 256 257 258 259
/**
 * handle_external_interrupt - used for external interruption interceptions
 *
 * This interception only occurs if the CPUSTAT_EXT_INT bit was set, or if
 * the new PSW does not have external interrupts disabled. In the first case,
 * we've got to deliver the interrupt manually, and in the second case, we
 * drop to userspace to handle the situation there.
 */
static int handle_external_interrupt(struct kvm_vcpu *vcpu)
{
	u16 eic = vcpu->arch.sie_block->eic;
260
	struct kvm_s390_irq irq;
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
	psw_t newpsw;
	int rc;

	vcpu->stat.exit_external_interrupt++;

	rc = read_guest_lc(vcpu, __LC_EXT_NEW_PSW, &newpsw, sizeof(psw_t));
	if (rc)
		return rc;
	/* We can not handle clock comparator or timer interrupt with bad PSW */
	if ((eic == EXT_IRQ_CLK_COMP || eic == EXT_IRQ_CPU_TIMER) &&
	    (newpsw.mask & PSW_MASK_EXT))
		return -EOPNOTSUPP;

	switch (eic) {
	case EXT_IRQ_CLK_COMP:
		irq.type = KVM_S390_INT_CLOCK_COMP;
		break;
	case EXT_IRQ_CPU_TIMER:
		irq.type = KVM_S390_INT_CPU_TIMER;
		break;
	case EXT_IRQ_EXTERNAL_CALL:
282 283
		if (kvm_s390_si_ext_call_pending(vcpu))
			return 0;
284
		irq.type = KVM_S390_INT_EXTERNAL_CALL;
285
		irq.u.extcall.code = vcpu->arch.sie_block->extcpuaddr;
286 287 288 289 290 291 292 293
		break;
	default:
		return -EOPNOTSUPP;
	}

	return kvm_s390_inject_vcpu(vcpu, &irq);
}

294 295 296 297 298 299 300 301 302 303
/**
 * Handle MOVE PAGE partial execution interception.
 *
 * This interception can only happen for guests with DAT disabled and
 * addresses that are currently not mapped in the host. Thus we try to
 * set up the mappings for the corresponding user pages here (or throw
 * addressing exceptions in case of illegal guest addresses).
 */
static int handle_mvpg_pei(struct kvm_vcpu *vcpu)
{
304
	unsigned long srcaddr, dstaddr;
305 306 307 308 309
	int reg1, reg2, rc;

	kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);

	/* Make sure that the source is paged-in */
310 311
	srcaddr = kvm_s390_real_to_abs(vcpu, vcpu->run->s.regs.gprs[reg2]);
	if (kvm_is_error_gpa(vcpu->kvm, srcaddr))
312
		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
313 314
	rc = kvm_arch_fault_in_page(vcpu, srcaddr, 0);
	if (rc != 0)
315 316 317
		return rc;

	/* Make sure that the destination is paged-in */
318 319
	dstaddr = kvm_s390_real_to_abs(vcpu, vcpu->run->s.regs.gprs[reg1]);
	if (kvm_is_error_gpa(vcpu->kvm, dstaddr))
320
		return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
321 322
	rc = kvm_arch_fault_in_page(vcpu, dstaddr, 1);
	if (rc != 0)
323 324
		return rc;

325
	kvm_s390_rewind_psw(vcpu, 4);
326 327 328 329 330 331 332 333

	return 0;
}

static int handle_partial_execution(struct kvm_vcpu *vcpu)
{
	if (vcpu->arch.sie_block->ipa == 0xb254)	/* MVPG */
		return handle_mvpg_pei(vcpu);
334 335
	if (vcpu->arch.sie_block->ipa >> 8 == 0xae)	/* SIGP */
		return kvm_s390_handle_sigp_pei(vcpu);
336 337 338 339

	return -EOPNOTSUPP;
}

340
static const intercept_handler_t intercept_funcs[] = {
341
	[0x00 >> 2] = handle_noop,
342 343 344
	[0x04 >> 2] = handle_instruction,
	[0x08 >> 2] = handle_prog,
	[0x0C >> 2] = handle_instruction_and_prog,
345
	[0x10 >> 2] = handle_noop,
346
	[0x14 >> 2] = handle_external_interrupt,
347
	[0x18 >> 2] = handle_noop,
348
	[0x1C >> 2] = kvm_s390_handle_wait,
349 350
	[0x20 >> 2] = handle_validity,
	[0x28 >> 2] = handle_stop,
351
	[0x38 >> 2] = handle_partial_execution,
352 353 354 355 356 357 358
};

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

359
	if (code & 3 || (code >> 2) >= ARRAY_SIZE(intercept_funcs))
360
		return -EOPNOTSUPP;
361 362 363
	func = intercept_funcs[code >> 2];
	if (func)
		return func(vcpu);
364
	return -EOPNOTSUPP;
365
}