trap.c revision 1.65
1/*	$OpenBSD: trap.c,v 1.65 2003/04/11 00:25:40 mickey Exp $	*/
2
3/*
4 * Copyright (c) 1998-2003 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 MIND,
27 * USE, 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 TRAPDEBUG */
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/syscall.h>
38#include <sys/ktrace.h>
39#include <sys/proc.h>
40#include <sys/signalvar.h>
41#include <sys/user.h>
42
43#include <net/netisr.h>
44
45#include "systrace.h"
46#include <dev/systrace.h>
47
48#include <uvm/uvm.h>
49
50#include <machine/autoconf.h>
51
52#include <machine/db_machdep.h>	/* XXX always needed for inst_store() */
53#ifdef DDB
54#ifdef TRAPDEBUG
55#include <ddb/db_output.h>
56#endif
57#endif
58
59const char *trap_type[] = {
60	"invalid",
61	"HPMC",
62	"power failure",
63	"recovery counter",
64	"external interrupt",
65	"LPMC",
66	"ITLB miss fault",
67	"instruction protection",
68	"Illegal instruction",
69	"break instruction",
70	"privileged operation",
71	"privileged register",
72	"overflow",
73	"conditional",
74	"assist exception",
75	"DTLB miss",
76	"ITLB non-access miss",
77	"DTLB non-access miss",
78	"data protection/rights/alignment",
79	"data break",
80	"TLB dirty",
81	"page reference",
82	"assist emulation",
83	"higher-priv transfer",
84	"lower-priv transfer",
85	"taken branch",
86	"data access rights",
87	"data protection",
88	"unaligned data ref",
89};
90int trap_types = sizeof(trap_type)/sizeof(trap_type[0]);
91
92int want_resched, astpending;
93
94void
95userret(struct proc *p, register_t pc, u_quad_t oticks)
96{
97	int sig;
98
99	/* take pending signals */
100	while ((sig = CURSIG(p)) != 0)
101		postsig(sig);
102
103	p->p_priority = p->p_usrpri;
104	if (astpending) {
105		astpending = 0;
106		if (p->p_flag & P_OWEUPC) {
107			p->p_flag &= ~P_OWEUPC;
108			ADDUPROF(p);
109		}
110	}
111	if (want_resched) {
112		/*
113		 * We're being preempted.
114		 */
115		preempt(NULL);
116		while ((sig = CURSIG(p)) != 0)
117			postsig(sig);
118	}
119
120	/*
121	 * If profiling, charge recent system time to the trapped pc.
122	 */
123	if (p->p_flag & P_PROFIL) {
124		extern int psratio;
125
126		addupc_task(p, pc, (int)(p->p_sticks - oticks) * psratio);
127	}
128
129	curpriority = p->p_priority;
130}
131
132void
133trap(type, frame)
134	int type;
135	struct trapframe *frame;
136{
137	struct proc *p = curproc;
138	vaddr_t va;
139	struct vm_map *map;
140	struct vmspace *vm;
141	register vm_prot_t vftype;
142	register pa_space_t space;
143	union sigval sv;
144	u_int opcode;
145	int ret, trapnum;
146	const char *tts;
147	vm_fault_t fault = VM_FAULT_INVALID;
148#ifdef DIAGNOSTIC
149	int oldcpl = cpl;
150#endif
151
152	trapnum = type & ~T_USER;
153	opcode = frame->tf_iir;
154	if (trapnum == T_ITLBMISS ||
155	    trapnum == T_EXCEPTION || trapnum == T_EMULATION) {
156		va = frame->tf_iioq_head;
157		space = frame->tf_iisq_head;
158		vftype = UVM_PROT_EXEC;
159	} else {
160		va = frame->tf_ior;
161		space = frame->tf_isr;
162		/* what is the vftype for the T_ITLBMISSNA ??? XXX */
163		if (va == frame->tf_iioq_head)
164			vftype = UVM_PROT_EXEC;
165		else if (inst_store(opcode))
166			vftype = UVM_PROT_WRITE;
167		else
168			vftype = UVM_PROT_READ;
169	}
170
171	if (frame->tf_flags & TFF_LAST)
172		p->p_md.md_regs = frame;
173
174	if (trapnum > trap_types)
175		tts = "reserved";
176	else
177		tts = trap_type[trapnum];
178
179#ifdef TRAPDEBUG
180	if (trapnum != T_INTERRUPT && trapnum != T_IBREAK)
181		db_printf("trap: %x, %s for %x:%x at %x:%x, fl=%x, fp=%p\n",
182		    type, tts, space, va, frame->tf_iisq_head,
183		    frame->tf_iioq_head, frame->tf_flags, frame);
184	else if (trapnum  == T_IBREAK)
185		db_printf("trap: break instruction %x:%x at %x:%x, fp=%p\n",
186		    break5(opcode), break13(opcode),
187		    frame->tf_iisq_head, frame->tf_iioq_head, frame);
188
189	{
190		extern int etext;
191		if (frame < (struct trapframe *)&etext) {
192			printf("trap: bogus frame ptr %p\n", frame);
193			goto dead_end;
194		}
195	}
196#endif
197	if (trapnum != T_INTERRUPT) {
198		uvmexp.traps++;
199		mtctl(frame->tf_eiem, CR_EIEM);
200	} else
201		uvmexp.intrs++;
202
203	switch (type) {
204	case T_NONEXIST:
205	case T_NONEXIST | T_USER:
206		/* we've got screwed up by the central scrutinizer */
207		printf("trap: elvis has just left the building!\n");
208		goto dead_end;
209
210	case T_RECOVERY:
211	case T_RECOVERY | T_USER:
212		/* XXX will implement later */
213		printf("trap: handicapped");
214		goto dead_end;
215
216#ifdef DIAGNOSTIC
217	case T_EXCEPTION:
218		panic("FPU/SFU emulation botch");
219
220		/* these just can't happen ever */
221	case T_PRIV_OP:
222	case T_PRIV_REG:
223		/* these just can't make it to the trap() ever */
224	case T_HPMC:
225	case T_HPMC | T_USER:
226#endif
227	case T_IBREAK:
228	case T_DATALIGN:
229	case T_DBREAK:
230	dead_end:
231#ifdef DDB
232		if (kdb_trap (type, va, frame)) {
233			if (type == T_IBREAK) {
234				/* skip break instruction */
235				frame->tf_iioq_head = frame->tf_iioq_tail;
236				frame->tf_iioq_tail += 4;
237			}
238			return;
239		}
240#else
241		if (type == T_DATALIGN)
242			panic ("trap: %s at 0x%x", tts, va);
243		else
244			panic ("trap: no debugger for \"%s\" (%d)", tts, type);
245#endif
246		break;
247
248	case T_IBREAK | T_USER:
249		/* XXX */
250		frame->tf_iioq_head = frame->tf_iioq_tail;
251		frame->tf_iioq_tail += 4;
252	case T_DBREAK | T_USER:
253		/* pass to user debugger */
254		break;
255
256	case T_EXCEPTION | T_USER: {
257		u_int64_t *fpp = (u_int64_t *)frame->tf_cr30;
258		u_int32_t *pex;
259		int i, flt;
260
261		pex = (u_int32_t *)&fpp[0];
262		for (i = 0, pex++; i < 7 && !*pex; i++, pex++);
263		flt = 0;
264		if (i < 7) {
265			u_int32_t stat = HPPA_FPU_OP(*pex);
266			if (stat == HPPA_FPU_UNMPL)
267				flt = FPE_FLTINV;
268			else if (stat & HPPA_FPU_V)
269				flt = FPE_FLTINV;
270			else if (stat & HPPA_FPU_Z)
271				flt = FPE_FLTDIV;
272			else if (stat & HPPA_FPU_I)
273				flt = FPE_FLTRES;
274			else if (stat & HPPA_FPU_O)
275				flt = FPE_FLTOVF;
276			else if (stat & HPPA_FPU_U)
277				flt = FPE_FLTUND;
278			/* still left: under/over-flow w/ inexact */
279			*pex = 0;
280		}
281		/* reset the trap flag, as if there was none */
282		fpp[0] &= ~(((u_int64_t)HPPA_FPU_T) << 32);
283		/* flush out, since load is done from phys, only 4 regs */
284		fdcache(HPPA_SID_KERNEL, (vaddr_t)fpp, 8 * 4);
285
286		sv.sival_int = va;
287		trapsignal(p, SIGFPE, type &~ T_USER, flt, sv);
288		}
289		break;
290
291	case T_EMULATION:
292		panic("trap: emulation trap in the kernel");
293		break;
294
295	case T_EMULATION | T_USER:
296		sv.sival_int = va;
297		trapsignal(p, SIGILL, type &~ T_USER, ILL_ILLOPC, sv);
298		break;
299
300	case T_OVERFLOW | T_USER:
301		sv.sival_int = va;
302		trapsignal(p, SIGFPE, type &~ T_USER, FPE_INTOVF, sv);
303		break;
304
305	case T_CONDITION | T_USER:
306		break;
307
308	case T_ILLEGAL | T_USER:
309		sv.sival_int = va;
310		trapsignal(p, SIGILL, type &~ T_USER, ILL_ILLOPC, sv);
311		break;
312
313	case T_PRIV_OP | T_USER:
314		sv.sival_int = va;
315		trapsignal(p, SIGILL, type &~ T_USER, ILL_PRVOPC, sv);
316		break;
317
318	case T_PRIV_REG | T_USER:
319		sv.sival_int = va;
320		trapsignal(p, SIGILL, type &~ T_USER, ILL_PRVREG, sv);
321		break;
322
323		/* these should never got here */
324	case T_HIGHERPL | T_USER:
325	case T_LOWERPL | T_USER:
326		sv.sival_int = va;
327		trapsignal(p, SIGSEGV, vftype, SEGV_ACCERR, sv);
328		break;
329
330	case T_IPROT | T_USER:
331	case T_DPROT | T_USER:
332		sv.sival_int = va;
333		trapsignal(p, SIGSEGV, vftype, SEGV_ACCERR, sv);
334		break;
335
336	case T_DATACC:
337	case T_DATACC | T_USER:
338		fault = VM_FAULT_PROTECT;
339	case T_ITLBMISS:
340	case T_ITLBMISS | T_USER:
341	case T_DTLBMISS:
342	case T_DTLBMISS | T_USER:
343	case T_ITLBMISSNA:
344	case T_ITLBMISSNA | T_USER:
345	case T_DTLBMISSNA:
346	case T_DTLBMISSNA | T_USER:
347	case T_TLB_DIRTY:
348	case T_TLB_DIRTY | T_USER:
349		/*
350		 * user faults out of user addr space are always a fail,
351		 * this happens on va >= VM_MAXUSER_ADDRESS, where
352		 * space id will be zero and therefore cause
353		 * a misbehave lower in the code.
354		 */
355		if (type & T_USER && va >= VM_MAXUSER_ADDRESS) {
356			sv.sival_int = va;
357			trapsignal(p, SIGSEGV, vftype, SEGV_ACCERR, sv);
358			break;
359		}
360
361		/*
362		 * it could be a kernel map for exec_map faults
363		 */
364		if (space == HPPA_SID_KERNEL)
365			map = kernel_map;
366		else {
367			vm = p->p_vmspace;
368			map = &vm->vm_map;
369		}
370
371		if (type & T_USER && map->pmap->pm_space != space) {
372			sv.sival_int = va;
373			trapsignal(p, SIGSEGV, vftype, SEGV_MAPERR, sv);
374			break;
375		}
376
377		ret = uvm_fault(map, hppa_trunc_page(va), fault, vftype);
378
379		/*
380		 * If this was a stack access we keep track of the maximum
381		 * accessed stack size.  Also, if uvm_fault gets a protection
382		 * failure it is due to accessing the stack region outside
383		 * the current limit and we need to reflect that as an access
384		 * error.
385		 */
386		if (space != 0 && va < (vaddr_t)vm->vm_minsaddr &&
387		    va >= (vaddr_t)vm->vm_maxsaddr + ctob(vm->vm_ssize)) {
388			if (ret == 0) {
389				vsize_t nss = btoc(va - USRSTACK + NBPG - 1);
390				if (nss > vm->vm_ssize)
391					vm->vm_ssize = nss;
392			} else if (ret == EACCES)
393				ret = EFAULT;
394		}
395
396		if (ret != 0) {
397			if (type & T_USER) {
398				sv.sival_int = va;
399				trapsignal(p, SIGSEGV, vftype,
400				    ret == EACCES? SEGV_ACCERR : SEGV_MAPERR,
401				    sv);
402			} else {
403				if (p && p->p_addr->u_pcb.pcb_onfault) {
404					frame->tf_iioq_tail = 4 +
405					    (frame->tf_iioq_head =
406						p->p_addr->u_pcb.pcb_onfault);
407#ifdef DDB
408					frame->tf_iir = 0;
409#endif
410				} else {
411					panic("trap: "
412					    "uvm_fault(%p, %x, %d, %d): %d",
413					    map, va, 0, vftype, ret);
414				}
415			}
416		}
417		break;
418
419	case T_DATALIGN | T_USER:
420		sv.sival_int = va;
421		trapsignal(p, SIGBUS, vftype, BUS_ADRALN, sv);
422		break;
423
424	case T_INTERRUPT:
425	case T_INTERRUPT | T_USER:
426		cpu_intr(frame);
427		break;
428
429	case T_CONDITION:
430		panic("trap: divide by zero in the kernel");
431		break;
432
433	case T_LOWERPL:
434	case T_DPROT:
435	case T_IPROT:
436	case T_OVERFLOW:
437	case T_ILLEGAL:
438	case T_HIGHERPL:
439	case T_TAKENBR:
440	case T_POWERFAIL:
441	case T_LPMC:
442	case T_PAGEREF:
443	case T_DATAPID:
444	case T_DATAPID | T_USER:
445		if (0 /* T-chip */) {
446			break;
447		}
448		/* FALLTHROUGH to unimplemented */
449	default:
450#if 0
451if (kdb_trap (type, va, frame))
452	return;
453#endif
454		panic("trap: unimplemented \'%s\' (%d)", tts, trapnum);
455	}
456
457#ifdef DIAGNOSTIC
458	if (cpl != oldcpl)
459		printf("WARNING: SPL (%d) NOT LOWERED ON "
460		    "TRAP (%d) EXIT\n", cpl, trapnum);
461#endif
462
463	if (trapnum != T_INTERRUPT)
464		splx(cpl);	/* process softints */
465
466	if (type & T_USER)
467		userret(p, frame->tf_iioq_head, 0);
468}
469
470void
471child_return(arg)
472	void *arg;
473{
474	struct proc *p = (struct proc *)arg;
475	userret(p, p->p_md.md_regs->tf_iioq_head, 0);
476#ifdef KTRACE
477	if (KTRPOINT(p, KTR_SYSRET))
478		ktrsysret(p, SYS_fork, 0, 0);
479#endif
480}
481
482
483/*
484 * call actual syscall routine
485 */
486void
487syscall(struct trapframe *frame)
488{
489	register struct proc *p = curproc;
490	register const struct sysent *callp;
491	int retq, nsys, code, argsize, argoff, oerror, error;
492	register_t args[8], rval[2];
493#ifdef DIAGNOSTIC
494	int oldcpl = cpl;
495#endif
496
497	uvmexp.syscalls++;
498
499	if (!USERMODE(frame->tf_iioq_head))
500		panic("syscall");
501
502	p->p_md.md_regs = frame;
503	nsys = p->p_emul->e_nsysent;
504	callp = p->p_emul->e_sysent;
505
506	argoff = 4; retq = 0;
507	switch (code = frame->tf_t1) {
508	case SYS_syscall:
509		code = frame->tf_arg0;
510		args[0] = frame->tf_arg1;
511		args[1] = frame->tf_arg2;
512		args[2] = frame->tf_arg3;
513		argoff = 3;
514		break;
515	case SYS___syscall:
516		if (callp != sysent)
517			break;
518		/*
519		 * this works, because quads get magically swapped
520		 * due to the args being layed backwards on the stack
521		 * and then copied in words
522		 */
523		code = frame->tf_arg0;
524		args[0] = frame->tf_arg2;
525		args[1] = frame->tf_arg3;
526		argoff = 2;
527		retq = 1;
528		break;
529	default:
530		args[0] = frame->tf_arg0;
531		args[1] = frame->tf_arg1;
532		args[2] = frame->tf_arg2;
533		args[3] = frame->tf_arg3;
534		break;
535	}
536
537	if (code < 0 || code >= nsys)
538		callp += p->p_emul->e_nosys;	/* bad syscall # */
539	else
540		callp += code;
541
542	oerror = error = 0;
543	if ((argsize = callp->sy_argsize)) {
544		int i;
545
546		for (i = 0, argsize -= argoff * 4;
547		    argsize > 0; i++, argsize -= 4) {
548			error = copyin((void *)(frame->tf_sp +
549			    HPPA_FRAME_ARG(i + 4)), args + i + argoff, 4);
550
551			if (error)
552				break;
553		}
554
555		/*
556		 * coming from syscall() or __syscall we must be
557		 * having one of those w/ a 64 bit arguments,
558		 * which needs a word swap due to the order
559		 * of the arguments on the stack.
560		 * this assumes that none of 'em are called
561		 * by their normal syscall number, maybe a regress
562		 * test should be used, to whatch the behaviour.
563		 */
564		if (!error && argoff < 4) {
565			int t;
566
567			i = 0;
568			switch (code) {
569			case SYS_lseek:		retq = 0;
570			case SYS_truncate:
571			case SYS_ftruncate:	i = 2;	break;
572			case SYS_preadv:
573			case SYS_pwritev:
574			case SYS_pread:
575			case SYS_pwrite:	i = 4;	break;
576			case SYS_mmap:		i = 6;	break;
577			}
578
579			if (i) {
580				t = args[i];
581				args[i] = args[i + 1];
582				args[i + 1] = t;
583			}
584		}
585	}
586
587#ifdef SYSCALL_DEBUG
588	scdebug_call(p, code, args);
589#endif
590#ifdef KTRACE
591	if (KTRPOINT(p, KTR_SYSCALL))
592		ktrsyscall(p, code, callp->sy_argsize, args);
593#endif
594	if (error)
595		goto bad;
596
597	rval[0] = 0;
598	rval[1] = frame->tf_ret1;
599#if NSYSTRACE > 0
600	if (ISSET(p->p_flag, P_SYSTRACE))
601		oerror = error = systrace_redirect(code, p, args, rval);
602	else
603#endif
604		oerror = error = (*callp->sy_call)(p, args, rval);
605	p = curproc;
606	frame = p->p_md.md_regs;
607	switch (error) {
608	case 0:
609		frame->tf_ret0 = rval[0];
610		frame->tf_ret1 = rval[!retq];
611		frame->tf_t1 = 0;
612		break;
613	case ERESTART:
614		frame->tf_iioq_head -= 12;
615		frame->tf_iioq_tail -= 12;
616	case EJUSTRETURN:
617		break;
618	default:
619	bad:
620		if (p->p_emul->e_errno)
621			error = p->p_emul->e_errno[error];
622		frame->tf_t1 = error;
623		break;
624	}
625#ifdef SYSCALL_DEBUG
626	scdebug_ret(p, code, oerror, rval);
627#endif
628	userret(p, frame->tf_iioq_head, 0);
629	splx(cpl);	/* process softints */
630#ifdef KTRACE
631	if (KTRPOINT(p, KTR_SYSRET))
632		ktrsysret(p, code, oerror, rval[0]);
633#endif
634#ifdef DIAGNOSTIC
635	if (cpl != oldcpl)
636		printf("WARNING: SPL (0x%x) NOT LOWERED ON "
637		    "syscall(0x%x, 0x%x, 0x%x, 0x%x...) EXIT, PID %d\n",
638		    cpl, code, args[0], args[1], args[2], p->p_pid);
639#endif
640}
641