Files
Arquivotheca.SunOS-4.1.4/usr.lib/liblwp/sparc/kernel/except.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

481 lines
13 KiB
C

/* Copyright (C) 1987 Sun Microsystems Inc. */
#include <lwp/common.h>
#include <lwp/queue.h>
#include <lwp/asynch.h>
#include <machlwp/machsig.h>
#include <machlwp/machdep.h>
#include <lwp/queue.h>
#include <lwp/cntxt.h>
#include <lwp/lwperror.h>
#include <lwp/message.h>
#include <lwp/process.h>
#include <lwp/schedule.h>
#include <lwp/alloc.h>
#include <machlwp/except.h>
#include <lwp/monitor.h>
#ifndef lint
SCCSID(@(#) except.c 1.1 94/10/31 Copyr 1987 Sun Micro);
#endif lint
/*
* PRIMITIVES contained herein:
* exc_handle(pattern, func, env)
* exc_unhandle()
* (*exc_bound(pattern, env))()
* exc_notify(pattern)
* exc_raise(process, pattern)
* exc_on_exit(func, arg)
* exc_uniqpatt()
*/
/* Globals to facilitate asm stuff. Must not be static. */
exccontext_t *__Cntxt;
int *__ProcRet;
int __ClientSp, __ClientRetAdr;
STATIC int ExcType; /* cookie for exception context caches */
/*
* exc_uniqpatt() -- PRIMITIVE.
* return a unique pattern that doesn't conflict with any
* preassigned patterns. BUGS: wraparound voids uniqueness guarantee.
* We know that 0 (EXITPATTERN), -1 (error) and -2 (CATCHALL) are
* reserved. Also, special meaning is reserved for 1..NUMSIG-1
* for synchronous traps.
* If -1 is returned, a non-unique pattern was returned.
* Subsequently, all patterns returned may be duplicates.
*/
int
exc_uniqpatt()
{
static int newpat = NUMSIG - 1;
newpat++;
if (newpat == CATCHALL) {
newpat = NUMSIG - 1;
SET_ERROR(__Curproc, LE_REUSE);
return (-1);
}
return (newpat);
}
/*
* exc_handle() -- PRIMITIVE.
* Establish an exception handler. A procedure calling
* exc_handle will be set up to return to a special
* cleanup procedure that pops all exception contexts
* before returning control to the original return point of the procedure.
* The context saved by the handler will allow exc_raise()
* to act as a non-local return to the point just after
* the call to exc_handle()
* Special context (e.g., fpa regs) is NOT saved by exc_handle.
*/
int
exc_handle(pattern, func, env)
int pattern; /* pattern to match on excraise */
caddr_t (*func)(); /* func bound to the exception for NOTIFY semantics */
caddr_t env; /* environment argument passed to func */
{
LWPINIT();
CLR_ERROR();
LOCK();
ERROR(((pattern == EXITPATTERN) || (pattern == -1)), LE_INVALIDARG);
GETCHUNK((exccontext_t *), __Cntxt, ExcType);
/* flush windows, get client return pc and stack pointer */
__exc_getclient();
__Cntxt->exc_sp = __ClientSp; /* save %sp of exc_handle caller */
/*
* Client called exc_handle.
* __ProcRet contains address where *client* will return to.
* We look on client's stack to find it.
* Note that __exc_getclient() had to flush the windows to ensure
* that the register save area of the client is current.
*/
__Cntxt->exc_pc = __ClientRetAdr;
__ProcRet = (int *)__ClientSp + PC_OFFSET;
__Cntxt->exc_pattern = pattern;
__Cntxt->exc_func = func;
__Cntxt->exc_env = env;
/*
* If the exit routine (which is installed like a breakpoint)
* isn't already established, do so by saving
* the correct return address and installing the exit routine
* exccleanup(). Otherwise, just bump the ref. count of
* exception handlers set within the current procedure.
* A ref count of 0 indicates the first handler in a procedure.
* Thus, the stack of handlers will look like (for example):
* 4 3 2 1 0 3 2 1 0 .... 1 0
* The ref count is only used to conveniently find the
* initial exception handler in a procedure.
*/
if ((*__ProcRet + 8) != (int)__exccleanup) {
__Cntxt->exc_retaddr = (int *)__ProcRet;
__Cntxt->exc_return = *__ProcRet;
__Cntxt->exc_refcnt = 0;
*__ProcRet = (int)__exccleanup;
*__ProcRet -= 8;
} else {
__Cntxt->exc_refcnt = 1 +
((exccontext_t *)FIRSTQ(&__Curproc->lwp_exchandles))->exc_refcnt;
}
FRONT_QUEUE(&__Curproc->lwp_exchandles, __Cntxt);
UNLOCK();
return (0);
}
/*
* exc_on_exit -- PRIMITIVE.
* Establish an exit handler for a procedure.
* Each procedure may establish at most one exit handler
* which will be invoked when the procedure exits (either
* via a return or as a result of an exception).
* The exit handler may not contain exc_raise's.
* Further, the argument may not contain the address of a local variable
* in the scope of the procedure.
* Note that all local context (stack) is gone when the exit handler
* is invoked. Also, any results returned by an exit handler are ignored:
* the value returned by the returning procedure is preserved.
*/
int
exc_on_exit(func, arg)
void (*func)();
caddr_t arg;
{
register exccontext_t *cntxt;
register exccontext_t *newcntxt;
LWPINIT();
CLR_ERROR();
LOCK();
GETCHUNK((exccontext_t *), newcntxt, ExcType);
newcntxt->exc_pattern = EXITPATTERN;
newcntxt->exc_func = (caddr_t (*)())func;
newcntxt->exc_env = arg;
newcntxt->exc_refcnt = 0;
__exc_getclient();
__ProcRet = (int *)__ClientSp + PC_OFFSET;
if ((*__ProcRet + 8) == (int)__exccleanup) {
/*
* make this the first entry in the stack of handlers
* for this process by adjusting ref counts.
*/
for (cntxt = (exccontext_t *)FIRSTQ(&__Curproc->lwp_exchandles);
cntxt != EXCNULL; cntxt = cntxt->exc_next) {
if (cntxt->exc_refcnt++ == 0) {
newcntxt->exc_next = cntxt->exc_next;
cntxt->exc_next = newcntxt;
newcntxt->exc_return = cntxt->exc_return;
newcntxt->exc_retaddr = cntxt->exc_retaddr;
break;
}
}
} else {
newcntxt->exc_retaddr = (int *)__ProcRet;
newcntxt->exc_return = *__ProcRet;
*__ProcRet = (int)__exccleanup;
*__ProcRet -= 8;
FRONT_QUEUE(&__Curproc->lwp_exchandles, newcntxt);
}
UNLOCK();
return (0);
}
/*
* exc_unhandle() -- PRIMITIVE.
* Pop the most recent handler from handler stack and remove
* __exccleanup if no more handlers remain in the procedure.
* It is illegal to remove an exit handler this way.
*/
int
exc_unhandle()
{
register exccontext_t *cntxt;
CLR_ERROR();
LOCK();
/*
* check that there is at least one handler left in
* the procedure by looking at the return address.
*/
__exc_getclient();
__ProcRet = (int *)__ClientSp + PC_OFFSET;
ERROR(((*__ProcRet + 8) != (int)__exccleanup), LE_NONEXIST);
REM_QUEUE((exccontext_t *), &__Curproc->lwp_exchandles, cntxt);
if (cntxt == EXCNULL)
__panic("garbage exception context");
if (cntxt->exc_pattern == EXITPATTERN) {
FRONT_QUEUE(&__Curproc->lwp_exchandles, cntxt);
TERROR(LE_NONEXIST);
}
/*
* if last one, (note that no exit handler can be set here)
* restore normal return address (replacing __exccleanup).
*/
if (cntxt->exc_refcnt == 0) {
/*
* make consistent so can look here
* to see if exit handler installed
*/
*cntxt->exc_retaddr = cntxt->exc_return;
}
FREECHUNK(ExcType, cntxt);
UNLOCK();
return (0);
}
/*
* exc_bound() -- PRIMITIVE.
* returns function bound to handler and sets the reference
* param "env" to the vaalue bound by the handler.
* env is useful for indicating context of local variables.
*/
caddr_t
(*exc_bound(pattern, env))()
register int pattern; /* pattern bound to handler */
caddr_t *env; /* environment to be returned to caller */
{
register __queue_t *q;
register caddr_t (*func)();
CLR_ERROR();
LOCK();
q = FIRSTQ(&__Curproc->lwp_exchandles);
while ((q != QNULL) && (((exccontext_t *)q)->exc_pattern != pattern)) {
q = q->q_next;
}
if (q == QNULL) {
UNLOCK();
return (CFUNCNULL);
} else {
*env = (((exccontext_t *)q)->exc_env);
func = ((exccontext_t *)q)->exc_func;
UNLOCK();
return (func);
}
}
/*
* exc_notify() -- PRIMITIVE.
* Utility for using excbound: if there is a non-null function
* bound to the handler bound to "pattern", invoke it with the
* bound argument. Otherwise, raise an exception with the pattern.
*/
int
exc_notify(pattern)
int pattern;
{
register caddr_t (*func)();
caddr_t env;
CLR_ERROR();
func = exc_bound(pattern, &env);
if (func != CFUNCNULL)
return ((int)func(env));
else
return (exc_raise(pattern));
}
/*
* exc_raise() -- PRIMITIVE.
* Raise an exception by searching for a matching handler.
*/
int
exc_raise(pattern)
int pattern; /* pattern to match the one set by exc_handle() */
{
register exccontext_t *cntxt;
register int i;
int popcount = 0;
caddr_t (*func)();
caddr_t env;
int patt;
int sp;
int pc;
int ret;
if ((pattern == EXITPATTERN) || (pattern == -1)) {
SET_ERROR(__Curproc, LE_INVALIDARG);
return (-1);
}
CLR_ERROR();
LOCK();
/* look for a match, but don't skip backout handlers */
for (cntxt = (exccontext_t *)FIRSTQ(&__Curproc->lwp_exchandles);
cntxt != EXCNULL; cntxt = cntxt->exc_next) {
if ((cntxt->exc_pattern != pattern) &&
(cntxt->exc_pattern != CATCHALL)) {
popcount++; /* don't zap now in case no match */
} else {
break;
}
}
ERROR((cntxt == EXCNULL), LE_NONEXIST);
/* destroy intervening contexts incl. this one */
for (i = 0; i <= popcount; i++) {
REM_QUEUE((exccontext_t *), &__Curproc->lwp_exchandles, cntxt);
func = cntxt->exc_func;
env = cntxt->exc_env;
patt = cntxt->exc_pattern;
FREECHUNK(ExcType, cntxt);
if (patt == EXITPATTERN) {
/*
* Do exit handling en route to a handler above.
* Note that we're on the client stack.
* If excraise were implemented as a system call,
* (i.e., on a sep. stack here),
* should context switch to this context and
* have the cleanup code reraise the exception
* (int lwp_needreraise to hold pattern added to cntxt.)
* UNLOCK so can call nugget cleanly (e.g., mon_exit).
*/
UNLOCK();
(void) (*func)(env);
LOCK();
}
}
/*
* If last handler in a procedure and not an
* exit handler, remove __exccleanup and return to the handler context.
* else, just return to the handler context.
* Note that we must be careful to not rely on restore to
* install the correct value from the register save area on
* window underflow since the current value of %i7 can get flushed
* to the window save area when doing the longjmp.
*/
sp = cntxt->exc_sp;
pc = cntxt->exc_pc;
if ((cntxt->exc_refcnt == 0) && (cntxt->exc_pattern != EXITPATTERN)) {
/* make save area consistent so can tell if handler installed */
*cntxt->exc_retaddr = cntxt->exc_return;
ret = cntxt->exc_return;
UNLOCK();
__exc_jmpandclean(pattern, sp, pc, ret);
} else {
UNLOCK();
__exc_longjmp(pattern, sp, pc);
}
/* NOTREACHED */
}
/*
* __exccleanup is an assembly language entry point
* that is called when a procedure that established
* one or more exception handlers returns. (it is called only once,
* regardless of how many handlers the procedure set up).
* It's function is to call exchelpclean().
* It must provide a way so it (__exccleanup) can return
* a different place (the original procedure return point).
* Note that exchelpclean() is called as a byproduct of the
* original procedure returning normally so the stack and
* registers are all as they should be. Thus, from the
* point of view of the original procedure, it looks like
* it called __exccleanup immediately upon return.
* Finally, note that it's important that contexts are
* NOT allocated on the stack since we would be clobbering
* them with the locals, etc. here. We could get around
* this by writing the whole thing in assembler, not calling
* any procedures (including this helper procedure), and carefully
* avoid messing with the stack.
* However, in environments (e.g., sys V) where interrupts can't
* be saved on an alternate stack, we could have interrupts
* clobbering the stack (and thus any cntxt info) on the sly.
*/
int
__exchelpclean()
{
register exccontext_t *cntxt = EXCNULL;
register int retloc;
register qheader_t *q = &__Curproc->lwp_exchandles;
int patt;
caddr_t env;
int refcnt;
caddr_t (*func)();
CLR_ERROR();
LOCK();
/*
* Discard all unused handlers (including CATCHALL).
* Must be at least one, since we're here cleaning up.
*/
while (!ISEMPTYQ(q)) {
REM_QUEUE((exccontext_t *), q, cntxt);
func = cntxt->exc_func;
env = cntxt->exc_env;
patt = cntxt->exc_pattern;
refcnt = cntxt->exc_refcnt;
retloc = cntxt->exc_return;
FREECHUNK(ExcType, cntxt);
if (patt == 0) {
UNLOCK();
(void) (*func)(env);
LOCK();
}
if (refcnt == 0)
break;
}
UNLOCK();
return (retloc); /* return addr to caller is put in %i7 in exccleanup */
}
/* clean up any exception handlers held by a dying process */
void
__exceptcleanup(corpse)
lwp_t *corpse;
{
register exccontext_t *cntxt;
while (!ISEMPTYQ(&corpse->lwp_exchandles)) {
REM_QUEUE((exccontext_t *), &corpse->lwp_exchandles, cntxt);
LWPTRACE(tr_EXCEPT, 1, ("freeing exception %x\n", cntxt));
FREECHUNK(ExcType, cntxt);
}
}
/* allocate a cache of exception contexts */
void
__init_exc()
{
ExcType =
__allocinit(sizeof (exccontext_t), NUMHANDLERS, IFUNCNULL, FALSE);
}
/*
* What to do when a trap occurs. Called from low.s.
* If any sort of handler was established, raise the exception.
* Otherwise, destroy the offending thread.
* When we have language-level exception handlers, we will need
* exc_signal(func) to tell the kernel where the raise routine is.
*/
int
__exc_trap(trapid)
int trapid; /* UNIX signal causing trap */
{
register caddr_t (*func)();
caddr_t env;
int result;
func = exc_bound(trapid, &env);
if (func != CFUNCNULL)
return ((int)func(env));
result = exc_raise(trapid);
if (result != -1) {
return (result);
} else {
(void)lwp_destroy(SELF);
/* NOTREACHED */
}
}