home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / mslang / vm / src / vmphys.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-15  |  14.9 KB  |  553 lines

  1. /***
  2. * vmphys.c -
  3. *
  4. *       Copyright (c) 1989-1992, Microsoft Corporation.  All rights reserved.
  5. *
  6. *Purpose:
  7. *
  8. * PUBLIC Functions:
  9. *
  10. *       VpAllocatePage:
  11. *
  12. *       VpReleasePage:
  13. *
  14. *       PVmLoadVp:
  15. *
  16. *******************************************************************************/
  17.  
  18. #pragma title("Virtual Memory Manager - Physical Memory Management")
  19.  
  20. #include <version.h>
  21. #include <system.h>
  22. #include <error.h>
  23. #include <vmassert.h>
  24. #include <ems.h>
  25. #include <xms.h>
  26.  
  27. #if !defined(DOS)
  28. #error DOS must be defined
  29. #endif
  30.  
  31. #include <vm.h>
  32. #include <vmp.h>
  33.  
  34. #include <limits.h>
  35. #include <malloc.h>
  36. #include <stdlib.h>
  37.  
  38. static unsigned _near _cVmUmb = 0;                       /* Number of UMBs allocated */
  39. static _segment _near rgsegVmUmb[cVmUmbMax-1];   /* Segments of allocated UMBs */
  40. static unsigned _near rgcPageVmUmb[cVmUmbMax-1]; /* Count of pages in each UMB */
  41.  
  42. unsigned _near _cPageDos;              /* Count of DOS pages (EMS/UMB not included) */
  43. unsigned _near _cVmPage;               /* Count of non-EMS pages allocated */
  44. _segment _near _segVmDos;              /* Segment of DOS heap allocation */
  45.  
  46. HPGD     _near _rghpgdHash[ipgdHashMax];
  47.  
  48. #pragma page()
  49.  
  50. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  51.  *
  52.  *                       CPageVmAllocateUmbs
  53.  *
  54.  *      This function allocates XMS upper memory blocks (UMBs) for use
  55.  * as physical pages for paging.
  56.  *
  57.  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  58.  *
  59.  * Entry Conditions:
  60.  *
  61.  *    DOS only.
  62.  *
  63.  * Exit Conditions:
  64.  *
  65.  *    The global variables _cVmUmb, rgsegVmUmb, and rgcPageVmUmb are
  66.  * initialized.  The function returns the total number of pages that
  67.  * can be created from the UMBs allocated.  There is a compile time
  68.  * limit (cVmUmbMax) on the number of UMBs that can be allocated.  The
  69.  * size of these UMBs and therefore the number of pages that can be
  70.  * allocated is unlimited.
  71.  *
  72.  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  73.  
  74.  
  75. unsigned LOCAL __CPageVmAllocateUmbs(void)
  76. {
  77.    unsigned cPageUmb;
  78.    ERR      err;
  79.  
  80.    cPageUmb = 0;                       /* No UMB pages allocated */
  81.  
  82.    if (__FXmsCheckInstalled())
  83.       while (_cVmUmb < cVmUmbMax - 1)
  84.       {
  85.      _segment segDummy;
  86.      unsigned cPara;
  87.  
  88.      /* To allocate a UMB, first request a block that is too large. */
  89.      /* Though this will fail, it returns the size of the largest. */
  90.      /* block available.  This is used to request a reasonable sized */
  91.      /* block that will be broken into one or more physical pages */
  92.  
  93.      cPara = 0xffff;
  94.      err = __ErrXmsRequestUpperMemoryBlock(&segDummy, (unsigned short _far *) &cPara);
  95.      if ((err != errXmsSmallerUMBIsAvailable) || (cPara < cbVmPage / 16))
  96.         break;
  97.  
  98.      /* Use the cPara field returned and round to a page boundary. */
  99.  
  100.      rgcPageVmUmb[_cVmUmb] = cPara / cParaPerPage;
  101.      cPara = rgcPageVmUmb[_cVmUmb] * cParaPerPage;
  102.      err = __ErrXmsRequestUpperMemoryBlock(&rgsegVmUmb[_cVmUmb], (unsigned short _far *) &cPara);
  103.      Assert(err == errNoError);
  104.      if (err != errNoError)        /* Just to be safe */
  105.         break;
  106.  
  107.      cPageUmb += rgcPageVmUmb[_cVmUmb];
  108.      _cVmUmb++;                    /* UMB successfully allocated */
  109.       }
  110.  
  111.    return(cPageUmb);
  112. }
  113.  
  114. #pragma page()
  115.  
  116. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  117.  *
  118.  *                       VmFreeUmbs
  119.  *
  120.  *      This function releases the XMS upper memory blocks (UMBs)
  121.  * allocated in CPageVmAllocateUmbs.
  122.  *
  123.  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  124.  *
  125.  * Entry Conditions:
  126.  *
  127.  *    DOS only.
  128.  *
  129.  * Exit Conditions:
  130.  *
  131.  *    The global variable _cVmUmb is reset to zero.
  132.  *
  133.  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  134.  
  135.  
  136. void LOCAL __VmFreeUmbs(void)
  137. {
  138.    ERR   err;
  139.  
  140.    while (_cVmUmb)
  141.    {
  142.       err = __ErrXmsReleaseUpperMemoryBlock(rgsegVmUmb[--_cVmUmb]);
  143.       Assert(err == errNoError);
  144.    }
  145. }
  146.  
  147. #pragma page()
  148.  
  149. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  150.  *
  151.  *                       FVmInitializePhys
  152.  *
  153.  *      This function initializes the physical memory manager.
  154.  *
  155.  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  156.  *
  157.  * Entry Conditions:
  158.  *
  159.  *    DOS only.
  160.  *
  161.  *    The parameter cVmParaMin specifies the minumum number of paras
  162.  * of physical memory to be allowed.  If this number of pages can not
  163.  * be allocated, the initialization fails.
  164.  *
  165.  *    The parameter cParaVmDosMax specifies the maximum number of
  166.  * paragraphs to be allocated from conventional DOS memory.  If the
  167.  * value is 0, the default of 0xffff is used.
  168.  *
  169.  *    The physical memory manager must not have been previously
  170.  * initialized without an intermediate call to VmTerminatePhys.
  171.  *
  172.  * Exit Conditions:
  173.  *
  174.  *    The function returns TRUE if the initialization is successful.
  175.  * Otherwise it returns FALSE.  Initialization can fail if there is
  176.  * not enough available DOS memory to allocate the miniumum number of
  177.  * pages or if there is not enough memory in the near heap to allocate
  178.  * the page descriptors for the minimum number of pages.
  179.  *
  180.  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  181.  
  182.  
  183. int PRIVATE __FVmInitializePhys(unsigned cVmParaMin, unsigned cParaVmDosMax)
  184. {
  185.    unsigned    cPageUmb;
  186.    unsigned    cParaDos;
  187.    HPGD        hpgd;
  188.    _segment    segPage;
  189.    unsigned    iPage;
  190.    unsigned    iUmb;
  191.    unsigned    ipgd;
  192.  
  193.    VmTracePrintf(("FVmInitializePage\n"));
  194.  
  195.    Assert(_osmode == DOS_MODE);
  196.  
  197.    if (cParaVmDosMax == 0)             /* Zero indicates no limit */
  198.       cParaVmDosMax = 0xffff;
  199.  
  200.    /* safety belts */
  201.    if (cVmParaMin > cParaVmDosMax)
  202.       return FALSE;
  203.  
  204.    /* Allocate upper memory blocks (UMBs) from an XMS driver.  This */
  205.    /* memory is within the 1M address space and is suitable for */
  206.    /* paging */
  207.  
  208.    cPageUmb = __CPageVmAllocateUmbs();
  209.  
  210.    /* Now allocate memory from the DOS heap to use for paging and for */
  211.    /* maintaining the page tables.  This memory is allocated as a */
  212.    /* single block so if the heap contains several big blocks, this */
  213.    /* allocation strategy may not be ideal.  I don't expect this to */
  214.    /* be a problem. */
  215.  
  216.    /* The size of the block allocated is subject to several constraints. */
  217.    /* The obvious constraint in the availability of memory in the DOS */
  218.    /* heap.  In addition, the application may impose an upper limit on */
  219.    /* the consumption of DOS memory as well as a lower limit on the */
  220.    /* number of pages needed in the paging area. */
  221.  
  222.    cParaDos = min(__CParaVmDosAvail(), cParaVmDosMax);
  223.  
  224.    /* The block of DOS memory allocated is divided into pages and page */
  225.    /* descriptors.  Each page descriptor requires one paragraph.  Each */
  226.    /* page requires (cbVmPage/16) paragraphs.  The gives a formula for */
  227.    /* memory required as */
  228.    /*          cParaDos = _cPageDos * (cbVmPage/16+1) + cPageUmb + _cVmPageEms;
  229.  
  230.    Assert(sizeof(PGD) == cbVmPara);            /* One PGD == 1 Paragraph */
  231.  
  232.         // How many whole pages of DOS and UMB 
  233.     _cVmPage = cPageUmb + (cParaDos / cParaPerPage);
  234.  
  235.     // subtract space needed for whole pages of PDs
  236.     _cVmPage -= _cVmPage/cParaPerPage;
  237.         
  238.     // DOS allocated in paragraphs not page units
  239.     // if enough extra paras, use them for PDs, if not get another page
  240.     _cVmPage -= (cParaDos % cParaPerPage) < (_cVmPage % cParaPerPage) ? 1 : 0;
  241.  
  242.     // ensure the amount of space available for swap area is enough
  243.     if (_cVmPage * cParaPerPage < cVmParaMin)
  244.     {
  245.         __VmFreeUmbs();
  246.         return FALSE;
  247.     }
  248.  
  249.     _cPageDos = _cVmPage - cPageUmb;
  250.  
  251.     // Cannot work with only one page of DOS swap area
  252.     if (_cPageDos <= 1)
  253.     {
  254.         __VmFreeUmbs();
  255.         return FALSE;
  256.     }
  257.  
  258.     // Grab only as needed for DOS pages and PD pages
  259.     cParaDos = (_cPageDos * cParaPerPage) + _cVmPage;
  260.  
  261.    _segVmDos = __SegVmDosAllocate(cParaDos);
  262.    if (_segVmDos == _NULLSEG)
  263.    {
  264.       Assert(FALSE);                   /* This should not be possible */
  265.       __VmFreeUmbs();                  /* Free already allocated UMBs */
  266.       return(FALSE);                   /* Return failure indication */
  267.    }
  268.  
  269.    /* Initialize the PGD structures */
  270.    (unsigned)hpgd = HpgdOfIPage0;
  271.  
  272.    {
  273.       size_t      cw;
  274.       short _far *pw = _segVmDos:>((short _based(void) *) 0);
  275.  
  276.       for (cw = 8 * _cVmPage; cw != 0; cw--)
  277.      *pw++ = 0;
  278.    }
  279.    segPage = (_segment) ((unsigned) _segVmDos + _cVmPage);
  280.  
  281.    for (iPage = 0; iPage < _cPageDos; iPage++)
  282.    {
  283.       PpgdOfHpgd(hpgd)->segPage = segPage;
  284.       PpgdOfHpgd(hpgd)->Flags   = (unsigned char) (iPage ? 0U : fDiscontinousPgd);
  285.  
  286.       hpgd = HpgdAdd(hpgd, 1);
  287.  
  288.       segPage = (_segment) ((unsigned) segPage) + (cbVmPage / 16);
  289.    }
  290.  
  291.    for (iUmb = 0; iUmb < _cVmUmb; iUmb++)
  292.    {
  293.       segPage  = rgsegVmUmb[iUmb];     /* Segment base of this UMB */
  294.       cPageUmb = rgcPageVmUmb[iUmb];   /* Number of pages in this UMB */
  295.  
  296.       for (iPage = 0; iPage < cPageUmb; iPage++)
  297.       {
  298.      PpgdOfHpgd(hpgd)->segPage = segPage;
  299.      PpgdOfHpgd(hpgd)->Flags   = (unsigned char) (fUmbPgd | (iPage ? 0U : fDiscontinousPgd));
  300.  
  301.      hpgd = HpgdAdd(hpgd, 1);
  302.  
  303.      segPage = (_segment) ((unsigned) segPage) + (cbVmPage / 16);
  304.       }
  305.    }
  306.  
  307.    /* Initialize hash table */
  308.  
  309.    for (ipgd = 0; ipgd < ipgdHashMax; ipgd++)
  310.       _rghpgdHash[ipgd] = hpgdNil;
  311.  
  312.    return(TRUE);
  313. }
  314.  
  315. #pragma page()
  316.  
  317. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  318.  *
  319.  *                       VmTerminatePhys
  320.  *
  321.  *      This function releases physical memory resources allocated in
  322.  * FVmInitializePhys.
  323.  *
  324.  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  325.  *
  326.  * Entry Conditions:
  327.  *
  328.  *    DOS only.
  329.  *
  330.  * Exit Conditions:
  331.  *
  332.  *    None.
  333.  *
  334.  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  335.  
  336.  
  337. void PRIVATE __VmTerminatePhys(void)
  338. {
  339.    __VmDosFreeSeg(_segVmDos);          /* Free DOS heap block */
  340.    __VmFreeUmbs();                     /* Free already allocated UMBs */
  341. }
  342.  
  343. #pragma page()
  344.  
  345. #ifdef  VMDEBUG
  346.  
  347. void LOCAL __AssertValidHashTable(void)
  348. {
  349.    HPGD     hpgd;
  350.    unsigned ipgd;
  351.    unsigned cPageAllocated;
  352.    unsigned cPageHashed;
  353.    unsigned cPageDiscardable;
  354.  
  355.    (unsigned)hpgd = HpgdOfIPage0;
  356.    cPageAllocated = 0;
  357.    cPageDiscardable = 0;
  358.  
  359.    for (ipgd = 0; ipgd < _cVmPage; ipgd++)
  360.    {
  361.       /* An unallocated page has timeRef == 0 and cLock == 0 and */
  362.       /* is not marked as dirty. */
  363.  
  364.       Assert((PpgdOfHpgd(hpgd)->Flags & fAllocatedPgd) ||
  365.          ((PpgdOfHpgd(hpgd)->timeRef == 0) &&
  366.           (PpgdOfHpgd(hpgd)->cLock   == 0) &&
  367.           !(PpgdOfHpgd(hpgd)->Flags & fDirtyPgd)));
  368.  
  369.       if (PpgdOfHpgd(hpgd)->Flags & fAllocatedPgd)
  370.      cPageAllocated++;
  371.  
  372.       hpgd = HpgdAdd(hpgd, 1);
  373.    }
  374.  
  375.    cPageHashed = 0;
  376.    for (ipgd = 0; ipgd < ipgdHashMax; ipgd++)
  377.    {
  378.       hpgd = _rghpgdHash[ipgd];
  379.       while (hpgd != hpgdNil)
  380.       {
  381.      Assert(ipgd == IpgdHashOfVp(PpgdOfHpgd(hpgd)->vp));
  382.      Assert(PpgdOfHpgd(hpgd)->Flags & fAllocatedPgd);
  383.      hpgd = PpgdOfHpgd(hpgd)->hpgdHashNext;
  384.      cPageHashed++;
  385.      Assert(cPageHashed <= cPageAllocated);
  386.       }
  387.    }
  388.  
  389.    Assert(cPageHashed == cPageAllocated);
  390. }
  391.  
  392. #endif  /* VMDEBUG */
  393.  
  394.  
  395. #pragma page()
  396.  
  397. void PRIVATE __VmWriteDirtyPgd(HPGD hpgd)
  398. {
  399.    VmTracePrintf(("VmWriteDirtyPgd: hpgd = %04X, vp = %08lX\n",
  400.           (unsigned) hpgd, PpgdOfHpgd(hpgd)->vp));
  401.  
  402.       Assert(PpgdOfHpgd(hpgd)->Flags & fDirtyPgd);
  403.  
  404.    Assert(PpgdOfHpgd(hpgd)->pte & (fEmsPte | fXmsPte | fDiskPte));
  405.  
  406.    if (PpgdOfHpgd(hpgd)->pte & fXmsPte)
  407.       __FVmSwapOutXmsPage(PpgdOfHpgd(hpgd)->pte, hpgd);
  408.  
  409.    else if (PpgdOfHpgd(hpgd)->pte & fEmsPte)
  410.       __FVmSwapOutEmsPage(PpgdOfHpgd(hpgd)->pte, hpgd);
  411.  
  412.    else
  413.       __FVmSwapOutDiskPage(PpgdOfHpgd(hpgd)->pte, hpgd);
  414.  
  415.    PpgdOfHpgd(hpgd)->Flags &= ~fDirtyPgd;
  416. }
  417.  
  418. #pragma page()
  419.  
  420. void PRIVATE __VmRemovePgdFromCache(HPGD hpgd)
  421. {
  422.    unsigned ipgd;
  423.    HPGD     hpgdNext;
  424.    HPGD     hpgdPrev;
  425.  
  426.    VmTracePrintf(("VmRemovePgdFromCache: hpgd = %04X, vp = %08lX\n",
  427.           (unsigned) hpgd, PpgdOfHpgd(hpgd)->vp));
  428.  
  429.    Assert(PpgdOfHpgd(hpgd)->Flags & fAllocatedPgd);
  430.  
  431. #ifdef  VMDEBUG
  432.    __AssertValidHashTable();
  433. #endif
  434.  
  435.    hpgdNext = _rghpgdHash[ipgd = IpgdHashOfVp(PpgdOfHpgd(hpgd)->vp)];
  436.    if (hpgdNext == hpgd)
  437.       _rghpgdHash[ipgd] = PpgdOfHpgd(hpgd)->hpgdHashNext;
  438.  
  439.    else {
  440.       while (hpgdNext != hpgd) {
  441.      Assert(hpgdNext != hpgdNil);
  442.  
  443.      hpgdPrev = hpgdNext;
  444.      hpgdNext = PpgdOfHpgd(hpgdNext)->hpgdHashNext;
  445.       }
  446.  
  447.       PpgdOfHpgd(hpgdPrev)->hpgdHashNext = PpgdOfHpgd(hpgd)->hpgdHashNext;
  448.    }
  449. }
  450.  
  451.  
  452. #pragma page()
  453.  
  454. HPGD PRIVATE __HpgdVmAllocate(unsigned cPage)
  455. {
  456.    unsigned iPage;
  457.    HPGD     hpgd;
  458.    HPGD     hpgdLru;
  459.    unsigned timeLru;
  460.  
  461.    VmTracePrintf(("HpgdVmAllocate\n"));
  462.  
  463. #ifdef  VMDEBUG
  464.    __AssertValidHashTable();
  465. #endif
  466.  
  467.    /* Make one pass over the allocated pages to find a free page or the */
  468.    /* least recently used allocated page */
  469.  
  470.  
  471.    (unsigned)hpgd = HpgdOfIPage0;      /* HPGD of first PGD */
  472.    hpgdLru = hpgdNil;                  /* No LRU page yet */
  473.    timeLru = UINT_MAX;                 /* Latest possible time */
  474.  
  475.    for (iPage = 0; iPage < _cVmPage; iPage++)
  476.    {
  477.       /* An unallocated page has timeRef == 0 and cLock == 0 and */
  478.       /* is not marked as dirty. */
  479.  
  480.       Assert((PpgdOfHpgd(hpgd)->Flags & fAllocatedPgd) ||
  481.          ((PpgdOfHpgd(hpgd)->timeRef == 0) &&
  482.           (PpgdOfHpgd(hpgd)->cLock   == 0) &&
  483.           !(PpgdOfHpgd(hpgd)->Flags & fDirtyPgd)));
  484.  
  485.       if ((PpgdOfHpgd(hpgd)->timeRef <= timeLru) &&
  486.       (PpgdOfHpgd(hpgd)->cLock == 0))
  487.       {
  488.      hpgdLru = hpgd;
  489.      timeLru = PpgdOfHpgd(hpgd)->timeRef;
  490.       }
  491.  
  492.       hpgd = HpgdAdd(hpgd, 1);
  493.    }
  494.  
  495.    if (hpgdLru != hpgdNil)
  496.    {
  497.       if (PpgdOfHpgd(hpgdLru)->Flags & fAllocatedPgd)
  498.      __VmRemovePgdFromCache(hpgdLru);
  499.  
  500.       else
  501.      PpgdOfHpgd(hpgdLru)->Flags |= fAllocatedPgd;
  502.  
  503.       if (PpgdOfHpgd(hpgdLru)->Flags & fDirtyPgd)
  504.      __VmWriteDirtyPgd(hpgdLru);
  505.    }
  506.  
  507.    VmTracePrintf(("HpgdVmAllocate: hpgd = %04X\n", (unsigned) hpgdLru));
  508.  
  509.    return(hpgdLru);
  510. }
  511.  
  512.  
  513. #pragma page()
  514.  
  515. #if     0
  516.  
  517.     /* Recoded in MASM for speed.  See vmutil.asm. */
  518.  
  519. HPGD PRIVATE HpgdSearchCache(VPVOID vp)
  520. {
  521.    unsigned ipgd;
  522.    HPGD     hpgd;
  523.  
  524.    VmTracePrintf(("HpgdSearchCache: vp = %08lX.\n", vp));
  525.  
  526. #ifdef  VMDEBUG
  527.    AssertValidHashTable();
  528. #endif
  529.  
  530.    Assert((vp >= cbVmPage) && (vp < vpMax));
  531.    vp = VpPageOfVp(vp);                /* Clear page offset */
  532.  
  533.    /* Hash vp and quickly check if already resident */
  534.  
  535.    hpgd = _rghpgdHash[ipgd = IpgdHashOfVp(vp)];
  536.    for (; hpgd != hpgdNil; hpgd = PpgdOfHpgd(hpgd)->hpgdHashNext)
  537.       if (PpgdOfHpgd(hpgd)->vp == vp)
  538.      break;
  539.  
  540.    VmTracePrintf(("HpgdSearchCache: hpgd = %04X.\n", (unsigned) hpgd));
  541.  
  542.    return(hpgd);
  543. }
  544.  
  545. #endif  /* 0 */
  546.  
  547. #pragma page()
  548.  
  549. void PRIVATE __VmUpdateTimestamps(void)
  550. {
  551.    _timeCur = 1;                       /* Reset counter. */
  552. }
  553.