mirror of
https://github.com/elliotnunn/mac-rom.git
synced 2024-12-28 01:29:20 +00:00
4325cdcc78
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included. The Tools directory, containing mostly junk, is also excluded.
824 lines
24 KiB
C
824 lines
24 KiB
C
/*
|
|
File: Switch.c
|
|
|
|
Contains: Process switching routines.
|
|
|
|
Written by: Erich Ringewald
|
|
|
|
Copyright: © 1986-1992 by Apple Computer, Inc., all rights reserved.
|
|
|
|
Change History (most recent first):
|
|
|
|
<22> 10/28/92 DTY Use new Get/Set macros to access ExpandMem.
|
|
<21> 10/27/92 DTY Change BlockMoves to BlockMoveData.
|
|
<20> 9/11/92 DRF Take out Òwhile(FSBUSY);Ó in ÒTheFutureÓ to allow for faster
|
|
switching.
|
|
<19> 8/26/92 DTY Roll in latest changes for A/UX.
|
|
<18> 3/30/92 DTY #1025416,<DDG>: Tell Skia to switch itÕs globals on a context
|
|
switch.
|
|
<17> 3/23/92 JSM OSEvents.h is obsolete, use Events.h.
|
|
<16> 2/18/92 DTY #1021226: Switch emMessageManagerGlobals again.
|
|
<15> 10/4/91 JSM Change PsychoticFarmerOrLater conditionals to TheFuture.
|
|
<14> 9/22/91 DTY Change PsychoticFarmerAndLater to PsychoticFarmerOrLater.
|
|
<13> 9/13/91 DTY Conditionalise previous change so it doesnÕt get built for
|
|
CubeE. (ItÕll get built for PsychoticFarmerAndLater.)
|
|
<12> 6/4/91 DFH Switch emMessageManagerGlobals.
|
|
<11> 1/28/91 DFH JSM,#81425:Include MFPrivate.h, since that's where
|
|
DisableSwitching and EnableSwitching are now.
|
|
<10> 1/18/91 DFH (ewa) Fix kill_vbl check of VBL record address. Was checking the
|
|
wrong element.
|
|
<9> 1/15/91 DFH (VL) Conditionalize out the AUX switch prototypes.
|
|
<8> 1/14/91 DFH (JDR) Conditionalize out AUX support.
|
|
<6> 12/19/90 gbm (dba) Fix off-by-one bug in an A/UX change. Also kill VBLs
|
|
whose queue entries are in an applicationÕs heap as well
|
|
as those whose task pointers are.
|
|
<5> 12/5/90 DFH Integrated AUX support.
|
|
<4> 11/6/90 DFH Renamed emAppleEventsGlobal to emAppleEvents.
|
|
<3> 11/1/90 DFH Unconditionalized edition mgr lomem switch.
|
|
<0> 9/2/86 ELR New Today.
|
|
|
|
*/
|
|
|
|
#include <types.h>
|
|
#include <memory.h>
|
|
#include <menus.h>
|
|
#include <osutils.h>
|
|
#include <quickdraw.h>
|
|
#include <events.h>
|
|
#include <resources.h>
|
|
#include <retrace.h>
|
|
#include <osutils.h>
|
|
#include <fonts.h>
|
|
#include <devices.h>
|
|
#include <errors.h>
|
|
#include <ExpandMemPriv.h>
|
|
#include <MFPrivate.h>
|
|
|
|
#include "Glue.h"
|
|
#include "Lomem.h"
|
|
#include "Data.h"
|
|
#include "SysMisc.h"
|
|
#include "Patches.h"
|
|
#include "Aux.h"
|
|
|
|
#ifdef MORE_SEGMENTS
|
|
#pragma segment kernel_segment
|
|
#endif MORE_SEGMENTS
|
|
|
|
void SwitchSkiaGlobals(ProcessSerialNumber theNewProcess)
|
|
= {0x303C, 0xFFFE, 0xA832};
|
|
|
|
/* Some function prototypes that should be in (yet another) header file */
|
|
void save_or_restore_dispatch(PCB *, int);
|
|
void kill_dispatch(PCB *);
|
|
void SwitchAllRegs(unsigned long *, unsigned long);
|
|
void SwitchCPURegs(unsigned long *, unsigned long);
|
|
#ifdef HAS_AUX_PROCESSMGR
|
|
void aux_ctxsw (unsigned long *, unsigned long, long);
|
|
void aux_fpxsw (unsigned long *, unsigned long, long);
|
|
#endif HAS_AUX_PROCESSMGR
|
|
void save_lomem(PEntryPtr);
|
|
void restore_lomem(PEntryPtr, Boolean);
|
|
void save_lmemtab(Ptr);
|
|
void restore_lmemtab(Ptr);
|
|
|
|
/* Function prototypes internal to this file */
|
|
void save_state(PEntryPtr);
|
|
void save_dce(PEntryPtr);
|
|
void save_vbl(PEntryPtr);
|
|
void AtomicVBLSave(Handle, unsigned long, unsigned long);
|
|
void save_hooks(PEntryPtr);
|
|
unsigned short restore_state(PEntryPtr, Boolean);
|
|
void restore_dce(PEntryPtr);
|
|
void restore_vbl(PEntryPtr);
|
|
void restore_hooks(PEntryPtr);
|
|
void kill_dce(THz);
|
|
void GiveGoodByeKiss(DCtlPtr);
|
|
void kill_vbl(PEntryPtr);
|
|
void kill_hooks(void);
|
|
|
|
/* Scheduling constant to cause applications to yield the CPU even if there is
|
|
* is no other reason to switch. For foreground processes, this is checked
|
|
* only when there are no pending events. For background processes, it is
|
|
* checked before events are. Of course, the process can continue with the
|
|
* processor (get a new quantum right away), if there are no other processes
|
|
* ready to be scheduled.
|
|
*/
|
|
#define QUANTUM (5)
|
|
|
|
/* switch_task. Switch to the specified process. The outgoing process,
|
|
* pCurrentProcess, can be nil. It happens when switching out of the dead
|
|
* app in c_exittoshell().
|
|
*/
|
|
void
|
|
switch_task(register PEntryPtr pNewProc)
|
|
{
|
|
register PEntryPtr pOldProc;
|
|
unsigned short ps;
|
|
#ifdef HAS_AUX_PROCESSMGR
|
|
long auxPID;
|
|
#endif HAS_AUX_PROCESSMGR
|
|
unsigned long olda5;
|
|
Ptr ourQDGlobals;
|
|
unsigned long *oldStackSave;
|
|
|
|
assert((pNewProc != nil) && (IsProcessEntry(pNewProc))
|
|
&& (pNewProc->p_state != PRSLEEPING));
|
|
|
|
/* Save Process Mgr a5 and proc globals */
|
|
olda5 = ProcessMgrA5Setup();
|
|
ourQDGlobals = *((Ptr *) olda5);
|
|
|
|
/* Do nothing if this would all be a NOP */
|
|
if ((pOldProc = pCurrentProcess) == pNewProc)
|
|
{
|
|
A5Restore(olda5);
|
|
return;
|
|
}
|
|
|
|
/* Let the error handling routines know we can't handle switching now,
|
|
* but only do it if we'll see the code on the other side of SwitchxxxRegs().
|
|
*/
|
|
if (pNewProc->p_slices > 0)
|
|
DisableSwitching();
|
|
|
|
#if TheFuture
|
|
|
|
// We don't do anything special for old apps anymore
|
|
|
|
#else
|
|
/* Wait until file system and AppleShare async calls are done.
|
|
* NOTE: This is done for the sole reason that MacWrite (4.5??) has an
|
|
* HFS completion routine that doesn't set up A5. If we can workaround
|
|
* (or forget) them, we should. This would let switches go faster, and
|
|
* allow apps to sleep pending I/O completion (i.e. WNE with big sleep,
|
|
* and an I/O completion routine that calls WakeupProcess).
|
|
*/
|
|
#ifdef HAS_AUX_PROCESSMGR
|
|
/* No need to wait for synchronous FM since A/UX does it asynchronously */
|
|
if (! AUXIsPresent)
|
|
#endif HAS_AUX_PROCESSMGR
|
|
while (FSBUSY)
|
|
;
|
|
|
|
#endif TheFuture
|
|
|
|
// <18> Call Skia to let it know that a context switch is occuring
|
|
|
|
if (skiaExists)
|
|
SwitchSkiaGlobals(pNewProc->p_serialNumber);
|
|
|
|
/* Update count of switched-in time for outgoing process.
|
|
* NOTE: On TICKS rollover we don't bother to updateÉ who'll miss 'em?
|
|
* Assume we never have to worry about p_activeTime rollover.
|
|
*/
|
|
if ((lastswitch < TICKS) && (pOldProc != nil))
|
|
pOldProc->p_activeTime += (TICKS - lastswitch);
|
|
lastswitch = TICKS;
|
|
|
|
/* Update count of slices for incoming process */
|
|
pNewProc->p_slices++;
|
|
|
|
/* Set time of next quantum cut (even works if TICKS is about to roll over) */
|
|
nextswitch = TICKS + QUANTUM;
|
|
|
|
/* Invalidate current process information during switch */
|
|
pCurrentProcess = nil;
|
|
|
|
/* Save outgoing state, set up incoming state */
|
|
save_state(pOldProc);
|
|
ps = restore_state(pNewProc, false);
|
|
|
|
/* Everything is in place. It's time for the final act of switching the CPU
|
|
* context from the old to the new. The SwitchAllRegs and SwitchCPURegs routines
|
|
* are entered by the old process, but are returned from by the new one. The most
|
|
* interesting part is that this switches the stack pointer: all local variables
|
|
* used after this use the values that the incomer saved when it was the outgoer.
|
|
*/
|
|
pCurrentProcess = pNewProc;
|
|
oldStackSave = (pOldProc != nil) ? &pOldProc->p_sp : nil;
|
|
#ifdef HAS_AUX_PROCESSMGR
|
|
if (AUXIsPresent)
|
|
{
|
|
auxPID = ((pOldProc == nil) || (pNewProc->aux_realpid != pOldProc->aux_realpid)) ? pNewProc->aux_realpid : 0;
|
|
if (MachineHasFPU)
|
|
aux_fpxsw(oldStackSave, pNewProc->p_sp, auxPID);
|
|
else
|
|
aux_ctxsw(oldStackSave, pNewProc->p_sp, auxPID);
|
|
}
|
|
else
|
|
{
|
|
#endif HAS_AUX_PROCESSMGR
|
|
if (MachineHasFPU)
|
|
SwitchAllRegs(oldStackSave, pNewProc->p_sp);
|
|
else
|
|
SwitchCPURegs(oldStackSave, pNewProc->p_sp);
|
|
#ifdef HAS_AUX_PROCESSMGR
|
|
}
|
|
#endif HAS_AUX_PROCESSMGR
|
|
|
|
/* Restore interrupt level to what old guy saved before passing the torch */
|
|
spl(ps);
|
|
|
|
/* let the error handling routines know we can handle switching again */
|
|
EnableSwitching();
|
|
|
|
/* Restore his A5 world and his quickdraw globals */
|
|
A5Restore(olda5);
|
|
*((Ptr *) olda5) = ourQDGlobals;
|
|
|
|
/* Now kill him! */
|
|
if (pCurrentProcess->p_condemned)
|
|
{
|
|
DSERRCODE = noErr;
|
|
ExitToShell();
|
|
}
|
|
}
|
|
|
|
#pragma segment kernel_segment
|
|
|
|
/* save_state. Save all process specific stuff in the process state records.
|
|
* We make an effort to remove all executable things (drivers, vbls, etc)
|
|
* before switching the patches or lomem. This guarantees that those
|
|
* executables never run without the context the need.
|
|
* NOTE: Should we switch timer tasks?
|
|
* NOTE: VBLs, drivers, and DAs in the system heap are not switched out by
|
|
* this code, so they better not rely on low memory and patches being
|
|
* fair game. Not only that, but they shouldn't rely on TWGetPID to tell
|
|
* them, because they'll see an incorrect value during the switch.
|
|
*/
|
|
void
|
|
save_state(PEntryPtr pPEntry)
|
|
{
|
|
PCB **pcbHdl;
|
|
|
|
/* Weed out the degenerates */
|
|
if ( (pPEntry == nil) || (pcbHdl = pPEntry->p_pcb) == nil)
|
|
return;
|
|
|
|
/* Disarm executables */
|
|
save_dce(pPEntry);
|
|
save_vbl(pPEntry);
|
|
save_hooks(pPEntry);
|
|
|
|
/* Remove the guy's patches and save off low memory */
|
|
save_or_restore_dispatch(*pcbHdl, SAVE_DISPATCH);
|
|
save_lomem(pPEntry);
|
|
}
|
|
|
|
/* restore_state. Restores all process specific stuff from the state records.
|
|
* NOTE: Interrupts must be turned off while restoring volatile portions of
|
|
* low memory (e.g. STKLOWPT), because they may be affected by the interrupts
|
|
* and VBLs that execute even as we speak.
|
|
*/
|
|
unsigned short
|
|
restore_state(register PEntryPtr pPEntry, Boolean shouldRestoreVolatile)
|
|
{
|
|
u_short ps;
|
|
|
|
if (pPEntry->p_pcb == nil)
|
|
return(0);
|
|
|
|
/* Restore data context (lomem) */
|
|
restore_lomem(pPEntry, shouldRestoreVolatile);
|
|
|
|
/* Now restore executable context */
|
|
save_or_restore_dispatch(*pPEntry->p_pcb, RESTORE_DISPATCH);
|
|
restore_dce(pPEntry);
|
|
restore_vbl(pPEntry);
|
|
restore_hooks(pPEntry);
|
|
|
|
/* Restore volatile state with interrupts off. These were deferred by
|
|
* restore_lomem() until we got to this section with interrupts off.
|
|
* Please excuse the lack of layering.
|
|
* NOTE: To leave interrupts off even less we could lift this into
|
|
* switch_task() itself.
|
|
*/
|
|
if (shouldRestoreVolatile == false)
|
|
{
|
|
ps = disable();
|
|
STKLOWPT = (*pPEntry->p_pcb)->stklowpt;
|
|
}
|
|
|
|
return(ps);
|
|
}
|
|
|
|
/* kill_state. Chuck the switched context of the specified process. The idea here is
|
|
* a) neutralize the context, and b) make sure that the info is lost so that another
|
|
* save_state/restore_state would not re-install any of the custom context. This is
|
|
* important when calling StandardLaunch in HandleShellDeath. We want the restore_state
|
|
* at the end of NewProcess to not re-install patches, dces, etc. The dces, vbls, and
|
|
* hooks are naturally handled, since the save_state will see genericness. The dispatch
|
|
* table, tho, must be explicitly removed now.
|
|
*/
|
|
void
|
|
kill_state(PEntryPtr pPEntry)
|
|
{
|
|
PCB **pcbHdl;
|
|
|
|
/* Do nothing if app had no state info */
|
|
if ((pcbHdl = pPEntry->p_pcb) == nil)
|
|
return;
|
|
|
|
/* Neutralize! */
|
|
kill_dispatch(*pcbHdl);
|
|
kill_dce((*pcbHdl)->applzone);
|
|
kill_vbl(pPEntry);
|
|
kill_hooks();
|
|
}
|
|
|
|
/* Save_lomem copies the current low memory state for the process described
|
|
* by `pp'.
|
|
*
|
|
* Low memory locations can be divided into 4 classes:
|
|
* (1) Undefined Memory: Doesn't matter what we do with these
|
|
* (2) Application Specific Memory: Must be switched.
|
|
* (3) Static System Memory: Must be switched.
|
|
* Only examples so far: BOOTVOL (SWITCHDATA?).
|
|
* (4) Dynamic System Memory: Must not be switched.
|
|
*
|
|
* Low memory should be switched out only after all the executable objects that
|
|
* use the low memory have been switched out. These executables include VBLs,
|
|
* drivers, and "hooks".
|
|
*
|
|
* NOTE: Assumes A5 = PROCESSMGRGLOBALS
|
|
*/
|
|
void
|
|
save_lomem(PEntryPtr pp)
|
|
{
|
|
PCB *pc;
|
|
|
|
assert(pp->p_pcb != nil && pp->p_lmemtool != nil);
|
|
|
|
pc = *pp->p_pcb;
|
|
|
|
/* Save ExpandMem switchables */
|
|
pc->emScriptAppGlobals = GetExpandMemScriptAppGlobals();
|
|
pc->emAppleEvents = GetExpandMemAppleEvents();
|
|
pc->emEditionMgrPerApp = GetExpandMemEditionMgrPerApp();
|
|
pc->emMessageManagerGlobals = GetExpandMemMessageManagerGlobals(0);
|
|
|
|
/* Save OS lomem */
|
|
pc->bootvol = BOOTVOL;
|
|
pc->switchdata = SWITCHDATA;
|
|
pc->defvcbptr = DEFVCBPTR;
|
|
pc->defaultwdcb = *((WDCBPtr) (WDCBSPTR + 2));
|
|
pc->defvrefnum = DEFVREFNUM;
|
|
pc->dserrcode = DSERRCODE;
|
|
|
|
/* Save toolbox lomem */
|
|
save_lmemtab(*pp->p_lmemtool);
|
|
|
|
/* Save randomly accessed toolbox lomem */
|
|
pc->stklowpt = STKLOWPT;
|
|
pc->appllimit = APPLLIMIT;
|
|
pc->applzone = APPLZONE;
|
|
pc->currenta5 = (unsigned long)CURRENTA5;
|
|
BlockMoveData(CURAPNAME, &pc->curapname, *((unsigned char *)CURAPNAME) + 1);
|
|
pc->curlayer = GetCurLayer();
|
|
pc->topmaphandle = TOPMAPHANDLE;
|
|
pc->curmap = CURMAP;
|
|
if (Colorized)
|
|
pc->menucinfo = MENUCINFO;
|
|
pc->sfsavedisk = SFSAVEDISK;
|
|
pc->curdirstore = CURDIRSTORE;
|
|
}
|
|
|
|
/* restore_lomem. Switches in the low memory settings of the given process.
|
|
* Low memory should be switched in before any executable objects that
|
|
* use the low memory have been switched in. These executables include VBLs,
|
|
* drivers, and "hooks".
|
|
*/
|
|
void
|
|
restore_lomem(PEntryPtr pp, Boolean shouldRestoreVolatile)
|
|
{
|
|
PCB *pc;
|
|
unsigned short ps;
|
|
|
|
assert(pp->p_pcb != nil && pp->p_lmemtool != nil);
|
|
|
|
pc = *pp->p_pcb;
|
|
|
|
/* Restore location zero to it's startup time value */
|
|
LOCATIONZERO = initLocationZero;
|
|
|
|
/* Restore ExpandMem switchables */
|
|
SetExpandMemScriptAppGlobals(pc->emScriptAppGlobals);
|
|
SetExpandMemAppleEvents(pc->emAppleEvents);
|
|
SetExpandMemEditionMgrPerApp(pc->emEditionMgrPerApp);
|
|
SetExpandMemMessageManagerGlobals(0, pc->emMessageManagerGlobals);
|
|
|
|
/* Restore OS lomem */
|
|
BOOTVOL = pc->bootvol;
|
|
SWITCHDATA = pc->switchdata;
|
|
DEFVCBPTR = pc->defvcbptr;
|
|
*((WDCBPtr) (WDCBSPTR + 2)) = pc->defaultwdcb;
|
|
DEFVREFNUM = pc->defvrefnum;
|
|
DSERRCODE = pc->dserrcode;
|
|
|
|
/* Restore toolbox lomem */
|
|
restore_lmemtab(*pp->p_lmemtool);
|
|
|
|
/* Restore interrupt sensitive lomem. */
|
|
if (shouldRestoreVolatile)
|
|
{
|
|
ps = disable();
|
|
STKLOWPT = pc->stklowpt;
|
|
APPLZONE = pc->applzone;
|
|
spl(ps);
|
|
}
|
|
else
|
|
{
|
|
STKLOWPT = 0;
|
|
APPLZONE = pc->applzone;
|
|
}
|
|
|
|
/* Restore randomly accessed toolbox lomem.
|
|
* NOTE: These must be done after the other toolbox entries since we may
|
|
* explicitly store values here (e.g. newproc()).
|
|
*/
|
|
APPLLIMIT = pc->appllimit;
|
|
CURRENTA5 = pc->currenta5;
|
|
BlockMoveData(&pc->curapname, CURAPNAME, Length(&(pc->curapname)) + 1);
|
|
SetCurLayer(pc->curlayer);
|
|
TOPMAPHANDLE = pc->topmaphandle;
|
|
CURMAP = pc->curmap;
|
|
if (Colorized)
|
|
MENUCINFO = pc->menucinfo;
|
|
SFSAVEDISK = pc->sfsavedisk;
|
|
CURDIRSTORE = pc->curdirstore;
|
|
}
|
|
|
|
/* save_vbl. Go through all of the current VBL tasks and replace the receiver
|
|
* addresses with dummies, and set all counts to a large number, making the VBL
|
|
* a NOP.
|
|
* This must be called before save_or_restore_dispatch and save_lomem, to ensure
|
|
* that no VBLs run without their required context.
|
|
*/
|
|
void
|
|
save_vbl(PEntryPtr pp)
|
|
{
|
|
unsigned long base, bound;
|
|
|
|
base = (u_long) pp->p_zone;
|
|
bound = base + pp->p_size;
|
|
AtomicVBLSave((*pp->p_pcb)->vblvars, base, bound);
|
|
}
|
|
|
|
/* restore_vbl. Reinstate the VBLs that were swapped out by previous save_vbl. */
|
|
void
|
|
restore_vbl(PEntryPtr pp)
|
|
{
|
|
VBLTask *pVBL;
|
|
VBLDescPtr pVBLSave;
|
|
VBLDescHdl hVBLSave;
|
|
short vblDescCount;
|
|
unsigned short ps;
|
|
|
|
assert(pp->p_pcb != nil);
|
|
|
|
hVBLSave = (*pp->p_pcb)->vblvars;
|
|
vblDescCount = (u_long) GetHandleSize((Handle) hVBLSave) / sizeof(VBLDesc);
|
|
|
|
pVBLSave = *hVBLSave;
|
|
while (--vblDescCount >= 0) // <6>
|
|
{
|
|
pVBL = pVBLSave->v_eladdr;
|
|
assert(pVBL->qType == vType);
|
|
|
|
ps = disable();
|
|
pVBL->vblCount = pVBLSave->v_count;
|
|
pVBL->vblAddr = pVBLSave->v_addr;
|
|
spl(ps);
|
|
|
|
pVBLSave++;
|
|
}
|
|
}
|
|
|
|
/* kill_vbl. Remove all VBL tasks local to the given process.
|
|
* Note that under A/UX tasks above PhysMemTop should be killed as well.
|
|
*/
|
|
void
|
|
kill_vbl(PEntryPtr pProc)
|
|
{
|
|
register VBLTask *pThisVBL, *pNextVBL;
|
|
register unsigned long vblAddr, base, bound;
|
|
#ifdef HAS_AUX_PROCESSMGR
|
|
register unsigned long secondBound;
|
|
#endif HAS_AUX_PROCESSMGR
|
|
|
|
base = (unsigned long) pProc->p_zone;
|
|
bound = base + pProc->p_size;
|
|
#ifdef HAS_AUX_PROCESSMGR
|
|
secondBound = (AUXIsPresent) ? PHYSMEMTOP : (0xFFFFFFFF);
|
|
#endif HAS_AUX_PROCESSMGR
|
|
|
|
pNextVBL = VBLQHDR->qHead;
|
|
while (pNextVBL != nil)
|
|
{
|
|
pThisVBL = pNextVBL;
|
|
pNextVBL = pNextVBL->qLink;
|
|
vblAddr = StripAddress((Ptr) pThisVBL->vblAddr);
|
|
if (((((unsigned long) pThisVBL) >= base) && (((unsigned long) pThisVBL) <= bound)) ||
|
|
((vblAddr >= base) && (vblAddr <= bound
|
|
#ifdef HAS_AUX_PROCESSMGR
|
|
|| ( AUXIsPresent && vblAddr > secondBound)
|
|
#endif
|
|
)))
|
|
(void) VRemove(pThisVBL);
|
|
}
|
|
}
|
|
|
|
/* save_dce. Switch out all drivers/DAs associated the specified process.
|
|
* This should be called before save_or_restore_dispatch and save_lomem.
|
|
*/
|
|
void
|
|
save_dce(PEntryPtr pp)
|
|
{
|
|
DCtlHandle *h;
|
|
u_short i, n, daNumber;
|
|
u_long crit, base, bound;
|
|
DCEDescPtr del;
|
|
|
|
assert(pp->p_pcb != nil);
|
|
daNumber = (*(pp->p_pcb))->p_daResource;
|
|
base = (unsigned long) pp->p_zone;
|
|
bound = base + pp->p_size;
|
|
|
|
/* tally up the DAs/drivers we'll be switching */
|
|
h = UNITTABLE;
|
|
i=0; n=0;
|
|
while (n < UNITNTRYCNT)
|
|
{
|
|
if (*h != nil)
|
|
{
|
|
/* Is it a DA handler one? */
|
|
if (n == daNumber)
|
|
i++;
|
|
|
|
/* swap local drivers */
|
|
else
|
|
{
|
|
if ( (((crit = (unsigned long) (**h)->dCtlWindow) == 0)
|
|
&& ((crit = (unsigned long) (**h)->dCtlStorage) == 0)) )
|
|
crit = (unsigned long) (**h)->dCtlDriver;
|
|
crit = (u_long) StripAddress((Ptr) crit);
|
|
|
|
if ( (crit >= base) && (crit < bound) )
|
|
i++;
|
|
}
|
|
}
|
|
h++;
|
|
n++;
|
|
}
|
|
|
|
/* Make handle correct size (back to 0 if drivers all went away) */
|
|
SetHandleSize((*pp->p_pcb)->dces, i*sizeof(DCEDesc));
|
|
|
|
/* No use going on */
|
|
if (i == 0)
|
|
return;
|
|
|
|
/* Do the save now that we have storage for it. */
|
|
del = *(*pp->p_pcb)->dces;
|
|
n = 0;
|
|
h = UNITTABLE;
|
|
while (n < UNITNTRYCNT)
|
|
{
|
|
Boolean removeIt;
|
|
|
|
if (*h != nil)
|
|
{
|
|
/* Check whether current DCE is local driver or DA from DA Handler */
|
|
if ((removeIt = (n == daNumber)) == false)
|
|
{
|
|
if ( ((crit = (unsigned long) (**h)->dCtlWindow) == 0)
|
|
&& ((crit = (unsigned long) (**h)->dCtlStorage) == 0) )
|
|
crit = (unsigned long) (**h)->dCtlDriver;
|
|
crit = (u_long) StripAddress((Ptr) crit);
|
|
removeIt = ((crit >= base) && (crit < bound));
|
|
}
|
|
|
|
/* Act on our findings */
|
|
if (removeIt)
|
|
{
|
|
assert(i-- != 0);
|
|
del->d_unit = n;
|
|
del++->d_handle = *h;
|
|
*h = nil;
|
|
}
|
|
}
|
|
h++;
|
|
n++;
|
|
}
|
|
}
|
|
|
|
/* restore_dce. Replace all the DCEs we switched out in save_dce. */
|
|
void
|
|
restore_dce(PEntryPtr pProc)
|
|
{
|
|
register DCEDescHdl hDCEDesc;
|
|
register DCEDescPtr pDCEDesc;
|
|
short dceDescCount;
|
|
|
|
assert(pProc->p_pcb != nil);
|
|
|
|
hDCEDesc = (*pProc->p_pcb)->dces;
|
|
pDCEDesc = *hDCEDesc;
|
|
dceDescCount = GetHandleSize(hDCEDesc) / sizeof(DCEDesc);
|
|
while (--dceDescCount >= 0)
|
|
{
|
|
*(UNITTABLE + pDCEDesc->d_unit) = pDCEDesc->d_handle;
|
|
pDCEDesc++;
|
|
}
|
|
}
|
|
|
|
/* kill_dce. Fix up the unit table when the given appl zone is nuked.
|
|
* This contains the basic functionality of the ROM routine InstallRDrivers().
|
|
* Zeroes out the DCE except for dCtlRefNum (restored because _DrvrInstall won't be
|
|
* called again on an allocated DCE). Also, gives appropriate GBKisses to drivers.
|
|
* NOTE: A known bug is that a system DA opened locally with no window or storage but
|
|
* a menu id will not be cleaned up or get a gb kiss.
|
|
* NOTE: There should be no need to StripAddress handles, only the MPs.
|
|
* NOTE: It is somewhat dangerous to not _ReleaseResource on the drivers if there are
|
|
* misbehaved drivers (such as the "Windows" DA) that store data in their 'DRVR' handles
|
|
* and depend on the data being reinitialized from disk since they assume they are app
|
|
* heap resource handles and thus will be removed from memory by _RsrcZoneInit.
|
|
*/
|
|
void
|
|
kill_dce(THz applZone)
|
|
{
|
|
u_long tmp;
|
|
Ptr baseCurAppHeap, boundsCurAppHeap;
|
|
DCtlHandle *unitEntry;
|
|
short unitNTryCnt;
|
|
DCtlPtr dCtlPtr;
|
|
short dRefNum;
|
|
|
|
baseCurAppHeap = (Ptr)applZone;
|
|
boundsCurAppHeap = ((THz) baseCurAppHeap)->bkLim;
|
|
unitEntry = UNITTABLE;
|
|
dRefNum = FIRSTDREFNUM+1;
|
|
unitNTryCnt = UNITNTRYCNT;
|
|
|
|
while (--unitNTryCnt >= 0)
|
|
{
|
|
/* Keep dRefNum and unitNTryCnt in synch */
|
|
dRefNum -= 1;
|
|
|
|
/* Get table entry (handle to driver) */
|
|
if ((tmp = (u_long) *unitEntry++) == nil)
|
|
continue;
|
|
|
|
/* Dereference it to get pointer to driver, can do nothing if it was purged */
|
|
if ((tmp = (u_long) *((DCtlHandle) tmp)) == nil)
|
|
continue;
|
|
|
|
/* Convert type just once */
|
|
dCtlPtr = (DCtlPtr) tmp;
|
|
|
|
/* Can do nothing if driver isn't available. */
|
|
if ((tmp = (u_long) StripAddress((Ptr) dCtlPtr->dCtlDriver)) == nil)
|
|
continue;
|
|
|
|
/* Look for driver based in application heap. Wipe it out! */
|
|
if (tmp > baseCurAppHeap && tmp < boundsCurAppHeap)
|
|
{
|
|
GiveGoodByeKiss(dCtlPtr);
|
|
MemClear(dCtlPtr,sizeof(DCtlEntry));
|
|
dCtlPtr->dCtlRefNum = dRefNum;
|
|
continue;
|
|
}
|
|
|
|
/* Look for driver based in system heap who has storage in the application heap. */
|
|
if (tmp < (u_long)SYSZONE->bkLim)
|
|
{
|
|
/* Is the storage handle in current application heap? */
|
|
tmp = (u_long) StripAddress((Ptr) dCtlPtr->dCtlStorage);
|
|
if (tmp > baseCurAppHeap && tmp < boundsCurAppHeap)
|
|
{
|
|
OSErr resErr;
|
|
Str255 driverNameString;
|
|
|
|
/* Get driver name now, since goodbye kiss may prompt the driver
|
|
* to nuke dCtlPtr->dCtlDriver.
|
|
*/
|
|
GetResInfo(dCtlPtr->dCtlDriver, nil, nil, &driverNameString);
|
|
resErr = RESERR;
|
|
|
|
/* Smooch! */
|
|
GiveGoodByeKiss(dCtlPtr);
|
|
|
|
/* Look for DA vs regular driver. If it's a DA, wipe it out altogether.
|
|
* If a driver, just nil the handle into the application heap (driver
|
|
* can later simple restore it before re-using). Do any drivers take
|
|
* advantage of this?
|
|
* NOTE: This assumes that all non-DA drivers start with a '.'.
|
|
*/
|
|
if ((resErr != noErr) ||
|
|
((Length(&driverNameString) != 0) && (StringByte(&driverNameString, 0) == '\0')))
|
|
{
|
|
HPurge(dCtlPtr->dCtlDriver);
|
|
MemClear(dCtlPtr, sizeof(DCtlEntry));
|
|
dCtlPtr->dCtlRefNum = dRefNum;
|
|
}
|
|
else
|
|
dCtlPtr->dCtlStorage = nil;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* At this point we have a valid driver w/ last tries for GBKiss if it has
|
|
* a window in the current heap. This is done primarily for the benefit of
|
|
* Calculator, which allocates its storage in the sys heap, but never removes it.
|
|
*/
|
|
tmp = (u_long) StripAddress((Ptr) dCtlPtr->dCtlWindow);
|
|
if (tmp > baseCurAppHeap && tmp < boundsCurAppHeap)
|
|
{
|
|
GiveGoodByeKiss(dCtlPtr);
|
|
HPurge(dCtlPtr->dCtlDriver);
|
|
MemClear(dCtlPtr,sizeof(DCtlEntry));
|
|
dCtlPtr->dCtlRefNum = dRefNum;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* GiveGoodByeKiss. Give this driver a goodbye kiss if it wants one. */
|
|
void
|
|
GiveGoodByeKiss(DCtlPtr dCtlPtr)
|
|
{
|
|
CntrlParam goodByeParamBlock;
|
|
|
|
/* Give him a goodbye kiss, if necessary */
|
|
if ((dCtlPtr->dCtlFlags & (DNeedGoodbye | DOpened)) == (DNeedGoodbye | DOpened))
|
|
{
|
|
goodByeParamBlock.csCode = csCodeGoodBye;
|
|
goodByeParamBlock.ioCRefNum = dCtlPtr->dCtlRefNum;
|
|
PBControl(&goodByeParamBlock, SyncHFS);
|
|
}
|
|
}
|
|
|
|
/* save_hooks. Switch out any "hook" that may depend on the application's patches
|
|
* or low memory. This should be called before save_or_restore_dispatch and
|
|
* save_lomem.
|
|
*/
|
|
void
|
|
save_hooks(PEntryPtr pProc)
|
|
{
|
|
PCB *pc;
|
|
|
|
pc = *pProc->p_pcb;
|
|
pc->fsqueuehook = FSQUEUEHOOK;
|
|
pc->pmsphook = *((Ptr *)(PMSPPTR+PMSPHOOKINDEX));
|
|
kill_hooks();
|
|
}
|
|
|
|
/* restore_hooks. Switch in the hooks that may depend on the application's patches
|
|
* or low memory. This should be called before save_or_restore_dispatch and
|
|
* restore_lomem.
|
|
*/
|
|
void
|
|
restore_hooks(PEntryPtr pProc)
|
|
{
|
|
PCB *pc;
|
|
|
|
pc = *pProc->p_pcb;
|
|
FSQUEUEHOOK = pc->fsqueuehook;
|
|
*((Ptr *)(PMSPPTR+PMSPHOOKINDEX)) = pc->pmsphook;
|
|
}
|
|
|
|
/* kill_hooks. Restore hooks to their neutral values. */
|
|
void
|
|
kill_hooks(void)
|
|
{
|
|
FSQUEUEHOOK = (void (**)())initFSQueueHook;
|
|
*((Ptr *)(PMSPPTR+PMSPHOOKINDEX)) = nil;
|
|
}
|
|
|
|
#pragma segment INIT
|
|
|
|
/* GetSwitchTab. Get table of toolbox switch locations and set up related globals */
|
|
void
|
|
GetSwitchTab(void)
|
|
{
|
|
short *pLen, len;
|
|
Handle switchTabHdl;
|
|
short lmemID;
|
|
|
|
/* Which table depends on whether the machine has color */
|
|
lmemID = (Colorized) ? COLOR_LOMEMTAB_ID : BW_LOMEMTAB_ID;
|
|
if ((switchTabHdl = GetResource(LOMEM_TAB_TYPE, lmemID)) == nil)
|
|
{
|
|
assert(RESERR == memFullErr);
|
|
SysError(dsMemFullErr);
|
|
}
|
|
|
|
/* Remember table in globals (resource must be marked resLocked) */
|
|
switchTabPtr = *switchTabHdl;
|
|
|
|
/* NOTE: This assumes only data addresses in the table */
|
|
for (pLen = switchTabPtr, lmemToolDataSize = 0; (len = *pLen) != 0; (Ptr)pLen += 6)
|
|
lmemToolDataSize += len;
|
|
}
|
|
#pragma segment kernel_segment
|