trap.c revision 1.18
1/*	$OpenBSD: trap.c,v 1.18 2000/01/25 22:39:57 mickey Exp $	*/
2
3/*
4 * Copyright (c) 1998-2000 Michael Shalayeff
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by Michael Shalayeff.
18 * 4. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#undef INTRDEBUG
34#undef TRAPDEBUG
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/kernel.h>
39#include <sys/syscall.h>
40#include <sys/ktrace.h>
41#include <sys/proc.h>
42#include <sys/user.h>
43#include <sys/acct.h>
44#include <sys/signal.h>
45#include <sys/device.h>
46
47#include <net/netisr.h>
48
49#include <vm/vm.h>
50#include <vm/vm_kern.h>
51#include <uvm/uvm.h>
52
53#include <machine/iomod.h>
54#include <machine/cpufunc.h>
55#include <machine/reg.h>
56#include <machine/autoconf.h>
57
58#ifdef DDB
59#include <machine/db_machdep.h>
60#endif
61
62#if defined(INTRDEBUG) || defined(TRAPDEBUG)
63#include <ddb/db_output.h>
64#endif
65
66
67const char *trap_type[] = {
68	"invalid",
69	"HPMC",
70	"power failure",
71	"recovery counter",
72	"external interrupt",
73	"LPMC",
74	"ITLB miss fault",
75	"instruction protection",
76	"Illegal instruction",
77	"break instruction",
78	"privileged operation",
79	"privileged register",
80	"overflow",
81	"conditional",
82	"assist exception",
83	"DTLB miss",
84	"ITLB non-access miss",
85	"DTLB non-access miss",
86	"data protection/rights/alignment",
87	"data break",
88	"TLB dirty",
89	"page reference",
90	"assist emulation",
91	"higher-priv transfer",
92	"lower-priv transfer",
93	"taken branch",
94	"data access rights",
95	"data protection",
96	"unaligned data ref",
97};
98int trap_types = sizeof(trap_type)/sizeof(trap_type[0]);
99
100u_int32_t sir;
101int want_resched;
102
103void pmap_hptdump __P((void));
104void cpu_intr __P((struct trapframe *frame));
105void syscall __P((struct trapframe *frame, int *args));
106
107static __inline void
108userret (struct proc *p, register_t pc, u_quad_t oticks)
109{
110	int sig;
111
112	/* take pending signals */
113	while ((sig = CURSIG(p)) != 0)
114		postsig(sig);
115
116	p->p_priority = p->p_usrpri;
117	if (want_resched) {
118		register int s;
119		/*
120		 * Since we are curproc, a clock interrupt could
121		 * change our priority without changing run queues
122		 * (the running process is not kept on a run queue).
123		 * If this happened after we setrunqueue ourselves but
124		 * before we switch()'ed, we might not be on the queue
125		 * indicated by our priority.
126		 */
127		s = splstatclock();
128		setrunqueue(p);
129		p->p_stats->p_ru.ru_nivcsw++;
130		mi_switch();
131		splx(s);
132		while ((sig = CURSIG(p)) != 0)
133			postsig(sig);
134	}
135
136	/*
137	 * If profiling, charge recent system time to the trapped pc.
138	 */
139	if (p->p_flag & P_PROFIL) {
140		extern int psratio;
141
142		addupc_task(p, pc, (int)(p->p_sticks - oticks) * psratio);
143	}
144
145	curpriority = p->p_priority;
146}
147
148void
149trap(type, frame)
150	int type;
151	struct trapframe *frame;
152{
153	struct proc *p = curproc;
154	struct pcb *pcbp;
155	register vaddr_t va;
156	register vm_map_t map;
157	struct vmspace *vm;
158	register vm_prot_t vftype;
159	register pa_space_t space;
160	u_int opcode;
161	int ret;
162	union sigval sv;
163	int s, si;
164	const char *tts;
165extern db_regs_t ddb_regs;
166ddb_regs = *frame;
167	opcode = frame->tf_iir;
168	if (type == T_ITLBMISS || type == T_ITLBMISSNA) {
169		va = frame->tf_iioq_head;
170		space = frame->tf_iisq_head;
171		vftype = VM_PROT_READ;	/* XXX VM_PROT_EXECUTE ??? */
172	} else {
173		va = frame->tf_ior;
174		space = frame->tf_isr;
175		vftype = inst_store(opcode) ? VM_PROT_WRITE : VM_PROT_READ;
176	}
177
178	if (frame->tf_flags & TFF_LAST)
179		p->p_md.md_regs = frame;
180
181#ifdef TRAPDEBUG
182	if ((type & ~T_USER) > trap_types)
183		tts = "reserved";
184	else
185		tts = trap_type[type & ~T_USER];
186
187	if (type != T_INTERRUPT && (type & ~T_USER) != T_IBREAK)
188		db_printf("trap: %d, %s for %x:%x at %x:%x, fl=%x, fp=%p\n",
189		    type, tts, space, va, frame->tf_iisq_head,
190		    frame->tf_iioq_head, frame->tf_flags, frame);
191	else if ((type & ~T_USER) == T_IBREAK)
192		db_printf("trap: break instruction %x:%x at %x:%x, fp=%p\n",
193		    break5(opcode), break13(opcode),
194		    frame->tf_iisq_head, frame->tf_iioq_head, frame);
195
196	{
197		extern int etext;
198		if (frame < (struct trapframe *)&etext) {
199			printf("trap: bogus frame ptr %p\n", frame);
200			goto dead_end;
201		}
202	}
203#endif
204	switch (type) {
205	case T_NONEXIST:
206	case T_NONEXIST|T_USER:
207#ifndef DDB
208		/* we've got screwed up by the central scrutinizer */
209		panic ("trap: elvis has just left the building!");
210		break;
211#else
212		goto dead_end;
213#endif
214	case T_RECOVERY:
215	case T_RECOVERY|T_USER:
216#ifndef DDB
217		/* XXX will implement later */
218		printf ("trap: handicapped");
219		break;
220#else
221		goto dead_end;
222#endif
223
224#ifdef DIAGNOSTIC
225	case T_EXCEPTION:
226		panic("FPU/SFU emulation botch");
227
228		/* these just can't happen ever */
229	case T_PRIV_OP:
230	case T_PRIV_REG:
231		/* these just can't make it to the trap() ever */
232	case T_HPMC:      case T_HPMC | T_USER:
233	case T_EMULATION: case T_EMULATION | T_USER:
234	case T_TLB_DIRTY: case T_TLB_DIRTY | T_USER:
235#endif
236	case T_IBREAK:
237	case T_DATALIGN:
238	case T_DBREAK:
239	dead_end:
240#ifdef DDB
241		if (kdb_trap (type, va, frame)) {
242			if (type == T_IBREAK) {
243				/* skip break instruction */
244				frame->tf_iioq_head = frame->tf_iioq_tail;
245				frame->tf_iioq_tail += 4;
246			}
247			return;
248		}
249#else
250		if (type == T_DATALIGN)
251			panic ("trap: %s at 0x%x", tts, va);
252		else
253			panic ("trap: no debugger for \"%s\" (%d)", tts, type);
254#endif
255		break;
256
257	case T_IBREAK | T_USER:
258	case T_DBREAK | T_USER:
259		/* pass to user debugger */
260		break;
261
262	case T_EXCEPTION | T_USER:	/* co-proc assist trap */
263		sv.sival_int = va;
264		trapsignal(p, SIGFPE, type &~ T_USER, FPE_FLTINV, sv);
265		break;
266
267	case T_OVERFLOW | T_USER:
268		sv.sival_int = va;
269		trapsignal(p, SIGFPE, type &~ T_USER, FPE_INTOVF, sv);
270		break;
271
272	case T_CONDITION | T_USER:
273		break;
274
275	case T_ILLEGAL | T_USER:
276		sv.sival_int = va;
277		trapsignal(p, SIGILL, type &~ T_USER, ILL_ILLOPC, sv);
278		break;
279
280	case T_PRIV_OP | T_USER:
281		sv.sival_int = va;
282		trapsignal(p, SIGILL, type &~ T_USER, ILL_PRVOPC, sv);
283		break;
284
285	case T_PRIV_REG | T_USER:
286		sv.sival_int = va;
287		trapsignal(p, SIGILL, type &~ T_USER, ILL_PRVREG, sv);
288		break;
289
290		/* these should never got here */
291	case T_HIGHERPL | T_USER:
292	case T_LOWERPL | T_USER:
293		sv.sival_int = va;
294		trapsignal(p, SIGSEGV, type &~ T_USER, SEGV_ACCERR, sv);
295		break;
296
297	case T_IPROT | T_USER:
298	case T_DPROT | T_USER:
299		sv.sival_int = va;
300		trapsignal(p, SIGSEGV, vftype, SEGV_ACCERR, sv);
301		break;
302
303	case T_DPROT:
304	case T_IPROT:
305	case T_DATACC:   	case T_DATACC   | T_USER:
306	case T_ITLBMISS:	case T_ITLBMISS | T_USER:
307	case T_DTLBMISS:	case T_DTLBMISS | T_USER:
308	case T_ITLBMISSNA:	case T_ITLBMISSNA | T_USER:
309	case T_DTLBMISSNA:	case T_DTLBMISSNA | T_USER:
310		va = trunc_page(va);
311		vm = p->p_vmspace;
312
313		if (!vm) {
314#ifdef TRAPDEBUG
315			printf("trap: no vm, p=%p\n", p);
316#endif
317			goto dead_end;
318		}
319
320		/*
321		 * it could be a kernel map for exec_map faults
322		 */
323		if (!(type & T_USER) && space == HPPA_SID_KERNEL)
324			map = kernel_map;
325		else
326			map = &vm->vm_map;
327
328		if (map->pmap->pmap_space != space) {
329#ifdef TRAPDEBUG
330			printf("trap: space missmatch %d != %d\n",
331			    space, map->pmap->pmap_space);
332#endif
333			/* actually dump the user, crap the kernel */
334			goto dead_end;
335		}
336
337		ret = uvm_fault(map, va, 0, vftype);
338
339#ifdef TRAPDEBUG
340		printf("uvm_fault(%p, %x, %d, %d)=%d\n",
341		    map, va, 0, vftype, ret);
342#endif
343
344		/*
345		 * If this was a stack access we keep track of the maximum
346		 * accessed stack size.  Also, if uvm_fault gets a protection
347		 * failure it is due to accessing the stack region outside
348		 * the current limit and we need to reflect that as an access
349		 * error.
350		 */
351		if (va >= (vaddr_t)vm->vm_maxsaddr + vm->vm_ssize) {
352			if (ret == KERN_SUCCESS) {
353				vsize_t nss = clrnd(btoc(va - USRSTACK + NBPG));
354				if (nss > vm->vm_ssize)
355					vm->vm_ssize = nss;
356			} else if (ret == KERN_PROTECTION_FAILURE)
357				ret = KERN_INVALID_ADDRESS;
358		}
359
360		if (ret != KERN_SUCCESS) {
361			if (type & T_USER) {
362printf("trapsignal: uvm_fault\n");
363				sv.sival_int = frame->tf_ior;
364				trapsignal(p, SIGSEGV, vftype, SEGV_MAPERR, sv);
365			} else {
366				if (p && p->p_addr->u_pcb.pcb_onfault) {
367#ifdef PMAPDEBUG
368					printf("trap: copyin/out %d\n",ret);
369#endif
370					pcbp = &p->p_addr->u_pcb;
371					frame->tf_iioq_tail = 4 +
372					    (frame->tf_iioq_head =
373						pcbp->pcb_onfault);
374					pcbp->pcb_onfault = 0;
375					break;
376				}
377#if 1
378if (kdb_trap (type, va, frame))
379	return;
380#else
381				panic("trap: uvm_fault(%p, %x, %d, %d): %d",
382				    map, va, 0, vftype, ret);
383#endif
384			}
385		}
386		break;
387
388	case T_DATALIGN | T_USER:
389		sv.sival_int = va;
390		trapsignal(p, SIGBUS, vftype, BUS_ADRALN, sv);
391		break;
392
393	case T_INTERRUPT:
394	case T_INTERRUPT|T_USER:
395		cpu_intr(frame);
396#if 0
397if (kdb_trap (type, va, frame))
398return;
399#endif
400		/* FALLTHROUGH */
401	case T_LOWERPL:
402		__asm __volatile ("ldcws 0(%1), %0"
403				  : "=r" (si) : "r" (&sir));
404		s = spl0();
405		if (si & SIR_CLOCK) {
406			splclock();
407			softclock();
408			spl0();
409		}
410
411		if (si & SIR_NET) {
412			register int ni;
413			/* use atomic "load & clear" */
414			__asm __volatile ("ldcws 0(%1), %0"
415					  : "=r" (ni) : "r" (&netisr));
416			splnet();
417#define	DONET(m,c) if (ni & (1 << (m))) c()
418#include "ether.h"
419#if NETHER > 0
420			DONET(NETISR_ARP, arpintr);
421#endif
422#ifdef INET
423			DONET(NETISR_IP, ipintr);
424#endif
425#ifdef INET6
426			DONET(NETISR_IPV6, ip6intr);
427#endif
428#ifdef NETATALK
429			DONET(NETISR_ATALK, atintr);
430#endif
431#ifdef IMP
432			DONET(NETISR_IMP, impintr);
433#endif
434#ifdef IPX
435			DONET(NETISR_IPX, ipxintr);
436#endif
437#ifdef NS
438			DONET(NETISR_NS, nsintr);
439#endif
440#ifdef ISO
441			DONET(NETISR_ISO, clnlintr);
442#endif
443#ifdef CCITT
444			DONET(NETISR_CCITT, ccittintr);
445#endif
446#ifdef NATM
447			DONET(NETISR_NATM, natmintr);
448#endif
449#include "ppp.h"
450#if NPPP > 0
451			DONET(NETISR_PPP, pppintr);
452#endif
453#include "bridge.h"
454#if NBRIDGE > 0
455			DONET(NETISR_BRIDGE, bridgeintr)
456#endif
457		}
458		splx(s);
459		break;
460
461	case T_OVERFLOW:
462	case T_CONDITION:
463	case T_ILLEGAL:
464	case T_HIGHERPL:
465	case T_TAKENBR:
466	case T_POWERFAIL:
467	case T_LPMC:
468	case T_PAGEREF:
469	case T_DATAPID:  	case T_DATAPID  | T_USER:
470		if (0 /* T-chip */) {
471			break;
472		}
473		/* FALLTHROUGH to unimplemented */
474	default:
475#if 1
476if (kdb_trap (type, va, frame))
477	return;
478#endif
479		panic ("trap: unimplemented \'%s\' (%d)", tts, type);
480	}
481
482	if (type & T_USER)
483		userret(p, p->p_md.md_regs->tf_iioq_head, 0);
484}
485
486void
487child_return(p)
488	struct proc *p;
489{
490	userret(p, p->p_md.md_regs->tf_iioq_head, 0);
491#ifdef KTRACE
492	if (KTRPOINT(p, KTR_SYSRET))
493		ktrsysret(p->p_tracep, SYS_fork, 0, 0);
494#endif
495}
496
497/*
498 * call actual syscall routine
499 * from the low-level syscall handler:
500 * - all HPPA_FRAME_NARGS syscall's arguments supposed to be copied onto
501 *   our stack, this wins compared to copyin just needed amount anyway
502 * - register args are copied onto stack too
503 */
504void
505syscall(frame, args)
506	struct trapframe *frame;
507	int *args;
508{
509	register struct proc *p;
510	register const struct sysent *callp;
511	int nsys, code, argsize, error;
512	int rval[2];
513
514	uvmexp.syscalls++;
515
516	if (!USERMODE(frame->tf_iioq_head))
517		panic("syscall");
518
519	p = curproc;
520	p->p_md.md_regs = frame;
521	nsys = p->p_emul->e_nsysent;
522	callp = p->p_emul->e_sysent;
523	code = frame->tf_t1;
524	switch (code) {
525	case SYS_syscall:
526		code = *args;
527		args += 1;
528		break;
529	case SYS___syscall:
530		if (callp != sysent)
531			break;
532		code = *args;
533		args += 2;
534	}
535
536	if (code < 0 || code >= nsys)
537		callp += p->p_emul->e_nosys;	/* bad syscall # */
538	else
539		callp += code;
540	argsize = callp->sy_argsize;
541
542#ifdef SYSCALL_DEBUG
543	scdebug_call(p, code, args);
544#endif
545#ifdef KTRACE
546	if (KTRPOINT(p, KTR_SYSCALL))
547		ktrsyscall(p->p_tracep, code, argsize, args);
548#endif
549
550	rval[0] = 0;
551	rval[1] = 0;
552	switch (error = (*callp->sy_call)(p, args, rval)) {
553	case 0:
554		p = curproc;			/* changes on exec() */
555		frame = p->p_md.md_regs;
556		frame->tf_ret0 = rval[0];
557		frame->tf_ret1 = rval[1];
558		frame->tf_t1 = 0;
559		break;
560	case ERESTART:
561		frame->tf_iioq_head -= 4; /* right? XXX */
562		frame->tf_iioq_tail -= 4; /* right? XXX */
563		break;
564	case EJUSTRETURN:
565		p = curproc;
566		break;
567	default:
568		if (p->p_emul->e_errno)
569			error = p->p_emul->e_errno[error];
570		frame->tf_t1 = error;
571		break;
572	}
573#ifdef SYSCALL_DEBUG
574	scdebug_ret(p, code, error, rval);
575#endif
576	userret(p, frame->tf_iioq_head, 0);
577#ifdef KTRACE
578	if (KTRPOINT(p, KTR_SYSRET))
579		ktrsysret(p->p_tracep, code, error, rval[0]);
580#endif
581}
582
583/* all the interrupts, minus cpu clock, which is the last */
584struct cpu_intr_vector {
585	struct evcnt evcnt;
586	int pri;
587	int (*handler) __P((void *));
588	void *arg;
589} cpu_intr_vectors[CPU_NINTS];
590
591void *
592cpu_intr_establish(pri, irq, handler, arg, dv)
593	int pri, irq;
594	int (*handler) __P((void *));
595	void *arg;
596	struct device *dv;
597{
598	register struct cpu_intr_vector *iv;
599
600	if (0 <= irq && irq < CPU_NINTS && cpu_intr_vectors[irq].handler)
601		return NULL;
602
603	iv = &cpu_intr_vectors[irq];
604	iv->pri = pri;
605	iv->handler = handler;
606	iv->arg = arg;
607	evcnt_attach(dv, dv->dv_xname, &iv->evcnt);
608
609	return iv;
610}
611
612void
613cpu_intr(frame)
614	struct trapframe *frame;
615{
616	u_int32_t eirr;
617	register struct cpu_intr_vector *iv;
618	register int bit;
619
620	do {
621		mfctl(CR_EIRR, eirr);
622		eirr &= frame->tf_eiem;
623		bit = ffs(eirr) - 1;
624		if (bit >= 0) {
625			mtctl(1 << bit, CR_EIRR);
626			eirr &= ~(1 << bit);
627			/* ((struct iomod *)cpu_gethpa(0))->io_eir = 0; */
628#ifdef INTRDEBUG
629			if (bit != 31)
630				db_printf ("cpu_intr: 0x%08x\n", (1 << bit));
631#endif
632			iv = &cpu_intr_vectors[bit];
633			if (iv->handler) {
634				register int s, r;
635
636				iv->evcnt.ev_count++;
637				s = splx(iv->pri);
638				/* no arg means pass the frame */
639				r = (iv->handler)(iv->arg? iv->arg:frame);
640				splx(s);
641#ifdef INTRDEBUG
642				if (!r)
643					db_printf ("%s: can't handle interrupt\n",
644						   iv->evcnt.ev_name);
645#endif
646			}
647#ifdef INTRDEBUG
648			else
649				db_printf ("cpu_intr: stray interrupt %d\n", bit);
650#endif
651		}
652	} while (eirr);
653}
654