diff options
Diffstat (limited to '')
-rw-r--r-- | arch/s390/include/asm/crw.h | 68 | ||||
-rw-r--r-- | arch/s390/include/asm/nmi.h | 66 | ||||
-rw-r--r-- | arch/s390/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/s390/kernel/nmi.c (renamed from drivers/s390/s390mach.c) | 246 | ||||
-rw-r--r-- | arch/s390/kernel/process.c | 2 | ||||
-rw-r--r-- | arch/s390/kvm/kvm-s390.c | 4 |
6 files changed, 183 insertions, 205 deletions
diff --git a/arch/s390/include/asm/crw.h b/arch/s390/include/asm/crw.h new file mode 100644 index 000000000000..2185a6d619d3 --- /dev/null +++ b/arch/s390/include/asm/crw.h @@ -0,0 +1,68 @@ +/* + * Data definitions for channel report processing + * Copyright IBM Corp. 2000,2009 + * Author(s): Ingo Adlung <adlung@de.ibm.com>, + * Martin Schwidefsky <schwidefsky@de.ibm.com>, + * Cornelia Huck <cornelia.huck@de.ibm.com>, + * Heiko Carstens <heiko.carstens@de.ibm.com>, + */ + +#ifndef _ASM_S390_CRW_H +#define _ASM_S390_CRW_H + +#include <linux/types.h> + +/* + * Channel Report Word + */ +struct crw { + __u32 res1 : 1; /* reserved zero */ + __u32 slct : 1; /* solicited */ + __u32 oflw : 1; /* overflow */ + __u32 chn : 1; /* chained */ + __u32 rsc : 4; /* reporting source code */ + __u32 anc : 1; /* ancillary report */ + __u32 res2 : 1; /* reserved zero */ + __u32 erc : 6; /* error-recovery code */ + __u32 rsid : 16; /* reporting-source ID */ +} __attribute__ ((packed)); + +typedef void (*crw_handler_t)(struct crw *, struct crw *, int); + +extern int crw_register_handler(int rsc, crw_handler_t handler); +extern void crw_unregister_handler(int rsc); +extern void crw_handle_channel_report(void); + +#define NR_RSCS 16 + +#define CRW_RSC_MONITOR 0x2 /* monitoring facility */ +#define CRW_RSC_SCH 0x3 /* subchannel */ +#define CRW_RSC_CPATH 0x4 /* channel path */ +#define CRW_RSC_CONFIG 0x9 /* configuration-alert facility */ +#define CRW_RSC_CSS 0xB /* channel subsystem */ + +#define CRW_ERC_EVENT 0x00 /* event information pending */ +#define CRW_ERC_AVAIL 0x01 /* available */ +#define CRW_ERC_INIT 0x02 /* initialized */ +#define CRW_ERC_TERROR 0x03 /* temporary error */ +#define CRW_ERC_IPARM 0x04 /* installed parm initialized */ +#define CRW_ERC_TERM 0x05 /* terminal */ +#define CRW_ERC_PERRN 0x06 /* perm. error, fac. not init */ +#define CRW_ERC_PERRI 0x07 /* perm. error, facility init */ +#define CRW_ERC_PMOD 0x08 /* installed parameters modified */ + +static inline int stcrw(struct crw *pcrw) +{ + int ccode; + + asm volatile( + " stcrw 0(%2)\n" + " ipm %0\n" + " srl %0,28\n" + : "=d" (ccode), "=m" (*pcrw) + : "a" (pcrw) + : "cc" ); + return ccode; +} + +#endif /* _ASM_S390_CRW_H */ diff --git a/arch/s390/include/asm/nmi.h b/arch/s390/include/asm/nmi.h new file mode 100644 index 000000000000..f4b60441adca --- /dev/null +++ b/arch/s390/include/asm/nmi.h @@ -0,0 +1,66 @@ +/* + * Machine check handler definitions + * + * Copyright IBM Corp. 2000,2009 + * Author(s): Ingo Adlung <adlung@de.ibm.com>, + * Martin Schwidefsky <schwidefsky@de.ibm.com>, + * Cornelia Huck <cornelia.huck@de.ibm.com>, + * Heiko Carstens <heiko.carstens@de.ibm.com>, + */ + +#ifndef _ASM_S390_NMI_H +#define _ASM_S390_NMI_H + +#include <linux/types.h> + +struct mci { + __u32 sd : 1; /* 00 system damage */ + __u32 pd : 1; /* 01 instruction-processing damage */ + __u32 sr : 1; /* 02 system recovery */ + __u32 : 1; /* 03 */ + __u32 cd : 1; /* 04 timing-facility damage */ + __u32 ed : 1; /* 05 external damage */ + __u32 : 1; /* 06 */ + __u32 dg : 1; /* 07 degradation */ + __u32 w : 1; /* 08 warning pending */ + __u32 cp : 1; /* 09 channel-report pending */ + __u32 sp : 1; /* 10 service-processor damage */ + __u32 ck : 1; /* 11 channel-subsystem damage */ + __u32 : 2; /* 12-13 */ + __u32 b : 1; /* 14 backed up */ + __u32 : 1; /* 15 */ + __u32 se : 1; /* 16 storage error uncorrected */ + __u32 sc : 1; /* 17 storage error corrected */ + __u32 ke : 1; /* 18 storage-key error uncorrected */ + __u32 ds : 1; /* 19 storage degradation */ + __u32 wp : 1; /* 20 psw mwp validity */ + __u32 ms : 1; /* 21 psw mask and key validity */ + __u32 pm : 1; /* 22 psw program mask and cc validity */ + __u32 ia : 1; /* 23 psw instruction address validity */ + __u32 fa : 1; /* 24 failing storage address validity */ + __u32 : 1; /* 25 */ + __u32 ec : 1; /* 26 external damage code validity */ + __u32 fp : 1; /* 27 floating point register validity */ + __u32 gr : 1; /* 28 general register validity */ + __u32 cr : 1; /* 29 control register validity */ + __u32 : 1; /* 30 */ + __u32 st : 1; /* 31 storage logical validity */ + __u32 ie : 1; /* 32 indirect storage error */ + __u32 ar : 1; /* 33 access register validity */ + __u32 da : 1; /* 34 delayed access exception */ + __u32 : 7; /* 35-41 */ + __u32 pr : 1; /* 42 tod programmable register validity */ + __u32 fc : 1; /* 43 fp control register validity */ + __u32 ap : 1; /* 44 ancillary report */ + __u32 : 1; /* 45 */ + __u32 ct : 1; /* 46 cpu timer validity */ + __u32 cc : 1; /* 47 clock comparator validity */ + __u32 : 16; /* 47-63 */ +}; + +struct pt_regs; + +extern void s390_handle_mcck(void); +extern void s390_do_machine_check(struct pt_regs *regs); + +#endif /* _ASM_S390_NMI_H */ diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 33e7aee70513..228e3105ded7 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -22,7 +22,7 @@ CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w obj-y := bitmap.o traps.o time.o process.o base.o early.o setup.o \ processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ s390_ext.o debug.o irq.o ipl.o dis.o diag.o mem_detect.o \ - vdso.o vtime.o sysinfo.o + vdso.o vtime.o sysinfo.o nmi.o obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o) obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o) diff --git a/drivers/s390/s390mach.c b/arch/s390/kernel/nmi.c index 8e437052e076..8d50eaf76a60 100644 --- a/drivers/s390/s390mach.c +++ b/arch/s390/kernel/nmi.c @@ -1,137 +1,23 @@ /* - * drivers/s390/s390mach.c - * S/390 machine check handler + * Machine check handler * - * Copyright IBM Corp. 2000,2008 - * Author(s): Ingo Adlung (adlung@de.ibm.com) - * Martin Schwidefsky (schwidefsky@de.ibm.com) - * Cornelia Huck <cornelia.huck@de.ibm.com> + * Copyright IBM Corp. 2000,2009 + * Author(s): Ingo Adlung <adlung@de.ibm.com>, + * Martin Schwidefsky <schwidefsky@de.ibm.com>, + * Cornelia Huck <cornelia.huck@de.ibm.com>, + * Heiko Carstens <heiko.carstens@de.ibm.com>, */ #include <linux/init.h> -#include <linux/sched.h> #include <linux/errno.h> -#include <linux/workqueue.h> #include <linux/time.h> -#include <linux/device.h> -#include <linux/kthread.h> -#include <asm/etr.h> +#include <linux/module.h> #include <asm/lowcore.h> -#include <asm/cio.h> +#include <asm/smp.h> +#include <asm/etr.h> #include <asm/cpu.h> -#include "s390mach.h" - -static struct semaphore m_sem; - -static NORET_TYPE void -s390_handle_damage(char *msg) -{ -#ifdef CONFIG_SMP - smp_send_stop(); -#endif - disabled_wait((unsigned long) __builtin_return_address(0)); - for(;;); -} - -static crw_handler_t crw_handlers[NR_RSCS]; - -/** - * s390_register_crw_handler() - register a channel report word handler - * @rsc: reporting source code to handle - * @handler: handler to be registered - * - * Returns %0 on success and a negative error value otherwise. - */ -int s390_register_crw_handler(int rsc, crw_handler_t handler) -{ - if ((rsc < 0) || (rsc >= NR_RSCS)) - return -EINVAL; - if (!cmpxchg(&crw_handlers[rsc], NULL, handler)) - return 0; - return -EBUSY; -} - -/** - * s390_unregister_crw_handler() - unregister a channel report word handler - * @rsc: reporting source code to handle - */ -void s390_unregister_crw_handler(int rsc) -{ - if ((rsc < 0) || (rsc >= NR_RSCS)) - return; - xchg(&crw_handlers[rsc], NULL); - synchronize_sched(); -} - -/* - * Retrieve CRWs and call function to handle event. - */ -static int s390_collect_crw_info(void *param) -{ - struct crw crw[2]; - int ccode; - struct semaphore *sem; - unsigned int chain; - int ignore; - - sem = (struct semaphore *)param; -repeat: - ignore = down_interruptible(sem); - chain = 0; - while (1) { - 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", __func__); - 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", - __func__, 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", __func__, chain); - if (ccode != 0) - break; - chain = tmp_crw.chn ? chain + 1 : 0; - continue; - } - ccode = stcrw(&crw[chain]); - if (ccode != 0) - break; - 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); - /* Check for overflows. */ - if (crw[chain].oflw) { - int i; - - pr_debug("%s: crw overflow detected!\n", __func__); - for (i = 0; i < NR_RSCS; i++) { - if (crw_handlers[i]) - crw_handlers[i](NULL, NULL, 1); - } - chain = 0; - continue; - } - if (crw[0].chn && !chain) { - chain++; - continue; - } - if (crw_handlers[crw[chain].rsc]) - crw_handlers[crw[chain].rsc](&crw[0], - chain ? &crw[1] : NULL, - 0); - /* chain is always 0 or 1 here. */ - chain = crw[chain].chn ? chain + 1 : 0; - } - goto repeat; - return 0; -} +#include <asm/nmi.h> +#include <asm/crw.h> struct mcck_struct { int kill_task; @@ -142,12 +28,18 @@ struct mcck_struct { static DEFINE_PER_CPU(struct mcck_struct, cpu_mcck); +static NORET_TYPE void s390_handle_damage(char *msg) +{ + smp_send_stop(); + disabled_wait((unsigned long) __builtin_return_address(0)); + while (1); +} + /* * Main machine check handler function. Will be called with interrupts enabled * or disabled and machine checks enabled or disabled. */ -void -s390_handle_mcck(void) +void s390_handle_mcck(void) { unsigned long flags; struct mcck_struct mcck; @@ -166,7 +58,7 @@ s390_handle_mcck(void) local_irq_restore(flags); if (mcck.channel_report) - up(&m_sem); + crw_handle_channel_report(); #ifdef CONFIG_MACHCHK_WARNING /* @@ -204,8 +96,7 @@ EXPORT_SYMBOL_GPL(s390_handle_mcck); * returns 0 if all registers could be validated * returns 1 otherwise */ -static int -s390_revalidate_registers(struct mci *mci) +static int notrace s390_revalidate_registers(struct mci *mci) { int kill_task; u64 tmpclock; @@ -214,22 +105,21 @@ s390_revalidate_registers(struct mci *mci) kill_task = 0; zero = 0; - /* General purpose registers */ - if (!mci->gr) + + 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) + } + if (!mci->fp) { /* * Floating point registers can't be restored and * therefore the process needs to be terminated. */ kill_task = 1; - + } #ifndef CONFIG_64BIT asm volatile( " ld 0,0(%0)\n" @@ -245,9 +135,8 @@ s390_revalidate_registers(struct mci *mci) 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; + fpt_creg_save_area = fpt_save_area + 128; #endif - /* Floating point control register */ if (!mci->fc) { /* * Floating point control register can't be restored. @@ -278,26 +167,25 @@ s390_revalidate_registers(struct mci *mci) " ld 15,120(%0)\n" : : "a" (fpt_save_area)); } - /* Revalidate access registers */ asm volatile( " lam 0,15,0(%0)" : : "a" (&S390_lowcore.access_regs_save_area)); - if (!mci->ar) + if (!mci->ar) { /* * Access registers have unknown contents. * Terminating task. */ kill_task = 1; - + } /* Revalidate control registers */ - if (!mci->cr) + if (!mci->cr) { /* * Control registers have unknown contents. * Can't recover and therefore stopping machine. */ s390_handle_damage("invalid control registers."); - else + } else { #ifdef CONFIG_64BIT asm volatile( " lctlg 0,15,0(%0)" @@ -307,12 +195,11 @@ s390_revalidate_registers(struct mci *mci) " lctl 0,15,0(%0)" : : "a" (&S390_lowcore.cregs_save_area)); #endif - + } /* * We don't even try to revalidate the TOD register, since we simply * can't write something sensible into that register. */ - #ifdef CONFIG_64BIT /* * See if we can revalidate the TOD programmable register with its @@ -330,7 +217,6 @@ s390_revalidate_registers(struct mci *mci) : : "a" (&S390_lowcore.tod_progreg_save_area) : "0", "cc"); #endif - /* Revalidate clock comparator register */ asm volatile( " stck 0(%1)\n" @@ -354,31 +240,35 @@ s390_revalidate_registers(struct mci *mci) #define MAX_IPD_COUNT 29 #define MAX_IPD_TIME (5 * 60 * USEC_PER_SEC) /* 5 minutes */ +#define ED_STP_ISLAND 6 /* External damage STP island check */ +#define ED_STP_SYNC 7 /* External damage STP sync check */ +#define ED_ETR_SYNC 12 /* External damage ETR sync check */ +#define ED_ETR_SWITCH 13 /* External damage ETR switch to local */ + /* * machine check handler. */ void notrace s390_do_machine_check(struct pt_regs *regs) { + static int ipd_count; static DEFINE_SPINLOCK(ipd_lock); static unsigned long long last_ipd; - static int ipd_count; + struct mcck_struct *mcck; unsigned long long tmp; struct mci *mci; - struct mcck_struct *mcck; int umode; lockdep_off(); - s390_idle_check(); mci = (struct mci *) &S390_lowcore.mcck_interruption_code; mcck = &__get_cpu_var(cpu_mcck); umode = user_mode(regs); - if (mci->sd) + 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 */ @@ -408,24 +298,17 @@ void notrace s390_do_machine_check(struct pt_regs *regs) * 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); - } - else { + } else { /* Processing damage -> stopping machine */ s390_handle_damage("received instruction processing " "damage machine check."); @@ -440,20 +323,18 @@ void notrace s390_do_machine_check(struct pt_regs *regs) mcck->kill_task = 1; mcck->mcck_code = *(unsigned long long *) mci; set_thread_flag(TIF_MCCK_PENDING); - } - else + } else { /* * Couldn't restore all register contents while in * kernel mode -> stopping machine. */ s390_handle_damage("unable to revalidate registers."); + } } - 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)) @@ -465,28 +346,23 @@ void notrace s390_do_machine_check(struct pt_regs *regs) if (S390_lowcore.external_damage_code & (1U << ED_STP_ISLAND)) stp_island_check(); } - 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; @@ -495,43 +371,13 @@ void notrace s390_do_machine_check(struct pt_regs *regs) lockdep_on(); } -/* - * s390_init_machine_check - * - * initialize machine check handling - */ -static int -machine_check_init(void) +static int __init machine_check_init(void) { - init_MUTEX_LOCKED(&m_sem); ctl_set_bit(14, 25); /* enable external damage MCH */ - ctl_set_bit(14, 27); /* enable system recovery MCH */ + ctl_set_bit(14, 27); /* enable system recovery MCH */ #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) -{ - struct task_struct *task; - - task = kthread_run(s390_collect_crw_info, &m_sem, "kmcheck"); - if (IS_ERR(task)) - return PTR_ERR(task); - ctl_set_bit(14, 28); /* enable channel report MCH */ - return 0; -} - -device_initcall (machine_check_crw_init); diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index e6b480625cb3..bd616fb31e75 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -39,6 +39,7 @@ #include <asm/processor.h> #include <asm/irq.h> #include <asm/timer.h> +#include <asm/nmi.h> #include "entry.h" asmlinkage void ret_from_fork(void) asm ("ret_from_fork"); @@ -68,7 +69,6 @@ unsigned long thread_saved_pc(struct task_struct *tsk) return sf->gprs[8]; } -extern void s390_handle_mcck(void); /* * The idle loop on a S390... */ diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index c55a4b9ffd88..caa4d2877016 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -23,7 +23,7 @@ #include <linux/timer.h> #include <asm/lowcore.h> #include <asm/pgtable.h> - +#include <asm/nmi.h> #include "kvm-s390.h" #include "gaccess.h" @@ -440,8 +440,6 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, return -EINVAL; /* not implemented yet */ } -extern void s390_handle_mcck(void); - static void __vcpu_run(struct kvm_vcpu *vcpu) { memcpy(&vcpu->arch.sie_block->gg14, &vcpu->arch.guest_gprs[14], 16); |