head 1.6; access; symbols version39-45:1.4 version39-41:1.2; locks rluebbert:1.6; comment @ * @; 1.6 date 94.06.19.15.13.50; author rluebbert; state Exp; branches; next 1.5; 1.5 date 93.11.05.21.59.18; author mwild; state Exp; branches; next 1.4; 1.4 date 92.10.20.16.25.24; author mwild; state Exp; branches; next 1.3; 1.3 date 92.08.09.20.57.59; author amiga; state Exp; branches; next 1.2; 1.2 date 92.07.04.19.20.20; author mwild; state Exp; branches; next 1.1; 1.1 date 92.05.14.19.55.40; author mwild; state Exp; branches; next ; desc @lowlevel signal code @ 1.6 log @*** empty log message *** @ text @/* * This file is part of ixemul.library for the Amiga. * Copyright (C) 1991, 1992 Markus M. Wild * Portions Copyright (C) 1994 Rafael W. Luebbert * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: machdep.c,v 1.4 1992/10/20 16:25:24 mwild Exp $ * * $Log: machdep.c,v $ * Revision 1.4 1992/10/20 16:25:24 mwild * no nasty 'c' polling in DEF-signalhandler... * * Revision 1.3 1992/08/09 20:57:59 amiga * add volatile to sysbase access, or the optimizer takes illegal shortcuts... * * Revision 1.2 1992/07/04 19:20:20 mwild * add yet another state in which not to force a context switch. * Probably unnecessary paranoia... * * Revision 1.1 1992/05/14 19:55:40 mwild * Initial revision * */ #define KERNEL #include "ixemul.h" #include #undef DEBUG #ifdef DEBUG #define DP(a) KPrintF a #else #define DP(a) #endif extern struct ExecBase *SysBase; /* jump to pc in supervisor mode, usp is set to USP before */ extern void volatile supervisor (u_int pc, u_int usp); /* context restore functions for 68000 and 68020 rsp */ /* takes the sigcontext * from the usp and restores it * Assumes it's called by Supervisor(), ie. with an exception frame */ extern void volatile do_sigreturn (void); /* * These two are callable with jsr from supervisor mode, and then * set up a fake exception frame and call do_sigreturn(). */ extern void volatile sup00_do_sigreturn_ssp (u_int ssp); extern void volatile sup00_do_sigreturn (void); extern void volatile restore_00 (); extern void volatile sup20_do_sigreturn_ssp (u_int ssp); extern void volatile sup20_do_sigreturn (void); extern void volatile restore_20 (); /* * Either one of sup{00,20}_do_sigreturn, set by configure_context_switch (); */ static void volatile (*sup_do_sigreturn) (void); static void volatile (*sup_do_sigreturn_ssp) (u_int ssp); void setrun (struct Task *t); void sendsig(struct user *p, sig_t catcher, int sig, int mask, unsigned code, void *addr); void sig_exit (); /* * NOTE: this variable belongs to ixemulbase, not to the current task. But * due to the nature of launch/switch, we can access them here without * locking. It quite a natural thing to do actually ;-) */ struct user *curproc; struct sigframe { int sf_signum; /* signo for handler */ int sf_code; /* additional info for handler */ void *sf_addr; /* yet another info for handler ;-)) */ sig_t sf_handler; /* handler addr for u_sigc */ struct sigcontext sf_sc; /* actual context */ }; void configure_context_switch () { if(betterthan68000()) { sup_do_sigreturn = sup20_do_sigreturn; sup_do_sigreturn_ssp = sup20_do_sigreturn_ssp; } else { sup_do_sigreturn = sup00_do_sigreturn; sup_do_sigreturn_ssp = sup00_do_sigreturn_ssp; } } void volatile sigreturn (struct sigcontext *sc) { supervisor ((u_int) do_sigreturn, (u_int) sc); } void volatile sig_trampoline (struct sigframe sf) { if (u.u_a4) asm ("movel %0,a4" : : "g" (u.u_a4)); sf.sf_handler (sf.sf_signum, sf.sf_code, sf.sf_addr, & sf.sf_sc); sigreturn (& sf.sf_sc); } /* * This one is executed in Supervisor mode, just before dispatching this * task, so be as quick as possible here ! */ void sig_launch () { struct ExecBase *SysBase = *(struct ExecBase **) 4; struct Task *me = SysBase->ThisTask; /* precalculate struct user, so we don't have to go thru SysBase all the time */ struct user *p = (struct user *) me->tc_TrapData; sigset_t sigmsg = sigmask (SIGMSG); sigset_t sigint = sigmask (SIGINT); sigset_t newsigs; int i; u_int usp, orig_usp; struct sigcontext *sc; u_int ret_pc, ret_ssp; usp = orig_usp = get_usp () + 8; /* set up by our glue_launch() stub */ /* remember who we are */ curproc = p; /* if we're inside ix_sleep, no signal processing is done to break the Wait there as soon as possible. Signals resume on return of ix_sleep */ if (p->p_stat == SSLEEP) return; /* special processing for Wait()ing in Commodore inet.library. They do reasonable interrupt checking, but only on SIGBREAKF_CTRL_C. So whenever we have a signal to deliver, send ^C.. */ if (p->p_stat == SWAIT) { #if 0 /* not working clean yet, have to think of a more elaborate solution later */ /* handle ^C here, since we're going to unconditionally raise it afterwards. */ if (!(p->p_sigignore & sigmask(SIGINT)) && SetSignal (0, SIGBREAKF_CTRL_C)) p->p_sig |= sigmask (SIGINT); #endif if (CURSIG (p)) Signal (me, SIGBREAKF_CTRL_C); return; } /* smells kludgy I know...... */ if (me->tc_TDNestCnt >= 0 || me->tc_IDNestCnt >= 0) return; /* the glue passes us the values of the pc and ssp to restore, if we should * decide to sup_do_sigreturn_ssp() out of here, instead of leaving harmlessly.. */ ret_pc = ((u_int *)usp)[-2]; ret_ssp = ((u_int *)usp)[-1]; /* push a sigcontext that will get us back if no other signals * were produced */ usp -= sizeof (struct sigcontext); sc = (struct sigcontext *) usp; set_usp (usp); sc->sc_onstack = p->u_onstack; sc->sc_mask = p->p_sigmask; sc->sc_sp = orig_usp; /* the OS context restore function expects a5 to contain the usp, so * we have to obey.. */ sc->sc_fp = orig_usp; sc->sc_ap = *(u_int *)&me->tc_Flags; sc->sc_pc = ret_pc; sc->sc_ps = get_sr (); /* * first check amigados signals. If SIGMSG is set to SIG_IGN or SIG_DFL, * we do our default mapping of SIGBREAKF_CTRL_C into SIGINT. */ newsigs = me->tc_SigRecvd &~ p->u_lastrcvsig; p->u_lastrcvsig = me->tc_SigRecvd; #ifdef SOCKET if (p->u_InetBase) { int urgmask = 1<u_sigurg; int iomask = 1<u_sigio; if (newsigs & urgmask) { if (! (p->p_sigignore & sigmask (SIGURG))) _psignal (me, SIGURG); me->tc_SigRecvd &= ~urgmask; p->u_lastrcvsig &= ~urgmask; } if (newsigs & iomask) { if (! (p->p_sigignore & sigmask (SIGIO))) _psignal (me, SIGIO); me->tc_SigRecvd &= ~iomask; p->u_lastrcvsig &= ~iomask; } } #endif if (((p->p_sigignore & sigmsg) || !(p->p_sigcatch & sigmsg)) && (newsigs & SIGBREAKF_CTRL_C)) { /* in that case send us a SIGINT, if it's not ignored */ if (!(p->p_sigignore & sigint)) _psignal (me, SIGINT); /* in this mode we fully handle and use SIGBREAKF_CTRL_C, so remove it * from the Exec signal mask */ me->tc_SigRecvd &= ~SIGBREAKF_CTRL_C; p->u_lastrcvsig &= ~SIGBREAKF_CTRL_C; } else if (newsigs && (p->p_sigcatch & sigmsg)) { /* if possible, deliver the signal directly to get a code argument */ if (!(p->p_flag & STRC) && !(p->p_sigmask & sigmsg)) { p->u_ru.ru_nsignals++; sendsig(p, p->u_signal[SIGMSG], SIGMSG, p->p_sigmask, newsigs, 0); p->p_sigmask |= p->u_sigmask[SIGMSG] | sigmsg; setrun (me); } else _psignal (me, SIGMSG); } /* stack watch.. NEVER do this when vforked, the sp is out of bounds then.. */ if (ix.ix_watch_stack && p->u_red_zone && ! p->p_vfork_msg && ! p->u_onstack) { if (usp < p->u_red_zone) _psignal (me, SIGSEGV); } if (i = CURSIG(p)) psig (p, i); /* now try to optimize. We could always call sup_do_sigreturn here, but if no * signals generated frames, we can just as well simply return, after having * restored our usp */ if (usp == get_usp ()) { /* this is probably not even necessary, since after processing sig_launch * the OS reinstalls the usp as me->tc_SPReg, but I guess it's cleaner to * do it explicitly here, to show that we reset usp to what it was before */ set_usp (orig_usp); return; } sup_do_sigreturn_ssp (ret_ssp); } /* * nothing magic about this... when leaving, we note that no process is currently * executing (at least none under our control ;-)) */ void switch_glue () { curproc = 0; } /* * Send an interrupt to process. * Called from psig() which is called from sig_launch, thus we are in * SUPERVISOR . */ void sendsig (struct user *p, sig_t catcher, int sig, int mask, unsigned code, void *addr) { struct ExecBase *SysBase = *(struct ExecBase **) 4; struct Task *me = SysBase->ThisTask; u_int usp, orig_usp; struct sigframe *sf; struct sigcontext *sc; int oonstack; orig_usp = get_usp(); /* get value to restore later */ oonstack = p->u_onstack; if (!p->u_onstack && (p->u_sigonstack & sigmask(sig))) { p->u_onstack = 1; usp = (u_int) p->u_sigsp; } else usp = orig_usp; /* push signal frame */ usp -= sizeof (struct sigframe); sf = (struct sigframe *) usp; /* fill out the frame */ sf->sf_signum = sig; sf->sf_code = code; sf->sf_addr = addr; sf->sf_handler = catcher; sf->sf_sc.sc_onstack = oonstack; sf->sf_sc.sc_mask = mask; sf->sf_sc.sc_sp = (int) orig_usp; /* previous sigcontext */ sf->sf_sc.sc_fp = 0; sf->sf_sc.sc_ap = *(u_int *)&me->tc_Flags; sf->sf_sc.sc_ps = get_sr (); /* we're in supervisor then */ sf->sf_sc.sc_pc = (int) sup_do_sigreturn;/* this pc will restore it */ /* push a signal context to call sig_trampoline */ usp -= sizeof (struct sigcontext); sc = (struct sigcontext *) usp; /* * NOTE: we set the default of a handler to Permit(), Enable(). I guess this * makes sense, since if either Forbid() or Disable() is active, it * shouldn't be possible to invoke a signal anyway, EXCEPT if the * task is Wait()ing, then the OS calls Switch() directly while * either Disable() or Forbid() is active (depends on OS version). */ sc->sc_onstack = p->u_onstack; sc->sc_mask = p->p_sigmask; sc->sc_sp = ((int) sf) - 4; /* so that sp@@(4) is the argument */ sc->sc_fp = 0; sc->sc_ap = (me->tc_Flags << 24) | (me->tc_State << 16) | ((u_char)(-1) << 8) | (u_char)(-1); sc->sc_ps = 0; /* thus we switch into user-mode now! */ sc->sc_pc = (int) sig_trampoline; set_usp (usp); } /* * called as the default action of a signal that terminates the process */ void sig_exit (unsigned int code) { #ifndef QUIET_SIGEXIT /* the whole purpose of this code inside `ifndef QUIET_SIGEXIT' is to * prettyprint and identify the job that just terminates * This stuff should be handled by a shell, but since there's (yet) no * shell that knows how to interpret a signal-exit code (ored with 0x80) * I have to do it here myself... */ extern char *sys_siglist[NSIG]; struct Process *me = (struct Process *)((*(struct ExecBase **)4)->ThisTask); char err_buf[255]; struct CommandLineInterface *cli; char process_name[255]; int is_fg; /* output differs depending on * o whether we're a CLI or a WB process (stderr or requester) * o whether this is a foreground or background process * o whether this is SIGINT or not */ /* make sure we're not interrupted in this last step.. */ syscall (SYS_sigsetmask, ~0); if (cli = BTOCPTR (me->pr_CLI)) { char *tmp = BTOCPTR (cli->cli_CommandName); int len = *tmp++; if (len > sizeof (process_name) - 1) len = sizeof (process_name) - 1; bcopy (tmp, process_name, len); process_name[len] = 0; is_fg = cli->cli_Interactive && !cli->cli_Background; } else { process_name[0] = 0; if (me->pr_Task.tc_Node.ln_Name) strncpy (process_name, me->pr_Task.tc_Node.ln_Name, sizeof (process_name) - 1); /* no WB process is ever considered fg */ is_fg = 0; } /* if is_fg and SIGINT, simulate tty-driver and display ^C */ if (!(is_fg && (code == SIGINT))) { strcpy (err_buf, (code < NSIG) ? sys_siglist[code] : "Unknown signal"); /* if is_fg, don't display the job */ if (! is_fg) { strcat (err_buf, " - "); strcat (err_buf, process_name); /* if we're a CLI we have an argument line saved, that we can print * as well */ if (cli) { int line_len; char *cp; /* we can display upto column 77, this should be save on all normal * amiga CLI windows */ line_len = 77 - strlen (err_buf) - 1; if (line_len > u.u_arglinelen) line_len = u.u_arglinelen; if (line_len > 0 && u.u_argline) { strcat (err_buf, " "); strncat (err_buf, u.u_argline, line_len); } /* now get rid of possible terminating line feeds/cr's */ for (cp = err_buf; *cp && *cp != '\n' && *cp != '\r'; cp++) ; *cp = 0; } } if (cli) { /* uniformly append ONE line feed */ strcat (err_buf, "\n"); syscall (SYS_write, 2, err_buf, strlen (err_buf)); } else ix_panic (err_buf); } else syscall (SYS_write, 2, "^C\n", 3); #endif syscall (SYS_exit, code | 0x80); /* not reached */ } /* * This is used to awaken a possibly sleeping sigsuspend() * and to force a context switch, if we send a signal to ourselves */ void setrun (struct Task *t) { struct user *p = (struct user *) t->tc_TrapData; volatile struct ExecBase *SysBase = *(volatile struct ExecBase **)4; u_int curr_disp; u_int sr; /* NOTE: the context switch is done to make sure sig_launch() is called as * soon as possible in the respective task. It's not nice if you can * return from a kill() to yourself, before the signal handler had a * chance to react accordingly to the signal.. */ asm volatile (" movel a5,a0 lea pc@@(Lget_sr),a5 movel 4:w,a6 jsr a6@@(-0x1e) movel a1,%0 bra Lskip Lget_sr: movew sp@@,a1 | get sr register from the calling function rte Lskip: movel a0,a5 " : "=g" (sr) : : "a0", "a1", "a6"); /* Don't force context switch if: o running in Supervisor mode o we setrun() some other process o running under either Forbid() or Disable() */ if ((sr & 0x2000) || SysBase->ThisTask != t || p->p_stat == SSLEEP || p->p_stat == SWAIT || SysBase->TDNestCnt >= 0 || SysBase->IDNestCnt >= 0) { extern int select(); /* make testing of p_stat and reaction atomic */ Disable (); if (p->p_stat == SWAIT) Signal (t, SIGBREAKF_CTRL_C); else if (p->p_wchan == (caddr_t) p) { DP (("setrun $%lx\n", p)); ix_wakeup (p); } else if (p->p_wchan == (caddr_t) select) Signal (t, 1<u_sleep_sig); Enable (); return; } for (curr_disp = SysBase->DispCount; curr_disp == SysBase->DispCount; ) ; /* this quite brute-force method, but the only thing I could think of that * really guarantees that there was a context switch.. */ } /* * Mapping from vector numbers into signals */ const static int hwtraptable[256] = { SIGILL, /* Reset initial stack pointer */ SIGILL, /* Reset initial program counter */ SIGBUS, /* Bus Error */ SIGBUS, /* Address Error */ SIGILL, /* Illegal Instruction */ SIGFPE, /* Zero Divide */ SIGFPE, /* CHK, CHK2 Instruction */ SIGFPE, /* cpTRAPcc, TRAPcc, TRAPV Instruction */ SIGILL, /* Privilege Violation */ SIGTRAP,/* Trace */ SIGEMT, /* Line 1010 Emulator */ SIGEMT, /* Line 1111 Emulator */ SIGILL, SIGILL, /* Coprocessor Protocol Violation */ SIGILL, /* Format Error */ SIGILL, /* Uninitialized Interrupt */ SIGILL, /* 16 */ SIGILL, /* 17 */ SIGILL, /* 18 */ SIGILL, /* 19 */ /* unimplemented, reserved */ SIGILL, /* 20 */ SIGILL, /* 21 */ SIGILL, /* 22 */ SIGILL, /* 23 */ SIGILL, /* spurious Interrupt */ SIGILL, /* Level 1 Interrupt Autovector */ SIGILL, /* Level 2 Interrupt Autovector */ SIGILL, /* Level 3 Interrupt Autovector */ SIGILL, /* Level 4 Interrupt Autovector */ SIGILL, /* Level 5 Interrupt Autovector */ SIGILL, /* Level 6 Interrupt Autovector */ SIGILL, /* Level 7 Interrupt Autovector */ SIGILL, /* Trap #0 (not available on Unix) */ SIGILL, /* Trap #1 */ SIGILL, /* Trap #2 */ SIGILL, /* Trap #3 */ SIGILL, /* Trap #4 */ SIGILL, /* Trap #5 */ SIGILL, /* Trap #6 */ SIGILL, /* Trap #7 */ SIGILL, /* Trap #8 */ SIGILL, /* Trap #9 */ SIGILL, /* Trap #10 */ SIGILL, /* Trap #11 */ SIGILL, /* Trap #12 */ SIGILL, /* Trap #13 */ SIGILL, /* Trap #14 */ SIGILL, /* Trap #15 (not available on Unix) */ SIGFPE, /* FPCP Branch or Set on Unordererd Condition */ SIGFPE, /* FPCP Inexact Result */ SIGFPE, /* FPCP Divide by Zero */ SIGFPE, /* FPCP Underflow */ SIGFPE, /* FPCP Operand Error */ SIGFPE, /* FPCP Overflow */ SIGFPE, /* FPCP Signaling NAN */ SIGILL, SIGBUS, /* MMU Configuration Error */ SIGILL, /* MMU Illegal Operation (only 68851) */ SIGILL, /* MMU Privilege Violation (only 68851) */ /* rest undefined or free user-settable.. */ }; /* * handle traps handled over from the lowlevel trap handlers */ void trap (u_int format, void *addr) { struct ExecBase *SysBase = *(struct ExecBase **) 4; struct Task *me = SysBase->ThisTask; /* precalculate struct user, so we don't have to go thru SysBase all the time */ struct user *p = (struct user *) me->tc_TrapData; int sig; u_int usp, orig_usp; struct sigcontext *sc; #if __GNUC__ != 2 || defined(BROKEN_GCC20) int volatile been_here; #endif u_int ret_pc, ret_ssp; usp = orig_usp = get_usp () + 8; /* skip argument parameters */ ret_pc = ((u_int *)usp)[-2]; ret_ssp = ((u_int *)usp)[-1]; /* push a sigcontext that will get us back here if no other signals * were produced */ usp -= sizeof (struct sigcontext); sc = (struct sigcontext *) usp; set_usp (usp); sc->sc_onstack = p->u_onstack; sc->sc_mask = p->p_sigmask; sc->sc_sp = orig_usp; sc->sc_fp = get_fp (); sc->sc_ap = *(u_int *)&me->tc_Flags; sc->sc_pc = ret_pc; sc->sc_ps = get_sr (); /* format contains the vector * 4, in the lower 12 bits */ sig = *(int *)((u_char *)hwtraptable + (format & 0x0fff)); trapsignal (me, sig, format, addr); if (sig = CURSIG(p)) psig (p, sig); /* now try to optimize. We could always call sup_do_sigreturn here, but if no * signals generated frames, we can just as well simply return, after having * restored our usp */ if (usp == get_usp ()) { set_usp (orig_usp); return; } sup_do_sigreturn_ssp (ret_ssp); } @ 1.5 log @add code to deal with inet.library @ text @d4 1 d42 1 a42 2 /* #undef DEBUG */ d44 1 a44 1 #define DP(a) kprintf a a54 1 extern void volatile restore_00 (), restore_20 (); d66 2 a68 4 /* * These two work the same, but they don't touch the ssp */ extern void volatile sup00_do_sigreturn (void); d70 1 a70 1 d100 2 a101 2 if (SysBase->AttnFlags & AFF_68020) { d104 3 a106 3 } else { d109 1 a109 1 } a150 1 d208 1 a208 1 d233 1 a233 1 a236 1 DP((" found SIGBREAKF_CTRL_C, sending us SIGINT\n")); a251 1 DP((" generating SIGMSG\n")); a268 2 { DP((" $%lx processing signal %ld.\n", p, i)); a269 1 } a314 2 DP(("sendsig (p=$%lx, sig=%ld, mask=$%lx)\n", p, sig, mask)); a395 3 DP(("SIG_EXIT:")); /* while (kgetchar() != 'c') ;*/ a411 1 DP((" cli-proc, is_fg = %ld, name = %s.\n", is_fg, process_name)); a421 1 DP((" wb-proc, name = %s\n", process_name)); a428 1 DP((" err_buf = %s\n", err_buf)); a463 1 DP((" 2err_buf = %s\n", err_buf)); a487 7 /* DP(("setrun ()\n")); */ #if 0 Signal (t, (1 << p->u_sleep_sig)); if (SysBase->ThisTask != t) return; #endif d496 1 a496 1 lea pc@@(Lget_sr-.+2),a5 a507 3 #if 0 if (sr & 0x2000) return; /* if in Supervisor, don't do this... */ #else a532 3 #endif DP(("setrun: forcing context switch\n")); a621 1 extern void restore_20(); a626 2 DP(("trap: format = $%lx, addr = $%lx, ret_pc = $%lx, ret_ssp = $%lx, rst20 = $%lx\n", format, addr, ret_pc, ret_ssp, restore_20)); @ 1.4 log @no nasty 'c' polling in DEF-signalhandler... @ text @d19 1 a19 1 * $Id: machdep.c,v 1.3 1992/08/09 20:57:59 amiga Exp $ d22 3 d160 20 d213 25 d541 1 a541 1 SysBase->TDNestCnt >= 0 || SysBase->IDNestCnt >= 0) d543 8 a550 1 if (p->p_wchan == (caddr_t) p) d555 4 @ 1.3 log @add volatile to sysbase access, or the optimizer takes illegal shortcuts... @ text @d19 1 a19 1 * $Id: machdep.c,v 1.2 1992/07/04 19:20:20 mwild Exp $ d22 3 d360 1 @ 1.2 log @add yet another state in which not to force a context switch. Probably unnecessary paranoia... @ text @d19 1 a19 1 * $Id: machdep.c,v 1.1 1992/05/14 19:55:40 mwild Exp $ d22 4 d43 2 a96 2 struct ExecBase *SysBase = *(struct ExecBase **)4; d451 1 a451 1 struct ExecBase *SysBase = *(struct ExecBase **)4; @ 1.1 log @Initial revision @ text @d19 1 a19 1 * $Id$ d21 4 a24 1 * $Log$ d484 1 a484 1 if ((sr & 0x2000) || SysBase->ThisTask != t || @