s390mach.c 12.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 *  drivers/s390/s390mach.c
 *   S/390 machine check handler
 *
 *  S390 version
 *    Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
 *    Author(s): Ingo Adlung (adlung@de.ibm.com)
 *		 Martin Schwidefsky (schwidefsky@de.ibm.com)
 */

#include <linux/init.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/workqueue.h>
H
Heiko Carstens 已提交
15
#include <linux/time.h>
16
#include <linux/device.h>
17
#include <linux/kthread.h>
M
Martin Schwidefsky 已提交
18
#include <asm/etr.h>
L
Linus Torvalds 已提交
19
#include <asm/lowcore.h>
20 21 22 23
#include <asm/cio.h>
#include "cio/cio.h"
#include "cio/chsc.h"
#include "cio/css.h"
24
#include "cio/chp.h"
L
Linus Torvalds 已提交
25 26 27 28
#include "s390mach.h"

static struct semaphore m_sem;

29
static NORET_TYPE void
L
Linus Torvalds 已提交
30 31 32 33 34 35
s390_handle_damage(char *msg)
{
#ifdef CONFIG_SMP
	smp_send_stop();
#endif
	disabled_wait((unsigned long) __builtin_return_address(0));
36
	for(;;);
L
Linus Torvalds 已提交
37 38 39 40 41 42 43 44 45 46
}

/*
 * Retrieve CRWs and call function to handle event.
 *
 * Note : we currently process CRWs for io and chsc subchannels only
 */
static int
s390_collect_crw_info(void *param)
{
47
	struct crw crw[2];
48
	int ccode;
L
Linus Torvalds 已提交
49
	struct semaphore *sem;
50
	unsigned int chain;
L
Linus Torvalds 已提交
51 52 53 54

	sem = (struct semaphore *)param;
repeat:
	down_interruptible(sem);
55
	chain = 0;
L
Linus Torvalds 已提交
56
	while (1) {
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
		if (unlikely(chain > 1)) {
			struct crw tmp_crw;

			printk(KERN_WARNING"%s: Code does not support more "
			       "than two chained crws; please report to "
			       "linux390@de.ibm.com!\n", __FUNCTION__);
			ccode = stcrw(&tmp_crw);
			printk(KERN_WARNING"%s: crw reports slct=%d, oflw=%d, "
			       "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
			       __FUNCTION__, tmp_crw.slct, tmp_crw.oflw,
			       tmp_crw.chn, tmp_crw.rsc, tmp_crw.anc,
			       tmp_crw.erc, tmp_crw.rsid);
			printk(KERN_WARNING"%s: This was crw number %x in the "
			       "chain\n", __FUNCTION__, chain);
			if (ccode != 0)
				break;
			chain = tmp_crw.chn ? chain + 1 : 0;
			continue;
		}
		ccode = stcrw(&crw[chain]);
L
Linus Torvalds 已提交
77 78
		if (ccode != 0)
			break;
C
Cornelia Huck 已提交
79 80 81 82 83
		printk(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, "
		       "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
		       crw[chain].slct, crw[chain].oflw, crw[chain].chn,
		       crw[chain].rsc, crw[chain].anc, crw[chain].erc,
		       crw[chain].rsid);
L
Linus Torvalds 已提交
84
		/* Check for overflows. */
85
		if (crw[chain].oflw) {
L
Linus Torvalds 已提交
86
			pr_debug("%s: crw overflow detected!\n", __FUNCTION__);
87
			css_schedule_eval_all();
88
			chain = 0;
L
Linus Torvalds 已提交
89 90
			continue;
		}
91
		switch (crw[chain].rsc) {
L
Linus Torvalds 已提交
92
		case CRW_RSC_SCH:
93 94 95
			if (crw[0].chn && !chain)
				break;
			pr_debug("source is subchannel %04X\n", crw[0].rsid);
96
			css_process_crw(crw[0].rsid, chain ? crw[1].rsid : 0);
L
Linus Torvalds 已提交
97 98 99 100 101
			break;
		case CRW_RSC_MONITOR:
			pr_debug("source is monitoring facility\n");
			break;
		case CRW_RSC_CPATH:
102
			pr_debug("source is channel path %02X\n", crw[0].rsid);
103 104 105 106 107 108
			/*
			 * Check for solicited machine checks. These are
			 * created by reset channel path and need not be
			 * reported to the common I/O layer.
			 */
			if (crw[chain].slct) {
C
Cornelia Huck 已提交
109 110
				pr_debug("solicited machine check for "
					 "channel path %02X\n", crw[0].rsid);
111 112
				break;
			}
113
			switch (crw[0].erc) {
L
Linus Torvalds 已提交
114
			case CRW_ERC_IPARM: /* Path has come. */
115
				chp_process_crw(crw[0].rsid, 1);
L
Linus Torvalds 已提交
116 117 118
				break;
			case CRW_ERC_PERRI: /* Path has gone. */
			case CRW_ERC_PERRN:
119
				chp_process_crw(crw[0].rsid, 0);
L
Linus Torvalds 已提交
120 121 122
				break;
			default:
				pr_debug("Don't know how to handle erc=%x\n",
123
					 crw[0].erc);
L
Linus Torvalds 已提交
124 125 126 127 128 129 130
			}
			break;
		case CRW_RSC_CONFIG:
			pr_debug("source is configuration-alert facility\n");
			break;
		case CRW_RSC_CSS:
			pr_debug("source is channel subsystem\n");
131
			chsc_process_crw();
L
Linus Torvalds 已提交
132 133 134 135 136
			break;
		default:
			pr_debug("unknown source\n");
			break;
		}
137 138
		/* chain is always 0 or 1 here. */
		chain = crw[chain].chn ? chain + 1 : 0;
L
Linus Torvalds 已提交
139 140 141 142 143
	}
	goto repeat;
	return 0;
}

144 145 146 147 148 149 150 151 152
struct mcck_struct {
	int kill_task;
	int channel_report;
	int warning;
	unsigned long long mcck_code;
};

static DEFINE_PER_CPU(struct mcck_struct, cpu_mcck);

L
Linus Torvalds 已提交
153
/*
154 155
 * Main machine check handler function. Will be called with interrupts enabled
 * or disabled and machine checks enabled or disabled.
L
Linus Torvalds 已提交
156 157
 */
void
158
s390_handle_mcck(void)
L
Linus Torvalds 已提交
159
{
160 161
	unsigned long flags;
	struct mcck_struct mcck;
L
Linus Torvalds 已提交
162

163 164 165 166 167 168 169 170 171 172 173 174
	/*
	 * Disable machine checks and get the current state of accumulated
	 * machine checks. Afterwards delete the old state and enable machine
	 * checks again.
	 */
	local_irq_save(flags);
	local_mcck_disable();
	mcck = __get_cpu_var(cpu_mcck);
	memset(&__get_cpu_var(cpu_mcck), 0, sizeof(struct mcck_struct));
	clear_thread_flag(TIF_MCCK_PENDING);
	local_mcck_enable();
	local_irq_restore(flags);
L
Linus Torvalds 已提交
175

176
	if (mcck.channel_report)
L
Linus Torvalds 已提交
177 178 179 180 181 182 183 184 185 186 187 188
		up(&m_sem);

#ifdef CONFIG_MACHCHK_WARNING
/*
 * The warning may remain for a prolonged period on the bare iron.
 * (actually till the machine is powered off, or until the problem is gone)
 * So we just stop listening for the WARNING MCH and prevent continuously
 * being interrupted.  One caveat is however, that we must do this per
 * processor and cannot use the smp version of ctl_clear_bit().
 * On VM we only get one interrupt per virtally presented machinecheck.
 * Though one suffices, we may get one interrupt per (virtual) processor.
 */
189
	if (mcck.warning) {	/* WARNING pending ? */
L
Linus Torvalds 已提交
190 191 192 193 194 195
		static int mchchk_wng_posted = 0;
		/*
		 * Use single machine clear, as we cannot handle smp right now
		 */
		__ctl_clear_bit(14, 24);	/* Disable WARNING MCH */
		if (xchg(&mchchk_wng_posted, 1) == 0)
196
			kill_cad_pid(SIGPWR, 1);
L
Linus Torvalds 已提交
197 198
	}
#endif
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239

	if (mcck.kill_task) {
		local_irq_enable();
		printk(KERN_EMERG "mcck: Terminating task because of machine "
		       "malfunction (code 0x%016llx).\n", mcck.mcck_code);
		printk(KERN_EMERG "mcck: task: %s, pid: %d.\n",
		       current->comm, current->pid);
		do_exit(SIGSEGV);
	}
}

/*
 * returns 0 if all registers could be validated
 * returns 1 otherwise
 */
static int
s390_revalidate_registers(struct mci *mci)
{
	int kill_task;
	u64 tmpclock;
	u64 zero;
	void *fpt_save_area, *fpt_creg_save_area;

	kill_task = 0;
	zero = 0;
	/* General purpose registers */
	if (!mci->gr)
		/*
		 * General purpose registers couldn't be restored and have
		 * unknown contents. Process needs to be terminated.
		 */
		kill_task = 1;

	/* Revalidate floating point registers */
	if (!mci->fp)
		/*
		 * Floating point registers can't be restored and
		 * therefore the process needs to be terminated.
		 */
		kill_task = 1;

240
#ifndef CONFIG_64BIT
241 242 243 244 245 246
	asm volatile(
		"	ld	0,0(%0)\n"
		"	ld	2,8(%0)\n"
		"	ld	4,16(%0)\n"
		"	ld	6,24(%0)"
		: : "a" (&S390_lowcore.floating_pt_save_area));
247 248 249
#endif

	if (MACHINE_HAS_IEEE) {
250
#ifdef CONFIG_64BIT
251 252 253 254 255 256 257 258 259 260 261 262
		fpt_save_area = &S390_lowcore.floating_pt_save_area;
		fpt_creg_save_area = &S390_lowcore.fpt_creg_save_area;
#else
		fpt_save_area = (void *) S390_lowcore.extended_save_area_addr;
		fpt_creg_save_area = fpt_save_area+128;
#endif
		/* Floating point control register */
		if (!mci->fc) {
			/*
			 * Floating point control register can't be restored.
			 * Task will be terminated.
			 */
263
			asm volatile("lfpc 0(%0)" : : "a" (&zero), "m" (zero));
264 265
			kill_task = 1;

266 267
		} else
			asm volatile("lfpc 0(%0)" : : "a" (fpt_creg_save_area));
268

269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
		asm volatile(
			"	ld	0,0(%0)\n"
			"	ld	1,8(%0)\n"
			"	ld	2,16(%0)\n"
			"	ld	3,24(%0)\n"
			"	ld	4,32(%0)\n"
			"	ld	5,40(%0)\n"
			"	ld	6,48(%0)\n"
			"	ld	7,56(%0)\n"
			"	ld	8,64(%0)\n"
			"	ld	9,72(%0)\n"
			"	ld	10,80(%0)\n"
			"	ld	11,88(%0)\n"
			"	ld	12,96(%0)\n"
			"	ld	13,104(%0)\n"
			"	ld	14,112(%0)\n"
			"	ld	15,120(%0)\n"
			: : "a" (fpt_save_area));
287 288 289
	}

	/* Revalidate access registers */
290 291 292
	asm volatile(
		"	lam	0,15,0(%0)"
		: : "a" (&S390_lowcore.access_regs_save_area));
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
	if (!mci->ar)
		/*
		 * Access registers have unknown contents.
		 * Terminating task.
		 */
		kill_task = 1;

	/* Revalidate control registers */
	if (!mci->cr)
		/*
		 * Control registers have unknown contents.
		 * Can't recover and therefore stopping machine.
		 */
		s390_handle_damage("invalid control registers.");
	else
308
#ifdef CONFIG_64BIT
309 310 311
		asm volatile(
			"	lctlg	0,15,0(%0)"
			: : "a" (&S390_lowcore.cregs_save_area));
312
#else
313 314 315
		asm volatile(
			"	lctl	0,15,0(%0)"
			: : "a" (&S390_lowcore.cregs_save_area));
316 317 318 319 320 321 322
#endif

	/*
	 * We don't even try to revalidate the TOD register, since we simply
	 * can't write something sensible into that register.
	 */

323
#ifdef CONFIG_64BIT
324 325 326 327 328
	/*
	 * See if we can revalidate the TOD programmable register with its
	 * old contents (should be zero) otherwise set it to zero.
	 */
	if (!mci->pr)
329 330 331 332
		asm volatile(
			"	sr	0,0\n"
			"	sckpf"
			: : : "0", "cc");
333 334
	else
		asm volatile(
335 336 337 338
			"	l	0,0(%0)\n"
			"	sckpf"
			: : "a" (&S390_lowcore.tod_progreg_save_area)
			: "0", "cc");
339 340 341
#endif

	/* Revalidate clock comparator register */
342 343 344 345
	asm volatile(
		"	stck	0(%1)\n"
		"	sckc	0(%1)"
		: "=m" (tmpclock) : "a" (&(tmpclock)) : "cc", "memory");
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360

	/* Check if old PSW is valid */
	if (!mci->wp)
		/*
		 * Can't tell if we come from user or kernel mode
		 * -> stopping machine.
		 */
		s390_handle_damage("old psw invalid.");

	if (!mci->ms || !mci->pm || !mci->ia)
		kill_task = 1;

	return kill_task;
}

361
#define MAX_IPD_COUNT	29
H
Heiko Carstens 已提交
362
#define MAX_IPD_TIME	(5 * 60 * USEC_PER_SEC) /* 5 minutes */
363

364 365 366 367 368 369
/*
 * machine check handler.
 */
void
s390_do_machine_check(struct pt_regs *regs)
{
370 371 372 373
	static DEFINE_SPINLOCK(ipd_lock);
	static unsigned long long last_ipd;
	static int ipd_count;
	unsigned long long tmp;
374 375 376 377
	struct mci *mci;
	struct mcck_struct *mcck;
	int umode;

378 379
	lockdep_off();

380 381 382 383 384 385 386 387 388 389 390 391
	mci = (struct mci *) &S390_lowcore.mcck_interruption_code;
	mcck = &__get_cpu_var(cpu_mcck);
	umode = user_mode(regs);

	if (mci->sd)
		/* System damage -> stopping machine */
		s390_handle_damage("received system damage machine check.");

	if (mci->pd) {
		if (mci->b) {
			/* Processing backup -> verify if we can survive this */
			u64 z_mcic, o_mcic, t_mcic;
392
#ifdef CONFIG_64BIT
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
			z_mcic = (1ULL<<63 | 1ULL<<59 | 1ULL<<29);
			o_mcic = (1ULL<<43 | 1ULL<<42 | 1ULL<<41 | 1ULL<<40 |
				  1ULL<<36 | 1ULL<<35 | 1ULL<<34 | 1ULL<<32 |
				  1ULL<<30 | 1ULL<<21 | 1ULL<<20 | 1ULL<<17 |
				  1ULL<<16);
#else
			z_mcic = (1ULL<<63 | 1ULL<<59 | 1ULL<<57 | 1ULL<<50 |
				  1ULL<<29);
			o_mcic = (1ULL<<43 | 1ULL<<42 | 1ULL<<41 | 1ULL<<40 |
				  1ULL<<36 | 1ULL<<35 | 1ULL<<34 | 1ULL<<32 |
				  1ULL<<30 | 1ULL<<20 | 1ULL<<17 | 1ULL<<16);
#endif
			t_mcic = *(u64 *)mci;

			if (((t_mcic & z_mcic) != 0) ||
			    ((t_mcic & o_mcic) != o_mcic)) {
				s390_handle_damage("processing backup machine "
						   "check with damage.");
			}
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432

			/*
			 * Nullifying exigent condition, therefore we might
			 * retry this instruction.
			 */

			spin_lock(&ipd_lock);

			tmp = get_clock();

			if (((tmp - last_ipd) >> 12) < MAX_IPD_TIME)
				ipd_count++;
			else
				ipd_count = 1;

			last_ipd = tmp;

			if (ipd_count == MAX_IPD_COUNT)
				s390_handle_damage("too many ipd retries.");

			spin_unlock(&ipd_lock);
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
		}
		else {
			/* Processing damage -> stopping machine */
			s390_handle_damage("received instruction processing "
					   "damage machine check.");
		}
	}
	if (s390_revalidate_registers(mci)) {
		if (umode) {
			/*
			 * Couldn't restore all register contents while in
			 * user mode -> mark task for termination.
			 */
			mcck->kill_task = 1;
			mcck->mcck_code = *(unsigned long long *) mci;
			set_thread_flag(TIF_MCCK_PENDING);
		}
		else
			/*
			 * Couldn't restore all register contents while in
			 * kernel mode -> stopping machine.
			 */
			s390_handle_damage("unable to revalidate registers.");
	}

M
Martin Schwidefsky 已提交
458 459 460 461 462 463 464 465 466 467 468 469 470
	if (mci->cd) {
		/* Timing facility damage */
		s390_handle_damage("TOD clock damaged");
	}

	if (mci->ed && mci->ec) {
		/* External damage */
		if (S390_lowcore.external_damage_code & (1U << ED_ETR_SYNC))
			etr_sync_check();
		if (S390_lowcore.external_damage_code & (1U << ED_ETR_SWITCH))
			etr_switch_to_local();
	}

471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
	if (mci->se)
		/* Storage error uncorrected */
		s390_handle_damage("received storage error uncorrected "
				   "machine check.");

	if (mci->ke)
		/* Storage key-error uncorrected */
		s390_handle_damage("received storage key-error uncorrected "
				   "machine check.");

	if (mci->ds && mci->fa)
		/* Storage degradation */
		s390_handle_damage("received storage degradation machine "
				   "check.");

	if (mci->cp) {
		/* Channel report word pending */
		mcck->channel_report = 1;
		set_thread_flag(TIF_MCCK_PENDING);
	}

	if (mci->w) {
		/* Warning pending */
		mcck->warning = 1;
		set_thread_flag(TIF_MCCK_PENDING);
	}
497
	lockdep_on();
L
Linus Torvalds 已提交
498 499 500 501 502 503 504 505 506 507 508
}

/*
 * s390_init_machine_check
 *
 * initialize machine check handling
 */
static int
machine_check_init(void)
{
	init_MUTEX_LOCKED(&m_sem);
M
Martin Schwidefsky 已提交
509
	ctl_set_bit(14, 25);	/* enable external damage MCH */
510
	ctl_set_bit(14, 27);    /* enable system recovery MCH */
L
Linus Torvalds 已提交
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
#ifdef CONFIG_MACHCHK_WARNING
	ctl_set_bit(14, 24);	/* enable warning MCH */
#endif
	return 0;
}

/*
 * Initialize the machine check handler really early to be able to
 * catch all machine checks that happen during boot
 */
arch_initcall(machine_check_init);

/*
 * Machine checks for the channel subsystem must be enabled
 * after the channel subsystem is initialized
 */
static int __init
machine_check_crw_init (void)
{
530 531 532 533 534
	struct task_struct *task;

	task = kthread_run(s390_collect_crw_info, &m_sem, "kmcheck");
	if (IS_ERR(task))
		return PTR_ERR(task);
L
Linus Torvalds 已提交
535 536 537 538 539
	ctl_set_bit(14, 28);	/* enable channel report MCH */
	return 0;
}

device_initcall (machine_check_crw_init);