mirror of
https://github.com/elliotnunn/mac-rom.git
synced 2024-12-28 01:29:20 +00:00
0ba83392d4
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.
821 lines
30 KiB
C
821 lines
30 KiB
C
/*
|
||
File: MemoryMgrPatches.c
|
||
|
||
Contains: Process Mgr heap routines that manipulate the heap without direct
|
||
knowledge of whether the heap is 24-bit or 32-bit. MemoryMgr24Patches.c
|
||
and MemoryMgr32Patches.c are virtually identical in source code. The
|
||
header files each uses, though, determines the how the block header
|
||
access macros are expanded. This (hopefully) makes it easier to
|
||
maintain given the 24/32 duality imposed on us.
|
||
|
||
Written by: Phil Goldman
|
||
|
||
Copyright: © 1986-1992 by Apple Computer, Inc., all rights reserved.
|
||
|
||
Change History (most recent first):
|
||
|
||
<13> 10/28/92 DTY Use new Get/Set macros to access ExpandMem.
|
||
<12> 6/22/92 DTY #1033275 <csd>: In ProcessManagerNewHiHandle, quad align the
|
||
blocks if weÕre running on an 040 machine. The alignment takes
|
||
the block header size into account, then subtracts it since
|
||
ChipOffRelocFromFree will add the block header size back on, so
|
||
the fudging is necessary to keep the block aligned properly.
|
||
<11> 4/15/91 DFH dba, WS#DFH-910415a : ReclaimSpaceIfSysHeapLow now checks
|
||
GetProcessMgrLoFreeBytes to see if enough is available there
|
||
without having to scan the system heap. This is often true, so
|
||
Process Mgr and Temp Memory allocations will be faster. We
|
||
noticed this with EPPC.
|
||
<10> 4/9/91 DFH VL, #83672 : Fixed ProcessMgrMaxBlock to use our own
|
||
GetProcessMgrHiMaxBlock, instead of MaxBlock, since we need to
|
||
avoid the straddle block area. That memory is accounted for in
|
||
StraddleBlockSize() and so a) the could be a block we could use
|
||
above the straddle, even if system heap preservation makes
|
||
StraddleBlockSize return 0, or b) if there is no such block
|
||
above the straddle, we would return MaxBlock() even though we
|
||
need to save it for the system.
|
||
<9> 4/7/91 DFH gbm, WS#JC-PM-005 : Fix StraddleBlockSize to not return a number
|
||
less than the header size, since that memory can not really be
|
||
allocated. Caused ProcessMgrMaxBlock (and therefore TempMaxMem)
|
||
to return negative number, since they subtract the header size
|
||
to convert to logical size.
|
||
<8> 4/4/91 DFH VL, WS#JC-PM-005 : Fixed ProcessMgrMaxBlock to calculate correct
|
||
size. Was incorrect, sometimes negative (!), when there was no
|
||
room left in the Process Mgr zone.
|
||
<7> 4/3/91 DFH kst, WS#DFH-910403a : Removed shortcut in ProcessMgrHiNewHandle
|
||
about whether to call ReclaimSpaceIfSysHeapLow. It was supposed
|
||
to do it only if the grow zone was NOT called, but the condition
|
||
was reversed. This meant we allowed ProcessMgrHiNewHandle to eat
|
||
too much of the System heap, resulting in needless shortages.
|
||
Checking bytesGrown was a bad idea anyway, since it ends up zero
|
||
even if the growzone succeeded. Removed the shortcut altogether
|
||
just to avoid problems.
|
||
<6> 4/2/91 DFH ewa, #86189 : Added VM_HoldSystemHeap call in
|
||
LowerProcessMgrZoneBottom. This was being done by VM's patch to
|
||
the ProcessMgrGZProc. Unfortunately, it is impossible for such a
|
||
patch to work, since it needs to a) not eat any volatile
|
||
registers, b) not use any stack (since our glue crawls the
|
||
caller's stack to find the memory manager's A6 register), and c)
|
||
be reentrant.
|
||
<5> 3/14/91 DFH gbm,#82504, #82681, #83168, #83182, #83207: Use new ExpandMem
|
||
variable for sys heap contiguity factor.
|
||
<4> 2/21/91 DFH dba,#82504, #82681, #83168, #83182, #83207: Process Mgr heap
|
||
allocation now makes sure there is sufficient memory in the
|
||
system heap even when ProcessMgr zone doesn't need to grow.
|
||
<2> 11/21/90 DFH (with BBM) Fixed GZ proc parameter adjustments. Fixed
|
||
ProcessMgrSysGZProc to pay attention to the result of chaining
|
||
through to the previous SysZone gzProc. Also, use ROUTINE_ADDR
|
||
when stting gzProc, to bypass jump table.
|
||
<0> x/xx/86 PYG New Today.
|
||
|
||
*/
|
||
|
||
#include <types.h>
|
||
#include <memory.h>
|
||
#include <menus.h>
|
||
#include <files.h>
|
||
#include <quickdraw.h>
|
||
#include <windows.h>
|
||
#include <osutils.h>
|
||
#include <devices.h>
|
||
#include <retrace.h>
|
||
#include <resources.h>
|
||
#include <segload.h>
|
||
#include <errors.h>
|
||
#include <GestaltEqu.h>
|
||
#include <VMCallsPriv.h>
|
||
|
||
#include "Glue.h"
|
||
#include "Lomem.h"
|
||
#include "Data.h"
|
||
#include "SysMisc.h"
|
||
#include "Patches.h"
|
||
#include "Zone.h"
|
||
|
||
/* Data internal to this file */
|
||
static Ptr oldSysGZProc; /* old system heap grow zone procedure */
|
||
|
||
/* Function prototypes internal to this file */
|
||
void InitMPBlock(Ptr, u_size);
|
||
void InitHeapRoutines(void);
|
||
long ProcessMgrHMakeMoreMasters(void);
|
||
|
||
#pragma segment INIT
|
||
|
||
/* InitHeapRoutines. Both the 24-bit and 32-bit heap management segments are pre-loaded.
|
||
* Now that we know the MMU mode, we can set up the routine vectors based on the
|
||
* appropriate segment, and release the other segment.
|
||
* NOTE: We preload both because the system heap is not growable when this routine is
|
||
* called, so we might not have room to load the needed segment. Otherwise, it is a
|
||
* Catch-22 because these are the very routines that make the sysheap growable again!
|
||
*/
|
||
void
|
||
InitHeapRoutines(void)
|
||
{
|
||
CodeEntry *pCodeEntry;
|
||
|
||
/* Pick the correct segment to set up the vectors, and the one to release */
|
||
if (In32BitMode)
|
||
{
|
||
InitVectorTable32();
|
||
pCodeEntry = InitVectorTable24;
|
||
}
|
||
else
|
||
{
|
||
InitVectorTable24();
|
||
pCodeEntry = InitVectorTable32;
|
||
}
|
||
|
||
/* Release the segment that was NOT used to set up the vectors */
|
||
ReleaseResource(GET_SCOD_HDL_FROM_FNC_PTR(pCodeEntry));
|
||
}
|
||
|
||
/* InitHeaps. Initialize the system and Process Mgr heaps. The fundamental change we
|
||
* implement is a flexible boundary between the system zone and the Process Mgr zone.
|
||
* To do this, we ensure that non-relocatable or locked blocks occur low in the
|
||
* system heap, or high in the Process Mgr heap. We start the ball rolling here by
|
||
* a) setting a handy flag to force the SYSZONE allocations low, b) moving existing
|
||
* relocatable blocks out of the Process Mgr heap, c) relocating the Process Mgr master
|
||
* pointer block high, and d) taking over the grow zone procedures of the two heaps.
|
||
* The original Process Mgr MP block is turned into a minimum size pointer block at the
|
||
* bottom of the heap. The system grow zone procedure will use a SetPtrSize on this
|
||
* block to get enough space to extend its zone into the bottom of the Process Mgr zone.
|
||
*
|
||
* NOTE: Error checking should be done in here!
|
||
* A5 world needed: PROCESSMGRGLOBALS.
|
||
*/
|
||
void
|
||
InitHeaps(void)
|
||
{
|
||
Handle newMasterBlockHdl;
|
||
unsigned short cMoreMasters;
|
||
u_size masterBlockSize;
|
||
|
||
/* Install vectored heap routines */
|
||
InitHeapRoutines();
|
||
|
||
/* Turn on the FNoRvrAlloc flag in the system heap so that all blocks
|
||
* are allocated low from now on.
|
||
*/
|
||
SYSZONE->flags |= FNoRvrAlloc;
|
||
|
||
/* Create new master pointer block for Process Mgr heap. First, adjust the MP
|
||
* growth increment to be at least enough to allocate the entire zone given
|
||
* an average block sizes of ZONE_BYTES_PER_MP. Then, allocate a block of
|
||
* corresponding size, move it high in the heap, and replace the free master
|
||
* pointer list with the contents of the new block. This gets the master
|
||
* pointers up high like everyone else. Note that at this point there should
|
||
* be no master pointers in use from the old set.
|
||
*/
|
||
cMoreMasters = (APPLZONE->bkLim - (Ptr)APPLZONE)/ZONE_BYTES_PER_MP;
|
||
if ((unsigned short) (APPLZONE->moreMast) < cMoreMasters)
|
||
APPLZONE->moreMast = cMoreMasters;
|
||
else
|
||
cMoreMasters = APPLZONE->moreMast;
|
||
|
||
newMasterBlockHdl = NewHandle(masterBlockSize = cMoreMasters * sizeof(Ptr));
|
||
MoveHHi(newMasterBlockHdl);
|
||
InitMPBlock(*newMasterBlockHdl,masterBlockSize);
|
||
|
||
/* Make the old master block just a token block. Assumes block shrinks downward.
|
||
* This block becomes the "dummyBlock" we SetPtrSize on when we want to grow or
|
||
* shrink ProcessMgrZone.
|
||
*/
|
||
SetPtrSize(ZoneMPBlockPtr(APPLZONE), SIZEOF_SMALLEST_PTR_DATUM);
|
||
|
||
/* The size of the system heap is only indirectly dependent on the size of memory.
|
||
* It is more a function of how many resources can be brought in from the sys res
|
||
* file. However, the current amount is definitely not enough. Therefore, we
|
||
* should add on only a fraction of the old cMoreMasters.
|
||
*/
|
||
if (SYSZONE->moreMast <= MAX_TOO_FEW_SYS_MPS)
|
||
{
|
||
SYSZONE->moreMast += (cMoreMasters >> 2);
|
||
THEZONE = SYSZONE;
|
||
MoreMasters();
|
||
THEZONE = APPLZONE;
|
||
}
|
||
|
||
/* Set the grow zone procedures for the system and Process Mgr heaps. Saves
|
||
* the previous setting for the system heap so we can chain to it.
|
||
*/
|
||
oldSysGZProc = SYSZONE->gzProc;
|
||
SYSZONE->gzProc = ROUTINE_ADDR(ProcessMgrSysGZProc);
|
||
APPLZONE->gzProc = ROUTINE_ADDR(ProcessMgr_GZProc_Glue);
|
||
|
||
/* And set the pertinent globals */
|
||
ProcessMgrZone = APPLZONE;
|
||
}
|
||
|
||
#pragma segment zone_tools
|
||
|
||
/* Initialize the non-relocatable block to be all MPs on the free list of THEZONE.
|
||
* Must use mode check instead of vector for MakeNonRelocatable call, because during
|
||
* startup the zone tool segment can not be loaded until after we're called.
|
||
* Assumes a5 == PROCESSMGRGLOBALS.
|
||
*/
|
||
void
|
||
InitMPBlock(Ptr masterBlock, u_size masterBlockSize)
|
||
{
|
||
Ptr *pMPCurrent, *pMPPrev;
|
||
|
||
/* Point zone free list at last long word in block */
|
||
pMPPrev = pMPCurrent = (Ptr *) (masterBlock + masterBlockSize);
|
||
--pMPPrev;
|
||
THEZONE->hFstFree = (Ptr)pMPPrev;
|
||
|
||
/* queue all MPs in block */
|
||
while (pMPPrev > (Ptr *)masterBlock)
|
||
*--pMPCurrent = --pMPPrev;
|
||
|
||
/* zero out last MP (lowest long in block) */
|
||
*pMPPrev = nil;
|
||
|
||
/* Turn block into a non-relocatable one. */
|
||
if (In32BitMode)
|
||
MakeNonRelocatable32(pMPPrev);
|
||
else
|
||
MakeNonRelocatable24(pMPPrev);
|
||
}
|
||
|
||
#ifdef MM_DEBUG
|
||
typedef pascal void (*PurgeProcPtr) (Handle hdl);
|
||
Ptr oldPurgeProc;
|
||
|
||
pascal void
|
||
DebugNotifyPurge(Handle hdl)
|
||
{
|
||
unsigned long olda5;
|
||
|
||
olda5 = ProcessMgrA5SimpleSetup();
|
||
|
||
*((Handle *) 0xF2) = hdl;
|
||
dbmsg("Purging hdl at $F2...");
|
||
|
||
if (oldPurgeProc != nil)
|
||
(*((PurgeProcPtr)oldPurgeProc))(hdl);
|
||
|
||
A5SimpleRestore(olda5);
|
||
}
|
||
#endif MM_DEBUG
|
||
|
||
/* ProcessMgrSysGZProc. Grow zone procedure for system heap. Call the old proc and if
|
||
* that fails try shrinking the Process Mgr heap and then growing the system heap.
|
||
* Attempts to get a little more than needed in case someone is doing a series of
|
||
* allocations.
|
||
* A5 world needed: None (Sets up PROCESSMGRGLOBALS where needed).
|
||
*/
|
||
pascal long
|
||
ProcessMgrSysGZProc(unsigned long cbNeeded)
|
||
{
|
||
Ptr localOldSysGZProc;
|
||
long retval;
|
||
unsigned long firstStab, olda5;
|
||
|
||
/* Make sure cbNeeded is big enough and longword aligned. If cbNeeded is too large
|
||
* (0xFFFFFFFD to 0xFFFFFFFF), the result of LONG_ALIGN will be 0. We quickly
|
||
* return 0 because we know it can't be got. Weird, nearly 0xFFFFFFFF, values
|
||
* usually happen when somebody subtracts a larger value from a smaller value
|
||
* to calculate the size to request.
|
||
*/
|
||
if (cbNeeded < MINGROWTH)
|
||
cbNeeded = MINGROWTH;
|
||
else if (LONG_ALIGN(cbNeeded) == 0)
|
||
return(0);
|
||
|
||
/* Get old grow zone proc */
|
||
retval = 0;
|
||
olda5 = ProcessMgrA5SimpleSetup();
|
||
localOldSysGZProc = oldSysGZProc;
|
||
A5SimpleRestore(olda5);
|
||
|
||
/* Call old routine w/ olda5 */
|
||
if (localOldSysGZProc != nil)
|
||
retval = CALL_FNC_PTR(GZProcPtr, localOldSysGZProc, (cbNeeded));
|
||
|
||
/* If that didn't get enough, try to shrink the Process Mgr heap. */
|
||
if (retval == 0)
|
||
{
|
||
olda5 = ProcessMgrA5SimpleSetup();
|
||
|
||
/* Try for a little extra, but fall back to required amount */
|
||
if ( ((firstStab = (cbNeeded + EXTRA_SLOP_FOR_NEXT_TIME)) < cbNeeded) ||
|
||
((retval = ShrinkProcessMgrZone(firstStab)) == 0) )
|
||
retval = ShrinkProcessMgrZone(cbNeeded);
|
||
|
||
/* If it worked, grow the system heap by the amount we got */
|
||
if (retval != 0)
|
||
ExtendZone(SYSZONE, retval);
|
||
|
||
A5SimpleRestore(olda5);
|
||
}
|
||
|
||
return(retval);
|
||
}
|
||
|
||
/* ShrinkSysHeap. Try to shrink the system heap by exactly cbNeeded bytes, but
|
||
* ensure we don't leave less than a minimum sized free block at the top.
|
||
* NOTE: Have to disable the sys gz proc to stop it from trying to shrink the
|
||
* Process Mgr heap, leading to an unwanted recursion. This should be changed so
|
||
* that the old sys gz proc would still be called.
|
||
*
|
||
* Returns the amount we were able to get (0 on failure).
|
||
* Assumes A5 == PROCESSMGRGLOBALS
|
||
*/
|
||
unsigned long
|
||
ShrinkSysHeap(unsigned long cbNeeded)
|
||
{
|
||
register u_size topSize;
|
||
register THz sysZone;
|
||
FreeBlockAddr pTopBlock;
|
||
THz theZone;
|
||
Ptr saveGZProc;
|
||
unsigned long realCBNeeded;
|
||
|
||
/* Don't even try if the amount is so big we overflow when trying to leave space */
|
||
if ((realCBNeeded = cbNeeded + GetExpandMemMinSysHeapFreeSpace()) < cbNeeded)
|
||
return(0);
|
||
|
||
/* Move to our zone and disable the growzone procedure
|
||
* NOTE: Assumes that this can not move ProcessMgrZone, or that THEZONE != ProcessMgrZone,
|
||
* since THEZONE is restored unconditionally, instead of using SafeSetZone and
|
||
* SafeRestoreZone.
|
||
*/
|
||
theZone = THEZONE;
|
||
THEZONE = sysZone = SYSZONE;
|
||
saveGZProc = sysZone->gzProc;
|
||
sysZone->gzProc = nil;
|
||
|
||
/* Torture the system heap to get a large enough free block on top */
|
||
(void)CompactMem(compactAll);
|
||
|
||
/* Did we get enough at the very top? */
|
||
topSize = GetTopBlockSize(pTopBlock = GetHighestLargeFreeBlock(0, nil));
|
||
if (topSize <= realCBNeeded)
|
||
{
|
||
long purgeResult;
|
||
|
||
/* Now try purging (then recompact to coelesce space that was freed up). */
|
||
(void)MaxMem(&purgeResult);
|
||
(void)CompactMem(compactAll);
|
||
|
||
/* Again, did we get enough at the very top? */
|
||
topSize = GetTopBlockSize(pTopBlock = GetHighestLargeFreeBlock(0, nil));
|
||
if (topSize <= realCBNeeded)
|
||
{
|
||
sysZone->gzProc = saveGZProc;
|
||
THEZONE = theZone;
|
||
return(0);
|
||
}
|
||
}
|
||
|
||
/* If we're here, pTopBlock points to a free block at the very top of the system
|
||
* zone, and the block size is at least as big as caller asked for. Reduce that
|
||
* free block by the surplus, and create a new zone trailer block immediately
|
||
* following it. Update zone header appropriately.
|
||
*/
|
||
sysZone->bkLim = (Ptr) FreeTop(pTopBlock, topSize - cbNeeded);
|
||
sysZone->zcbFree -= cbNeeded;
|
||
|
||
/* Cleanup and return */
|
||
sysZone->gzProc = saveGZProc;
|
||
THEZONE = theZone;
|
||
|
||
return(cbNeeded);
|
||
}
|
||
|
||
/* Globals to communicate between ProcessMgr_GZProc_Glue() and LowerProcessMgrZoneBottom(). */
|
||
THz oldProcessMgrZone, newProcessMgrZone;
|
||
|
||
/* Lower the bottom of the Process Mgr heap by cbLower bytes, where
|
||
* cbLower >= MINGROWTH.
|
||
* Assumes A5 = PROCESSMGRGLOBALS
|
||
* NOTE: Have to handle case where old ptr had size correction.
|
||
*/
|
||
void
|
||
LowerProcessMgrZoneBottom(unsigned long cbLower)
|
||
{
|
||
long VMResult;
|
||
|
||
/* Set globals do glue can see them */
|
||
oldProcessMgrZone = ProcessMgrZone;
|
||
newProcessMgrZone = (Ptr)oldProcessMgrZone - cbLower;
|
||
|
||
/* Move the zone */
|
||
MoveZone(oldProcessMgrZone, newProcessMgrZone);
|
||
|
||
/* Let VM know what happened. VM needs to "hold down" the entire system heap. Since
|
||
* we just shrank that heap, VM can reassess the heap, most likely unholding the area
|
||
* we removed.
|
||
*/
|
||
if ((Gestalt(gestaltVMAttr,&VMResult) == noErr) && (VMResult & (1 << gestaltVMPresent)))
|
||
(void) VM_HoldSystemHeap();
|
||
}
|
||
|
||
/* ProcessMgrGZProc. Grow zone procedure for Process Mgr heap. Try shrinking the
|
||
* system heap from the top so we can lower the bottom of the ProcessMgrZone. The actual
|
||
* amount grown might be more than asked for, because we add extra slop to forestall
|
||
* doing this again.
|
||
* NOTE: This changes the bottom of the heap!! After this is called, any pointers to
|
||
* the bottom of the heap must be reset to THEZONE. In fact, the rudest example of
|
||
* this is the gzProc glue must reset the memory manager's A6 on the stack. The memory
|
||
* manager uses A6 to hold the zone pointer relevant to the current call.
|
||
* A5 world needed: None (Sets up PROCESSMGRGLOBALS where needed).
|
||
*/
|
||
pascal unsigned long
|
||
ProcessMgrGZProc(unsigned long cbNeeded)
|
||
{
|
||
unsigned long olda5, firstStab, retval;
|
||
|
||
/* Make sure cbNeeded is big enough and longword aligned. If cbNeeded is too large
|
||
* (0xFFFFFFFD to 0xFFFFFFFF), the result of LONG_ALIGN will be 0. We quickly
|
||
* return 0 because we know it can't be got. Weird, nearly 0xFFFFFFFF, values
|
||
* usually happen when somebody subtracts a larger value from a smaller value
|
||
* to calculate the size to request.
|
||
*/
|
||
if (cbNeeded < MINGROWTH)
|
||
cbNeeded = MINGROWTH;
|
||
else if (LONG_ALIGN(cbNeeded) == 0)
|
||
return(0);
|
||
|
||
/* Squeeze blood from a stone, return 0 if it can't be done */
|
||
olda5 = ProcessMgrA5SimpleSetup();
|
||
if ( ((firstStab = (cbNeeded + EXTRA_SLOP_FOR_NEXT_TIME)) < cbNeeded) ||
|
||
((retval = ShrinkSysHeap(firstStab)) == 0) )
|
||
retval = ShrinkSysHeap(cbNeeded);
|
||
|
||
/* If it worked, move our zone header down and free up the difference. */
|
||
if (retval != 0)
|
||
LowerProcessMgrZoneBottom(retval);
|
||
|
||
A5SimpleRestore(olda5);
|
||
return(retval);
|
||
}
|
||
|
||
/* ReclaimSpaceIfSysHeapLow. Check whether, after the allocation of the specified handle,
|
||
* the space available to the system heap is below our predefined threshold. If it is,
|
||
* we try to grow the system heap to get the emMinSysHeapFreeSpace, or at least as much
|
||
* as we can.
|
||
*/
|
||
void
|
||
ReclaimSpaceIfSysHeapLow(Handle *theNewHandle)
|
||
{
|
||
Boolean handleInStraddle;
|
||
u_size lowPM, highSys, roomInPM;
|
||
|
||
/* Make dirtcheap check first to avoid expensive scan of the more popular heap. */
|
||
if (GetProcessMgrLoFreeBytes(nil, nil) >= GetExpandMemMinSysHeapFreeSpace())
|
||
return;
|
||
|
||
/* Just leave if there is sufficient memory or if the handle is not in the bottom
|
||
* part of the Process Mgr heap where it can be usefully reclaimed.
|
||
*/
|
||
if ( (StraddleBlockSize(&lowPM, &highSys, theNewHandle, &handleInStraddle) != 0) ||
|
||
(handleInStraddle == false) )
|
||
return;
|
||
|
||
/* We can get more if we dispose the new handle first. Bound sys heap growth to the
|
||
* emMinSysHeapFreeSpace, since freeing the block may give us more than we need.
|
||
*/
|
||
roomInPM = lowPM + (u_size) GetHandleSize(theNewHandle);
|
||
LogicalToPhysicalSize(roomInPM);
|
||
if (highSys + roomInPM > GetExpandMemMinSysHeapFreeSpace())
|
||
roomInPM = GetExpandMemMinSysHeapFreeSpace() - highSys;
|
||
|
||
DisposHandle(theNewHandle);
|
||
if ((roomInPM = ShrinkProcessMgrZone(roomInPM)) != 0)
|
||
ExtendZone(SYSZONE, roomInPM);
|
||
MEMERROR = memFullErr;
|
||
*theNewHandle = nil;
|
||
}
|
||
|
||
/* ProcessMgrHiNewHandle. Allocate a new handle high up in the heap. A handle is allocated as
|
||
* high in the heap as possible, except that it will be kept below any non-relocatable
|
||
* blocks (this does not mean locked handles) not including the first block in the
|
||
* heap. This last part is done to allow for Excel's ceiling pointer.
|
||
* Assumes A5 = PROCESSMGRGLOBALS
|
||
* NOTE: Funky resursion when we call ProcessMgrHMakeMoreMasters and it calls us.
|
||
*/
|
||
|
||
#define MAX_ALLOC_TRIES 4 /* Limit of purge-growzone-compact cycle */
|
||
|
||
Handle
|
||
ProcessMgrHiNewHandle(u_size size, Ptr lockPtr)
|
||
{
|
||
FreeBlockAddr freeAddr;
|
||
u_size largestFreeBlock;
|
||
u_size blockHeaderSize;
|
||
unsigned long bytesNeeded, bytesGrown, purgeResult;
|
||
short sizeCorrection, allocTries;
|
||
Handle theHandle;
|
||
THz theZone;
|
||
|
||
/* Switch to the appropriate zone */
|
||
SafeSetZone(theZone, ProcessMgrZone);
|
||
|
||
/* Check for enough MPs, but don't get one until after the heap has settled. */
|
||
theHandle = nil;
|
||
if ((THEZONE->hFstFree == nil) && ((MEMERROR = ProcessMgrHMakeMoreMasters()) < 0))
|
||
goto Return_theHandle;
|
||
|
||
/* Return error if requested size is larger than Memory Mgr allows */
|
||
if (size > MAX_LOGICAL_SIZE)
|
||
{
|
||
MEMERROR = paramErr;
|
||
goto Return_theHandle;
|
||
}
|
||
|
||
/* Enforce size minimum and alignment, set size correction appropriately. */
|
||
|
||
// 68040 memory manager (Terror ROM) has been patched to quadword align
|
||
// all blocks. we have to do this in our blocks too so that we're
|
||
// consistent and also remove the possibility of physical size of the block
|
||
// being > 16 bytes beyond logical end of block.
|
||
|
||
bytesNeeded = size;
|
||
if (size < MIN_LOGICAL_SIZE)
|
||
size = MIN_LOGICAL_SIZE;
|
||
else {
|
||
if (CPUFLAG == 4) { // <12>
|
||
blockHeaderSize = In32BitMode ? SIZEOF_32BIT_BLOCK_HEADER : SIZEOF_24BIT_BLOCK_HEADER; // <12> Use the right block header size depending on the memory mode weÕre in.
|
||
size += blockHeaderSize; // <12> Add in the size of the block header, so that
|
||
QUAD_ALIGN(size); // <12> when the size of the block header is added in
|
||
size -= blockHeaderSize; // <12> ChipOffRelocFromFree, the block will stay quad aligned.
|
||
} // <12>
|
||
else // <12>
|
||
LONG_ALIGN(size);
|
||
}
|
||
|
||
sizeCorrection = size - bytesNeeded;
|
||
|
||
/* Compact memory before anything else. */
|
||
(void)CompactMem(compactAll);
|
||
|
||
/* Loop a limited amount to allocate block.
|
||
* NOTE: bytesNeeded has a different meaning inside this loop. Above, it was used
|
||
* as a temp to figure sizeCorrection. Inside here, it is used to figure the amount
|
||
* to ask the gzProc for. It's convenient that above use provides a valid initial
|
||
* value of bytesNeeded for the (bytesGrown >= bytesNeeded) check.
|
||
*/
|
||
bytesGrown = 0;
|
||
for (allocTries = MAX_ALLOC_TRIES+1; allocTries; --allocTries)
|
||
{
|
||
/* Compact memory if GZ got a worthwhile amount. */
|
||
if (bytesGrown >= bytesNeeded)
|
||
{
|
||
bytesGrown = 0;
|
||
(void)CompactMem(compactAll);
|
||
}
|
||
|
||
/* Is there a big enough block right now? */
|
||
if ((freeAddr = GetHighestLargeFreeBlock(size, lockPtr)) != nil)
|
||
break;
|
||
|
||
/* Allocation try failed, so try purging. Get out if it worked. Otherwise,
|
||
* ask for remainder from grow zone procedure.
|
||
* NOTE: Of course, growing the zone won't help much unless that biggest block
|
||
* can be merged with the added zone area (i.e. there are no intervening locked
|
||
* or non-relocatable blocks). Should we be smarter here (like look for the
|
||
* free block closest to the end of the zone that grows)?
|
||
* NOTE: Don't trust MaxMem return value if we have additional requirement
|
||
* that memory be allocated below a ceiling. Unconditionally ask the GZ proc
|
||
* for the full amount.
|
||
*/
|
||
if (size <= (largestFreeBlock = MaxMem(&purgeResult)))
|
||
{
|
||
if (lockPtr == nil)
|
||
continue;
|
||
bytesNeeded = size;
|
||
}
|
||
else
|
||
bytesNeeded = size - largestFreeBlock;
|
||
|
||
/* At this point we were able to get a block that isn't quite big enough.
|
||
* Call grow zone procedure. Give up if we get nothing out of it.
|
||
*/
|
||
if ( (THEZONE->gzProc == nil)
|
||
|| ((bytesGrown = CALL_FNC_PTR(GZProcPtr, THEZONE->gzProc, (bytesNeeded))) == 0) )
|
||
break;
|
||
}
|
||
|
||
/* Could not find a big enough block */
|
||
if (freeAddr == nil)
|
||
{
|
||
MEMERROR = memFullErr;
|
||
goto Return_theHandle;
|
||
}
|
||
|
||
/* freeAddr is a FreeBlockAddr of a block that's at least big enough (probably
|
||
* a lot bigger!). Take what we need from the high end of it.
|
||
*/
|
||
theHandle = (Handle) THEZONE->hFstFree;
|
||
assert(theHandle != nil);
|
||
THEZONE->hFstFree = *theHandle;
|
||
ChipOffRelocFromFree(freeAddr, theHandle, size, sizeCorrection);
|
||
|
||
/* Make sure we're not taking too much. */
|
||
ReclaimSpaceIfSysHeapLow(&theHandle);
|
||
|
||
Return_theHandle:
|
||
SafeRestoreZone(theZone);
|
||
return(theHandle);
|
||
}
|
||
|
||
/* ProcessMgrHMakeMoreMasters. Allocate a new master ptr block for THEZONE.
|
||
* NOTE: Uses a fake master pointer (on the stack) in order for ProcessMgrHiNewHandle to allocate
|
||
* the MP block. Goes out of use when block is converted to non-relocateble. Might be
|
||
* better to have a HiFreeBlock routine that would be called here and by ProcessMgrHiNewHandle.
|
||
* The free block could then be converted to either a relocatble or non-relocatable.
|
||
* Assumes A5 == PROCESSMGRGLOBALS
|
||
*/
|
||
long
|
||
ProcessMgrHMakeMoreMasters(void)
|
||
{
|
||
u_size mpBlockSize;
|
||
Handle mpBlockHdl;
|
||
Ptr fakeMP = nil;
|
||
|
||
assert(THEZONE->hFstFree == nil);
|
||
|
||
/* Allocate the MP block high and lock it */
|
||
mpBlockSize = THEZONE->moreMast * sizeof(Ptr);
|
||
THEZONE->hFstFree = &fakeMP;
|
||
mpBlockHdl = ProcessMgrHiNewHandle(mpBlockSize, nil);
|
||
if (MEMERROR != noErr)
|
||
return(MEMERROR);
|
||
|
||
/* Convert block data to free MP list, and make the block unrelocatable
|
||
* (with the implication that fakeMP will no longer be needed).
|
||
*/
|
||
InitMPBlock(*mpBlockHdl, mpBlockSize);
|
||
|
||
return(noErr);
|
||
}
|
||
|
||
#pragma segment Main
|
||
|
||
/* In order to set the growzone proc for everybody except Write 4.5 and 4.6,
|
||
* which both have a serious Process Mgr bug which causes the gz proc to ALWAYS
|
||
* return non-0.
|
||
* NOTE: This travesty should be taken out as soon as Write is rev'ed
|
||
*/
|
||
#define IS_MACWRITE_46_OR_45(pProc) ((pProc)->p_signature == 'MACA' && (pProc)->p_version <= '0460')
|
||
|
||
/* Grow zone procedure for application heaps. Checks for special case of running
|
||
* on Process Mgr stack and if so tries to grow the heap itself since the memory
|
||
* manager looks at HEAPEND instead of APPLZONE->bkLim against APPLLIMIT as the
|
||
* grow constraint.
|
||
* A5 world needed: None (Sets up own to get Process Mgr globals).
|
||
*/
|
||
pascal long
|
||
MyGrowZone(unsigned long cbNeeded)
|
||
{
|
||
THz applZone;
|
||
ProcPtr gzProc;
|
||
Ptr oldHeapEnd;
|
||
long retval;
|
||
PEntryPtr pProc;
|
||
unsigned long olda5;
|
||
extern Boolean inLauncheesWorld;
|
||
|
||
/* Make sure cbNeeded is big enough and longword aligned. If cbNeeded is too large
|
||
* (0xFFFFFFFD to 0xFFFFFFFF), the result of LONG_ALIGN will be 0. We quickly
|
||
* return 0 because we know it can't be got. Weird, nearly 0xFFFFFFFF, values
|
||
* usually happen when somebody subtracts a larger value from a smaller value
|
||
* to calculate the size to request.
|
||
*/
|
||
if (cbNeeded < MINGROWTH)
|
||
cbNeeded = MINGROWTH;
|
||
else if (LONG_ALIGN(cbNeeded) == 0)
|
||
return(0);
|
||
|
||
retval = 0;
|
||
olda5 = ProcessMgrA5SimpleSetup();
|
||
applZone = APPLZONE;
|
||
|
||
/* Check global indicating we are using the kernelstack. If it's non-zero,
|
||
* assume we are the one causing the heap to grow. Do our cheap extension
|
||
* bounded by APPLLIMIT.
|
||
*/
|
||
if ((kernelbusy != 0) && ((cbNeeded + (unsigned long) applZone->bkLim) < APPLLIMIT))
|
||
{
|
||
ExtendZone(applZone, cbNeeded);
|
||
A5SimpleRestore(olda5);
|
||
retval = 1;
|
||
}
|
||
else
|
||
{
|
||
/* Don't call gz proc if launching another (or ever for Write <= 4.6) */
|
||
pProc = pCurrentProcess;
|
||
gzProc = (inLauncheesWorld || IS_MACWRITE_46_OR_45(pProc))
|
||
? nil : (*(pProc->p_pcb))->p_gzproc;
|
||
A5SimpleRestore(olda5);
|
||
|
||
/* Call user routine. We restore HEAPEND in case we are running off the
|
||
* ProcessMgr stack (which means HEAPEND has been modified).
|
||
*/
|
||
if (gzProc != nil)
|
||
{
|
||
oldHeapEnd = HEAPEND;
|
||
HEAPEND = applZone->bkLim;
|
||
retval = CALL_FNC_PTR(GZProcPtr, gzProc, (cbNeeded));
|
||
HEAPEND = oldHeapEnd;
|
||
}
|
||
}
|
||
|
||
return(retval);
|
||
}
|
||
|
||
/* SafeSetGrowZone. The C language part of our patch to SetGrowZone. Record caller's
|
||
* gzProc so MyGrowZone can chain to it, if necessary. Also, don't let applications set
|
||
* the growzone for the system heap or the Process Mgr heap, since those cases are too
|
||
* tricky for them to do right. Returns chain-through address so assembly-langyage part
|
||
* of the trap can call it with proper register setup. Returns nil if chain-through
|
||
* should not be done.
|
||
* NOTE: Ensures that the "user" gzProc isn't our own! This happens if an application
|
||
* tries to save/restore the gzProc by a direct fetch (since no GetGrowZone exists!)
|
||
* and a later SetGrowZone with what he found. Letting this happen would result in
|
||
* an infinite loop as our growzone chains to p_gzproc.
|
||
* NOTE: Assumes that there is no such thing as _SetGrowZone, SYS.
|
||
*/
|
||
Ptr
|
||
SafeSetGrowZone(ProcPtr gzProc)
|
||
{
|
||
register THz theZone;
|
||
Ptr retval;
|
||
unsigned long olda5;
|
||
|
||
olda5 = ProcessMgrA5SimpleSetup();
|
||
|
||
/* Record app's APPLZONE gzProc */
|
||
retval = nil;
|
||
if ((theZone = THEZONE) == APPLZONE)
|
||
{
|
||
if ((ProcPtr)(StripAddress(gzProc)) != MyGrowZone)
|
||
(*(pCurrentProcess->p_pcb))->p_gzproc = gzProc;
|
||
}
|
||
|
||
/* Prevent meddling in the system and Process Mgr heaps */
|
||
else if ((theZone != SYSZONE) && (theZone != ProcessMgrZone))
|
||
retval = (Ptr) patchtraps[SETGROWZONE].oldtrap;
|
||
|
||
A5SimpleRestore(olda5);
|
||
return(retval);
|
||
}
|
||
|
||
/* StraddleBlockSize. Calculate the number of free/purgeable bytes in the area of
|
||
* memory shared by the System and Process Mgr heaps. This is a physical count.
|
||
*/
|
||
u_size
|
||
StraddleBlockSize(u_size *lowPMStorage, u_size *highSysStorage, Handle blockOfInterest, Boolean *sawInterestingBlock)
|
||
{
|
||
u_size blockSize;
|
||
|
||
assert(lowPMStorage != nil);
|
||
|
||
/* Figure out how much we can get from the top of the system heap, and
|
||
* add in what we can get from the bottom of the Process Mgr heap.
|
||
*/
|
||
blockSize = GetSystemHiFreeBytes();
|
||
if (highSysStorage != nil)
|
||
*highSysStorage = blockSize;
|
||
blockSize += *lowPMStorage = GetProcessMgrLoFreeBytes(blockOfInterest, sawInterestingBlock);
|
||
|
||
/* Allow for the fact that we must leave space at the top of the system heap */
|
||
if (blockSize < GetExpandMemMinSysHeapFreeSpace() + MIN_PHYSICAL_SIZE_EITHER_MODE)
|
||
blockSize = 0;
|
||
else
|
||
blockSize -= GetExpandMemMinSysHeapFreeSpace();
|
||
|
||
return(blockSize);
|
||
}
|
||
|
||
/* ProcessMgrMaxBlock. Act like a MaxBlock() on the Process Mgr heap, except that we
|
||
* count purgeable space.
|
||
*/
|
||
u_size
|
||
ProcessMgrMaxBlock(void)
|
||
{
|
||
u_size straddleBlockSize, hiMaxBlock, junk, headerBytes;
|
||
|
||
/* Calculate largest block above the straddle area. */
|
||
hiMaxBlock = GetProcessMgrHiMaxBlock();
|
||
|
||
/* Calculate amount that we can get by combining the top of the system heap
|
||
* with the bottom of the Process Mgr zone. StraddleBlockSize() result is
|
||
* physical block size. ProcessMgrMaxBlock returns the maximum *logical* block size
|
||
* that could be requested of TempNewHandle. Since TempNewHandle actually leaves
|
||
* us with two blocks, one at the top of the system heap, and one block
|
||
* at the bottom of the Process Mgr heap, we subtract two header sizes from the
|
||
* physical size.
|
||
*/
|
||
straddleBlockSize = StraddleBlockSize(&junk, nil, nil, nil);
|
||
headerBytes = (In32BitMode) ? 2*SIZEOF_32BIT_BLOCK_HEADER : 2*SIZEOF_24BIT_BLOCK_HEADER;
|
||
if (straddleBlockSize > headerBytes)
|
||
straddleBlockSize -= headerBytes;
|
||
else
|
||
straddleBlockSize = 0;
|
||
|
||
/* Result is the larger of the two. */
|
||
return((straddleBlockSize > hiMaxBlock) ? straddleBlockSize : hiMaxBlock);
|
||
}
|