1 /* $NetBSD: trap.c,v 1.74 2022/05/28 21:14:56 andvar Exp $ */
2
3 /*-
4 * Copyright (c) 2011 Reinoud Zandijk <reinoud@netbsd.org>
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 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.74 2022/05/28 21:14:56 andvar Exp $");
31
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/proc.h>
36 #include <sys/systm.h>
37 #include <sys/userret.h>
38 #include <sys/kauth.h>
39 #include <sys/errno.h>
40
41 #include <uvm/uvm_extern.h>
42 #include <machine/cpu.h>
43 #include <machine/pcb.h>
44 #include <machine/pmap.h>
45 #include <machine/machdep.h>
46 #include <machine/intr.h>
47 #include <machine/thunk.h>
48
49 #include "opt_kgdb.h"
50
51 #ifdef KGDB
52 #include <sys/kgdb.h>
53 #endif
54
55 /* define maximum signal number */
56 #ifndef NSIG
57 #define NSIG 64
58 #endif
59
60 /* forwards and externals */
61 void setup_signal_handlers(void);
62 void stop_all_signal_handlers(void);
63
64 static sigfunc_t pagefault;
65 static sigfunc_t illegal_instruction;
66 static sigfunc_t alarm;
67 static sigfunc_t sigio;
68 static sigfunc_t pass_on;
69
70 void kgdb_kernel_trap(int signo, vaddr_t pc, vaddr_t va, ucontext_t *ctx);
71
72 /* raw signal handlers */
73 static char sig_stack[SIGSTKSZ];
74 static stack_t sigstk;
75 ucontext_t jump_ucp;
76
77 sigfunc_t *sig_funcs[NSIG];
78
79 /* segv, bus */
80 extern bool pmap_fault(pmap_t pmap, vaddr_t va, vm_prot_t *atype);
81
82 /* alarm */
83 void setup_clock_intr(void);
84 extern void clock_intr(void *priv);
85
86 extern int clock_running;
87 void *alrm_ih;
88
89 /* sigio handlers */
90 struct intr_handler {
91 int (*func)(void *);
92 void *arg;
93 };
94 #define SIGIO_MAX_HANDLERS 8
95 static struct intr_handler sigio_intr_handler[SIGIO_MAX_HANDLERS];
96
97 /* misc */
98 int astpending = 0;
99
100
101 /* XXX why is it here ? */
102 void
startlwp(void * arg)103 startlwp(void *arg)
104 {
105 /* nothing here */
106 }
107
108
109 void
setup_signal_handlers(void)110 setup_signal_handlers(void)
111 {
112 int i;
113
114 /*
115 * Set up the alternative signal stack. This prevents signals to be
116 * pushed on the NetBSD/usermode userland's stack with all desastrous
117 * effects. Especially ld.so and friends have such tiny stacks that
118 * its not feasible.
119 */
120 sigstk.ss_sp = sig_stack;
121 sigstk.ss_size = SIGSTKSZ;
122 sigstk.ss_flags = 0;
123 if (thunk_sigaltstack(&sigstk, 0) < 0)
124 panic("can't set alternate stacksize: %d",
125 thunk_geterrno());
126
127 for (i = 0; i < NSIG; i++)
128 sig_funcs[i] = NULL;
129
130 /* HUP */
131 /* INT */ /* ttycons ^C */
132 /* QUIT */
133 signal_intr_establish(SIGILL, illegal_instruction);
134 signal_intr_establish(SIGTRAP, pass_on); /* special */
135 /* ABRT */
136 /* SIGEMT */
137 signal_intr_establish(SIGFPE, pass_on);
138 /* KILL */
139 signal_intr_establish(SIGBUS, pagefault);
140 signal_intr_establish(SIGSEGV, pagefault);
141 /* SYS */
142 /* PIPE */
143 signal_intr_establish(SIGALRM, alarm);
144 /* TERM */
145 /* URG */
146 /* STOP */
147 /* TSTP */ /* ttycons ^Z */
148 /* CONT */
149 /* CHLD */
150 /* GTTIN */
151 /* TTOU */
152 signal_intr_establish(SIGIO, sigio);
153 /* XCPU */
154 /* XFSZ */
155 /* VTALRM */
156 /* PROF */
157 /* WINCH */
158 /* INFO */
159 /* USR1 */
160 /* USR2 */
161 /* PWR */
162 }
163
164
165 /* XXX yes this is blunt */
166 void
stop_all_signal_handlers(void)167 stop_all_signal_handlers(void)
168 {
169 int i;
170 for (i = 0; i < NSIG; i++)
171 if (sig_funcs[i])
172 thunk_sigblock(i);
173 }
174
175
176 void
setup_clock_intr(void)177 setup_clock_intr(void)
178 {
179 /* setup soft interrupt handler */
180 alrm_ih = softint_establish(SOFTINT_CLOCK,
181 clock_intr, NULL);
182 }
183
184
185 /* ast and userret */
186 static void
ast(struct lwp * l)187 ast(struct lwp *l)
188 {
189 struct pcb *pcb;
190
191 curcpu()->ci_data.cpu_ntrap++;
192
193 do {
194 astpending = 0;
195 mi_userret(l);
196 } while (astpending);
197
198 #if 0
199 /* profiling */
200 if (l->l_pflag & LP_OWEUPC) {
201 l->l_pflag &= ~LP_OWEUPC;
202 ADDUPROF(l);
203 }
204 #endif
205
206 KASSERT(l == curlwp); KASSERT(l);
207 pcb = lwp_getpcb(l); KASSERT(pcb);
208 }
209
210
211 void
userret(struct lwp * l)212 userret(struct lwp *l)
213 {
214 /* invoke MI userret code */
215 mi_userret(l);
216
217 ast(l);
218 }
219
220
221 #ifdef DEBUG
222 /*
223 * Uncomment the following if you want to receive information about what
224 * triggered the fault. Mainly for debugging and porting purposes
225 */
226 static void
print_mem_access_siginfo(int sig,siginfo_t * info,void * ctx,vaddr_t pc,vaddr_t va,vaddr_t sp)227 print_mem_access_siginfo(int sig, siginfo_t *info, void *ctx,
228 vaddr_t pc, vaddr_t va, vaddr_t sp)
229 {
230 #if 0
231 thunk_printf_debug("SIGSEGV or SIGBUS!\n");
232 thunk_printf_debug("\tsi_signo = %d\n", info->si_signo);
233 thunk_printf_debug("\tsi_errno = %d\n", info->si_errno);
234 thunk_printf_debug("\tsi_code = %d\n", info->si_code);
235 if (info->si_code == SEGV_MAPERR)
236 thunk_printf_debug("\t\tSEGV_MAPERR\n");
237 if (info->si_code == SEGV_ACCERR)
238 thunk_printf_debug("\t\tSEGV_ACCERR\n");
239 if (info->si_code == BUS_ADRALN)
240 thunk_printf_debug("\t\tBUS_ADRALN\n");
241 if (info->si_code == BUS_ADRERR)
242 thunk_printf_debug("\t\tBUS_ADRERR\n");
243 if (info->si_code == BUS_OBJERR)
244 thunk_printf_debug("\t\tBUS_OBJERR\n");
245 thunk_printf_debug("\tsi_addr = %p\n", info->si_addr);
246 thunk_printf_debug("\tsi_trap = %d\n", info->si_trap);
247 #endif
248
249 #if 0
250 thunk_printf("memaccess error, pc %p, va %p, sp %p\n",
251 (void *) pc, (void *) va, (void *) sp);
252 #endif
253 }
254
255 /*
256 * Uncomment the following if you want to receive information about what
257 * triggered the fault. Mainly for debugging and porting purposes
258 */
259 static void
print_illegal_instruction_siginfo(int sig,siginfo_t * info,void * ctx,vaddr_t pc,vaddr_t va,vaddr_t sp)260 print_illegal_instruction_siginfo(int sig, siginfo_t *info, void *ctx,
261 vaddr_t pc, vaddr_t va, vaddr_t sp)
262 {
263 #if 0
264 thunk_printf("SIGILL!\n");
265 thunk_printf("\tsi_signo = %d\n", info->si_signo);
266 thunk_printf("\tsi_errno = %d\n", info->si_errno);
267 thunk_printf("\tsi_code = %d\n", info->si_code);
268 if (info->si_code == ILL_ILLOPC)
269 thunk_printf("\t\tIllegal opcode");
270 if (info->si_code == ILL_ILLOPN)
271 thunk_printf("\t\tIllegal operand");
272 if (info->si_code == ILL_ILLADR)
273 thunk_printf("\t\tIllegal addressing mode");
274 if (info->si_code == ILL_ILLTRP)
275 thunk_printf("\t\tIllegal trap");
276 if (info->si_code == ILL_PRVOPC)
277 thunk_printf("\t\tPrivileged opcode");
278 if (info->si_code == ILL_PRVREG)
279 thunk_printf("\t\tPrivileged register");
280 if (info->si_code == ILL_COPROC)
281 thunk_printf("\t\tCoprocessor error");
282 if (info->si_code == ILL_BADSTK)
283 thunk_printf("\t\tInternal stack error");
284 thunk_printf("\tsi_addr = %p\n", info->si_addr);
285 thunk_printf("\tsi_trap = %d\n", info->si_trap);
286
287 thunk_printf("%p : ", info->si_addr);
288 for (int i = 0; i < 10; i++)
289 thunk_printf("%02x ", *((uint8_t *) info->si_addr + i));
290 thunk_printf("\n");
291 #endif
292
293 #if 0
294 thunk_printf("sigill\n");
295 #endif
296 }
297 #else /* DEBUG */
298 #define print_mem_access_siginfo(s, i, c, p, v, sp) {}
299 #define print_illegal_instruction_siginfo(s, i, c, p, v, sp) {}
300 #endif /* DEBUG */
301
302
303 static void
handle_signal(int sig,siginfo_t * info,void * ctx)304 handle_signal(int sig, siginfo_t *info, void *ctx)
305 {
306 sigfunc_t *f;
307 ucontext_t *ucp = ctx;
308 struct lwp *l;
309 struct pcb *pcb;
310 vaddr_t va, sp, pc, fp;
311 long from_userland;
312
313 if (sig == SIGBUS || sig == SIGSEGV || sig == SIGILL) {
314 if (info->si_code == SI_NOINFO)
315 panic("received signal %d with no info",
316 info->si_signo);
317 }
318
319 f = sig_funcs[sig];
320 KASSERT(f);
321
322 /* get address of possible faulted memory access and page align it */
323 va = (vaddr_t) info->si_addr;
324 va = trunc_page(va);
325
326 /* get PC address of possibly faulted instruction */
327 pc = md_get_pc(ctx);
328
329 /*
330 * short-cut for SIGTRAP as we have NO indication anything is valid
331 */
332 #ifdef KGDB
333 if (sig == SIGTRAP) {
334 from_userland = 0;
335 if (pc < kmem_user_end)
336 from_userland = 1;
337 if (!from_userland) {
338 kgdb_kernel_trap(sig, pc, va, ucp);
339 return;
340 }
341 }
342 #endif
343
344 /* get stack pointer for nesting */
345 sp = md_get_sp(ctx);
346
347 if (sig == SIGBUS || sig == SIGSEGV)
348 print_mem_access_siginfo(sig, info, ctx, pc, va, sp);
349 if (sig == SIGILL)
350 print_illegal_instruction_siginfo(sig, info, ctx, pc, va, sp);
351
352 /* get thread */
353 l = curlwp; KASSERT(l);
354 pcb = lwp_getpcb(l); KASSERT(pcb);
355
356 /* currently running on the dedicated signal stack */
357
358 /* if we're running on a userland stack, switch to the system stack */
359 from_userland = 0;
360 if ((sp < (vaddr_t) pcb->sys_stack) ||
361 (sp > (vaddr_t) pcb->sys_stack_top)) {
362 sp = (vaddr_t) pcb->sys_stack_top - sizeof(register_t);
363 fp = (vaddr_t) &pcb->pcb_userret_ucp;
364 if (pc < kmem_user_end)
365 from_userland = 1;
366 } else {
367 /* stack grows down */
368 fp = sp - sizeof(ucontext_t) - sizeof(register_t); /* slack */
369 sp = fp - sizeof(register_t); /* slack */
370
371 /* sanity check before copying */
372 if (fp - 4*PAGE_SIZE < (vaddr_t) pcb->sys_stack)
373 panic("%s: out of system stack", __func__);
374 }
375
376 memcpy((void *) fp, ucp, sizeof(ucontext_t));
377 memcpy(&jump_ucp, ucp, sizeof(ucontext_t));
378
379 /* create context */
380 jump_ucp.uc_stack.ss_sp = (void *) pcb->sys_stack;
381 jump_ucp.uc_stack.ss_size = sp - (vaddr_t) pcb->sys_stack;
382 jump_ucp.uc_link = (void *) fp; /* link to old frame on stack */
383
384 /* prevent multiple nested SIGIOs */
385 if (sig == SIGIO)
386 thunk_sigfillset(&jump_ucp.uc_sigmask);
387 else
388 thunk_sigemptyset(&jump_ucp.uc_sigmask);
389 jump_ucp.uc_flags = _UC_STACK | _UC_CPU | _UC_SIGMASK;
390
391 thunk_makecontext(&jump_ucp,
392 (void (*)(void)) f,
393 4, info, (void *) from_userland, (void *) pc, (void *) va);
394
395 /* switch to the new context on return from signal */
396 thunk_setcontext(&jump_ucp);
397 // memcpy(ctx, &pcb->pcb_ucp, sizeof(ucontext_t));
398 }
399
400
401 void
signal_intr_establish(int sig,sigfunc_t f)402 signal_intr_establish(int sig, sigfunc_t f)
403 {
404 static struct sigaction sa;
405
406 sig_funcs[sig] = f;
407
408 sa.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
409 sa.sa_sigaction = (void *) handle_signal;
410 thunk_sigfillset(&sa.sa_mask);
411 if (thunk_sigaction(sig, &sa, NULL) == -1)
412 panic("couldn't register SIG%d handler: %d", sig,
413 thunk_geterrno());
414 }
415
416
417 /*
418 * Context for handing page faults from the sigsegv handler; check if its a
419 * pmap reference fault or let uvm handle it.
420 */
421 static void
pagefault(siginfo_t * info,vaddr_t from_userland,vaddr_t pc,vaddr_t va)422 pagefault(siginfo_t *info, vaddr_t from_userland, vaddr_t pc, vaddr_t va)
423 {
424 struct proc *p;
425 struct lwp *l;
426 struct pcb *pcb;
427 struct vmspace *vm;
428 struct vm_map *vm_map;
429 vm_prot_t atype;
430 void *onfault;
431 int from_kernel, lwp_errno, error;
432 ksiginfo_t ksi;
433
434 l = curlwp; KASSERT(l);
435 pcb = lwp_getpcb(l);
436 p = l->l_proc;
437 vm = p->p_vmspace;
438
439 lwp_errno = thunk_geterrno();
440
441 vm_map = &vm->vm_map;
442 from_kernel = (pc >= kmem_k_start) && (!from_userland);
443 if (from_kernel && (va >= VM_MIN_KERNEL_ADDRESS))
444 vm_map = kernel_map;
445
446 #if 0
447 thunk_printf("%s: l %p, pcb %p\n", __func__, l, pcb);
448 thunk_printf("\tpc %p, va %p\n", (void *) pc, (void *) va);
449 #endif
450
451 /* can pmap handle it? on its own? (r/m) emulation */
452 if (pmap_fault(vm_map->pmap, va, &atype)) {
453 /* no use doing anything else here */
454 goto out_quick;
455 }
456
457 /* ask UVM */
458 #if 0
459 thunk_printf("%s: l %p, pcb %p, ", __func__, l, pcb);
460 thunk_printf("pc %p, va %p ", (void *) pc, (void *) va);
461 thunk_printf("derived atype %d\n", atype);
462 #endif
463 thunk_printf_debug("pmap fault couldn't handle it! : "
464 "derived atype %d\n", atype);
465
466 onfault = pcb->pcb_onfault;
467 pcb->pcb_onfault = NULL;
468 error = uvm_fault(vm_map, va, atype);
469 pcb->pcb_onfault = onfault;
470
471 if (vm_map != kernel_map) {
472 if (error == 0)
473 uvm_grow(l->l_proc, va);
474 }
475 if (error == EACCES)
476 error = EFAULT;
477
478 /* if uvm handled it, return */
479 if (error == 0) {
480 // thunk_printf("pagefault leave (uvm)\n");
481 goto out;
482 }
483
484 /* check if its from copyin/copyout */
485 if (onfault) {
486 panic("%s: can't call onfault yet\n", __func__);
487 /* XXX implement me ? */
488 /* jump to given onfault */
489 // tf = &kernel_tf;
490 // memset(tf, 0, sizeof(struct trapframe));
491 // tf->tf_pc = onfault;
492 // tf->tf_io[0] = (rv == EACCES) ? EFAULT : rv;
493 goto out;
494 }
495
496 if (from_kernel) {
497 thunk_printf("%s: uvm fault %d, pc %p, va %p, from_kernel %d\n",
498 __func__, error, (void *) pc, (void *) va, from_kernel);
499 panic("Unhandled page fault in kernel mode");
500 }
501
502 /* send signal */
503 /* something got wrong */
504 thunk_printf_debug("%s: uvm fault %d, pc %p, va %p, from_kernel %d\n",
505 __func__, error, (void *) pc, (void *) va, from_kernel);
506
507 thunk_printf_debug("giving signal to userland\n");
508
509 KASSERT(from_userland);
510 KSI_INIT_TRAP(&ksi);
511 ksi.ksi_signo = info->si_signo;
512 ksi.ksi_trap = 0; /* XXX */
513 ksi.ksi_code = (error == EPERM) ? SEGV_ACCERR : SEGV_MAPERR;
514 ksi.ksi_addr = (void *) va;
515
516 if (error == ENOMEM) {
517 printf("UVM: pid %d.%d (%s), uid %d killed: "
518 "out of swap\n",
519 p->p_pid, l->l_lid, p->p_comm,
520 l->l_cred ? kauth_cred_geteuid(l->l_cred) : -1);
521 ksi.ksi_signo = SIGKILL;
522 }
523
524 #if 0
525 p->p_emul->e_trapsignal(l, &ksi);
526 #else
527 trapsignal(l, &ksi);
528 #endif
529
530 // thunk_printf("pagefault leave\n");
531 out:
532 if (from_userland)
533 userret(l);
534 out_quick:
535 thunk_seterrno(lwp_errno);
536 pcb->pcb_errno = lwp_errno;
537 }
538
539
540 /*
541 * handle an illegal instruction.
542 *
543 * arguments 'pc' and 'va' are ignored here
544 */
545 static void
illegal_instruction(siginfo_t * info,vaddr_t from_userland,vaddr_t pc,vaddr_t va)546 illegal_instruction(siginfo_t *info, vaddr_t from_userland, vaddr_t pc, vaddr_t va)
547 {
548 struct lwp *l = curlwp;
549 struct pcb *pcb = lwp_getpcb(l);
550 ucontext_t *ucp = &pcb->pcb_userret_ucp;
551 ksiginfo_t ksi;
552
553 // thunk_printf("%s: l %p, pcb %p\n", __func__, l, pcb);
554
555 KASSERT(from_userland);
556
557 /* if its a syscall ... */
558 if (md_syscall_check_opcode(ucp)) {
559 syscall();
560 userret(l);
561 return;
562 }
563
564 thunk_printf("%s: giving SIGILL (TRAP)\n", __func__);
565
566 KASSERT(from_userland);
567 KSI_INIT_TRAP(&ksi);
568 ksi.ksi_signo = SIGILL;
569 ksi.ksi_trap = 0; /* XXX */
570 ksi.ksi_errno = 0; // info->si_errno;
571 ksi.ksi_code = 0; // info->si_code;
572 ksi.ksi_addr = (void *) md_get_pc(ucp); /* only reliable source */
573
574 #if 0
575 p->p_emul->e_trapsignal(l, &ksi);
576 #else
577 trapsignal(l, &ksi);
578 #endif
579 userret(l);
580 }
581
582
583 /*
584 * handle pass to userland signals
585 *
586 * arguments other than the original siginfo_t are not used
587 */
588 static void
pass_on(siginfo_t * info,vaddr_t from_userland,vaddr_t pc,vaddr_t va)589 pass_on(siginfo_t *info, vaddr_t from_userland, vaddr_t pc, vaddr_t va)
590 {
591 struct lwp *l = curlwp;
592 struct pcb *pcb = lwp_getpcb(l);
593 ucontext_t *ucp = &pcb->pcb_userret_ucp;
594 ksiginfo_t ksi;
595
596 KASSERT(from_userland);
597 KSI_INIT_TRAP(&ksi);
598 ksi.ksi_signo = info->si_signo;
599 ksi.ksi_trap = 0; /* XXX ? */
600 ksi.ksi_errno = info->si_errno;
601 ksi.ksi_code = info->si_code;
602 ksi.ksi_addr = (void *) md_get_pc(ucp); /* only reliable source */
603
604 trapsignal(l, &ksi);
605 userret(l);
606 }
607
608
609 /*
610 * handle alarm, a clock ticker.
611 *
612 * arguments 'pc' and 'va' are ignored here
613 */
614 static void
alarm(siginfo_t * info,vaddr_t from_userland,vaddr_t pc,vaddr_t va)615 alarm(siginfo_t *info, vaddr_t from_userland, vaddr_t pc, vaddr_t va)
616 {
617 struct lwp *l = curlwp;
618 struct pcb *pcb = lwp_getpcb(l); KASSERT(pcb);
619
620 if (!clock_running)
621 return;
622 // thunk_printf("%s: l %p, pcb %p\n", __func__, l, pcb);
623
624 softint_schedule(alrm_ih);
625
626 KASSERT(l == curlwp);
627 if (from_userland)
628 userret(l);
629 }
630
631
632 /*
633 * handle sigio, a mux for all io operations.
634 *
635 * arguments 'pc' and 'va' are ignored here
636 */
637 static void
sigio(siginfo_t * info,vaddr_t from_userland,vaddr_t pc,vaddr_t va)638 sigio(siginfo_t *info, vaddr_t from_userland, vaddr_t pc, vaddr_t va)
639 {
640 struct lwp *l = curlwp;
641 struct pcb *pcb = lwp_getpcb(l); KASSERT(pcb);
642 struct intr_handler *sih;
643 unsigned int n, pass;
644
645 // thunk_printf("%s: l %p, pcb %p\n", __func__, l, pcb);
646 for (pass = 0; pass < 2; pass++) {
647 for (n = 0; n < SIGIO_MAX_HANDLERS; n++) {
648 sih = &sigio_intr_handler[n];
649 if (sih->func)
650 sih->func(sih->arg);
651 }
652 }
653
654 KASSERT(l == curlwp);
655 if (from_userland)
656 userret(l); /* or ast? */
657 }
658
659
660 /* sigio register function */
661 void *
sigio_intr_establish(int (* func)(void *),void * arg)662 sigio_intr_establish(int (*func)(void *), void *arg)
663 {
664 struct intr_handler *sih;
665 unsigned int n;
666
667 for (n = 0; n < SIGIO_MAX_HANDLERS; n++) {
668 sih = &sigio_intr_handler[n];
669 if (sih->func == NULL) {
670 sih->func = func;
671 sih->arg = arg;
672 return sih;
673 }
674 }
675
676 panic("increase SIGIO_MAX_HANDLERS");
677 }
678
679