linux/arch/s390/kernel/nmi.c
<<
>>
Prefs
   1/*
   2 *   Machine check handler
   3 *
   4 *    Copyright IBM Corp. 2000, 2009
   5 *    Author(s): Ingo Adlung <adlung@de.ibm.com>,
   6 *               Martin Schwidefsky <schwidefsky@de.ibm.com>,
   7 *               Cornelia Huck <cornelia.huck@de.ibm.com>,
   8 *               Heiko Carstens <heiko.carstens@de.ibm.com>,
   9 */
  10
  11#include <linux/kernel_stat.h>
  12#include <linux/init.h>
  13#include <linux/errno.h>
  14#include <linux/hardirq.h>
  15#include <linux/time.h>
  16#include <linux/module.h>
  17#include <asm/lowcore.h>
  18#include <asm/smp.h>
  19#include <asm/etr.h>
  20#include <asm/cputime.h>
  21#include <asm/nmi.h>
  22#include <asm/crw.h>
  23
  24struct mcck_struct {
  25        int kill_task;
  26        int channel_report;
  27        int warning;
  28        unsigned long long mcck_code;
  29};
  30
  31static DEFINE_PER_CPU(struct mcck_struct, cpu_mcck);
  32
  33static void s390_handle_damage(char *msg)
  34{
  35        smp_send_stop();
  36        disabled_wait((unsigned long) __builtin_return_address(0));
  37        while (1);
  38}
  39
  40/*
  41 * Main machine check handler function. Will be called with interrupts enabled
  42 * or disabled and machine checks enabled or disabled.
  43 */
  44void s390_handle_mcck(void)
  45{
  46        unsigned long flags;
  47        struct mcck_struct mcck;
  48
  49        /*
  50         * Disable machine checks and get the current state of accumulated
  51         * machine checks. Afterwards delete the old state and enable machine
  52         * checks again.
  53         */
  54        local_irq_save(flags);
  55        local_mcck_disable();
  56        mcck = __get_cpu_var(cpu_mcck);
  57        memset(&__get_cpu_var(cpu_mcck), 0, sizeof(struct mcck_struct));
  58        clear_thread_flag(TIF_MCCK_PENDING);
  59        local_mcck_enable();
  60        local_irq_restore(flags);
  61
  62        if (mcck.channel_report)
  63                crw_handle_channel_report();
  64        /*
  65         * A warning may remain for a prolonged period on the bare iron.
  66         * (actually until the machine is powered off, or the problem is gone)
  67         * So we just stop listening for the WARNING MCH and avoid continuously
  68         * being interrupted.  One caveat is however, that we must do this per
  69         * processor and cannot use the smp version of ctl_clear_bit().
  70         * On VM we only get one interrupt per virtally presented machinecheck.
  71         * Though one suffices, we may get one interrupt per (virtual) cpu.
  72         */
  73        if (mcck.warning) {     /* WARNING pending ? */
  74                static int mchchk_wng_posted = 0;
  75
  76                /* Use single cpu clear, as we cannot handle smp here. */
  77                __ctl_clear_bit(14, 24);        /* Disable WARNING MCH */
  78                if (xchg(&mchchk_wng_posted, 1) == 0)
  79                        kill_cad_pid(SIGPWR, 1);
  80        }
  81        if (mcck.kill_task) {
  82                local_irq_enable();
  83                printk(KERN_EMERG "mcck: Terminating task because of machine "
  84                       "malfunction (code 0x%016llx).\n", mcck.mcck_code);
  85                printk(KERN_EMERG "mcck: task: %s, pid: %d.\n",
  86                       current->comm, current->pid);
  87                do_exit(SIGSEGV);
  88        }
  89}
  90EXPORT_SYMBOL_GPL(s390_handle_mcck);
  91
  92/*
  93 * returns 0 if all registers could be validated
  94 * returns 1 otherwise
  95 */
  96static int notrace s390_revalidate_registers(struct mci *mci)
  97{
  98        int kill_task;
  99        u64 zero;
 100        void *fpt_save_area, *fpt_creg_save_area;
 101
 102        kill_task = 0;
 103        zero = 0;
 104
 105        if (!mci->gr) {
 106                /*
 107                 * General purpose registers couldn't be restored and have
 108                 * unknown contents. Process needs to be terminated.
 109                 */
 110                kill_task = 1;
 111        }
 112        if (!mci->fp) {
 113                /*
 114                 * Floating point registers can't be restored and
 115                 * therefore the process needs to be terminated.
 116                 */
 117                kill_task = 1;
 118        }
 119#ifndef CONFIG_64BIT
 120        asm volatile(
 121                "       ld      0,0(%0)\n"
 122                "       ld      2,8(%0)\n"
 123                "       ld      4,16(%0)\n"
 124                "       ld      6,24(%0)"
 125                : : "a" (&S390_lowcore.floating_pt_save_area));
 126#endif
 127
 128        if (MACHINE_HAS_IEEE) {
 129#ifdef CONFIG_64BIT
 130                fpt_save_area = &S390_lowcore.floating_pt_save_area;
 131                fpt_creg_save_area = &S390_lowcore.fpt_creg_save_area;
 132#else
 133                fpt_save_area = (void *) S390_lowcore.extended_save_area_addr;
 134                fpt_creg_save_area = fpt_save_area + 128;
 135#endif
 136                if (!mci->fc) {
 137                        /*
 138                         * Floating point control register can't be restored.
 139                         * Task will be terminated.
 140                         */
 141                        asm volatile("lfpc 0(%0)" : : "a" (&zero), "m" (zero));
 142                        kill_task = 1;
 143
 144                } else
 145                        asm volatile("lfpc 0(%0)" : : "a" (fpt_creg_save_area));
 146
 147                asm volatile(
 148                        "       ld      0,0(%0)\n"
 149                        "       ld      1,8(%0)\n"
 150                        "       ld      2,16(%0)\n"
 151                        "       ld      3,24(%0)\n"
 152                        "       ld      4,32(%0)\n"
 153                        "       ld      5,40(%0)\n"
 154                        "       ld      6,48(%0)\n"
 155                        "       ld      7,56(%0)\n"
 156                        "       ld      8,64(%0)\n"
 157                        "       ld      9,72(%0)\n"
 158                        "       ld      10,80(%0)\n"
 159                        "       ld      11,88(%0)\n"
 160                        "       ld      12,96(%0)\n"
 161                        "       ld      13,104(%0)\n"
 162                        "       ld      14,112(%0)\n"
 163                        "       ld      15,120(%0)\n"
 164                        : : "a" (fpt_save_area));
 165        }
 166        /* Revalidate access registers */
 167        asm volatile(
 168                "       lam     0,15,0(%0)"
 169                : : "a" (&S390_lowcore.access_regs_save_area));
 170        if (!mci->ar) {
 171                /*
 172                 * Access registers have unknown contents.
 173                 * Terminating task.
 174                 */
 175                kill_task = 1;
 176        }
 177        /* Revalidate control registers */
 178        if (!mci->cr) {
 179                /*
 180                 * Control registers have unknown contents.
 181                 * Can't recover and therefore stopping machine.
 182                 */
 183                s390_handle_damage("invalid control registers.");
 184        } else {
 185#ifdef CONFIG_64BIT
 186                asm volatile(
 187                        "       lctlg   0,15,0(%0)"
 188                        : : "a" (&S390_lowcore.cregs_save_area));
 189#else
 190                asm volatile(
 191                        "       lctl    0,15,0(%0)"
 192                        : : "a" (&S390_lowcore.cregs_save_area));
 193#endif
 194        }
 195        /*
 196         * We don't even try to revalidate the TOD register, since we simply
 197         * can't write something sensible into that register.
 198         */
 199#ifdef CONFIG_64BIT
 200        /*
 201         * See if we can revalidate the TOD programmable register with its
 202         * old contents (should be zero) otherwise set it to zero.
 203         */
 204        if (!mci->pr)
 205                asm volatile(
 206                        "       sr      0,0\n"
 207                        "       sckpf"
 208                        : : : "0", "cc");
 209        else
 210                asm volatile(
 211                        "       l       0,0(%0)\n"
 212                        "       sckpf"
 213                        : : "a" (&S390_lowcore.tod_progreg_save_area)
 214                        : "0", "cc");
 215#endif
 216        /* Revalidate clock comparator register */
 217        set_clock_comparator(S390_lowcore.clock_comparator);
 218        /* Check if old PSW is valid */
 219        if (!mci->wp)
 220                /*
 221                 * Can't tell if we come from user or kernel mode
 222                 * -> stopping machine.
 223                 */
 224                s390_handle_damage("old psw invalid.");
 225
 226        if (!mci->ms || !mci->pm || !mci->ia)
 227                kill_task = 1;
 228
 229        return kill_task;
 230}
 231
 232#define MAX_IPD_COUNT   29
 233#define MAX_IPD_TIME    (5 * 60 * USEC_PER_SEC) /* 5 minutes */
 234
 235#define ED_STP_ISLAND   6       /* External damage STP island check */
 236#define ED_STP_SYNC     7       /* External damage STP sync check */
 237#define ED_ETR_SYNC     12      /* External damage ETR sync check */
 238#define ED_ETR_SWITCH   13      /* External damage ETR switch to local */
 239
 240/*
 241 * machine check handler.
 242 */
 243void notrace s390_do_machine_check(struct pt_regs *regs)
 244{
 245        static int ipd_count;
 246        static DEFINE_SPINLOCK(ipd_lock);
 247        static unsigned long long last_ipd;
 248        struct mcck_struct *mcck;
 249        unsigned long long tmp;
 250        struct mci *mci;
 251        int umode;
 252
 253        nmi_enter();
 254        inc_irq_stat(NMI_NMI);
 255        mci = (struct mci *) &S390_lowcore.mcck_interruption_code;
 256        mcck = &__get_cpu_var(cpu_mcck);
 257        umode = user_mode(regs);
 258
 259        if (mci->sd) {
 260                /* System damage -> stopping machine */
 261                s390_handle_damage("received system damage machine check.");
 262        }
 263        if (mci->pd) {
 264                if (mci->b) {
 265                        /* Processing backup -> verify if we can survive this */
 266                        u64 z_mcic, o_mcic, t_mcic;
 267#ifdef CONFIG_64BIT
 268                        z_mcic = (1ULL<<63 | 1ULL<<59 | 1ULL<<29);
 269                        o_mcic = (1ULL<<43 | 1ULL<<42 | 1ULL<<41 | 1ULL<<40 |
 270                                  1ULL<<36 | 1ULL<<35 | 1ULL<<34 | 1ULL<<32 |
 271                                  1ULL<<30 | 1ULL<<21 | 1ULL<<20 | 1ULL<<17 |
 272                                  1ULL<<16);
 273#else
 274                        z_mcic = (1ULL<<63 | 1ULL<<59 | 1ULL<<57 | 1ULL<<50 |
 275                                  1ULL<<29);
 276                        o_mcic = (1ULL<<43 | 1ULL<<42 | 1ULL<<41 | 1ULL<<40 |
 277                                  1ULL<<36 | 1ULL<<35 | 1ULL<<34 | 1ULL<<32 |
 278                                  1ULL<<30 | 1ULL<<20 | 1ULL<<17 | 1ULL<<16);
 279#endif
 280                        t_mcic = *(u64 *)mci;
 281
 282                        if (((t_mcic & z_mcic) != 0) ||
 283                            ((t_mcic & o_mcic) != o_mcic)) {
 284                                s390_handle_damage("processing backup machine "
 285                                                   "check with damage.");
 286                        }
 287
 288                        /*
 289                         * Nullifying exigent condition, therefore we might
 290                         * retry this instruction.
 291                         */
 292                        spin_lock(&ipd_lock);
 293                        tmp = get_tod_clock();
 294                        if (((tmp - last_ipd) >> 12) < MAX_IPD_TIME)
 295                                ipd_count++;
 296                        else
 297                                ipd_count = 1;
 298                        last_ipd = tmp;
 299                        if (ipd_count == MAX_IPD_COUNT)
 300                                s390_handle_damage("too many ipd retries.");
 301                        spin_unlock(&ipd_lock);
 302                } else {
 303                        /* Processing damage -> stopping machine */
 304                        s390_handle_damage("received instruction processing "
 305                                           "damage machine check.");
 306                }
 307        }
 308        if (s390_revalidate_registers(mci)) {
 309                if (umode) {
 310                        /*
 311                         * Couldn't restore all register contents while in
 312                         * user mode -> mark task for termination.
 313                         */
 314                        mcck->kill_task = 1;
 315                        mcck->mcck_code = *(unsigned long long *) mci;
 316                        set_thread_flag(TIF_MCCK_PENDING);
 317                } else {
 318                        /*
 319                         * Couldn't restore all register contents while in
 320                         * kernel mode -> stopping machine.
 321                         */
 322                        s390_handle_damage("unable to revalidate registers.");
 323                }
 324        }
 325        if (mci->cd) {
 326                /* Timing facility damage */
 327                s390_handle_damage("TOD clock damaged");
 328        }
 329        if (mci->ed && mci->ec) {
 330                /* External damage */
 331                if (S390_lowcore.external_damage_code & (1U << ED_ETR_SYNC))
 332                        etr_sync_check();
 333                if (S390_lowcore.external_damage_code & (1U << ED_ETR_SWITCH))
 334                        etr_switch_to_local();
 335                if (S390_lowcore.external_damage_code & (1U << ED_STP_SYNC))
 336                        stp_sync_check();
 337                if (S390_lowcore.external_damage_code & (1U << ED_STP_ISLAND))
 338                        stp_island_check();
 339        }
 340        if (mci->se)
 341                /* Storage error uncorrected */
 342                s390_handle_damage("received storage error uncorrected "
 343                                   "machine check.");
 344        if (mci->ke)
 345                /* Storage key-error uncorrected */
 346                s390_handle_damage("received storage key-error uncorrected "
 347                                   "machine check.");
 348        if (mci->ds && mci->fa)
 349                /* Storage degradation */
 350                s390_handle_damage("received storage degradation machine "
 351                                   "check.");
 352        if (mci->cp) {
 353                /* Channel report word pending */
 354                mcck->channel_report = 1;
 355                set_thread_flag(TIF_MCCK_PENDING);
 356        }
 357        if (mci->w) {
 358                /* Warning pending */
 359                mcck->warning = 1;
 360                set_thread_flag(TIF_MCCK_PENDING);
 361        }
 362        nmi_exit();
 363}
 364
 365static int __init machine_check_init(void)
 366{
 367        ctl_set_bit(14, 25);    /* enable external damage MCH */
 368        ctl_set_bit(14, 27);    /* enable system recovery MCH */
 369        ctl_set_bit(14, 24);    /* enable warning MCH */
 370        return 0;
 371}
 372arch_initcall(machine_check_init);
 373