trap.c revision 1.27
1/*	$OpenBSD: trap.c,v 1.27 2001/04/01 06:13:40 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/* #define INTRDEBUG */
34/* #define 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/signalvar.h>
43#include <sys/user.h>
44#include <sys/acct.h>
45#include <sys/signal.h>
46#include <sys/device.h>
47
48#include <net/netisr.h>
49
50#include <vm/vm.h>
51#include <vm/vm_kern.h>
52#include <uvm/uvm.h>
53
54#include <machine/iomod.h>
55#include <machine/cpufunc.h>
56#include <machine/reg.h>
57#include <machine/autoconf.h>
58
59#ifdef DDB
60#include <machine/db_machdep.h>
61#endif
62
63#if defined(INTRDEBUG) || defined(TRAPDEBUG)
64#include <ddb/db_output.h>
65#endif
66
67
68const char *trap_type[] = {
69	"invalid",
70	"HPMC",
71	"power failure",
72	"recovery counter",
73	"external interrupt",
74	"LPMC",
75	"ITLB miss fault",
76	"instruction protection",
77	"Illegal instruction",
78	"break instruction",
79	"privileged operation",
80	"privileged register",
81	"overflow",
82	"conditional",
83	"assist exception",
84	"DTLB miss",
85	"ITLB non-access miss",
86	"DTLB non-access miss",
87	"data protection/rights/alignment",
88	"data break",
89	"TLB dirty",
90	"page reference",
91	"assist emulation",
92	"higher-priv transfer",
93	"lower-priv transfer",
94	"taken branch",
95	"data access rights",
96	"data protection",
97	"unaligned data ref",
98};
99int trap_types = sizeof(trap_type)/sizeof(trap_type[0]);
100
101u_int32_t sir;
102int want_resched, astpending;
103
104void pmap_hptdump __P((void));
105void cpu_intr __P((struct trapframe *frame));
106void syscall __P((struct trapframe *frame, int *args));
107
108static __inline void
109userret (struct proc *p, register_t pc, u_quad_t oticks)
110{
111	int sig;
112
113	/* take pending signals */
114	while ((sig = CURSIG(p)) != 0)
115		postsig(sig);
116
117	p->p_priority = p->p_usrpri;
118	if (want_resched) {
119		register int s;
120		/*
121		 * Since we are curproc, a clock interrupt could
122		 * change our priority without changing run queues
123		 * (the running process is not kept on a run queue).
124		 * If this happened after we setrunqueue ourselves but
125		 * before we switch()'ed, we might not be on the queue
126		 * indicated by our priority.
127		 */
128		s = splstatclock();
129		setrunqueue(p);
130		p->p_stats->p_ru.ru_nivcsw++;
131		mi_switch();
132		splx(s);
133		while ((sig = CURSIG(p)) != 0)
134			postsig(sig);
135	}
136
137	/*
138	 * If profiling, charge recent system time to the trapped pc.
139	 */
140	if (p->p_flag & P_PROFIL) {
141		extern int psratio;
142
143		addupc_task(p, pc, (int)(p->p_sticks - oticks) * psratio);
144	}
145
146	curpriority = p->p_priority;
147}
148
149void
150trap(type, frame)
151	int type;
152	struct trapframe *frame;
153{
154	struct proc *p = curproc;
155	struct pcb *pcbp;
156	register vaddr_t va;
157	register vm_map_t map;
158	struct vmspace *vm;
159	register vm_prot_t vftype;
160	register pa_space_t space;
161	u_int opcode;
162	int ret;
163	union sigval sv;
164	int s, si;
165	const char *tts;
166
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#endif
235	case T_IBREAK:
236	case T_DATALIGN:
237	case T_DBREAK:
238	dead_end:
239#ifdef DDB
240		if (kdb_trap (type, va, frame)) {
241			if (type == T_IBREAK) {
242				/* skip break instruction */
243				frame->tf_iioq_head = frame->tf_iioq_tail;
244				frame->tf_iioq_tail += 4;
245			}
246			return;
247		}
248#else
249		if (type == T_DATALIGN)
250			panic ("trap: %s at 0x%x", tts, va);
251		else
252			panic ("trap: no debugger for \"%s\" (%d)", tts, type);
253#endif
254		break;
255
256	case T_IBREAK | T_USER:
257	case T_DBREAK | T_USER:
258		/* pass to user debugger */
259		break;
260
261	case T_EXCEPTION | T_USER:	/* co-proc assist trap */
262		sv.sival_int = va;
263		trapsignal(p, SIGFPE, type &~ T_USER, FPE_FLTINV, sv);
264		break;
265
266	case T_OVERFLOW | T_USER:
267		sv.sival_int = va;
268		trapsignal(p, SIGFPE, type &~ T_USER, FPE_INTOVF, sv);
269		break;
270
271	case T_CONDITION | T_USER:
272		break;
273
274	case T_ILLEGAL | T_USER:
275		sv.sival_int = va;
276		trapsignal(p, SIGILL, type &~ T_USER, ILL_ILLOPC, sv);
277		break;
278
279	case T_PRIV_OP | T_USER:
280		sv.sival_int = va;
281		trapsignal(p, SIGILL, type &~ T_USER, ILL_PRVOPC, sv);
282		break;
283
284	case T_PRIV_REG | T_USER:
285		sv.sival_int = va;
286		trapsignal(p, SIGILL, type &~ T_USER, ILL_PRVREG, sv);
287		break;
288
289		/* these should never got here */
290	case T_HIGHERPL | T_USER:
291	case T_LOWERPL | T_USER:
292		sv.sival_int = va;
293		trapsignal(p, SIGSEGV, type &~ T_USER, SEGV_ACCERR, sv);
294		break;
295
296	case T_IPROT | T_USER:
297	case T_DPROT | T_USER:
298		sv.sival_int = va;
299		trapsignal(p, SIGSEGV, vftype, SEGV_ACCERR, sv);
300		break;
301
302	case T_DATACC:   	case T_USER | T_DATACC:
303	case T_ITLBMISS:	case T_USER | T_ITLBMISS:
304	case T_DTLBMISS:	case T_USER | T_DTLBMISS:
305	case T_ITLBMISSNA:	case T_USER | T_ITLBMISSNA:
306	case T_DTLBMISSNA:	case T_USER | T_DTLBMISSNA:
307	case T_TLB_DIRTY:	case T_USER | T_TLB_DIRTY:
308		va = hppa_trunc_page(va);
309		vm = p->p_vmspace;
310
311		if (!vm) {
312#ifdef TRAPDEBUG
313			printf("trap: no vm, p=%p\n", p);
314#endif
315			goto dead_end;
316		}
317
318		/*
319		 * it could be a kernel map for exec_map faults
320		 */
321		if (!(type & T_USER) && space == HPPA_SID_KERNEL)
322			map = kernel_map;
323		else
324			map = &vm->vm_map;
325
326		if (map->pmap->pmap_space != space) {
327#ifdef TRAPDEBUG
328			printf("trap: space missmatch %d != %d\n",
329			    space, map->pmap->pmap_space);
330#endif
331			/* actually dump the user, crap the kernel */
332			goto dead_end;
333		}
334
335		ret = uvm_fault(map, va, 0, vftype);
336
337#ifdef TRAPDEBUG
338		printf("uvm_fault(%p, %x, %d, %d)=%d\n",
339		    map, va, 0, vftype, ret);
340#endif
341
342		/*
343		 * If this was a stack access we keep track of the maximum
344		 * accessed stack size.  Also, if uvm_fault gets a protection
345		 * failure it is due to accessing the stack region outside
346		 * the current limit and we need to reflect that as an access
347		 * error.
348		 */
349		if (va >= (vaddr_t)vm->vm_maxsaddr + vm->vm_ssize) {
350			if (ret == KERN_SUCCESS) {
351				vsize_t nss = clrnd(btoc(va - USRSTACK + NBPG));
352				if (nss > vm->vm_ssize)
353					vm->vm_ssize = nss;
354			} else if (ret == KERN_PROTECTION_FAILURE)
355				ret = KERN_INVALID_ADDRESS;
356		}
357
358		if (ret != KERN_SUCCESS) {
359			if (type & T_USER) {
360printf("trapsignal: uvm_fault\n");
361				sv.sival_int = frame->tf_ior;
362				trapsignal(p, SIGSEGV, vftype, SEGV_MAPERR, sv);
363			} else {
364				if (p && p->p_addr->u_pcb.pcb_onfault) {
365#ifdef PMAPDEBUG
366					printf("trap: copyin/out %d\n",ret);
367#endif
368					pcbp = &p->p_addr->u_pcb;
369					frame->tf_iioq_tail = 4 +
370					    (frame->tf_iioq_head =
371						pcbp->pcb_onfault);
372					pcbp->pcb_onfault = 0;
373					break;
374				}
375#if 1
376if (kdb_trap (type, va, frame))
377	return;
378#else
379				panic("trap: uvm_fault(%p, %x, %d, %d): %d",
380				    map, va, 0, vftype, ret);
381#endif
382			}
383		}
384		break;
385
386	case T_DATALIGN | T_USER:
387		sv.sival_int = va;
388		trapsignal(p, SIGBUS, vftype, BUS_ADRALN, sv);
389		break;
390
391	case T_INTERRUPT:
392	case T_INTERRUPT|T_USER:
393		frame->tf_flags |= TFF_INTR;
394		cpu_intr(frame);
395#if 0
396if (kdb_trap (type, va, frame))
397return;
398#endif
399		/* FALLTHROUGH */
400	case T_LOWERPL:
401		__asm __volatile ("ldcws 0(%1), %0"
402				  : "=r" (si) : "r" (&sir));
403		s = spl0();
404		if (si & SIR_CLOCK) {
405			splclock();
406			softclock();
407			spl0();
408		}
409
410		if (si & SIR_NET) {
411			register int ni;
412			/* use atomic "load & clear" */
413			__asm __volatile ("ldcws 0(%1), %0"
414					  : "=r" (ni) : "r" (&netisr));
415			splnet();
416#define	DONETISR(m,c) if (ni & (1 << (m))) c()
417#include <net/netisr_dispatch.h>
418		}
419		splx(s);
420		break;
421
422	case T_DPROT:
423	case T_IPROT:
424	case T_OVERFLOW:
425	case T_CONDITION:
426	case T_ILLEGAL:
427	case T_HIGHERPL:
428	case T_TAKENBR:
429	case T_POWERFAIL:
430	case T_LPMC:
431	case T_PAGEREF:
432	case T_DATAPID:  	case T_DATAPID  | T_USER:
433		if (0 /* T-chip */) {
434			break;
435		}
436		/* FALLTHROUGH to unimplemented */
437	default:
438#if 1
439if (kdb_trap (type, va, frame))
440	return;
441#endif
442		panic ("trap: unimplemented \'%s\' (%d)", tts, type);
443	}
444
445	if (type & T_USER)
446		userret(p, p->p_md.md_regs->tf_iioq_head, 0);
447}
448
449void
450child_return(p)
451	struct proc *p;
452{
453	userret(p, p->p_md.md_regs->tf_iioq_head, 0);
454#ifdef KTRACE
455	if (KTRPOINT(p, KTR_SYSRET))
456		ktrsysret(p, SYS_fork, 0, 0);
457#endif
458}
459
460/*
461 * call actual syscall routine
462 * from the low-level syscall handler:
463 * - all HPPA_FRAME_NARGS syscall's arguments supposed to be copied onto
464 *   our stack, this wins compared to copyin just needed amount anyway
465 * - register args are copied onto stack too
466 */
467void
468syscall(frame, args)
469	struct trapframe *frame;
470	int *args;
471{
472	register struct proc *p;
473	register const struct sysent *callp;
474	int nsys, code, argsize, error;
475	int rval[2];
476
477	uvmexp.syscalls++;
478
479	if (!USERMODE(frame->tf_iioq_head))
480		panic("syscall");
481
482	p = curproc;
483	p->p_md.md_regs = frame;
484	nsys = p->p_emul->e_nsysent;
485	callp = p->p_emul->e_sysent;
486	code = frame->tf_t1;
487	switch (code) {
488	case SYS_syscall:
489		code = *args;
490		args += 1;
491		break;
492	case SYS___syscall:
493		if (callp != sysent)
494			break;
495		code = *args;
496		args += 2;
497	}
498
499	if (code < 0 || code >= nsys)
500		callp += p->p_emul->e_nosys;	/* bad syscall # */
501	else
502		callp += code;
503	argsize = callp->sy_argsize;
504
505#ifdef SYSCALL_DEBUG
506	scdebug_call(p, code, args);
507#endif
508#ifdef KTRACE
509	if (KTRPOINT(p, KTR_SYSCALL))
510		ktrsyscall(p, code, argsize, args);
511#endif
512
513	rval[0] = 0;
514	rval[1] = 0;
515	switch (error = (*callp->sy_call)(p, args, rval)) {
516	case 0:
517		p = curproc;			/* changes on exec() */
518		frame = p->p_md.md_regs;
519		frame->tf_ret0 = rval[0];
520		frame->tf_ret1 = rval[1];
521		frame->tf_t1 = 0;
522		break;
523	case ERESTART:
524		frame->tf_iioq_head -= 4; /* right? XXX */
525		frame->tf_iioq_tail -= 4; /* right? XXX */
526		break;
527	case EJUSTRETURN:
528		p = curproc;
529		break;
530	default:
531		if (p->p_emul->e_errno)
532			error = p->p_emul->e_errno[error];
533		frame->tf_t1 = error;
534		break;
535	}
536#ifdef SYSCALL_DEBUG
537	scdebug_ret(p, code, error, rval);
538#endif
539	userret(p, frame->tf_iioq_head, 0);
540#ifdef KTRACE
541	if (KTRPOINT(p, KTR_SYSRET))
542		ktrsysret(p, code, error, rval[0]);
543#endif
544}
545
546/* all the interrupts, minus cpu clock, which is the last */
547struct cpu_intr_vector {
548	struct evcnt evcnt;
549	int pri;
550	int (*handler) __P((void *));
551	void *arg;
552} cpu_intr_vectors[CPU_NINTS];
553
554void *
555cpu_intr_establish(pri, irq, handler, arg, dv)
556	int pri, irq;
557	int (*handler) __P((void *));
558	void *arg;
559	struct device *dv;
560{
561	register struct cpu_intr_vector *iv;
562
563	if (0 <= irq && irq < CPU_NINTS && cpu_intr_vectors[irq].handler)
564		return NULL;
565
566	iv = &cpu_intr_vectors[irq];
567	iv->pri = pri;
568	iv->handler = handler;
569	iv->arg = arg;
570	evcnt_attach(dv, dv->dv_xname, &iv->evcnt);
571
572	return iv;
573}
574
575void
576cpu_intr(frame)
577	struct trapframe *frame;
578{
579	u_int32_t eirr;
580	register struct cpu_intr_vector *iv;
581	register int bit;
582
583	do {
584		mfctl(CR_EIRR, eirr);
585		eirr &= frame->tf_eiem;
586		bit = ffs(eirr) - 1;
587		if (bit >= 0) {
588			mtctl(1 << bit, CR_EIRR);
589			eirr &= ~(1 << bit);
590			/* ((struct iomod *)cpu_gethpa(0))->io_eir = 0; */
591#ifdef INTRDEBUG
592			if (bit != 31)
593				db_printf ("cpu_intr: 0x%08x\n", (1 << bit));
594#endif
595			iv = &cpu_intr_vectors[bit];
596			if (iv->handler) {
597				register int s, r;
598
599				iv->evcnt.ev_count++;
600				s = splx(iv->pri);
601				/* no arg means pass the frame */
602				r = (iv->handler)(iv->arg? iv->arg:frame);
603				splx(s);
604#ifdef INTRDEBUG
605				if (!r)
606					db_printf ("%s: can't handle interrupt\n",
607						   iv->evcnt.ev_name);
608#endif
609			}
610#ifdef INTRDEBUG
611			else
612				db_printf ("cpu_intr: stray interrupt %d\n", bit);
613#endif
614		}
615	} while (eirr);
616}
617