added mtd driver
[linux-2.4.git] / drivers / s390 / s390mach.c
1 /*
2  *  arch/s390/kernel/s390mach.c
3  *   S/390 machine check handler,
4  *            currently only channel-reports are supported
5  *
6  *  S390 version
7  *    Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
8  *    Author(s): Ingo Adlung (adlung@de.ibm.com)
9  */
10
11 #include <linux/config.h>
12 #include <linux/spinlock.h>
13 #include <linux/init.h>
14 #include <linux/slab.h>
15 #ifdef CONFIG_SMP
16 #include <linux/smp.h>
17 #endif
18
19 #include <asm/irq.h>
20 #include <asm/lowcore.h>
21 #include <asm/semaphore.h>
22 #include <asm/s390io.h>
23 #include <asm/s390dyn.h>
24 #include <asm/s390mach.h>
25 #ifdef CONFIG_MACHCHK_WARNING
26 #include <asm/signal.h>
27 #endif
28
29 extern void ctrl_alt_del(void);
30
31 #define S390_MACHCHK_DEBUG
32
33 static int         s390_machine_check_handler( void * parm );
34 static void        s390_enqueue_mchchk( mache_t *mchchk );
35 static mache_t    *s390_dequeue_mchchk( void );
36 static void        s390_enqueue_free_mchchk( mache_t *mchchk );
37 static mache_t    *s390_dequeue_free_mchchk( void );
38 static int         s390_collect_crw_info( void );
39 #ifdef CONFIG_MACHCHK_WARNING
40 static int         s390_post_warning( void );
41 #endif
42
43 static mache_t    *mchchk_queue_head = NULL;
44 static mache_t    *mchchk_queue_tail = NULL;
45 static mache_t    *mchchk_queue_free = NULL;
46 static crwe_t     *crw_buffer_anchor = NULL;
47 static spinlock_t  mchchk_queue_lock = SPIN_LOCK_UNLOCKED;
48 static spinlock_t  crw_queue_lock    = SPIN_LOCK_UNLOCKED;
49
50 static struct semaphore s_sem;
51
52 #ifdef CONFIG_MACHCHK_WARNING
53 static int mchchk_wng_posted = 0;
54 #endif
55
56 /*
57  * s390_init_machine_check
58  *
59  * initialize machine check handling
60  */
61 void s390_init_machine_check( void )
62 {
63         crwe_t  *pcrwe;  /* CRW buffer element pointer */
64         mache_t *pmache;   /* machine check element pointer */
65
66         init_MUTEX_LOCKED( &s_sem );
67
68         pcrwe = kmalloc( MAX_CRW_PENDING * sizeof( crwe_t), GFP_KERNEL);
69
70         if ( pcrwe )
71         {
72                 int i;
73
74                 crw_buffer_anchor = pcrwe;
75
76                 for ( i=0; i < MAX_CRW_PENDING-1; i++)
77                 {
78                         pcrwe->crwe_next = (crwe_t *)((unsigned long)pcrwe + sizeof(crwe_t));
79                 pcrwe            = pcrwe->crwe_next;
80
81                 } /* endfor */  
82
83                 pcrwe->crwe_next = NULL;
84
85         }
86         else
87         {
88                 panic( "s390_init_machine_check : unable to obtain memory\n");          
89
90         } /* endif */
91
92         pmache = kmalloc( MAX_MACH_PENDING * sizeof( mache_t), GFP_KERNEL);
93
94         if ( pmache )
95         {
96                 int i;
97
98                 for ( i=0; i < MAX_MACH_PENDING; i++)
99                 {
100                         s390_enqueue_free_mchchk( pmache );
101                    pmache = (mache_t *)((unsigned long)pmache + sizeof(mache_t));
102
103                 } /* endfor */  
104         }
105         else
106         {
107                 panic( "s390_init_machine_check : unable to obtain memory\n");          
108
109         } /* endif */
110
111 #ifdef S390_MACHCHK_DEBUG
112         printk( KERN_NOTICE "init_mach : starting machine check handler\n");
113 #endif  
114
115         kernel_thread( s390_machine_check_handler, &s_sem, CLONE_FS | CLONE_FILES);
116
117         ctl_clear_bit( 14, 25 );  // disable damage MCH         
118
119         ctl_set_bit( 14, 26 ); /* enable degradation MCH */
120         ctl_set_bit( 14, 27 ); /* enable system recovery MCH */
121 #if 1
122         ctl_set_bit( 14, 28 );          // enable channel report MCH
123 #endif
124 #ifdef CONFIG_MACHCHK_WARNING
125         ctl_set_bit( 14, 24);   /* enable warning MCH */
126 #endif
127
128 #ifdef S390_MACHCHK_DEBUG
129         printk( KERN_DEBUG "init_mach : machine check buffer : head = %08X\n",
130             (unsigned)&mchchk_queue_head);
131         printk( KERN_DEBUG "init_mach : machine check buffer : tail = %08X\n",
132             (unsigned)&mchchk_queue_tail);
133         printk( KERN_DEBUG "init_mach : machine check buffer : free = %08X\n",
134             (unsigned)&mchchk_queue_free);
135         printk( KERN_DEBUG "init_mach : CRW entry buffer anchor = %08X\n",
136             (unsigned)&crw_buffer_anchor);
137         printk( KERN_DEBUG "init_mach : machine check handler ready\n");
138 #endif  
139
140         return;
141 }
142
143 static void s390_handle_damage(char * msg){
144
145         unsigned long caller = (unsigned long) __builtin_return_address(0);
146
147         printk(KERN_EMERG "%s\n", msg);
148 #ifdef CONFIG_SMP
149         smp_send_stop();
150 #endif
151         disabled_wait(caller);
152         return;
153 }
154
155 /*
156  * s390_do_machine_check
157  *
158  * mchine check pre-processor, collecting the machine check info,
159  *  queueing it and posting the machine check handler for processing.
160  */
161 void s390_do_machine_check( void )
162 {
163         int      crw_count;
164         mcic_t   mcic;
165
166 #ifdef S390_MACHCHK_DEBUG
167         printk( KERN_INFO "s390_do_machine_check : starting ...\n");
168 #endif
169
170         memcpy( &mcic,
171                 &S390_lowcore.mcck_interruption_code,
172                 sizeof(__u64));
173                 
174         if (mcic.mcc.mcd.sd) /* system damage */
175                 s390_handle_damage("received system damage machine check\n");
176
177         if (mcic.mcc.mcd.pd) /* instruction processing damage */
178                 s390_handle_damage("received instruction processing damage machine check\n");
179
180         if (mcic.mcc.mcd.se) /* storage error uncorrected */
181                 s390_handle_damage("received storage error uncorrected machine check\n");
182
183         if (mcic.mcc.mcd.sc) /* storage error corrected */
184                 printk(KERN_WARNING "received storage error corrected machine check\n");
185
186         if (mcic.mcc.mcd.ke) /* storage key-error uncorrected */
187                 s390_handle_damage("received storage key-error uncorrected machine check\n");
188
189         if (mcic.mcc.mcd.ds && mcic.mcc.mcd.fa) /* storage degradation */
190                 s390_handle_damage("received storage degradation machine check\n");
191
192         if ( mcic.mcc.mcd.cp )  // CRW pending ?
193         {
194                 crw_count = s390_collect_crw_info();
195
196                 if ( crw_count )
197                 {
198                         up( &s_sem );
199
200                 } /* endif */
201
202         } /* endif */
203 #ifdef CONFIG_MACHCHK_WARNING
204 /*
205  * The warning may remain for a prolonged period on the bare iron.
206  * (actually till the machine is powered off, or until the problem is gone)
207  * So we just stop listening for the WARNING MCH and prevent continuously
208  * being interrupted.  One caveat is however, that we must do this per 
209  * processor and cannot use the smp version of ctl_clear_bit().
210  * On VM we only get one interrupt per virtally presented machinecheck.
211  * Though one suffices, we may get one interrupt per (virtual) processor. 
212  */
213         if ( mcic.mcc.mcd.w )   // WARNING pending ?
214         {
215                 // Use single machine clear, as we cannot handle smp right now
216                 __ctl_clear_bit( 14, 24 );      // Disable WARNING MCH
217
218                 if ( ! mchchk_wng_posted )
219                 { 
220                         mchchk_wng_posted = s390_post_warning();
221
222                         if ( mchchk_wng_posted )
223                         {
224                                 up( &s_sem );
225
226                         } /* endif */
227
228                 } /* endif */
229
230         } /* endif */
231 #endif
232
233 #ifdef S390_MACHCHK_DEBUG
234         printk( KERN_INFO "s390_do_machine_check : done \n");
235 #endif
236
237         return;
238 }
239
240 /*
241  * s390_machine_check_handler
242  *
243  * machine check handler, dequeueing machine check entries
244  *  and processing them
245  */
246 static int s390_machine_check_handler( void *parm)
247 {
248         struct semaphore *sem = parm;
249         unsigned long     flags;
250         mache_t          *pmache;
251
252         int               found = 0;
253
254         /* set name to something sensible */
255         strcpy (current->comm, "kmcheck");
256
257
258         /* block all signals */
259         sigfillset(&current->blocked);
260
261 #ifdef S390_MACHCHK_DEBUG
262         printk( KERN_NOTICE "mach_handler : ready\n");
263 #endif  
264
265         do {
266
267 #ifdef S390_MACHCHK_DEBUG
268                 printk( KERN_NOTICE "mach_handler : waiting for wakeup\n");
269 #endif  
270
271                 down_interruptible( sem );
272
273 #ifdef S390_MACHCHK_DEBUG
274                 printk( KERN_NOTICE "\nmach_handler : wakeup ... \n");
275 #endif  
276                 found = 0; /* init ... */
277
278                 __save_flags( flags );
279                 __cli();
280
281                 do {
282
283                 pmache = s390_dequeue_mchchk();
284
285                 if ( pmache )
286                 {
287                         found = 1;
288                 
289                         if ( pmache->mcic.mcc.mcd.cp )
290                         {
291                                 crwe_t *pcrwe_n;
292                                 crwe_t *pcrwe_h;
293
294                                 s390_do_crw_pending( pmache->mc.crwe );
295
296                                 pcrwe_h = pmache->mc.crwe;
297                                 pcrwe_n = pmache->mc.crwe->crwe_next;
298
299                                 pmache->mcic.mcc.mcd.cp = 0;
300                                 pmache->mc.crwe         = NULL;
301
302                                 spin_lock( &crw_queue_lock);
303
304                                 while ( pcrwe_h )
305                                 {
306                                         pcrwe_h->crwe_next = crw_buffer_anchor;
307                                         crw_buffer_anchor  = pcrwe_h;
308                                         pcrwe_h            = pcrwe_n;
309
310                                         if ( pcrwe_h != NULL )
311                                                 pcrwe_n = pcrwe_h->crwe_next;
312
313                                 } /* endwhile */
314
315                                 spin_unlock( &crw_queue_lock);
316
317                         } /* endif */
318
319 #ifdef CONFIG_MACHCHK_WARNING
320                         if ( pmache->mcic.mcc.mcd.w )
321                         {
322                                 ctrl_alt_del();         // shutdown NOW!
323 #ifdef S390_MACHCHK_DEBUG
324                         printk( KERN_DEBUG "mach_handler : kill -SIGPWR init\n");
325 #endif
326                         } /* endif */
327 #endif
328
329 #ifdef CONFIG_MACHCHK_WARNING
330                         if ( pmache->mcic.mcc.mcd.w )
331                         {
332                                 ctrl_alt_del();         // shutdown NOW!
333 #ifdef S390_MACHCHK_DEBUG
334                         printk( KERN_DEBUG "mach_handler : kill -SIGPWR init\n");
335 #endif
336                         } /* endif */
337 #endif
338
339                         s390_enqueue_free_mchchk( pmache );
340                 }
341                 else
342                 {
343
344                         // unconditional surrender ...
345 #ifdef S390_MACHCHK_DEBUG
346                         printk( KERN_DEBUG "mach_handler : nothing to do, sleeping\n");
347 #endif  
348
349                 } /* endif */   
350
351                 } while ( pmache );
352
353                 __restore_flags( flags );
354
355         } while ( 1 );
356
357         return( 0);
358 }
359
360 /*
361  * s390_dequeue_mchchk
362  *
363  * Dequeue an entry from the machine check queue
364  *
365  * Note : The queue elements provide for a double linked list.
366  *  We dequeue entries from the tail, and enqueue entries to
367  *  the head.
368  *
369  */
370 static mache_t *s390_dequeue_mchchk( void )
371 {
372         mache_t *qe;
373
374         spin_lock( &mchchk_queue_lock );
375
376         qe = mchchk_queue_tail;
377
378    if ( qe != NULL )
379    {
380       mchchk_queue_tail = qe->prev;
381
382       if ( mchchk_queue_tail != NULL )
383       {
384                         mchchk_queue_tail->next = NULL;
385                 }
386                 else
387       {
388                         mchchk_queue_head = NULL;
389
390       } /* endif */
391
392         } /* endif */
393
394         spin_unlock( &mchchk_queue_lock );
395
396         return qe;
397 }
398
399 /*
400  * s390_enqueue_mchchk
401  *
402  * Enqueue an entry to the machine check queue.
403  *
404  * Note : The queue elements provide for a double linked list.
405  *  We enqueue entries to the head, and dequeue entries from
406  *  the tail.
407  *
408  */
409 static void s390_enqueue_mchchk( mache_t *pmache )
410 {
411         spin_lock( &mchchk_queue_lock );
412
413         if ( pmache != NULL )
414         {
415
416                 if ( mchchk_queue_head == NULL )  /* first element */
417                 {
418                         pmache->next      = NULL;
419                         pmache->prev      = NULL;
420
421                         mchchk_queue_head = pmache;
422                         mchchk_queue_tail = pmache;
423                 }
424                 else /* new head */
425                 {
426                         pmache->prev            = NULL;
427                         pmache->next            = mchchk_queue_head;
428
429                         mchchk_queue_head->prev = pmache;
430                         mchchk_queue_head       = pmache;
431
432                 } /* endif */
433
434         } /* endif */
435
436         spin_unlock( &mchchk_queue_lock );
437
438         return;
439 }
440
441
442 /*
443  * s390_enqueue_free_mchchk
444  *
445  * Enqueue a free entry to the free queue.
446  *
447  * Note : While the queue elements provide for a double linked list,
448  *  the free queue entries are only concatenated by means of a
449  *  single linked list (forward concatenation).
450  *
451  */
452 static void s390_enqueue_free_mchchk( mache_t *pmache )
453 {
454         if ( pmache != NULL)
455         {
456                 memset( pmache, '\0', sizeof( mache_t ));
457
458                 spin_lock( &mchchk_queue_lock );
459                 
460                 pmache->next = mchchk_queue_free;
461
462                 mchchk_queue_free = pmache;
463
464                 spin_unlock( &mchchk_queue_lock );
465
466         } /* endif */
467
468         return;
469 }
470
471 /*
472  * s390_dequeue_free_mchchk
473  *
474  * Dequeue an entry from the free queue.
475  *
476  * Note : While the queue elements provide for a double linked list,
477  *  the free queue entries are only concatenated by means of a
478  *  single linked list (forward concatenation).
479  *
480  */
481 static mache_t *s390_dequeue_free_mchchk( void )
482 {
483         mache_t *qe;
484
485         spin_lock( &mchchk_queue_lock );
486
487         qe = mchchk_queue_free;
488
489         if ( qe != NULL )
490         {
491                 mchchk_queue_free = qe->next;
492
493         } /* endif */
494
495         spin_unlock( &mchchk_queue_lock );
496
497         return qe;
498 }
499
500 /*
501  * s390_collect_crw_info
502  *
503  * Retrieve CRWs. If a CRW was found a machine check element
504  *  is dequeued from the free chain, filled and enqueued to
505  *  be processed.
506  *
507  * The function returns the number of CRWs found.
508  *
509  * Note : We must always be called disabled ...
510  */
511 static int s390_collect_crw_info( void )
512 {
513         crw_t    tcrw;     /* temporarily holds a CRW */
514         int      ccode;    /* condition code from stcrw() */
515         crwe_t  *pcrwe;    /* pointer to CRW buffer entry */
516
517         mache_t *pmache = NULL; /* ptr to mchchk entry */
518         int      chain  = 0;    /* indicate chaining */
519         crwe_t  *pccrw  = NULL; /* ptr to current CRW buffer entry */
520         int      count  = 0;    /* CRW count */
521
522 #ifdef S390_MACHCHK_DEBUG
523         printk( KERN_DEBUG "crw_info : looking for CRWs ...\n");
524 #endif
525
526         do
527         {
528                 ccode = stcrw( (__u32 *)&tcrw);
529
530                 if ( ccode == 0 )
531                 {
532                         count++;
533                         
534 #ifdef S390_MACHCHK_DEBUG
535                         printk( KERN_DEBUG "crw_info : CRW reports "
536                                 "slct=%d, oflw=%d, chn=%d, "
537                                 "rsc=%X, anc=%d, erc=%X, "
538                                 "rsid=%X\n",
539                                 tcrw.slct,
540                                 tcrw.oflw,
541                                 tcrw.chn,
542                                 tcrw.rsc,
543                                 tcrw.anc,
544                                 tcrw.erc,
545                                 tcrw.rsid );
546 #endif
547
548                         /*
549                          * Dequeue a CRW entry from the free chain
550                          *  and process it ...
551                          */
552                         spin_lock( &crw_queue_lock );
553
554                         pcrwe = crw_buffer_anchor;
555
556                         if ( pcrwe == NULL )
557                         {
558                                 spin_unlock( &crw_queue_lock );
559                                 printk( KERN_CRIT"crw_info : "
560                                         "no CRW buffer entries available\n");
561                                 break;
562
563                         } /* endif */
564                         
565                         crw_buffer_anchor = pcrwe->crwe_next;
566                         pcrwe->crwe_next  = NULL;
567
568                         spin_unlock( &crw_queue_lock );
569
570                         memcpy( &(pcrwe->crw), &tcrw, sizeof(crw_t));
571
572                         /*
573                          * If it is the first CRW, chain it to the mchchk
574                          *  buffer entry, otherwise to the last CRW entry.
575                          */
576                         if ( chain == 0 )
577                         {
578                                 pmache = s390_dequeue_free_mchchk();
579
580                                 if ( pmache != NULL )
581                                 {
582                                         memset( pmache, '\0', sizeof(mache_t));
583
584                                         pmache->mcic.mcc.mcd.cp = 1;
585                                         pmache->mc.crwe         = pcrwe;
586                                         pccrw                   = pcrwe;
587
588                                 }
589                                 else
590                                 {
591                                         panic( "crw_info : "
592                                                "unable to dequeue "
593                                                "free mchchk buffer");                           
594
595                                 } /* endif */
596                         }
597                         else
598                         {
599                                 pccrw->crwe_next = pcrwe;
600                                 pccrw            = pcrwe;
601
602                         } /* endif */   
603
604                         if ( pccrw->crw.chn )
605                         {
606 #ifdef S390_MACHCHK_DEBUG
607                                 printk( KERN_DEBUG "crw_info : "
608                                         "chained CRWs pending ...\n\n");
609 #endif
610                                 chain = 1;
611                         }
612                         else
613                         {
614                                 chain = 0;
615
616                                 /*
617                                  * We can enqueue the mchchk buffer if
618                                  *  there aren't more CRWs chained.
619                                  */
620                                 s390_enqueue_mchchk( pmache);
621
622                         } /* endif */
623
624                 } /* endif */
625
626         } while ( ccode == 0 );
627
628         return( count );
629 }
630
631 #ifdef CONFIG_MACHCHK_WARNING
632 /*
633  * s390_post_warning
634  *
635  * Post a warning type machine check
636  *
637  * The function returns 1 when succesfull (panics otherwise)
638  */
639 static int s390_post_warning( void )
640 {
641         mache_t  *pmache = NULL; /* ptr to mchchk entry */
642
643         pmache = s390_dequeue_free_mchchk();
644
645         if ( pmache != NULL )
646         {
647                 memset( pmache, '\0', sizeof(mache_t) );
648
649                 pmache->mcic.mcc.mcd.w = 1;
650
651                 s390_enqueue_mchchk( pmache );
652         }
653         else
654         {
655                 panic(  "post_warning : "
656                         "unable to dequeue "
657                         "free mchchk buffer" );
658         } /* endif */
659
660 #ifdef S390_MACHCHK_DEBUG
661         printk( KERN_DEBUG "post_warning : 1 warning machine check posted\n");
662 #endif
663
664         return ( 1 );
665 }
666 #endif