]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/sys/alpha/alpha/interrupt.c
Clone Kip's Xen on stable/6 tree so that I can work on improving FreeBSD/amd64
[FreeBSD/FreeBSD.git] / 6 / sys / alpha / alpha / interrupt.c
1 /* $NetBSD: interrupt.c,v 1.23 1998/02/24 07:38:01 thorpej Exp $ */
2 /*-
3  * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University.
4  * All rights reserved.
5  *
6  * Authors: Keith Bostic, Chris G. Demetriou
7  * 
8  * Permission to use, copy, modify and distribute this software and
9  * its documentation is hereby granted, provided that both the copyright
10  * notice and this permission notice appear in all copies of the
11  * software, derivative works or modified versions, and any portions
12  * thereof, and that both notices appear in supporting documentation.
13  * 
14  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 
15  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 
16  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17  * 
18  * Carnegie Mellon requests users of this software to return to
19  *
20  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
21  *  School of Computer Science
22  *  Carnegie Mellon University
23  *  Pittsburgh PA 15213-3890
24  *
25  * any improvements or extensions that they make and grant Carnegie the
26  * rights to redistribute these changes.
27  */
28 /*-
29  * Additional Copyright (c) 1997 by Matthew Jacob for NASA/Ames Research Center.
30  * Redistribute and modify at will, leaving only this additional copyright
31  * notice.
32  */
33
34 #include <sys/cdefs.h>                  /* RCS ID & Copyright macro defns */
35 /* __KERNEL_RCSID(0, "$NetBSD: interrupt.c,v 1.23 1998/02/24 07:38:01 thorpej Exp $");*/
36 __FBSDID("$FreeBSD$");
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/bus.h>
41 #include <sys/interrupt.h>
42 #include <sys/kdb.h>
43 #include <sys/kernel.h>
44 #include <sys/ktr.h>
45 #include <sys/lock.h>
46 #include <sys/malloc.h>
47 #include <sys/mutex.h>
48 #include <sys/proc.h>
49 #include <sys/sched.h>
50 #include <sys/smp.h>
51 #include <sys/unistd.h>
52 #include <sys/vmmeter.h>
53
54 #include <machine/bwx.h>
55 #include <machine/cpuconf.h>
56 #include <machine/frame.h>
57 #include <machine/intr.h>
58 #include <machine/md_var.h>
59 #include <machine/reg.h>
60 #include <machine/rpb.h>
61 #include <machine/smp.h>
62
63 #ifdef EVCNT_COUNTERS
64 struct evcnt clock_intr_evcnt;  /* event counter for clock intrs. */
65 #else
66 #include <machine/intrcnt.h>
67 #endif
68
69 volatile int mc_expected, mc_received;
70
71 static void 
72 dummy_perf(unsigned long vector, struct trapframe *framep)  
73 {
74         printf("performance interrupt!\n");
75 }
76
77 void (*perf_irq)(unsigned long, struct trapframe *) = dummy_perf;
78
79
80 static u_int schedclk2;
81 static void alpha_clock_interrupt(struct trapframe *framep);
82
83 void
84 interrupt(a0, a1, a2, framep)
85         unsigned long a0, a1, a2;
86         struct trapframe *framep;
87 {
88         struct thread *td;
89 #ifdef SMP
90         register_t s;
91 #endif
92
93         /*
94          * Find our per-cpu globals.
95          */
96 #ifdef SMP
97         s = intr_disable();
98 #endif
99         pcpup = (struct pcpu *) alpha_pal_rdval();
100         td = curthread;
101 #ifdef SMP
102         td->td_md.md_kernnest++;
103         intr_restore(s);
104 #endif
105         atomic_add_int(&td->td_intr_nesting_level, 1);
106
107 #if KSTACK_GUARD_PAGES == 0
108 #ifndef SMP
109         {
110                 if ((caddr_t) framep < (caddr_t) td->td_pcb + 1024) {
111                         panic("possible stack overflow\n");
112                 }
113         }
114 #endif
115 #endif
116
117         framep->tf_regs[FRAME_TRAPARG_A0] = a0;
118         framep->tf_regs[FRAME_TRAPARG_A1] = a1;
119         framep->tf_regs[FRAME_TRAPARG_A2] = a2;
120         switch (a0) {
121 #ifdef SMP
122         case ALPHA_INTR_XPROC:  /* interprocessor interrupt */
123                 CTR0(KTR_INTR|KTR_SMP, "interprocessor interrupt");
124                 smp_handle_ipi(framep); /* note: lock not taken */
125                 break;
126 #endif
127                 
128         case ALPHA_INTR_CLOCK:  /* clock interrupt */
129                 CTR0(KTR_INTR, "clock interrupt");
130                 alpha_clock_interrupt(framep);
131                 break;
132
133         case ALPHA_INTR_ERROR:  /* Machine Check or Correctable Error */
134                 a0 = alpha_pal_rdmces();
135                 if (platform.mcheck_handler)
136                         (*platform.mcheck_handler)(a0, framep, a1, a2);
137                 else
138                         machine_check(a0, framep, a1, a2);
139                 break;
140
141         case ALPHA_INTR_DEVICE: /* I/O device interrupt */
142                 PCPU_LAZY_INC(cnt.v_intr);
143                 if (platform.iointr)
144                         (*platform.iointr)(framep, a1);
145                 break;
146
147         case ALPHA_INTR_PERF:   /* interprocessor interrupt */
148                 perf_irq(a1, framep);
149                 break;
150
151         case ALPHA_INTR_PASSIVE:
152 #if     0
153                 printf("passive release interrupt vec 0x%lx (ignoring)\n", a1);
154 #endif
155                 break;
156
157         default:
158                 panic("unexpected interrupt: type 0x%lx vec 0x%lx a2 0x%lx\n",
159                     a0, a1, a2);
160                 /* NOTREACHED */
161         }
162         atomic_subtract_int(&td->td_intr_nesting_level, 1);
163 }
164
165 void
166 set_iointr(niointr)
167         void (*niointr)(void *, unsigned long);
168 {
169         if (platform.iointr)
170                 panic("set iointr twice");
171         platform.iointr = niointr;
172 }
173
174
175 void
176 machine_check(mces, framep, vector, param)
177         unsigned long mces;
178         struct trapframe *framep;
179         unsigned long vector, param;
180 {
181         const char *type;
182
183         /* Make sure it's an error we know about. */
184         if ((mces & (ALPHA_MCES_MIP|ALPHA_MCES_SCE|ALPHA_MCES_PCE)) == 0) {
185                 type = "fatal machine check or error (unknown type)";
186                 goto fatal;
187         }
188
189         /* Machine checks. */
190         if (mces & ALPHA_MCES_MIP) {
191                 /* If we weren't expecting it, then we punt. */
192                 if (!mc_expected) {
193                         type = "unexpected machine check";
194                         goto fatal;
195                 }
196
197                 mc_expected = 0;
198                 mc_received = 1;
199         }
200
201         /* System correctable errors. */
202         if (mces & ALPHA_MCES_SCE)
203                 printf("Warning: received system correctable error.\n");
204
205         /* Processor correctable errors. */
206         if (mces & ALPHA_MCES_PCE)
207                 printf("Warning: received processor correctable error.\n"); 
208
209         /* Clear pending machine checks and correctable errors */
210         alpha_pal_wrmces(mces);
211         return;
212
213 fatal:
214         /* Clear pending machine checks and correctable errors */
215         alpha_pal_wrmces(mces);
216
217         printf("\n");
218         printf("%s:\n", type);
219         printf("\n");
220         printf("    mces    = 0x%lx\n", mces);
221         printf("    vector  = 0x%lx\n", vector);
222         printf("    param   = 0x%lx\n", param);
223         printf("    pc      = 0x%lx\n", framep->tf_regs[FRAME_PC]);
224         printf("    ra      = 0x%lx\n", framep->tf_regs[FRAME_RA]);
225         printf("    curproc = %p\n", curproc);
226         if (curproc != NULL)
227                 printf("        pid = %d, comm = %s\n", curproc->p_pid,
228                     curproc->p_comm);
229         printf("\n");
230 #ifdef KDB
231         kdb_trap(ALPHA_KENTRY_MM, mces, framep);
232 #endif
233         panic("machine check");
234 }
235
236 int
237 badaddr(addr, size)
238         void *addr;
239         size_t size;
240 {
241         return(badaddr_read(addr, size, NULL));
242 }
243
244 int
245 badaddr_read(addr, size, rptr)
246         void *addr;
247         size_t size;
248         void *rptr;
249 {
250         long rcpt;
251
252         /* Get rid of any stale machine checks that have been waiting.  */
253         alpha_pal_draina();
254
255         /* Tell the trap code to expect a machine check. */
256         mc_received = 0;
257         mc_expected = 1;
258
259         /* Read from the test address, and make sure the read happens. */
260         alpha_mb();
261         switch (size) {
262         case sizeof (u_int8_t):
263                 if (alpha_implver() >= ALPHA_IMPLVER_EV5
264                     && alpha_amask(ALPHA_AMASK_BWX) == 0)
265                         rcpt = ldbu((vm_offset_t)addr);
266                 else
267                         rcpt = *(volatile u_int8_t *)addr;
268                 break;
269
270         case sizeof (u_int16_t):
271                 if (alpha_implver() >= ALPHA_IMPLVER_EV5
272                     && alpha_amask(ALPHA_AMASK_BWX) == 0)
273                         rcpt = ldwu((vm_offset_t)addr);
274                 else
275                         rcpt = *(volatile u_int16_t *)addr;
276                 break;
277
278         case sizeof (u_int32_t):
279                 rcpt = *(volatile u_int32_t *)addr;
280                 break;
281
282         case sizeof (u_int64_t):
283                 rcpt = *(volatile u_int64_t *)addr;
284                 break;
285
286         default:
287                 panic("badaddr: invalid size (%ld)\n", size);
288         }
289         alpha_mb();
290         alpha_mb(); /* magic for ev5 2100A  & maybe more */
291
292         /* Make sure we took the machine check, if we caused one. */
293         alpha_pal_draina();
294
295         /* disallow further machine checks */
296         mc_expected = 0;
297
298         if (rptr && mc_received == 0) {
299                 switch (size) {
300                 case sizeof (u_int8_t):
301                         *(volatile u_int8_t *)rptr = rcpt;
302                         break;
303
304                 case sizeof (u_int16_t):
305                         *(volatile u_int16_t *)rptr = rcpt;
306                         break;
307
308                 case sizeof (u_int32_t):
309                         *(volatile u_int32_t *)rptr = rcpt;
310                         break;
311
312                 case sizeof (u_int64_t):
313                         *(volatile u_int64_t *)rptr = rcpt;
314                         break;
315                 }
316         }
317         /* Return non-zero (i.e. true) if it's a bad address. */
318         return (mc_received);
319 }
320
321 #define HASHVEC(vector) ((vector) % 31)
322
323 LIST_HEAD(alpha_intr_list, alpha_intr);
324
325 struct alpha_intr {
326     LIST_ENTRY(alpha_intr) list; /* chain handlers in this hash bucket */
327     uintptr_t           vector; /* vector to match */
328     struct intr_event   *ie;    /* interrupt event structure */
329     volatile long       *cntp;  /* interrupt counter */
330     void                (*disable)(uintptr_t);
331 };
332
333 static struct mtx alpha_intr_hash_lock;
334 static struct alpha_intr_list alpha_intr_hash[31];
335
336 static void     ithds_init(void *dummy);
337
338 static void
339 ithds_init(void *dummy)
340 {
341
342         mtx_init(&alpha_intr_hash_lock, "intr table", NULL, MTX_SPIN);
343 }
344 SYSINIT(ithds_init, SI_SUB_INTR, SI_ORDER_SECOND, ithds_init, NULL);
345
346 int
347 alpha_setup_intr(const char *name, uintptr_t vector, driver_intr_t handler, void *arg,
348                  enum intr_type flags, void **cookiep, volatile long *cntp,
349                  void (*disable)(uintptr_t), void (*enable)(uintptr_t))
350 {
351         int h = HASHVEC(vector);
352         struct alpha_intr *i;
353         int errcode;
354
355         /*
356          * XXX - Can we have more than one device on a vector?  If so, we have
357          * a race condition here that needs to be worked around similar to
358          * the fashion done in the i386 inthand_add() function.
359          */
360         
361         /* First, check for an existing hash table entry for this vector. */
362         mtx_lock_spin(&alpha_intr_hash_lock);
363         for (i = LIST_FIRST(&alpha_intr_hash[h]); i && i->vector != vector;
364             i = LIST_NEXT(i, list))
365                 ;       /* nothing */
366         mtx_unlock_spin(&alpha_intr_hash_lock);
367
368         if (i == NULL) {
369                 /* None was found, so create an entry. */
370                 i = malloc(sizeof(struct alpha_intr), M_DEVBUF, M_NOWAIT);
371                 if (i == NULL)
372                         return ENOMEM;
373                 i->vector = vector;
374                 i->cntp = cntp;
375                 i->disable = disable;
376                 errcode = intr_event_create(&i->ie, (void *)vector, 0,
377                     (void (*)(void *))enable, "intr:");
378                 if (errcode) {
379                         free(i, M_DEVBUF);
380                         return errcode;
381                 }
382
383                 mtx_lock_spin(&alpha_intr_hash_lock);
384                 LIST_INSERT_HEAD(&alpha_intr_hash[h], i, list);
385                 mtx_unlock_spin(&alpha_intr_hash_lock);
386         }
387
388         /* Second, add this handler. */
389         return (intr_event_add_handler(i->ie, name, handler, arg,
390             intr_priority(flags), flags, cookiep));
391 }
392
393 int
394 alpha_teardown_intr(void *cookie)
395 {
396
397         return (intr_event_remove_handler(cookie));
398 }
399
400 /*
401  * XXX: Alpha doesn't count stray interrupts like some of the other archs.
402  */
403 void
404 alpha_dispatch_intr(void *frame, unsigned long vector)
405 {
406         int h = HASHVEC(vector);
407         struct alpha_intr *i;
408         struct intr_event *ie;
409         struct intr_handler *ih;
410         int error, thread;
411
412         /*
413          * Walk the hash bucket for this vector looking for this vector's
414          * interrupt structure.
415          */
416         for (i = LIST_FIRST(&alpha_intr_hash[h]); i && i->vector != vector;
417             i = LIST_NEXT(i, list))
418                 ;       /* nothing */
419
420         /* No interrupt structure for this vector. */
421         if (i == NULL)
422                 return;
423
424         ie = i->ie;
425         KASSERT(ie != NULL, ("interrupt structure without an event"));
426
427         /*
428          * As an optimization, if an event has no handlers, don't
429          * schedule it to run.
430          */
431         if (TAILQ_EMPTY(&ie->ie_handlers))
432                 return;
433
434         atomic_add_long(i->cntp, 1);
435
436         /*
437          * It seems that we need to return from an interrupt back to PAL
438          * on the same CPU that received the interrupt, so pin the interrupted
439          * thread to the current CPU until we return from the interrupt.
440          */
441         sched_pin();
442
443         /* Execute all fast interrupt handlers directly. */     
444         thread = 0;
445         critical_enter();
446         TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) {
447                 if (!(ih->ih_flags & IH_FAST)) {
448                         thread = 1;
449                         continue;
450                 }
451                 CTR4(KTR_INTR, "%s: exec %p(%p) for %s", __func__,
452                     ih->ih_handler, ih->ih_argument, ih->ih_name);
453                 ih->ih_handler(ih->ih_argument);
454         }
455
456         /*
457          * If the ithread needs to run, disable the source and schedule the
458          * thread.
459          */
460         if (thread) {
461                 if (i->disable) {
462                         CTR1(KTR_INTR,
463                             "alpha_dispatch_intr: disabling vector 0x%x",
464                             i->vector);
465                         i->disable(i->vector);
466                 }
467                 error = intr_event_schedule_thread(ie);
468                 KASSERT(error == 0, ("got an impossible stray interrupt"));
469         }
470         critical_exit();
471         sched_unpin();
472 }
473
474 static void
475 alpha_clock_interrupt(struct trapframe *framep)
476 {
477
478         PCPU_LAZY_INC(cnt.v_intr);
479 #ifdef EVCNT_COUNTERS
480         clock_intr_evcnt.ev_count++;
481 #else
482         intrcnt[INTRCNT_CLOCK]++;
483 #endif
484         if (platform.clockintr) {
485                 critical_enter();
486 #ifdef SMP
487                 /*
488                  * Only one processor drives the actual timer.
489                  */
490                 if (PCPU_GET(cpuid) == 0) {
491 #endif
492                         (*platform.clockintr)(framep);
493                         /* divide hz (1024) by 8 to get stathz (128) */
494                         if ((++schedclk2 & 0x7) == 0) {
495                                 if (profprocs != 0)
496                                         profclock((struct clockframe *)framep);
497                                 statclock((struct clockframe *)framep);
498                         }
499 #ifdef SMP
500                 } else {
501                         hardclock_process((struct clockframe *)framep);
502                         if ((schedclk2 & 0x7) == 0) {
503                                 if (profprocs != 0)
504                                         profclock((struct clockframe *)framep);
505                                 statclock((struct clockframe *)framep);
506                         }
507                 }
508 #endif
509                 critical_exit();
510         }
511 }