/* * Copyright (c) 1987 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #if defined(LIBC_SCCS) && !defined(KERNEL) && !defined(SUPERVISOR) <@(#)csv.s 2.4 (2.11BSD GTE) 12/24/92\0> .even #endif /* * C register save and restore routines. When a C routine is called its stack * frame (after csv or ovhandlr have set things up) looks like this: * * _________________________________ * | return address to caller | * |-------------------------------| * r5-> | old r5 ("frame pointer") | * |-------------------------------| * | previous __ovno | * |-------------------------------| * | old r4 | * |-------------------------------| * | old r3 | * |-------------------------------| * | old r2 | * |-------------------------------| * sp-> | empty parameter slot | * |_______________________________| * * The "empty parameter slot" is a simple optimization the compiler uses to * avoid overhead in its generated parameter cleanup code after a function * has been called. Rather than (for example) generating: * * mov foo,-(sp) / push parameter * jsr pc,bar / call function * tst (sp)+ / throw away parameter * * The compiler always keeps one empty slot on the stack and generates: * * mov foo,(sp) / pass parameter * jsr pc,bar / call function * * The savings are not quite so dramatic when more than one parameter is * passed, but still worth the effort. If the function has any local stack * variables, space for them will have be be allocated by the function thereby * "moving" the empty parameter slot down. */ /* * KERNEP and SUPERVISOR (network code) notes: * * Stack checking can be enabled for the KERNEL (and SUPERVISOR network) * by defining CHECKSTACK in the config file. Looks to see if the * kernel and supervisor (network) stacks have collided, etc. * * Networking kernel doesn't currently need overlays (and probably never * will). If it's ever necessary to overlay the networking code, it would * be done in exactly the same manner as that used by the kernel. This would * actually end up simplifying things ... * * It's debatable whether or not the standard library, KERNEL, and * SUPERVISOR copies of csv/cret should be integrated this way, because * the ifdef's sometimes make it hard to keep track of things. But the * code and comments here are complicated enough that it's worth the effort * to keep all three versions in general sync. */ #include "DEFS.h" #ifndef SUPERVISOR /* networking kernel doesn't use overlays */ #ifdef KERNEL #include "../machine/mch_iopage.h" #include "../machine/koverlay.h" #endif .data #ifdef KERNEL .globl ova, ovd / overlay descriptor tables #endif .globl __ovno / currently mapped overlay __ovno: 0 .text .globl _etext / used by cret to determine returns to overlay / area (any return address higher than _etext) / the loader defines only if referenced /* * The loader (/bin/ld) generates `thunks' for functions loaded in overlays. * A thunk resides in the base segment and takes the name _foo which is the * function's interface to the outside world (this allows pointers to functions * to work even if functions are located in overlays). The function itself is * renamed to ~foo. * * ovhndlr(1-9,a-f) are called from the thunks via a jsr r5 after r1 is set to * the location of the first instruction in the subroutine after the call to * csv (~foo+4). * * _foo: mov $~foo+4,r1 * jsr r5,ovhndlr`n' * * The ovhndlr`n' in turn move the requested overlay number into r0 and * branch to ovhndlr which sets the overlay, simulates a csv and transfers to * (r1) (~foo+4). Thus, the function's call to csv is bypassed. */ #define ovh(x, n) .globl ovhndlr/**/x; \ ovhndlr/**/x: \ mov $n,r0; \ br ovhndlr; ovh(1,001); ovh(2,002); ovh(3,003); ovh(4,004) ovh(5,005); ovh(6,006); ovh(7,007); ovh(8,010) ovh(9,011); ovh(a,012); ovh(b,013); ovh(c,014) ovh(d,015); ovh(e,016); ovh(f,017) #ifndef KERNEL emt = 0104000 / overlays switched by emulator trap / overlay number is placed in r0 #endif KERNEL /* * ovhndlr(ov::r0, ~foo+4::r1, _foo+8::r5) * * Ovhandler builds the new stack frame as fast as possible to avoid problems * with getting interrupted halfway through. if we get interrupted between the * "jsr r5,ovhndlr`x'" and the "mov sp,r5" below and a longjmp is attempted, * rollback will fail utterly. If we get interrupted before we finish saving * registers reserved for register variables and an attempt is made to longjmp * to the caller of our service routine rollback will incorrectly pull `saved' * register values from the incomplete stack frame leading to * non-deterministic behavior. * * There's very little that can be done about this asside from changing the * entire stack frame sequence which involves changes to the compiler, loader, * this file and any other routine (like rollback) which `knows' how the C * stack frame is put together ... */ ovhndlr: #if defined(KERNEL) && defined(CHECKSTACK) cmp sp, $_u / before the u. area? blo 9f / yes, assume it's remapped, so OK cmp sp, $KERN_SBASE / falling off the bottom? bhi 9f / no, also OK spl 7 / lock out interrupts halt / can't 'panic', that uses the stack! br . / in case continue is done 9: #endif mov sp,r5 / simulate a csv ... mov __ovno,-(sp) cmp r0,(sp) / is the requested overlay already mapped? beq 1f #ifdef KERNEL mov PS,-(sp) / save PS SPL7 mov r0,__ovno / set new overlay number asl r0 / compute descriptor index and map mov ova(r0), OVLY_PAR / the new overlay in mov ovd(r0), OVLY_PDR mov (sp)+,PS / restore PS, unmask interrupts #else emt / no, ask the kernel for it ... mov r0,__ovno / and indicate the new overlay /* * SPECIAL NOTE: the pair of instructions "emt" and "mov r0,__ovno" * form a race condition. A signal can interrupt the sequence * causing all sorts of nastiness if the signal routine calls a * routine in the old overlay or is itself in the old overlay ... * There is no solution that doesn't involve changing the way * an overlay switch from the kernal. Most [potential] solutions * significantly increase the overhead of an overlay switch. * A tenable solution has yet to be found ... */ #endif 1: mov r4,-(sp) mov r3,-(sp) mov r2,-(sp) jsr pc,(r1) / jsr leaves empty parameter slot on stack #endif /* !SUPERVISOR */ /* * Csv for routines called directly (in base or intra-overlay calls). * no overlays have been changed, so we just save the previous overlay * number on the stack. Note that r0 isn't set to the current overlay * because we weren't called through a thunk. */ .globl csv csv: #if (defined(KERNEL) || defined(SUPERVISOR)) && defined(CHECKSTACK) #ifdef SUPERVISOR cmp sp,$NET_STOP / out of the top of the stack? bhi 8f / yes, die cmp sp,$NET_SBASE / falling off the bottom? bhi 9f / no, so OK 8: spl 7 / lock out interrupts halt / can't 'panic', that uses the stack! br . / in case continue is done 9: #else cmp sp,$_u / before the u. area? blo 9f / assume it was remapped, so OK cmp sp,$KERN_SBASE / falling off the bottom? bhi 9f / no, so OK spl 7 / lock out interrupts halt / can't 'panic', that uses the stack! br . / in case continue is done 9: #endif #endif mov r5,r1 / save transfer address, mov sp,r5 #ifdef SUPERVISOR clr -(sp) #else mov __ovno,-(sp) #endif mov r4,-(sp) mov r3,-(sp) mov r2,-(sp) jsr pc,(r1) /* * Cret is used by everyone so it has to check if we're returning to an * overlay different from the one that may be currently mapped. */ .globl cret cret: #if (defined(KERNEL) || defined(SUPERVISOR)) && defined(CHECKSTACK) #ifdef SUPERVISOR cmp sp,$NET_STOP / out of the top of the stack? bhi 8f / yes, die cmp sp,$NET_SBASE / falling off the bottom? bhi 9f / no, so OK 8: spl 7 / lock out interrupts halt / can't 'panic', that uses the stack! br . / in case continue is done 9: #else cmp sp,$_u / before the u. area? blo 9f / assume it was remapped, so OK cmp sp,$KERN_SBASE / falling off the bottom? bhi 9f / no, so OK spl 7 / lock out interrupts halt / can't 'panic', that uses the stack! br . / in case continue is done 9: #endif #endif mov r5,r2 #ifdef SUPERVISOR tst -(r2) / skip over overlay slot #else mov -(r2),r4 / r4 = old __ovno - if non-zero we've started bne 2f / using overlays so we'll have to make / sure the old overlay is mapped if we're / returning to the overlay area 1: #endif mov -(r2),r4 / restore registers, reset stack, pop frame mov -(r2),r3 / pointer and return mov -(r2),r2 mov r5,sp / (more interrupt problems here *sigh* ...) mov (sp)+,r5 rts pc #ifndef SUPERVISOR 2: /* * Not returning to base segment, so check that the right * overlay is mapped in, and if not change the mapping. */ cmp r4,__ovno / current overlay same as old overlay? beq 1b / lucked out! #if defined(KERNEL) && defined(INET) cmp 2(r5),$Kretu / must always restore overlays if returning beq 3f / from SKcall #endif cmp 2(r5),$_etext / returning to base segment? blos 1b / sometimes things *do* work out ... #ifdef KERNEL 3: mov PS,-(sp) / (sigh) returning to a different overlay SPL7 mov r4,__ovno / set new overlay number asl r4 / and map the new overlay in mov ova(r4), OVLY_PAR mov ovd(r4), OVLY_PDR mov (sp)+,PS / restore PS, unmask interrupts br 1b #else mov r0,r3 / (sigh) returning to a different overlay - mov r4,r0 / have to save r0 because we can't trash emt / a function result and ask UNIX to switch mov r4,__ovno / the old overlay in mov r3,r0 / note that as with ovhndlr the pair "emt" br 1b / "mov r4,__ovno" can be interrupted #endif #endif /* !SUPERVISOR */