home *** CD-ROM | disk | FTP | other *** search
- head 1.3;
- access;
- symbols
- version39-41:1.2;
- locks;
- comment @ * @;
-
-
- 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.3
- log
- @add volatile to sysbase access, or the optimizer takes illegal shortcuts...
- @
- text
- @/*
- * This file is part of ixemul.library for the Amiga.
- * Copyright (C) 1991, 1992 Markus M. Wild
- *
- * 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.2 1992/07/04 19:20:20 mwild Exp $
- *
- * $Log: machdep.c,v $
- * 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 <exec/execbase.h>
-
- /* #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 */
- extern void volatile restore_00 (), restore_20 ();
-
- /* 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 sup20_do_sigreturn_ssp (u_int ssp);
- /*
- * These two work the same, but they don't touch the ssp
- */
- extern void volatile sup00_do_sigreturn (void);
- extern void volatile sup20_do_sigreturn (void);
-
- /*
- * 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 (SysBase->AttnFlags & AFF_68020)
- {
- 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;
-
- /* 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;
-
- if (((p->p_sigignore & sigmsg) || !(p->p_sigcatch & sigmsg))
- && (newsigs & SIGBREAKF_CTRL_C))
- {
- DP((" found SIGBREAKF_CTRL_C, sending us SIGINT\n"));
- /* 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))
- {
- DP((" generating SIGMSG\n"));
- 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))
- {
- DP((" $%lx processing signal %ld.\n", p, i));
- 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;
-
- DP(("sendsig (p=$%lx, sig=%ld, mask=$%lx)\n", p, sig, mask));
-
- 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
- */
-
- DP(("SIG_EXIT:"));
-
- /* 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;
-
- DP((" cli-proc, is_fg = %ld, name = %s.\n", is_fg, process_name));
- }
- 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;
-
- DP((" wb-proc, name = %s\n", process_name));
- }
-
- /* 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");
-
- DP((" err_buf = %s\n", err_buf));
-
- /* 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");
- DP((" 2err_buf = %s\n", err_buf));
- 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;
- /* DP(("setrun ()\n")); */
-
- #if 0
- Signal (t, (1 << p->u_sleep_sig));
-
- if (SysBase->ThisTask != t) return;
- #endif
-
- /* 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-.+2),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");
-
- #if 0
- if (sr & 0x2000) return; /* if in Supervisor, don't do this... */
- #else
- /* 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 ||
- SysBase->TDNestCnt >= 0 || SysBase->IDNestCnt >= 0)
- {
- if (p->p_wchan == (caddr_t) p)
- {
- DP (("setrun $%lx\n", p));
- ix_wakeup (p);
- }
- return;
- }
- #endif
-
- DP(("setrun: forcing context switch\n"));
- 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;
-
- extern void restore_20();
-
- usp = orig_usp = get_usp () + 8; /* skip argument parameters */
- ret_pc = ((u_int *)usp)[-2];
- ret_ssp = ((u_int *)usp)[-1];
-
- DP(("trap: format = $%lx, addr = $%lx, ret_pc = $%lx, ret_ssp = $%lx, rst20 = $%lx\n", format, addr, ret_pc, ret_ssp, restore_20));
-
- /* 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.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 ||
- @
-