mirror of
https://github.com/elliotnunn/mac-rom.git
synced 2024-12-28 16:31:01 +00:00
824 lines
24 KiB
C
824 lines
24 KiB
C
|
/*
|
|||
|
File: Switch.c
|
|||
|
|
|||
|
Contains: Process switching routines.
|
|||
|
|
|||
|
Written by: Erich Ringewald
|
|||
|
|
|||
|
Copyright: <EFBFBD> 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 <EFBFBD>while(FSBUSY);<EFBFBD> in <EFBFBD>TheFuture<EFBFBD> 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<EFBFBD>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<EFBFBD>t get built for
|
|||
|
CubeE. (It<EFBFBD>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<EFBFBD>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<EFBFBD> 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
|