home *** CD-ROM | disk | FTP | other *** search
- /***
- * vmphys.c -
- *
- * Copyright (c) 1989-1992, Microsoft Corporation. All rights reserved.
- *
- *Purpose:
- *
- * PUBLIC Functions:
- *
- * VpAllocatePage:
- *
- * VpReleasePage:
- *
- * PVmLoadVp:
- *
- *******************************************************************************/
-
- #pragma title("Virtual Memory Manager - Physical Memory Management")
-
- #include <version.h>
- #include <system.h>
- #include <error.h>
- #include <vmassert.h>
- #include <ems.h>
- #include <xms.h>
-
- #if !defined(DOS)
- #error DOS must be defined
- #endif
-
- #include <vm.h>
- #include <vmp.h>
-
- #include <limits.h>
- #include <malloc.h>
- #include <stdlib.h>
-
- static unsigned _near _cVmUmb = 0; /* Number of UMBs allocated */
- static _segment _near rgsegVmUmb[cVmUmbMax-1]; /* Segments of allocated UMBs */
- static unsigned _near rgcPageVmUmb[cVmUmbMax-1]; /* Count of pages in each UMB */
-
- unsigned _near _cPageDos; /* Count of DOS pages (EMS/UMB not included) */
- unsigned _near _cVmPage; /* Count of non-EMS pages allocated */
- _segment _near _segVmDos; /* Segment of DOS heap allocation */
-
- HPGD _near _rghpgdHash[ipgdHashMax];
-
- #pragma page()
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
- * CPageVmAllocateUmbs
- *
- * This function allocates XMS upper memory blocks (UMBs) for use
- * as physical pages for paging.
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
- * Entry Conditions:
- *
- * DOS only.
- *
- * Exit Conditions:
- *
- * The global variables _cVmUmb, rgsegVmUmb, and rgcPageVmUmb are
- * initialized. The function returns the total number of pages that
- * can be created from the UMBs allocated. There is a compile time
- * limit (cVmUmbMax) on the number of UMBs that can be allocated. The
- * size of these UMBs and therefore the number of pages that can be
- * allocated is unlimited.
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
- unsigned LOCAL __CPageVmAllocateUmbs(void)
- {
- unsigned cPageUmb;
- ERR err;
-
- cPageUmb = 0; /* No UMB pages allocated */
-
- if (__FXmsCheckInstalled())
- while (_cVmUmb < cVmUmbMax - 1)
- {
- _segment segDummy;
- unsigned cPara;
-
- /* To allocate a UMB, first request a block that is too large. */
- /* Though this will fail, it returns the size of the largest. */
- /* block available. This is used to request a reasonable sized */
- /* block that will be broken into one or more physical pages */
-
- cPara = 0xffff;
- err = __ErrXmsRequestUpperMemoryBlock(&segDummy, (unsigned short _far *) &cPara);
- if ((err != errXmsSmallerUMBIsAvailable) || (cPara < cbVmPage / 16))
- break;
-
- /* Use the cPara field returned and round to a page boundary. */
-
- rgcPageVmUmb[_cVmUmb] = cPara / cParaPerPage;
- cPara = rgcPageVmUmb[_cVmUmb] * cParaPerPage;
- err = __ErrXmsRequestUpperMemoryBlock(&rgsegVmUmb[_cVmUmb], (unsigned short _far *) &cPara);
- Assert(err == errNoError);
- if (err != errNoError) /* Just to be safe */
- break;
-
- cPageUmb += rgcPageVmUmb[_cVmUmb];
- _cVmUmb++; /* UMB successfully allocated */
- }
-
- return(cPageUmb);
- }
-
- #pragma page()
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
- * VmFreeUmbs
- *
- * This function releases the XMS upper memory blocks (UMBs)
- * allocated in CPageVmAllocateUmbs.
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
- * Entry Conditions:
- *
- * DOS only.
- *
- * Exit Conditions:
- *
- * The global variable _cVmUmb is reset to zero.
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
- void LOCAL __VmFreeUmbs(void)
- {
- ERR err;
-
- while (_cVmUmb)
- {
- err = __ErrXmsReleaseUpperMemoryBlock(rgsegVmUmb[--_cVmUmb]);
- Assert(err == errNoError);
- }
- }
-
- #pragma page()
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
- * FVmInitializePhys
- *
- * This function initializes the physical memory manager.
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
- * Entry Conditions:
- *
- * DOS only.
- *
- * The parameter cVmParaMin specifies the minumum number of paras
- * of physical memory to be allowed. If this number of pages can not
- * be allocated, the initialization fails.
- *
- * The parameter cParaVmDosMax specifies the maximum number of
- * paragraphs to be allocated from conventional DOS memory. If the
- * value is 0, the default of 0xffff is used.
- *
- * The physical memory manager must not have been previously
- * initialized without an intermediate call to VmTerminatePhys.
- *
- * Exit Conditions:
- *
- * The function returns TRUE if the initialization is successful.
- * Otherwise it returns FALSE. Initialization can fail if there is
- * not enough available DOS memory to allocate the miniumum number of
- * pages or if there is not enough memory in the near heap to allocate
- * the page descriptors for the minimum number of pages.
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
- int PRIVATE __FVmInitializePhys(unsigned cVmParaMin, unsigned cParaVmDosMax)
- {
- unsigned cPageUmb;
- unsigned cParaDos;
- HPGD hpgd;
- _segment segPage;
- unsigned iPage;
- unsigned iUmb;
- unsigned ipgd;
-
- VmTracePrintf(("FVmInitializePage\n"));
-
- Assert(_osmode == DOS_MODE);
-
- if (cParaVmDosMax == 0) /* Zero indicates no limit */
- cParaVmDosMax = 0xffff;
-
- /* safety belts */
- if (cVmParaMin > cParaVmDosMax)
- return FALSE;
-
- /* Allocate upper memory blocks (UMBs) from an XMS driver. This */
- /* memory is within the 1M address space and is suitable for */
- /* paging */
-
- cPageUmb = __CPageVmAllocateUmbs();
-
- /* Now allocate memory from the DOS heap to use for paging and for */
- /* maintaining the page tables. This memory is allocated as a */
- /* single block so if the heap contains several big blocks, this */
- /* allocation strategy may not be ideal. I don't expect this to */
- /* be a problem. */
-
- /* The size of the block allocated is subject to several constraints. */
- /* The obvious constraint in the availability of memory in the DOS */
- /* heap. In addition, the application may impose an upper limit on */
- /* the consumption of DOS memory as well as a lower limit on the */
- /* number of pages needed in the paging area. */
-
- cParaDos = min(__CParaVmDosAvail(), cParaVmDosMax);
-
- /* The block of DOS memory allocated is divided into pages and page */
- /* descriptors. Each page descriptor requires one paragraph. Each */
- /* page requires (cbVmPage/16) paragraphs. The gives a formula for */
- /* memory required as */
- /* cParaDos = _cPageDos * (cbVmPage/16+1) + cPageUmb + _cVmPageEms;
-
- Assert(sizeof(PGD) == cbVmPara); /* One PGD == 1 Paragraph */
-
- // How many whole pages of DOS and UMB
- _cVmPage = cPageUmb + (cParaDos / cParaPerPage);
-
- // subtract space needed for whole pages of PDs
- _cVmPage -= _cVmPage/cParaPerPage;
-
- // DOS allocated in paragraphs not page units
- // if enough extra paras, use them for PDs, if not get another page
- _cVmPage -= (cParaDos % cParaPerPage) < (_cVmPage % cParaPerPage) ? 1 : 0;
-
- // ensure the amount of space available for swap area is enough
- if (_cVmPage * cParaPerPage < cVmParaMin)
- {
- __VmFreeUmbs();
- return FALSE;
- }
-
- _cPageDos = _cVmPage - cPageUmb;
-
- // Cannot work with only one page of DOS swap area
- if (_cPageDos <= 1)
- {
- __VmFreeUmbs();
- return FALSE;
- }
-
- // Grab only as needed for DOS pages and PD pages
- cParaDos = (_cPageDos * cParaPerPage) + _cVmPage;
-
- _segVmDos = __SegVmDosAllocate(cParaDos);
- if (_segVmDos == _NULLSEG)
- {
- Assert(FALSE); /* This should not be possible */
- __VmFreeUmbs(); /* Free already allocated UMBs */
- return(FALSE); /* Return failure indication */
- }
-
- /* Initialize the PGD structures */
- (unsigned)hpgd = HpgdOfIPage0;
-
- {
- size_t cw;
- short _far *pw = _segVmDos:>((short _based(void) *) 0);
-
- for (cw = 8 * _cVmPage; cw != 0; cw--)
- *pw++ = 0;
- }
- segPage = (_segment) ((unsigned) _segVmDos + _cVmPage);
-
- for (iPage = 0; iPage < _cPageDos; iPage++)
- {
- PpgdOfHpgd(hpgd)->segPage = segPage;
- PpgdOfHpgd(hpgd)->Flags = (unsigned char) (iPage ? 0U : fDiscontinousPgd);
-
- hpgd = HpgdAdd(hpgd, 1);
-
- segPage = (_segment) ((unsigned) segPage) + (cbVmPage / 16);
- }
-
- for (iUmb = 0; iUmb < _cVmUmb; iUmb++)
- {
- segPage = rgsegVmUmb[iUmb]; /* Segment base of this UMB */
- cPageUmb = rgcPageVmUmb[iUmb]; /* Number of pages in this UMB */
-
- for (iPage = 0; iPage < cPageUmb; iPage++)
- {
- PpgdOfHpgd(hpgd)->segPage = segPage;
- PpgdOfHpgd(hpgd)->Flags = (unsigned char) (fUmbPgd | (iPage ? 0U : fDiscontinousPgd));
-
- hpgd = HpgdAdd(hpgd, 1);
-
- segPage = (_segment) ((unsigned) segPage) + (cbVmPage / 16);
- }
- }
-
- /* Initialize hash table */
-
- for (ipgd = 0; ipgd < ipgdHashMax; ipgd++)
- _rghpgdHash[ipgd] = hpgdNil;
-
- return(TRUE);
- }
-
- #pragma page()
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
- * VmTerminatePhys
- *
- * This function releases physical memory resources allocated in
- * FVmInitializePhys.
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
- * Entry Conditions:
- *
- * DOS only.
- *
- * Exit Conditions:
- *
- * None.
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
- void PRIVATE __VmTerminatePhys(void)
- {
- __VmDosFreeSeg(_segVmDos); /* Free DOS heap block */
- __VmFreeUmbs(); /* Free already allocated UMBs */
- }
-
- #pragma page()
-
- #ifdef VMDEBUG
-
- void LOCAL __AssertValidHashTable(void)
- {
- HPGD hpgd;
- unsigned ipgd;
- unsigned cPageAllocated;
- unsigned cPageHashed;
- unsigned cPageDiscardable;
-
- (unsigned)hpgd = HpgdOfIPage0;
- cPageAllocated = 0;
- cPageDiscardable = 0;
-
- for (ipgd = 0; ipgd < _cVmPage; ipgd++)
- {
- /* An unallocated page has timeRef == 0 and cLock == 0 and */
- /* is not marked as dirty. */
-
- Assert((PpgdOfHpgd(hpgd)->Flags & fAllocatedPgd) ||
- ((PpgdOfHpgd(hpgd)->timeRef == 0) &&
- (PpgdOfHpgd(hpgd)->cLock == 0) &&
- !(PpgdOfHpgd(hpgd)->Flags & fDirtyPgd)));
-
- if (PpgdOfHpgd(hpgd)->Flags & fAllocatedPgd)
- cPageAllocated++;
-
- hpgd = HpgdAdd(hpgd, 1);
- }
-
- cPageHashed = 0;
- for (ipgd = 0; ipgd < ipgdHashMax; ipgd++)
- {
- hpgd = _rghpgdHash[ipgd];
- while (hpgd != hpgdNil)
- {
- Assert(ipgd == IpgdHashOfVp(PpgdOfHpgd(hpgd)->vp));
- Assert(PpgdOfHpgd(hpgd)->Flags & fAllocatedPgd);
- hpgd = PpgdOfHpgd(hpgd)->hpgdHashNext;
- cPageHashed++;
- Assert(cPageHashed <= cPageAllocated);
- }
- }
-
- Assert(cPageHashed == cPageAllocated);
- }
-
- #endif /* VMDEBUG */
-
-
- #pragma page()
-
- void PRIVATE __VmWriteDirtyPgd(HPGD hpgd)
- {
- VmTracePrintf(("VmWriteDirtyPgd: hpgd = %04X, vp = %08lX\n",
- (unsigned) hpgd, PpgdOfHpgd(hpgd)->vp));
-
- Assert(PpgdOfHpgd(hpgd)->Flags & fDirtyPgd);
-
- Assert(PpgdOfHpgd(hpgd)->pte & (fEmsPte | fXmsPte | fDiskPte));
-
- if (PpgdOfHpgd(hpgd)->pte & fXmsPte)
- __FVmSwapOutXmsPage(PpgdOfHpgd(hpgd)->pte, hpgd);
-
- else if (PpgdOfHpgd(hpgd)->pte & fEmsPte)
- __FVmSwapOutEmsPage(PpgdOfHpgd(hpgd)->pte, hpgd);
-
- else
- __FVmSwapOutDiskPage(PpgdOfHpgd(hpgd)->pte, hpgd);
-
- PpgdOfHpgd(hpgd)->Flags &= ~fDirtyPgd;
- }
-
- #pragma page()
-
- void PRIVATE __VmRemovePgdFromCache(HPGD hpgd)
- {
- unsigned ipgd;
- HPGD hpgdNext;
- HPGD hpgdPrev;
-
- VmTracePrintf(("VmRemovePgdFromCache: hpgd = %04X, vp = %08lX\n",
- (unsigned) hpgd, PpgdOfHpgd(hpgd)->vp));
-
- Assert(PpgdOfHpgd(hpgd)->Flags & fAllocatedPgd);
-
- #ifdef VMDEBUG
- __AssertValidHashTable();
- #endif
-
- hpgdNext = _rghpgdHash[ipgd = IpgdHashOfVp(PpgdOfHpgd(hpgd)->vp)];
- if (hpgdNext == hpgd)
- _rghpgdHash[ipgd] = PpgdOfHpgd(hpgd)->hpgdHashNext;
-
- else {
- while (hpgdNext != hpgd) {
- Assert(hpgdNext != hpgdNil);
-
- hpgdPrev = hpgdNext;
- hpgdNext = PpgdOfHpgd(hpgdNext)->hpgdHashNext;
- }
-
- PpgdOfHpgd(hpgdPrev)->hpgdHashNext = PpgdOfHpgd(hpgd)->hpgdHashNext;
- }
- }
-
-
- #pragma page()
-
- HPGD PRIVATE __HpgdVmAllocate(unsigned cPage)
- {
- unsigned iPage;
- HPGD hpgd;
- HPGD hpgdLru;
- unsigned timeLru;
-
- VmTracePrintf(("HpgdVmAllocate\n"));
-
- #ifdef VMDEBUG
- __AssertValidHashTable();
- #endif
-
- /* Make one pass over the allocated pages to find a free page or the */
- /* least recently used allocated page */
-
-
- (unsigned)hpgd = HpgdOfIPage0; /* HPGD of first PGD */
- hpgdLru = hpgdNil; /* No LRU page yet */
- timeLru = UINT_MAX; /* Latest possible time */
-
- for (iPage = 0; iPage < _cVmPage; iPage++)
- {
- /* An unallocated page has timeRef == 0 and cLock == 0 and */
- /* is not marked as dirty. */
-
- Assert((PpgdOfHpgd(hpgd)->Flags & fAllocatedPgd) ||
- ((PpgdOfHpgd(hpgd)->timeRef == 0) &&
- (PpgdOfHpgd(hpgd)->cLock == 0) &&
- !(PpgdOfHpgd(hpgd)->Flags & fDirtyPgd)));
-
- if ((PpgdOfHpgd(hpgd)->timeRef <= timeLru) &&
- (PpgdOfHpgd(hpgd)->cLock == 0))
- {
- hpgdLru = hpgd;
- timeLru = PpgdOfHpgd(hpgd)->timeRef;
- }
-
- hpgd = HpgdAdd(hpgd, 1);
- }
-
- if (hpgdLru != hpgdNil)
- {
- if (PpgdOfHpgd(hpgdLru)->Flags & fAllocatedPgd)
- __VmRemovePgdFromCache(hpgdLru);
-
- else
- PpgdOfHpgd(hpgdLru)->Flags |= fAllocatedPgd;
-
- if (PpgdOfHpgd(hpgdLru)->Flags & fDirtyPgd)
- __VmWriteDirtyPgd(hpgdLru);
- }
-
- VmTracePrintf(("HpgdVmAllocate: hpgd = %04X\n", (unsigned) hpgdLru));
-
- return(hpgdLru);
- }
-
-
- #pragma page()
-
- #if 0
-
- /* Recoded in MASM for speed. See vmutil.asm. */
-
- HPGD PRIVATE HpgdSearchCache(VPVOID vp)
- {
- unsigned ipgd;
- HPGD hpgd;
-
- VmTracePrintf(("HpgdSearchCache: vp = %08lX.\n", vp));
-
- #ifdef VMDEBUG
- AssertValidHashTable();
- #endif
-
- Assert((vp >= cbVmPage) && (vp < vpMax));
- vp = VpPageOfVp(vp); /* Clear page offset */
-
- /* Hash vp and quickly check if already resident */
-
- hpgd = _rghpgdHash[ipgd = IpgdHashOfVp(vp)];
- for (; hpgd != hpgdNil; hpgd = PpgdOfHpgd(hpgd)->hpgdHashNext)
- if (PpgdOfHpgd(hpgd)->vp == vp)
- break;
-
- VmTracePrintf(("HpgdSearchCache: hpgd = %04X.\n", (unsigned) hpgd));
-
- return(hpgd);
- }
-
- #endif /* 0 */
-
- #pragma page()
-
- void PRIVATE __VmUpdateTimestamps(void)
- {
- _timeCur = 1; /* Reset counter. */
- }
-