OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [s390/] [s390mach.c] - Blame information for rev 81

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 *  drivers/s390/s390mach.c
3
 *   S/390 machine check handler
4
 *
5
 *  S390 version
6
 *    Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
7
 *    Author(s): Ingo Adlung (adlung@de.ibm.com)
8
 *               Martin Schwidefsky (schwidefsky@de.ibm.com)
9
 */
10
 
11
#include <linux/init.h>
12
#include <linux/sched.h>
13
#include <linux/errno.h>
14
#include <linux/workqueue.h>
15
#include <linux/time.h>
16
#include <linux/device.h>
17
#include <linux/kthread.h>
18
#include <asm/etr.h>
19
#include <asm/lowcore.h>
20
#include <asm/cio.h>
21
#include "cio/cio.h"
22
#include "cio/chsc.h"
23
#include "cio/css.h"
24
#include "cio/chp.h"
25
#include "s390mach.h"
26
 
27
static struct semaphore m_sem;
28
 
29
static NORET_TYPE void
30
s390_handle_damage(char *msg)
31
{
32
#ifdef CONFIG_SMP
33
        smp_send_stop();
34
#endif
35
        disabled_wait((unsigned long) __builtin_return_address(0));
36
        for(;;);
37
}
38
 
39
/*
40
 * Retrieve CRWs and call function to handle event.
41
 *
42
 * Note : we currently process CRWs for io and chsc subchannels only
43
 */
44
static int
45
s390_collect_crw_info(void *param)
46
{
47
        struct crw crw[2];
48
        int ccode;
49
        struct semaphore *sem;
50
        unsigned int chain;
51
 
52
        sem = (struct semaphore *)param;
53
repeat:
54
        down_interruptible(sem);
55
        chain = 0;
56
        while (1) {
57
                if (unlikely(chain > 1)) {
58
                        struct crw tmp_crw;
59
 
60
                        printk(KERN_WARNING"%s: Code does not support more "
61
                               "than two chained crws; please report to "
62
                               "linux390@de.ibm.com!\n", __FUNCTION__);
63
                        ccode = stcrw(&tmp_crw);
64
                        printk(KERN_WARNING"%s: crw reports slct=%d, oflw=%d, "
65
                               "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
66
                               __FUNCTION__, tmp_crw.slct, tmp_crw.oflw,
67
                               tmp_crw.chn, tmp_crw.rsc, tmp_crw.anc,
68
                               tmp_crw.erc, tmp_crw.rsid);
69
                        printk(KERN_WARNING"%s: This was crw number %x in the "
70
                               "chain\n", __FUNCTION__, chain);
71
                        if (ccode != 0)
72
                                break;
73
                        chain = tmp_crw.chn ? chain + 1 : 0;
74
                        continue;
75
                }
76
                ccode = stcrw(&crw[chain]);
77
                if (ccode != 0)
78
                        break;
79
                printk(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, "
80
                       "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
81
                       crw[chain].slct, crw[chain].oflw, crw[chain].chn,
82
                       crw[chain].rsc, crw[chain].anc, crw[chain].erc,
83
                       crw[chain].rsid);
84
                /* Check for overflows. */
85
                if (crw[chain].oflw) {
86
                        pr_debug("%s: crw overflow detected!\n", __FUNCTION__);
87
                        css_schedule_eval_all();
88
                        chain = 0;
89
                        continue;
90
                }
91
                switch (crw[chain].rsc) {
92
                case CRW_RSC_SCH:
93
                        if (crw[0].chn && !chain)
94
                                break;
95
                        pr_debug("source is subchannel %04X\n", crw[0].rsid);
96
                        css_process_crw(crw[0].rsid, chain ? crw[1].rsid : 0);
97
                        break;
98
                case CRW_RSC_MONITOR:
99
                        pr_debug("source is monitoring facility\n");
100
                        break;
101
                case CRW_RSC_CPATH:
102
                        pr_debug("source is channel path %02X\n", crw[0].rsid);
103
                        /*
104
                         * Check for solicited machine checks. These are
105
                         * created by reset channel path and need not be
106
                         * reported to the common I/O layer.
107
                         */
108
                        if (crw[chain].slct) {
109
                                pr_debug("solicited machine check for "
110
                                         "channel path %02X\n", crw[0].rsid);
111
                                break;
112
                        }
113
                        switch (crw[0].erc) {
114
                        case CRW_ERC_IPARM: /* Path has come. */
115
                                chp_process_crw(crw[0].rsid, 1);
116
                                break;
117
                        case CRW_ERC_PERRI: /* Path has gone. */
118
                        case CRW_ERC_PERRN:
119
                                chp_process_crw(crw[0].rsid, 0);
120
                                break;
121
                        default:
122
                                pr_debug("Don't know how to handle erc=%x\n",
123
                                         crw[0].erc);
124
                        }
125
                        break;
126
                case CRW_RSC_CONFIG:
127
                        pr_debug("source is configuration-alert facility\n");
128
                        break;
129
                case CRW_RSC_CSS:
130
                        pr_debug("source is channel subsystem\n");
131
                        chsc_process_crw();
132
                        break;
133
                default:
134
                        pr_debug("unknown source\n");
135
                        break;
136
                }
137
                /* chain is always 0 or 1 here. */
138
                chain = crw[chain].chn ? chain + 1 : 0;
139
        }
140
        goto repeat;
141
        return 0;
142
}
143
 
144
struct mcck_struct {
145
        int kill_task;
146
        int channel_report;
147
        int warning;
148
        unsigned long long mcck_code;
149
};
150
 
151
static DEFINE_PER_CPU(struct mcck_struct, cpu_mcck);
152
 
153
/*
154
 * Main machine check handler function. Will be called with interrupts enabled
155
 * or disabled and machine checks enabled or disabled.
156
 */
157
void
158
s390_handle_mcck(void)
159
{
160
        unsigned long flags;
161
        struct mcck_struct mcck;
162
 
163
        /*
164
         * Disable machine checks and get the current state of accumulated
165
         * machine checks. Afterwards delete the old state and enable machine
166
         * checks again.
167
         */
168
        local_irq_save(flags);
169
        local_mcck_disable();
170
        mcck = __get_cpu_var(cpu_mcck);
171
        memset(&__get_cpu_var(cpu_mcck), 0, sizeof(struct mcck_struct));
172
        clear_thread_flag(TIF_MCCK_PENDING);
173
        local_mcck_enable();
174
        local_irq_restore(flags);
175
 
176
        if (mcck.channel_report)
177
                up(&m_sem);
178
 
179
#ifdef CONFIG_MACHCHK_WARNING
180
/*
181
 * The warning may remain for a prolonged period on the bare iron.
182
 * (actually till the machine is powered off, or until the problem is gone)
183
 * So we just stop listening for the WARNING MCH and prevent continuously
184
 * being interrupted.  One caveat is however, that we must do this per
185
 * processor and cannot use the smp version of ctl_clear_bit().
186
 * On VM we only get one interrupt per virtally presented machinecheck.
187
 * Though one suffices, we may get one interrupt per (virtual) processor.
188
 */
189
        if (mcck.warning) {     /* WARNING pending ? */
190
                static int mchchk_wng_posted = 0;
191
                /*
192
                 * Use single machine clear, as we cannot handle smp right now
193
                 */
194
                __ctl_clear_bit(14, 24);        /* Disable WARNING MCH */
195
                if (xchg(&mchchk_wng_posted, 1) == 0)
196
                        kill_cad_pid(SIGPWR, 1);
197
        }
198
#endif
199
 
200
        if (mcck.kill_task) {
201
                local_irq_enable();
202
                printk(KERN_EMERG "mcck: Terminating task because of machine "
203
                       "malfunction (code 0x%016llx).\n", mcck.mcck_code);
204
                printk(KERN_EMERG "mcck: task: %s, pid: %d.\n",
205
                       current->comm, current->pid);
206
                do_exit(SIGSEGV);
207
        }
208
}
209
 
210
/*
211
 * returns 0 if all registers could be validated
212
 * returns 1 otherwise
213
 */
214
static int
215
s390_revalidate_registers(struct mci *mci)
216
{
217
        int kill_task;
218
        u64 tmpclock;
219
        u64 zero;
220
        void *fpt_save_area, *fpt_creg_save_area;
221
 
222
        kill_task = 0;
223
        zero = 0;
224
        /* General purpose registers */
225
        if (!mci->gr)
226
                /*
227
                 * General purpose registers couldn't be restored and have
228
                 * unknown contents. Process needs to be terminated.
229
                 */
230
                kill_task = 1;
231
 
232
        /* Revalidate floating point registers */
233
        if (!mci->fp)
234
                /*
235
                 * Floating point registers can't be restored and
236
                 * therefore the process needs to be terminated.
237
                 */
238
                kill_task = 1;
239
 
240
#ifndef CONFIG_64BIT
241
        asm volatile(
242
                "       ld      0,0(%0)\n"
243
                "       ld      2,8(%0)\n"
244
                "       ld      4,16(%0)\n"
245
                "       ld      6,24(%0)"
246
                : : "a" (&S390_lowcore.floating_pt_save_area));
247
#endif
248
 
249
        if (MACHINE_HAS_IEEE) {
250
#ifdef CONFIG_64BIT
251
                fpt_save_area = &S390_lowcore.floating_pt_save_area;
252
                fpt_creg_save_area = &S390_lowcore.fpt_creg_save_area;
253
#else
254
                fpt_save_area = (void *) S390_lowcore.extended_save_area_addr;
255
                fpt_creg_save_area = fpt_save_area+128;
256
#endif
257
                /* Floating point control register */
258
                if (!mci->fc) {
259
                        /*
260
                         * Floating point control register can't be restored.
261
                         * Task will be terminated.
262
                         */
263
                        asm volatile("lfpc 0(%0)" : : "a" (&zero), "m" (zero));
264
                        kill_task = 1;
265
 
266
                } else
267
                        asm volatile("lfpc 0(%0)" : : "a" (fpt_creg_save_area));
268
 
269
                asm volatile(
270
                        "       ld      0,0(%0)\n"
271
                        "       ld      1,8(%0)\n"
272
                        "       ld      2,16(%0)\n"
273
                        "       ld      3,24(%0)\n"
274
                        "       ld      4,32(%0)\n"
275
                        "       ld      5,40(%0)\n"
276
                        "       ld      6,48(%0)\n"
277
                        "       ld      7,56(%0)\n"
278
                        "       ld      8,64(%0)\n"
279
                        "       ld      9,72(%0)\n"
280
                        "       ld      10,80(%0)\n"
281
                        "       ld      11,88(%0)\n"
282
                        "       ld      12,96(%0)\n"
283
                        "       ld      13,104(%0)\n"
284
                        "       ld      14,112(%0)\n"
285
                        "       ld      15,120(%0)\n"
286
                        : : "a" (fpt_save_area));
287
        }
288
 
289
        /* Revalidate access registers */
290
        asm volatile(
291
                "       lam     0,15,0(%0)"
292
                : : "a" (&S390_lowcore.access_regs_save_area));
293
        if (!mci->ar)
294
                /*
295
                 * Access registers have unknown contents.
296
                 * Terminating task.
297
                 */
298
                kill_task = 1;
299
 
300
        /* Revalidate control registers */
301
        if (!mci->cr)
302
                /*
303
                 * Control registers have unknown contents.
304
                 * Can't recover and therefore stopping machine.
305
                 */
306
                s390_handle_damage("invalid control registers.");
307
        else
308
#ifdef CONFIG_64BIT
309
                asm volatile(
310
                        "       lctlg   0,15,0(%0)"
311
                        : : "a" (&S390_lowcore.cregs_save_area));
312
#else
313
                asm volatile(
314
                        "       lctl    0,15,0(%0)"
315
                        : : "a" (&S390_lowcore.cregs_save_area));
316
#endif
317
 
318
        /*
319
         * We don't even try to revalidate the TOD register, since we simply
320
         * can't write something sensible into that register.
321
         */
322
 
323
#ifdef CONFIG_64BIT
324
        /*
325
         * See if we can revalidate the TOD programmable register with its
326
         * old contents (should be zero) otherwise set it to zero.
327
         */
328
        if (!mci->pr)
329
                asm volatile(
330
                        "       sr      0,0\n"
331
                        "       sckpf"
332
                        : : : "0", "cc");
333
        else
334
                asm volatile(
335
                        "       l       0,0(%0)\n"
336
                        "       sckpf"
337
                        : : "a" (&S390_lowcore.tod_progreg_save_area)
338
                        : "0", "cc");
339
#endif
340
 
341
        /* Revalidate clock comparator register */
342
        asm volatile(
343
                "       stck    0(%1)\n"
344
                "       sckc    0(%1)"
345
                : "=m" (tmpclock) : "a" (&(tmpclock)) : "cc", "memory");
346
 
347
        /* Check if old PSW is valid */
348
        if (!mci->wp)
349
                /*
350
                 * Can't tell if we come from user or kernel mode
351
                 * -> stopping machine.
352
                 */
353
                s390_handle_damage("old psw invalid.");
354
 
355
        if (!mci->ms || !mci->pm || !mci->ia)
356
                kill_task = 1;
357
 
358
        return kill_task;
359
}
360
 
361
#define MAX_IPD_COUNT   29
362
#define MAX_IPD_TIME    (5 * 60 * USEC_PER_SEC) /* 5 minutes */
363
 
364
/*
365
 * machine check handler.
366
 */
367
void
368
s390_do_machine_check(struct pt_regs *regs)
369
{
370
        static DEFINE_SPINLOCK(ipd_lock);
371
        static unsigned long long last_ipd;
372
        static int ipd_count;
373
        unsigned long long tmp;
374
        struct mci *mci;
375
        struct mcck_struct *mcck;
376
        int umode;
377
 
378
        lockdep_off();
379
 
380
        mci = (struct mci *) &S390_lowcore.mcck_interruption_code;
381
        mcck = &__get_cpu_var(cpu_mcck);
382
        umode = user_mode(regs);
383
 
384
        if (mci->sd)
385
                /* System damage -> stopping machine */
386
                s390_handle_damage("received system damage machine check.");
387
 
388
        if (mci->pd) {
389
                if (mci->b) {
390
                        /* Processing backup -> verify if we can survive this */
391
                        u64 z_mcic, o_mcic, t_mcic;
392
#ifdef CONFIG_64BIT
393
                        z_mcic = (1ULL<<63 | 1ULL<<59 | 1ULL<<29);
394
                        o_mcic = (1ULL<<43 | 1ULL<<42 | 1ULL<<41 | 1ULL<<40 |
395
                                  1ULL<<36 | 1ULL<<35 | 1ULL<<34 | 1ULL<<32 |
396
                                  1ULL<<30 | 1ULL<<21 | 1ULL<<20 | 1ULL<<17 |
397
                                  1ULL<<16);
398
#else
399
                        z_mcic = (1ULL<<63 | 1ULL<<59 | 1ULL<<57 | 1ULL<<50 |
400
                                  1ULL<<29);
401
                        o_mcic = (1ULL<<43 | 1ULL<<42 | 1ULL<<41 | 1ULL<<40 |
402
                                  1ULL<<36 | 1ULL<<35 | 1ULL<<34 | 1ULL<<32 |
403
                                  1ULL<<30 | 1ULL<<20 | 1ULL<<17 | 1ULL<<16);
404
#endif
405
                        t_mcic = *(u64 *)mci;
406
 
407
                        if (((t_mcic & z_mcic) != 0) ||
408
                            ((t_mcic & o_mcic) != o_mcic)) {
409
                                s390_handle_damage("processing backup machine "
410
                                                   "check with damage.");
411
                        }
412
 
413
                        /*
414
                         * Nullifying exigent condition, therefore we might
415
                         * retry this instruction.
416
                         */
417
 
418
                        spin_lock(&ipd_lock);
419
 
420
                        tmp = get_clock();
421
 
422
                        if (((tmp - last_ipd) >> 12) < MAX_IPD_TIME)
423
                                ipd_count++;
424
                        else
425
                                ipd_count = 1;
426
 
427
                        last_ipd = tmp;
428
 
429
                        if (ipd_count == MAX_IPD_COUNT)
430
                                s390_handle_damage("too many ipd retries.");
431
 
432
                        spin_unlock(&ipd_lock);
433
                }
434
                else {
435
                        /* Processing damage -> stopping machine */
436
                        s390_handle_damage("received instruction processing "
437
                                           "damage machine check.");
438
                }
439
        }
440
        if (s390_revalidate_registers(mci)) {
441
                if (umode) {
442
                        /*
443
                         * Couldn't restore all register contents while in
444
                         * user mode -> mark task for termination.
445
                         */
446
                        mcck->kill_task = 1;
447
                        mcck->mcck_code = *(unsigned long long *) mci;
448
                        set_thread_flag(TIF_MCCK_PENDING);
449
                }
450
                else
451
                        /*
452
                         * Couldn't restore all register contents while in
453
                         * kernel mode -> stopping machine.
454
                         */
455
                        s390_handle_damage("unable to revalidate registers.");
456
        }
457
 
458
        if (mci->cd) {
459
                /* Timing facility damage */
460
                s390_handle_damage("TOD clock damaged");
461
        }
462
 
463
        if (mci->ed && mci->ec) {
464
                /* External damage */
465
                if (S390_lowcore.external_damage_code & (1U << ED_ETR_SYNC))
466
                        etr_sync_check();
467
                if (S390_lowcore.external_damage_code & (1U << ED_ETR_SWITCH))
468
                        etr_switch_to_local();
469
        }
470
 
471
        if (mci->se)
472
                /* Storage error uncorrected */
473
                s390_handle_damage("received storage error uncorrected "
474
                                   "machine check.");
475
 
476
        if (mci->ke)
477
                /* Storage key-error uncorrected */
478
                s390_handle_damage("received storage key-error uncorrected "
479
                                   "machine check.");
480
 
481
        if (mci->ds && mci->fa)
482
                /* Storage degradation */
483
                s390_handle_damage("received storage degradation machine "
484
                                   "check.");
485
 
486
        if (mci->cp) {
487
                /* Channel report word pending */
488
                mcck->channel_report = 1;
489
                set_thread_flag(TIF_MCCK_PENDING);
490
        }
491
 
492
        if (mci->w) {
493
                /* Warning pending */
494
                mcck->warning = 1;
495
                set_thread_flag(TIF_MCCK_PENDING);
496
        }
497
        lockdep_on();
498
}
499
 
500
/*
501
 * s390_init_machine_check
502
 *
503
 * initialize machine check handling
504
 */
505
static int
506
machine_check_init(void)
507
{
508
        init_MUTEX_LOCKED(&m_sem);
509
        ctl_set_bit(14, 25);    /* enable external damage MCH */
510
        ctl_set_bit(14, 27);    /* enable system recovery MCH */
511
#ifdef CONFIG_MACHCHK_WARNING
512
        ctl_set_bit(14, 24);    /* enable warning MCH */
513
#endif
514
        return 0;
515
}
516
 
517
/*
518
 * Initialize the machine check handler really early to be able to
519
 * catch all machine checks that happen during boot
520
 */
521
arch_initcall(machine_check_init);
522
 
523
/*
524
 * Machine checks for the channel subsystem must be enabled
525
 * after the channel subsystem is initialized
526
 */
527
static int __init
528
machine_check_crw_init (void)
529
{
530
        struct task_struct *task;
531
 
532
        task = kthread_run(s390_collect_crw_info, &m_sem, "kmcheck");
533
        if (IS_ERR(task))
534
                return PTR_ERR(task);
535
        ctl_set_bit(14, 28);    /* enable channel report MCH */
536
        return 0;
537
}
538
 
539
device_initcall (machine_check_crw_init);

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.