1 /* $OpenBSD: trap.c,v 1.57 2023/12/13 15:57:22 miod Exp $ */
2 /* $NetBSD: exception.c,v 1.32 2006/09/04 23:57:52 uwe Exp $ */
3 /* $NetBSD: syscall.c,v 1.6 2006/03/07 07:21:50 thorpej Exp $ */
4
5 /*-
6 * Copyright (c) 2002 The NetBSD Foundation, Inc. All rights reserved.
7 * Copyright (c) 1990 The Regents of the University of California.
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * the University of Utah, and William Jolitz.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 *
37 * @(#)trap.c 7.4 (Berkeley) 5/13/91
38 */
39
40 /*-
41 * Copyright (c) 1995 Charles M. Hannum. All rights reserved.
42 *
43 * This code is derived from software contributed to Berkeley by
44 * the University of Utah, and William Jolitz.
45 *
46 * Redistribution and use in source and binary forms, with or without
47 * modification, are permitted provided that the following conditions
48 * are met:
49 * 1. Redistributions of source code must retain the above copyright
50 * notice, this list of conditions and the following disclaimer.
51 * 2. Redistributions in binary form must reproduce the above copyright
52 * notice, this list of conditions and the following disclaimer in the
53 * documentation and/or other materials provided with the distribution.
54 * 3. All advertising materials mentioning features or use of this software
55 * must display the following acknowledgement:
56 * This product includes software developed by the University of
57 * California, Berkeley and its contributors.
58 * 4. Neither the name of the University nor the names of its contributors
59 * may be used to endorse or promote products derived from this software
60 * without specific prior written permission.
61 *
62 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
63 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
64 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
65 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
66 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
67 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
68 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
69 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
70 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
71 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
72 * SUCH DAMAGE.
73 *
74 * @(#)trap.c 7.4 (Berkeley) 5/13/91
75 */
76
77 /*
78 * SH3 Trap and System call handling
79 *
80 * T.Horiuchi 1998.06.8
81 */
82
83 #include <sys/param.h>
84 #include <sys/systm.h>
85 #include <sys/proc.h>
86 #include <sys/pool.h>
87 #include <sys/kernel.h>
88 #include <sys/signal.h>
89 #include <sys/resourcevar.h>
90 #include <sys/signalvar.h>
91 #include <uvm/uvm_extern.h>
92 #include <sys/syscall.h>
93 #include <sys/syscall_mi.h>
94
95 #include <sh/cache.h>
96 #include <sh/cpu.h>
97 #include <sh/mmu.h>
98 #include <sh/pcb.h>
99 #include <sh/trap.h>
100 #ifdef SH4
101 #include <sh/fpu.h>
102 #endif
103
104 #ifdef DDB
105 #include <machine/db_machdep.h>
106 #endif
107
108 const char * const exp_type[] = {
109 NULL, /* 000 (reset vector) */
110 NULL, /* 020 (reset vector) */
111 "TLB miss/invalid (load)", /* 040 EXPEVT_TLB_MISS_LD */
112 "TLB miss/invalid (store)", /* 060 EXPEVT_TLB_MISS_ST */
113 "initial page write", /* 080 EXPEVT_TLB_MOD */
114 "TLB protection violation (load)", /* 0a0 EXPEVT_TLB_PROT_LD */
115 "TLB protection violation (store)", /* 0c0 EXPEVT_TLB_PROT_ST */
116 "address error (load)", /* 0e0 EXPEVT_ADDR_ERR_LD */
117 "address error (store)", /* 100 EXPEVT_ADDR_ERR_ST */
118 "FPU", /* 120 EXPEVT_FPU */
119 NULL, /* 140 (reset vector) */
120 "unconditional trap (TRAPA)", /* 160 EXPEVT_TRAPA */
121 "reserved instruction code exception", /* 180 EXPEVT_RES_INST */
122 "illegal slot instruction exception", /* 1a0 EXPEVT_SLOT_INST */
123 NULL, /* 1c0 (external interrupt) */
124 "user break point trap", /* 1e0 EXPEVT_BREAK */
125 NULL, NULL, NULL, NULL, /* 200-260 */
126 NULL, NULL, NULL, NULL, /* 280-2e0 */
127 NULL, NULL, NULL, NULL, /* 300-360 */
128 NULL, NULL, NULL, NULL, /* 380-3e0 */
129 NULL, NULL, NULL, NULL, /* 400-460 */
130 NULL, NULL, NULL, NULL, /* 480-4e0 */
131 NULL, NULL, NULL, NULL, /* 500-560 */
132 NULL, NULL, NULL, NULL, /* 580-5e0 */
133 NULL, NULL, NULL, NULL, /* 600-660 */
134 NULL, NULL, NULL, NULL, /* 680-6e0 */
135 NULL, NULL, NULL, NULL, /* 700-760 */
136 NULL, NULL, NULL, NULL, /* 780-7e0 */
137 "FPU disabled", /* 800 EXPEVT_FPU_DISABLE */
138 "slot FPU disabled" /* 820 EXPEVT_FPU_SLOT_DISABLE */
139 };
140 const int exp_types = sizeof exp_type / sizeof exp_type[0];
141
142 void general_exception(struct proc *, struct trapframe *, uint32_t);
143 void tlb_exception(struct proc *, struct trapframe *, uint32_t);
144 void ast(struct proc *, struct trapframe *);
145 void syscall(struct proc *, struct trapframe *);
146 void cachectl(struct proc *, struct trapframe *);
147
148 /*
149 * void general_exception(struct proc *p, struct trapframe *tf):
150 * p ... curproc when exception occurred.
151 * tf ... full user context.
152 * va ... fault va for user mode EXPEVT_ADDR_ERR_{LD,ST}
153 */
154 void
general_exception(struct proc * p,struct trapframe * tf,uint32_t va)155 general_exception(struct proc *p, struct trapframe *tf, uint32_t va)
156 {
157 int expevt = tf->tf_expevt;
158 int tra = _reg_read_4(SH_(TRA));
159 int usermode = !KERNELMODE(tf->tf_ssr);
160 union sigval sv;
161
162 uvmexp.traps++;
163
164 /*
165 * This function is entered at splhigh. Restore the interrupt
166 * level to what it was when the trap occurred.
167 */
168 splx(tf->tf_ssr & PSL_IMASK);
169
170 if (usermode) {
171 if (p == NULL)
172 goto do_panic;
173 KDASSERT(p->p_md.md_regs == tf); /* check exception depth */
174 expevt |= EXP_USER;
175 refreshcreds(p);
176 }
177
178 switch (expevt) {
179 case EXPEVT_BREAK:
180 #ifdef DDB
181 if (db_ktrap(EXPEVT_BREAK, 0, tf))
182 return;
183 else
184 #endif
185 goto do_panic;
186 break;
187 case EXPEVT_TRAPA:
188 #ifdef DDB
189 /* Check for ddb request */
190 if (tra == (_SH_TRA_BREAK << 2) &&
191 db_ktrap(expevt, tra, tf))
192 return;
193 else
194 #endif
195 goto do_panic;
196 break;
197 case EXPEVT_TRAPA | EXP_USER:
198 /* Check for debugger break */
199 switch (tra) {
200 case _SH_TRA_BREAK << 2:
201 tf->tf_spc -= 2; /* back to the breakpoint address */
202 sv.sival_ptr = (void *)tf->tf_spc;
203 trapsignal(p, SIGTRAP, expevt & ~EXP_USER, TRAP_BRKPT,
204 sv);
205 goto out;
206 case _SH_TRA_SYSCALL << 2:
207 syscall(p, tf);
208 return;
209 case _SH_TRA_CACHECTL << 2:
210 cachectl(p, tf);
211 return;
212 default:
213 sv.sival_ptr = (void *)tf->tf_spc;
214 trapsignal(p, SIGILL, expevt & ~EXP_USER, ILL_ILLTRP,
215 sv);
216 goto out;
217 }
218 break;
219
220 case EXPEVT_ADDR_ERR_LD: /* FALLTHROUGH */
221 case EXPEVT_ADDR_ERR_ST:
222 KDASSERT(p && p->p_md.md_pcb->pcb_onfault != NULL);
223 if (p == NULL || p->p_md.md_pcb->pcb_onfault == 0)
224 goto do_panic;
225 tf->tf_spc = (int)p->p_md.md_pcb->pcb_onfault;
226 break;
227
228 case EXPEVT_ADDR_ERR_LD | EXP_USER: /* FALLTHROUGH */
229 case EXPEVT_ADDR_ERR_ST | EXP_USER:
230 sv.sival_ptr = (void *)va;
231 if (((int)va) < 0)
232 trapsignal(p, SIGSEGV, expevt & ~EXP_USER, SEGV_ACCERR,
233 sv);
234 else
235 trapsignal(p, SIGBUS, expevt & ~EXP_USER, BUS_ADRALN,
236 sv);
237 goto out;
238
239 case EXPEVT_RES_INST | EXP_USER: /* FALLTHROUGH */
240 case EXPEVT_SLOT_INST | EXP_USER:
241 sv.sival_ptr = (void *)tf->tf_spc;
242 trapsignal(p, SIGILL, expevt & ~EXP_USER, ILL_ILLOPC, sv);
243 goto out;
244
245 case EXPEVT_BREAK | EXP_USER:
246 sv.sival_ptr = (void *)tf->tf_spc;
247 trapsignal(p, SIGTRAP, expevt & ~EXP_USER, TRAP_TRACE, sv);
248 goto out;
249
250 #ifdef SH4
251 case EXPEVT_FPU_DISABLE | EXP_USER: /* FALLTHROUGH */
252 case EXPEVT_FPU_SLOT_DISABLE | EXP_USER:
253 sv.sival_ptr = (void *)tf->tf_spc;
254 trapsignal(p, SIGILL, expevt & ~EXP_USER, ILL_COPROC, sv);
255 goto out;
256
257 case EXPEVT_FPU | EXP_USER:
258 {
259 int fpscr, sigi;
260
261 /* XXX worth putting in the trapframe? */
262 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
263 fpscr = (fpscr & FPSCR_CAUSE_MASK) >> FPSCR_CAUSE_SHIFT;
264 if (fpscr & FPEXC_E)
265 sigi = FPE_FLTINV; /* XXX any better value? */
266 else if (fpscr & FPEXC_V)
267 sigi = FPE_FLTINV;
268 else if (fpscr & FPEXC_Z)
269 sigi = FPE_FLTDIV;
270 else if (fpscr & FPEXC_O)
271 sigi = FPE_FLTOVF;
272 else if (fpscr & FPEXC_U)
273 sigi = FPE_FLTUND;
274 else if (fpscr & FPEXC_I)
275 sigi = FPE_FLTRES;
276 else
277 sigi = 0; /* shouldn't happen */
278 sv.sival_ptr = (void *)tf->tf_spc;
279 trapsignal(p, SIGFPE, expevt & ~EXP_USER, sigi, sv);
280 }
281 goto out;
282 #endif
283
284 default:
285 goto do_panic;
286 }
287
288 if (!usermode)
289 return;
290 out:
291 userret(p);
292 return;
293
294 do_panic:
295 if ((expevt >> 5) < exp_types && exp_type[expevt >> 5] != NULL)
296 printf("fatal %s", exp_type[expevt >> 5]);
297 else
298 printf("EXPEVT 0x%03x", expevt);
299 printf(" in %s mode\n", expevt & EXP_USER ? "user" : "kernel");
300 printf("va 0x%x spc 0x%x ssr 0x%x pr 0x%x \n",
301 va, tf->tf_spc, tf->tf_ssr, tf->tf_pr);
302
303 panic("general_exception");
304 /* NOTREACHED */
305 }
306
307
308 /*
309 * void tlb_exception(struct proc *p, struct trapframe *tf, uint32_t va):
310 * p ... curproc when exception occurred.
311 * tf ... full user context.
312 * va ... fault address.
313 */
314 void
tlb_exception(struct proc * p,struct trapframe * tf,uint32_t va)315 tlb_exception(struct proc *p, struct trapframe *tf, uint32_t va)
316 {
317 struct vm_map *map;
318 pmap_t pmap;
319 union sigval sv;
320 int usermode;
321 int err, track, access_type;
322 const char *panic_msg;
323
324 #define TLB_ASSERT(assert, msg) \
325 do { \
326 if (!(assert)) { \
327 panic_msg = msg; \
328 goto tlb_panic; \
329 } \
330 } while(/*CONSTCOND*/0)
331
332 /*
333 * This function is entered at splhigh. Restore the interrupt
334 * level to what it was when the trap occurred.
335 */
336 splx(tf->tf_ssr & PSL_IMASK);
337
338 usermode = !KERNELMODE(tf->tf_ssr);
339 if (usermode) {
340 KDASSERT(p->p_md.md_regs == tf);
341 refreshcreds(p);
342 } else {
343 KDASSERT(p == NULL || /* idle */
344 p == &proc0 || /* kthread */
345 p->p_md.md_regs != tf); /* other */
346 }
347
348 switch (tf->tf_expevt) {
349 case EXPEVT_TLB_MISS_LD:
350 track = PG_PMAP_REF;
351 access_type = PROT_READ;
352 break;
353 case EXPEVT_TLB_MISS_ST:
354 track = PG_PMAP_REF;
355 access_type = PROT_WRITE;
356 break;
357 case EXPEVT_TLB_MOD:
358 track = PG_PMAP_REF | PG_PMAP_MOD;
359 access_type = PROT_WRITE;
360 break;
361 case EXPEVT_TLB_PROT_LD:
362 TLB_ASSERT((int)va > 0,
363 "kernel virtual protection fault (load)");
364 if (usermode) {
365 sv.sival_ptr = (void *)va;
366 trapsignal(p, SIGSEGV, tf->tf_expevt, SEGV_ACCERR, sv);
367 goto out;
368 } else {
369 TLB_ASSERT(p->p_md.md_pcb->pcb_onfault != NULL,
370 "no copyin/out fault handler (load protection)");
371 tf->tf_spc = (int)p->p_md.md_pcb->pcb_onfault;
372 }
373 return;
374
375 case EXPEVT_TLB_PROT_ST:
376 track = 0; /* call uvm_fault first. (COW) */
377 access_type = PROT_WRITE;
378 break;
379
380 default:
381 TLB_ASSERT(0, "impossible expevt");
382 }
383
384 /* Select address space */
385 if (usermode) {
386 if (!uvm_map_inentry(p, &p->p_spinentry, PROC_STACK(p),
387 "[%s]%d/%d sp=%lx inside %lx-%lx: not MAP_STACK\n",
388 uvm_map_inentry_sp, p->p_vmspace->vm_map.sserial))
389 goto out;
390
391 TLB_ASSERT(p != NULL, "no curproc");
392 map = &p->p_vmspace->vm_map;
393 pmap = map->pmap;
394 } else {
395 if ((int)va < 0) {
396 map = kernel_map;
397 pmap = pmap_kernel();
398 } else {
399 TLB_ASSERT(p != NULL &&
400 p->p_md.md_pcb->pcb_onfault != NULL,
401 "invalid user-space access from kernel mode");
402 if (va == 0) {
403 tf->tf_spc = (int)p->p_md.md_pcb->pcb_onfault;
404 return;
405 }
406 map = &p->p_vmspace->vm_map;
407 pmap = map->pmap;
408 }
409 }
410
411 /* Lookup page table. if entry found, load it. */
412 if (track && __pmap_pte_load(pmap, va, track)) {
413 if (usermode)
414 userret(p);
415 return;
416 }
417
418 err = uvm_fault(map, va, 0, access_type);
419 if (usermode && access_type == PROT_READ && err == EACCES) {
420 access_type = PROT_EXEC;
421 err = uvm_fault(map, va, 0, access_type);
422 }
423
424 /* User stack extension */
425 if (err == 0 && map != kernel_map)
426 uvm_grow(p, va);
427
428 /* Page in. load PTE to TLB. */
429 if (err == 0) {
430 int loaded = __pmap_pte_load(pmap, va, track);
431 TLB_ASSERT(loaded, "page table entry not found");
432 if (usermode)
433 userret(p);
434 return;
435 }
436
437 /* Page not found. */
438 if (usermode) {
439 sv.sival_ptr = (void *)va;
440 if (err == ENOMEM) {
441 printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
442 p->p_p->ps_pid, p->p_p->ps_comm,
443 p->p_ucred ? (int)p->p_ucred->cr_uid : -1);
444 trapsignal(p, SIGKILL, tf->tf_expevt, SEGV_MAPERR, sv);
445 } else
446 trapsignal(p, SIGSEGV, tf->tf_expevt, SEGV_MAPERR, sv);
447 goto out;
448 } else {
449 TLB_ASSERT(p->p_md.md_pcb->pcb_onfault,
450 "no copyin/out fault handler (page not found)");
451 tf->tf_spc = (int)p->p_md.md_pcb->pcb_onfault;
452 }
453 return;
454
455 out:
456 userret(p);
457 ast(p, tf);
458 return;
459
460 tlb_panic:
461 panic("tlb_exception: %s\n"
462 "expevt=%x va=%08x ssr=%08x spc=%08x proc=%p onfault=%p",
463 panic_msg, tf->tf_expevt, va, tf->tf_ssr, tf->tf_spc,
464 p, p ? p->p_md.md_pcb->pcb_onfault : NULL);
465 #undef TLB_ASSERT
466 }
467
468
469 /*
470 * void ast(struct proc *p, struct trapframe *tf):
471 * p ... curproc when exception occurred.
472 * tf ... full user context.
473 * This is called upon exception return. if return from kernel to user,
474 * handle asynchronous software traps and context switch if needed.
475 */
476 void
ast(struct proc * p,struct trapframe * tf)477 ast(struct proc *p, struct trapframe *tf)
478 {
479 if (KERNELMODE(tf->tf_ssr))
480 return;
481 KDASSERT(p != NULL);
482 KDASSERT(p->p_md.md_regs == tf);
483
484 while (p->p_md.md_astpending) {
485 p->p_md.md_astpending = 0;
486 refreshcreds(p);
487 uvmexp.softs++;
488 mi_ast(p, curcpu()->ci_want_resched);
489 userret(p);
490 }
491 }
492
493 void
cachectl(struct proc * p,struct trapframe * tf)494 cachectl(struct proc *p, struct trapframe *tf)
495 {
496 vaddr_t va;
497 vsize_t len;
498
499 if (!SH_HAS_UNIFIED_CACHE) {
500 va = (vaddr_t)tf->tf_r4;
501 len = (vsize_t)tf->tf_r5;
502
503 if (va < VM_MIN_ADDRESS || va >= VM_MAXUSER_ADDRESS ||
504 va + len <= va || va + len >= VM_MAXUSER_ADDRESS)
505 len = 0;
506
507 if (len != 0)
508 sh_icache_sync_range_index(va, len);
509 }
510
511 userret(p);
512 }
513
514 void
syscall(struct proc * p,struct trapframe * tf)515 syscall(struct proc *p, struct trapframe *tf)
516 {
517 caddr_t params;
518 const struct sysent *callp = sysent;
519 int error, opc;
520 int argsize;
521 register_t code, args[8], rval[2];
522
523 uvmexp.syscalls++;
524
525 opc = tf->tf_spc;
526 params = (caddr_t)tf->tf_r15;
527
528 code = tf->tf_r0;
529 // XXX out of range stays on syscall0, which we assume is enosys
530 if (code > 0 && code < SYS_MAXSYSCALL)
531 callp += code;
532
533 argsize = callp->sy_argsize;
534
535 if (argsize) {
536 register_t *ap;
537 int off_t_arg;
538
539 switch (code) {
540 default: off_t_arg = 0; break;
541 case SYS_lseek:
542 case SYS_truncate:
543 case SYS_ftruncate: off_t_arg = 1; break;
544 case SYS_preadv:
545 case SYS_pwritev:
546 case SYS_pread:
547 case SYS_pwrite: off_t_arg = 3; break;
548 }
549
550 ap = args;
551
552 *ap++ = tf->tf_r4; argsize -= sizeof(int);
553 *ap++ = tf->tf_r5; argsize -= sizeof(int);
554 *ap++ = tf->tf_r6; argsize -= sizeof(int);
555 /*
556 * off_t args aren't split between register
557 * and stack, but rather r7 is skipped and
558 * the entire off_t is on the stack.
559 */
560 if (off_t_arg != 3) {
561 *ap++ = tf->tf_r7;
562 argsize -= sizeof(int);
563 }
564
565 if (argsize > 0) {
566 if ((error = copyin(params, ap, argsize)))
567 goto bad;
568 }
569 }
570
571 rval[0] = 0;
572 rval[1] = tf->tf_r1;
573
574 error = mi_syscall(p, code, callp, args, rval);
575
576 switch (error) {
577 case 0:
578 tf->tf_r0 = rval[0];
579 tf->tf_r1 = rval[1];
580 tf->tf_ssr |= PSL_TBIT; /* T bit */
581 break;
582 case ERESTART:
583 /* 2 = TRAPA instruction size */
584 tf->tf_spc = opc - 2;
585 break;
586 case EJUSTRETURN:
587 /* nothing to do */
588 break;
589 default:
590 bad:
591 tf->tf_r0 = error;
592 tf->tf_ssr &= ~PSL_TBIT; /* T bit */
593 break;
594 }
595
596 mi_syscall_return(p, code, error, rval);
597 }
598
599 /*
600 * void child_return(void *arg):
601 *
602 * uvm_fork sets this routine to proc_trampoline's service function.
603 * when returning from here, jump to userland.
604 */
605 void
child_return(void * arg)606 child_return(void *arg)
607 {
608 struct proc *p = arg;
609 struct trapframe *tf = p->p_md.md_regs;
610
611 tf->tf_r0 = 0;
612 tf->tf_ssr |= PSL_TBIT; /* This indicates no error. */
613
614 mi_child_return(p);
615 }
616
617