#define SCCSID  "@(#)pginit.c   6.19 92/03/26"

/*
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      7/22/94  Change Team  Defect 76643
 *               Finish allocating Fat Cache after initializing high memory
 *      9/29/95  Change Team defect 138343
 *               Allow proper sizing of swapper bitmap if in-memory swap
 *               file is present. @V3.0RV01
 */

#define FNO_NUM  FNO_PGINIT_C
#include <fnomem.h>

#define INCL_DOSERRORS
#define INCL_OS2STD
#include <os2.h>

#define INCL_DOSP
#include <os2p.h>

#include <osmac.h>
#include <mi.h>
#include <dossymc.h>
#include <sigdef.h>
#include <except.h>
#include <pgexport.h>
#include <vmexport.h>
#include <tkexport.h>
#include <infoseg.h>
#include <curdir.h>
#include <dirent.h>
#include <find.h>
#include <screen.h>
#include <sem.h>
#include <tkptda.h>
#include <smexport.h>
#include <pglocal.h>
#include <protmode.h>
#include <seldesc.h>
#include <selxport.h>
#include <sysinvar.h>
#include <vmowner.h>
#include <ldrxport.h>
#include <stdlib.h>
#include <string.h>

extern unsigned char DDwith24BitDMA;    // d67673 - true if one present

// b734704 build break
unsigned long CheckABIOS( void );


void pgTrap2BootFix(frame_t frame);
void pgGetErrorLogFrame(int index,ulong_t *pFrame);
extern void pgInitDosTable(void);
extern void FinishBufInit(void); /*76643*/
extern void smIncreaseBitmapForInmemFile(void);    /* @V3.0RV01 */

#define PT_INIT (PT_PRESENT | PT_RING3WRITE | PT_RING3ACCESS | PT_RESIDENT)

#define PhysToPT(paddr) \
        (pgPhysToPTFrame((paddr)) | PT_INIT)
#define pgPhysToPTFrame(paddr) \
        (((ulong_t)(paddr) & ~PAGEMASK) >> (PAGESHIFT - PT_FRAMESHIFT))


#ifdef  PMDEBUG
PrintArena(register struct arena_s *pa)
{
//  printf("Arenainfo: \\%%08x\n", pa);
//  printf("          paddr     laddr     size      sel       aflags\n");
    printf("Arenainfo: %%%08x\n", pa);
    printf("          paddr     laddr     size      sel   afl   ofl       ownr\n");
    while ((pa->a_aflags & AF_VEND) == 0) {
        if (pa->a_aflags & AF_LADDR) {
            printf("laddr   ");
        } else if (pa->a_aflags & AF_FREE) {
            printf("free    ");
        } else if (pa->a_aflags & AF_INVALID) {
            printf("invalid ");
        } else if (pa->a_aflags & AF_PADDR) {
            printf("sparse  ");
        } else if (pa->a_aflags & AF_PEND) {
            printf("pend    ");
        } else {
            printf("        ");
        }
//      printf("  %08x  %08x  %08x  %08x  %08x\n", pa->a_paddr, pa->a_laddr,
//             pa->a_size, pa->a_sel, pa->a_aflags);
        printf("  %08x  %08x  %08x  %04x  %04x  %08x  %04x\n", pa->a_paddr,
               pa->a_laddr, pa->a_size, pa->a_sel, pa->a_aflags, pa->a_oflags,
               pa->a_owner);
        ++pa;
    }
    return(0);
}
#endif


/***EP  pgDeleteArena - Delete an arena record
 *
 *      pgDeleteArena removes an arena record from the arena info array
 *
 *      ENTRY   paDelete - pointer to arena record to be removed
 *
 *      EXIT    NONE
 *
 */
void pgDeleteArena(struct arena_s *paDelete)
{
    register struct arena_s *pa = paDelete;

    while((pa->a_aflags & AF_VEND) == 0)
    {
        *(pa) = *(pa+1);
        pa++;
    }
}


/***EP  pgInsertArena - Insert an arena record into the arena info array
 *
 *      pgInsertArena inserts an arena record into the arena info array
 *      after the supplied arena record.
 *
 *      ENTRY   paInsert - pointer to arena record to be removed
 *
 *      EXIT    NONE
 *
 */
void pgInsertArena(struct arena_s *paInsert)
{
    register struct arena_s *pa = paInsert;

    while((pa->a_aflags & AF_VEND) == 0)
    {
        pa++;
    }

    while(pa != paInsert)
    {
        *(pa+1) = *pa;
        pa--;
    }
}


/***LC+ Physical memory map prior to page manager initialization:
 *
 *      (16 meg) 1000000 -----> +-------+
 *                       ^      [invalid]
 *                      128k    [  ROM  ]
 *                       v      [shadow ]
 *  (15 7/8 meg)  fe0000 -----> +-------+ <---- DGROUP descriptor base
 *                       ^      |  new  |
 *                       |      | phys  |
 *                       |      |DGROUP |
 *                      256k    +-------+
 *                       |      |       |
 *                       |      |hi free|
 *                       v      |       |
 *  (15 5/8 meg)  fa0000 -----> +-------+
 *                              [       ]
 *                              [  hi   ]
 *                              [invalid]
 *                              [       ]
 *   (himem top)  xx0000 -----> +-------+
 *                              |       |
 *                              |hi free|
 *                              |       |
 *       (1 meg)  100000 -----> +-------+
 *                              | fixed |
 *        (640k)   a0000 -----> +-------+
 *                              |sysinit|
 *                              +-------+
 *                              |lo free|
 *                              +-------+
 *                              |DGROUP |
 *                              | load  |
 *                              | image |
 *                              +-------+
 *                              |hi segs|
 *                              | load  |
 *                              | image |
 *                 xx000 -----> +-------+
 *                              |lo segs|
 *                              | load  |
 *                              | image |
 *          (4k)    1000 -----> +-------+
 *                              |page 0 |
 *           (0)       0 -----> +-------+
 *
 *      Regions enclosed in "||" contain valid physical memory.
 *      Regions enclosed in "[]" contain no physical memory.
 *
 *      Upon control transfer to PGInit(), DS is loaded with the DGROUP
 *      selector value.  The DGROUP descriptor base is set to the physical
 *      address just *above* "new phys DGROUP", and the limit is set to
 *      (4 gig - 1) to allow access to all of physical memory.  PGInit()
 *      is passed the bias value that can be added to a physical address
 *      in order to allow physical memory to be accessed via near pointers
 *      in C (relative to the DGROUP selector).  Since PGInit() maintains
 *      linear == physical for much of the physical arena, the same bias
 *      value can be used (and is required) to address physical memory
 *      after paging is enabled, up until the DGROUP descriptor is set
 *      to map DGROUP just below the top of the linear address space.
 *
 *
 *
 *      Linear and physical memory map just before paging is enabled:
 *
 *      The following diagram is a physical and linear map of memory just
 *      prior to enabling paging for the first time.  The DGROUP data segment
 *      has already been moved to the last contiguous block of physical memory
 *      (large enough to hold DGROUP) represented in the arenainfo array.
 *      The DGROUP selector linear base is still non-zero, points to the
 *      "new physical DGROUP" physical address, and in order to address raw
 *      physical memory, the linearbias value must be added to the physical
 *      address (in order to create a DGROUP relative linear pointer).  This
 *      only works with paging disabled, unless page tables are also set up.
 *
 *     (4 gig) 100000000 ---------------------> +-------+
 *                                              [invalid]
 *                                              +-------+ <---+
 *                                              |  new  | <---|+
 *                                              |linear | <---||+
 *                                              |DGROUP | <---|||+
 *                                              +-------+ <---||||+
 *                                              |  pf   |     |||||
 *                                              |entries|     |||||
 *                                ------------> +-------+     |||||
 *                                 ^            | high  |
 *                                 |            [       ]     linear
 *                                 |            [       ]     DGROUP
 *                                page tables   |12-16m |     alias
 *                                 |            [       ]
 *                                 |            [       ]     |||||
 *                                 v            | 0-4m  |     |||||
 *                                ------------> +-------+     |||||
 *                                              |pagedir|     |||||
 *                                              +-------+     |||||
 *                                              [handles]     |||||
 *                                              +-------+     |||||
 *                                              [       ]     |||||
 *                                              [invalid]     |||||
 *                                              [       ]     |||||
 *      (16 meg) 1000000 -----> +-------+       +-------+     |||||
 *                       ^      [invalid]       [invalid]     |||||
 *                      128k    [  ROM  ]       [  ROM  ]     |||||
 *                       v      [shadow ]       [shadow ]     |||||
 *  (15 7/8 meg)  ffe000 -----> +-------+  <=>  +-------+ <---+||||
 *                       ^      |  new  |  <=>  |  new  | <----+|||
 *                       |      | phys  |  <=>  | phys  | <-----+||
 *                       |      |DGROUP |  <=>  |DGROUP | <------+|
 *                      256k    +-------+  <=>  +-------+ <-------+
 *                       |      |       |       [       ]
 *                       |      |hi free|       [invalid]
 *                       v      |       |       [       ]
 *  (15 5/8 meg)  ffa000 -----> +-------+       +-------+
 *                              [       ]       [       ]
 *                              [  hi   ]       [       ]
 *                              [invalid]       [invalid]
 *                              [       ]       [       ]
 *   (himem top)  x00000 -----> +-------+       +-------+
 *                              |       |       [       ]
 *                              |hi free|       [invalid]
 *                              |       |       [       ]
 *       (1 meg)  100000 -----> +-------+  <=>  +-------+
 *                              | fixed |  <=>  | fixed |
 *        (640k)   a0000 -----> +-------+  <=>  +-------+
 *                              |sysinit|  <=>  |sysinit|
 *                              +-------+  <=>  +-------+
 *                              |lo free|  <=>  |lo free|
 *                              +-------+  <=>  +-------+
 *                              |DGROUP |  <=>  |DGROUP |
 *                              | load  |  <=>  | load  |
 *                              | image |  <=>  | image |
 *                              +-------+  <=>  +-------+
 *                              |hi segs|  <=>  |hi segs|
 *                              | load  |  <=>  | load  |
 *                              | image |  <=>  | image |
 *                 xx000 -----> +-------+  <=>  +-------+
 *                              |lo segs|  <=>  |lo segs|
 *                              | load  |  <=>  | load  |
 *                              | image |  <=>  | image |
 *          (4k)    1000 -----> +-------+  <=>  +-------+
 *                              |page 0 |  <=>  |page 0 |
 *           (0)       0 -----> +-------+  <=>  +-------+
 *
 *                    Physical Memory Map  <->  Linear Memory Map
 *                    -------- ------ ---       ------ ------ ---
 *
 *      Regions enclosed in "||" contain or map to valid physical memory.
 *      Regions enclosed in "[]" are invalid.  "<=>" implies linear
 *      addresses map to the same physical addresses.
 *
 *      Once paging is enabled, DGROUP can be addressed just below the top
 *      of the linear address space (just below 4 gigabytes - 64k).  This
 *      linear mapping is set up prior to enabling paging, and is meant to be
 *      used to address all of linear memory during normal system operation.
 *
 *      Immediately after enabling paging, the virtual memory manager will
 *      be called to make the DGROUP descriptor map the object at its new
 *      linear address, and to also allow DGROUP to map all of linear memory
 *      without having to add any bias value.  The virtual memory manager does
 *      this by setting the base in DGROUP's descriptor to zero. Then the
 *      "old physical DGROUP" linear mapping is discarded.
 */
/*end*/



/***EP  PGInit - Initialize page manager data structures and enable paging.
 *
 *      PGInit initializes page manager data structures and enables paging.
 *
 *          - walk arenainfo array and initialize the pagerange structure
 *          - save lowest allocated linear address and physical page count
 *          - compute pf array size
 *          - allocate pf array linear address and physical pages
 *          - compute pf cache hash table size
 *          - allocate pf cache hash table linear address and physical pages
 *          - allocate system page tables linear address range
 *          - allocate system page tables physical pages (high addresses only)
 *          - allocate page directory linear address and physical page
 *          - allocate user page tables linear address range
 *          - allocate linear==physical page table physical page
 *          - initialize page directory
 *          - initialize linear==physical page table
 *          - initialize allocated system page tables
 *          - initialize system page tables allocation and present counts
 *          - enable paging, leave swapping disabled
 *          - call selector manager to adjust kernel descriptor bases
 *          - free linear==physical page table physical page
 *          - clear out linear==physical page directory entry
 *          - initialize pf array
 *          - initialize pf idle list to contain all free frames
 *
 *      ENTRY   arenainfo - initial segment location information
 *              linearbias - linear bias added to paddr to create near pointer
 *      EXIT    NONE
 *      EFFECTS CR0, CR3, DGROUP linear address, access and limit
 *
 */
#pragma code_seg("CINIT_TEXT", "CODE")

void
PGInit(arenainfo, linearbias)
struct arena_s *arenainfo;
laddr_t linearbias;
{
    int i, j;
    int nbytes;
    ulong_t size;
    ulong_t cpg;
    frame_t firstframe;
    frame_t frame;
    struct pagerange_s *ppr;
    struct pagerange_s *pprend;
    register struct arena_s *pa;
    struct arena_s *pend;
    laddr_t laddr;
    paddr_t paddr;
    laddr_t ptladdr;
    laddr_t loptladdr;
//  paddr_t loptpaddr;        b111111
    paddr_t loptpaddr[4];  // b111111
    paddr_t prtpaddr;
    struct pte_s pte;
    struct pte_s *ppde;
    register struct pte_s *ppte;
    int nptes;
    laddr_t pfladdr;
    frame_t frameLastFree;
    struct pf_s *ppfLastFree;
    struct pf_s *ppfFastHead;
    struct pf_s *ppfFastTail;
    register struct pf_s *ppf;
    int alloced;
    page_t page;
    struct vp_s *pvp;
    ulong_t pVerify;
    extern laddr_t MSladdr;
    extern struct pte_s MSpte;
    extern struct pte_s MSpde;
#ifdef PMDEBUG
    laddr_t debugpteladdr;
    extern void DebugPagingEnabled(laddr_t ppte, laddr_t pteladdr);
#   define DEBUGPTES    1
#else
#   define DEBUGPTES    0
#endif

    /*
     * Sanity check the arena info array. For free blocks, verify the physical
     * storage really exists. This is done by writing the address of each page
     * at the start of the page and then seeing if the address is still there.
     * Also delete any entries with a size of zero.
     */

//#ifdef PMDEBUG
//    PrintArena(arenainfo);
//    _asm int 3;
//#endif

    pgAssert((linearbias & PAGEMASK) == 0);

    for(pa = arenainfo; (pa->a_aflags & AF_PEND) == 0; ++pa)
    {
//      pgAssert(pa->a_paddr + pa->a_size == (pa+1)->a_paddr);

        if(pa->a_size == 0)
        {
            pgDeleteArena(pa);
            pa--;
            continue;
        }

        if((pa->a_aflags & AF_FREE) != 0)
        {
            pgAssert((pa->a_paddr & PAGEMASK) == 0);

            pVerify = pa->a_paddr + linearbias;
            while(pVerify < pa->a_paddr + pa->a_size + linearbias)
            {
                *(ulong_t *) pVerify = pVerify;
                pVerify += PAGESIZE;
            }

            pVerify = pa->a_paddr + linearbias;
            while((pVerify < pa->a_paddr + pa->a_size + linearbias) &&
                  (*(ulong_t *) pVerify == pVerify))
            {
                pVerify += PAGESIZE;
            }

            if(pVerify < pa->a_paddr + pa->a_size + linearbias)
            {
                pVerify -= linearbias;

                if(pVerify == pa->a_paddr)
                {
                    pa->a_aflags = AF_INVALID;
                }
                else
                {
                    pgInsertArena(pa);
                    pa->a_size = pVerify - pa->a_paddr;
                    pa++;
                    pa->a_paddr  = pVerify;
                    pa->a_size   = (pa+1)->a_paddr - pVerify;
                    pa->a_aflags = AF_INVALID;
                    pa->a_laddr  = 0;
                    pa->a_sel    = 0;
                    pa->a_oflags = 0;
                    pa->a_owner  = 0;
                }
            }
        }
    }

    /*
     * Combine any adjacent free blocks into 1 free block.
     */

    for(pa = arenainfo; (pa->a_aflags & AF_PEND) == 0; ++pa)
    {
        if((pa->a_aflags & AF_FREE) != 0)
        {
            while(((pa+1)->a_aflags & AF_FREE) != 0)
            {
                pgAssert(((pa+1)->a_aflags & AF_PEND) == 0);

                pa->a_size += (pa+1)->a_size;
                pgDeleteArena(pa+1);
            }
        }
    }

    /*
     * if the block just before pend is invalid then move pend to the
     * real end of physical memory.
     */

    pa--;
    if((pa->a_aflags & AF_INVALID) != 0)
    {
        pgDeleteArena(pa);
        pa->a_paddr = (pa-1)->a_paddr + (pa-1)->a_size;
    }

//#ifdef PMDEBUG
//    PrintArena(arenainfo);
//#endif

    /*
     * Count the number of physical ranges so we can allocate the page
     * range table later.  Also pick up the first block of physical memory
     * above 1M, the end of the physical arena and lowest in-use linear
     * address along the way.
     */
    baseladdr = firstframe = -1;
    for (pa = arenainfo; (pa->a_aflags & AF_PEND) == 0; ++pa) {
        if ((pa->a_aflags & AF_INVALID) == 0) {
            if (firstframe == -1) {
                firstframe = 0;
                ++pgPageablePAI.pai_nranges;
            }
            if (pa1M == 0 && pa->a_paddr >= _1MEG) {
                pa1M = pa;
            }
        } else {
            firstframe = -1;
        }

        if ((pa->a_aflags & AF_LADDR) && pa->a_laddr < baseladdr) {
            /*
             * Round the linear address down so none of the allocations in
             * pgAllocArena will share the same linear page.
             */
            baseladdr = pa->a_laddr & ~PAGEMASK;
        }
    }
    paPend = pa;                        /* end of array describing phys mem */

    /*
     * Continue scanning to the end of the array.
     */
    for (; (pa->a_aflags & AF_VEND) == 0; ++pa) {
        if ((pa->a_aflags & AF_LADDR) && pa->a_laddr < baseladdr) {
            /*
             * Round the linear address down so none of the allocations in
             * pgAllocArena will share the same linear page.
             */
            baseladdr = pa->a_laddr & ~PAGEMASK;
        }
    }
    paVend = pa;                        /* end of arenainfo array */

    /*
     * Allocate the linear address space for the sparse pf array.
     * Include enough space for the two list heads in case they're
     * at the end of the array.
     */
    pgAllocArena(arenainfo, sizeof(struct pf_s) *
                 (2 + (paPend->a_paddr >> PAGESHIFT)),
                 AA_LINEAR + AA_SPECIFIC, 0, (laddr_t *)&pft, PGPFOWNER);

    /*
     * Allocate the two page range tables.  The resident page range table may
     * be smaller (but we don't yet know by how much), and it could be larger
     * by at most one entry.  Allocate the largest it may be.
     */
    nbytes = pgPageablePAI.pai_nranges * sizeof(struct pagerange_s);
    prtpaddr = pgAllocArena(arenainfo, nbytes, AA_LINEAR+AA_PHYSICAL, 0,
                            (laddr_t *)&pgPageablePAI.pai_pprt, PGPRTOWNER);
    nbytes += sizeof(struct pagerange_s);
    pgAllocArena(arenainfo, nbytes, AA_LINEAR+AA_PHYSICAL, 0,
                 (laddr_t *)&pgResidentPAI.pai_pprt, PGPRTOWNER);

    /*
     * Allocate the idle and free list heads from invalid physical memory
     * (we need only their pf structures).
     */
    for (pa = arenainfo;; ++pa) {
        if (pa->a_aflags & (AF_INVALID|AF_PEND)) {
            frame = pa->a_paddr >> PAGESHIFT;
            if (pa->a_aflags & AF_PEND) {
                cpg = frame == 0 ? 0 : _1MEG - frame;   /* 1M = 4G/PAGESIZE */
            } else {
                cpg = ((pa+1)->a_paddr >> PAGESHIFT) - frame;
            }

            if (cpg > 2) {
                /*
                 * We found an invalid region we can use for the list heads.
                 */
                pgFreeList.li_headframe = frame;
                pgFreeList.li_headppf = pft + frame;
                pgIdleList.li_headframe = frame + 1;
                pgIdleList.li_headppf = pft + frame + 1;

                /*
                 * We need to insert a new record.  Slide the following
                 * records up.
                 */
                for (pend = paVend; pend >= pa; --pend) {
                    *(pend+1) = *pend;
                }
                ++paPend;
                ++paVend;

                /*
                 * The new record describes a region of 2 pages.
                 */
                (pa+1)->a_paddr = (frame + 2) << PAGESHIFT;
                (pa+1)->a_size -= (pa+1)->a_paddr - pa->a_paddr;
                pa->a_size = (pa+1)->a_paddr - pa->a_paddr;

                pa->a_aflags = 0;               /* ether */
                break;
            }

            if (pa->a_aflags & AF_PEND) {
                panic("PGAllocHeads");          /* 4G of physical memory!!?? */
            }
        }
    }

    /*
     * Fill in the pagerange array, and count physical pages.  One pagerange
     * structure is initialized for each contiguous range of physical pages
     * in the system.
     */
    ppr = (struct pagerange_s *)(prtpaddr + linearbias);
    firstframe = -1;
    nbytes = 0;
    for (pa = arenainfo; ; pa++) {
        size = (pa+1)->a_paddr - pa->a_paddr;
        if (pa->a_aflags & (AF_INVALID | AF_PEND)) {
            if (firstframe != -1) {
                /*
                 * Found an invalid range (or the end of the arenainfo array);
                 * fill in the next page range structure:
                 *      - last valid page in previous range
                 *      - first valid page in previous range
                 */
                ppr->pr_lastframe = (pa->a_paddr >> PAGESHIFT) - 1;
                ppr->pr_firstframe = firstframe;
                pgPhysPages += (nbytes >> PAGESHIFT);
                if (pa->a_aflags & AF_PEND) {
                    pgPhysMax= pgTruePhysMax = (pa->a_paddr >> PAGESHIFT) - 1;
                    break;
                }
                ++ppr;
                nbytes = 0;
                firstframe = -1;
            }
        } else {
            if (firstframe == -1) {
                firstframe = pa->a_paddr >> PAGESHIFT;
            }
            nbytes += size;
        }
    }

    /*
     * Commit the regions of the pf array that map to valid pf's.
     */
    ppr = (struct pagerange_s *)(prtpaddr + linearbias);
    for (i = pgPageablePAI.pai_nranges; i > 0; --i, ++ppr) {
        /*
         * Get the address of the region mapping the current range.
         */
        pfladdr = (laddr_t)(pft + ppr->pr_firstframe);

        /*
         * Scan the ranges until we find one whose pf's start in another page.
         * Since pgAllocArena cannot handle allocating sparse memory that must
         * reside in the same page on multiple calls, we'll allocate these
         * invalid regions and decommit them later.
         */
        pprend = ppr + 1;
        while (i > 1 &&
            ((laddr_t)(pft + pprend->pr_firstframe) & ~PAGEMASK) ==
            (((laddr_t)(pft + (pprend-1)->pr_lastframe + 1) - 1) & ~PAGEMASK)) {

            ++pprend;
            --i;
        }
        --pprend;

        /*
         * Get the size of the region mapping all of the ranges we just scanned.
         */
        size = (pprend->pr_lastframe - ppr->pr_firstframe + 1) *
               sizeof(struct pf_s);

        pgAllocArena(arenainfo, size, AA_PHYSICAL+AA_SPECIFIC,
                     pfladdr & PAGEMASK, SSToDS(&pfladdr), PGPFOWNER);

        /*
         * Decommit any invalid regions if we committed more than one range.
         */
        while (pprend > ppr) {
            pfladdr = (laddr_t)(pft + ppr->pr_lastframe + 1);
            size = ((ppr+1)->pr_firstframe - ppr->pr_lastframe - 1) *
                   sizeof(struct pf_s);
            pgDecommitArena(arenainfo, pfladdr, size);
            ++ppr;
        }
    }

    /*
     * Remove the two invalid pages used for the list heads from the page
     * range array.
     */
    pgPhysPages -= 2;
    ppr = (struct pagerange_s *)(prtpaddr + linearbias);
    for (i = pgPageablePAI.pai_nranges; i > 0; --i, ++ppr) {
        if (ppr->pr_lastframe >= pgIdleList.li_headframe) {
            ppr->pr_lastframe -= 2;
            break;
        }
    }

    /*
     * Allocate linear address range for pgAliasTemp aliases.  The page
     * tables mapping this range must always be present even though there
     * is no phsyical memory committed.  Later we bump up the allocation
     * and present counts to enforce this.
     */
    pgAllocArena(arenainfo, PAGESIZE * 2, AA_LINEAR+AA_SPECIFIC, 0,
                 (laddr_t *)&pgAliasLaddr, PGALIASOWNER);

    /*
     * Allocate 68K (17-page) linear address range for Kernel Stack
     * region. The page tables mapping this range will be altered in
     * PGSwitchContext. At most 4 pages of this region will be present.
     * The others will be made invalid.  The first page allows for
     * stack selector underflow as needed.  To simplify calculations,
     * pKStackBase points to the second page of this region, so that
     * pKStackBase + 64K is the top of the region.
     */
    pgAllocArena(arenainfo, PAGESIZE * 17, AA_LINEAR+AA_SPECIFIC, 0,
                 (laddr_t *)&pKStackBase, PGKRNLSTAKOWNER);
    (laddr_t)pKStackBase += PAGESIZE;

#ifdef PMDEBUG
    /*
     * Allocate one pte for a debugger alias
     */
    pgAllocArena(arenainfo, PAGESIZE, AA_LINEAR+AA_SPECIFIC, 0,
                 SSToDS(&debugpteladdr), PGDBGALIASOWNER);
#endif

    /*
     * Allocate the vp structure linear address range, and commit the first
     * page.
     */

    /*
     * perf enh - if were in a constrained system (ie < 0x600 pages)
     *            set the PGSysConstrained bit
     */
    pgAssert(pgPhysPages);  // must be set by here

    if (pgPhysPages < 0x600) {
       PGGlobalFlags |= PG_SYS_CONSTRAINED;  // else , already set to zero
    }

    /*
     * perf enh - we don't need 10 meg worth of vps in a constrained system
     *            use 2 meg instead
     */
    if (PGGlobalFlags & PG_SYS_CONSTRAINED) {
        pgAllocArena(arenainfo, _1MEG * 2, AA_LINEAR+AA_SPECIFIC,
                  0, (laddr_t *)&pgpVPBase, PGVPOWNER);
    } else {
        pgAllocArena(arenainfo, _1MEG * sizeof(struct vp_s),
                  AA_LINEAR+AA_SPECIFIC, 0, (laddr_t *)&pgpVPBase, PGVPOWNER);
    }

    pgAllocArena(arenainfo, PAGESIZE, AA_PHYSICAL+AA_SPECIFIC, 0,
                 (laddr_t *)&pgpVPBase, PGVPOWNER);

    /*
     * Allocate page directory linear address and physical page.
     */
    pgPhysPageDir = pgAllocArena(arenainfo, PAGESIZE, AA_LINEAR+AA_PHYSICAL+
                    AA_SPECIFIC, 0, (laddr_t *)&pgpPageDir, PGDIROWNER);


    /* d64983 - Certain machines (ie Dell) won't install with 18 meg
     * The problem is that when VMInit does the PGAlloc for the Object
     * Record Table, the syspde is on the same 4 meg region as the object
     * page that is being committed.  This causes an infinite loop between
     * pgPinPt and pgPinForcePresent, which will cause a stack overflow and
     * eventually lead to a triple fault reboot.
     * To prevent this from occurring, make sure the SysPageTables are
     * all contained within one 4 meg region (mapped by one pde)
     */

    /* d75520 - The above defect didn't cover if the size to be allocate
     * was igual to zero.
     */

    if ( ( (ulong_t)pgpPageDir -   ((ulong_t)pgpPageDir & ~(_4MEG - 1)) )
                          <  (MAXSYSPDE * PAGESIZE))
    {

      if ( (ulong_t)pgpPageDir -   ((ulong_t)pgpPageDir & ~(_4MEG - 1)) ) /* d75520 */
        /*
         *  allocate pad to force system page tables to be contained
         *  with in a 4 meg region which is mapped by a single pde
         */
        pgAllocArena(arenainfo,
           ( (ulong_t)pgpPageDir -   ((ulong_t)pgpPageDir & ~(_4MEG - 1)) ),
                         AA_LINEAR+AA_SPECIFIC, 0,
                         (laddr_t *)&pgpSysPageTables, PGDBGALIASOWNER);
    }
    /* end d64983 */

    /*
     * Allocate system page tables linear address range.
     */
    pgAllocArena(arenainfo, MAXSYSPDE * PAGESIZE, AA_LINEAR+AA_SPECIFIC, 0,
                 (laddr_t *)&pgpSysPageTables, PGSYSPTEOWNER);

    pgSysPtpg = (ulong_t) pgpSysPageTables >> PAGESHIFT;

// GSR start
    /*
     * Allocate global shared region page tables linear address range.
     */
    pgAllocArena(arenainfo, MAXGLOBALSHRPDE * PAGESIZE, AA_LINEAR+AA_SPECIFIC,
                  0, (laddr_t *)&pgpShrCRPageTables, PGCRPTEOWNER);
// GSR end


    /*
     * Zero page directory entries.
     */
    memset((void *)(pgPhysPageDir + linearbias), 0,
           NPTEPPT * sizeof(struct pte_s));

    /*
     * Set pgData global linear addresses.
     */
    pgData.pd_ppte = pgpSysPageTables;
    pgData.pd_pcalloc = pgcPteAllocated;
    pgData.pd_pcpresent = pgcPtePresent;
    pgData.pd_pcresident = pgcPteResident;
    pgData.pd_base = MINSYSPAGE;
    pgData.pd_maxpde = MAXSYSPDE;
    pgData.pd_ppde = pgPagePdTopPte(((ulong_t)pgpSysPageTables >> PAGESHIFT),
                                    &pgData);
// GSR start
    /*
     * Set pgShrData global linear addresses.
     */

    pgShrData.pd_ppte = pgpShrCRPageTables;
    pgShrData.pd_pcalloc = pgcShrPteAllocated;
    pgShrData.pd_pcpresent = pgcShrPtePresent;
    pgShrData.pd_pcresident = pgcShrPteResident;
//  pgShrData.pd_base = MINGLOBALSHRPAGE;
    pgShrData.pd_base = MINGLOBALSHRPAGE;
    pgShrData.pd_maxpde = MAXGLOBALSHRPDE;
    pgShrData.pd_ppde = pgPagePdTopPte(((ulong_t)pgpShrCRPageTables >> PAGESHIFT),
                                    &pgData);
// GSR end

    /*
     * Map any system arena virtual addresses specified in arena info records to
     * their corresponding physical addresses by initializing PTE's.  Allocate
     * page tables on the fly.
     *
     * Because we may be modifying the arenainfo array (via pgAllocArena) while
     * scanning it, we have to repeat the process until we make a pass through
     * without modifying it (should be only 2 passes).
     *
     * This scan assumes that the difference of the "a_paddr" fields of any
     * two consecutive arena info records (up to the record marked "AF_PEND")
     * gives the size of the region mapped by the lower record.
     */
    do {
        alloced = FALSE;

        for (pa = arenainfo; (pa->a_aflags & AF_PEND) == 0; ++pa) {
            if ((pa->a_aflags & (AF_LADDR|AF_PADDR)) &&
                (pa->a_aflags & AF_MAPPED) == 0)  {
                /*
                 * We have found an arena info entry that maps a region of
                 * linear and physical address space in the system arena,
                 * and it has not been mapped on a previous iteration.
                 *
                 * Get the count of pages mapped by arena info record, and
                 * round the addresses.
                 */
                cpg = (((pa+1)->a_paddr + PAGEMASK) >> PAGESHIFT) -
                      (pa->a_paddr >> PAGESHIFT);
                laddr = pa->a_laddr & ~PAGEMASK;
                paddr = pa->a_paddr & ~PAGEMASK;

                pa->a_aflags |= AF_MAPPED;      /* flag that we've hit this */

                /*
                 * Operate on one page table at a time.
                 */
                while (cpg > 0) {
                    /*
                     * Get the count of pages in the object up to the page
                     * table boundary, and get a pointer to the page directory
                     * entry mapping this page table.
                     */
                    i = min(cpg, (PAGEDIRSIZE - (laddr & ~PAGEDIRMASK)) >>
                                  PAGESHIFT);
                    ppde = (struct pte_s *)(pgPhysPageDir + linearbias) +
                           (laddr >> PAGEDIRSHIFT);

                    if (ppde->pt_long == 0) {
                        /*
                         * We need to allocate a page table.
                         *
                         * Get its linear address.
                         */
                        ptladdr = (laddr_t)(pgpSysPageTables +
                                  (laddr >> PAGESHIFT) - MINSYSPAGE) &
                                  ~PAGEMASK;

                        /*
                         * Allocate the page table and initialize the page
                         * directory entry.
                         */
                        ppde->pt_long = PhysToPT(pgAllocArena(arenainfo,
                            PAGESIZE, AA_PHYSICAL+AA_SPECIFIC, 0,
                            SSToDS(&ptladdr), PGSYSPTEOWNER));

                        memset((void *)((ppde->pt_long & ~PAGEMASK)+linearbias),
                               0, PAGESIZE);    /* zero any unused ptes */

                        alloced = TRUE;
                    }

                    /*
                     * Get the pointer to the pte mapping the object.
                     */
                    pte.pt_long = PhysToPT(paddr);
                    ppte = (struct pte_s *)((ppde->pt_long & ~PAGEMASK) +
                           linearbias) + ((laddr & PAGETABLEMASK) >> PAGESHIFT);

                    /*
                     * Adjust the loop variables.
                     */
                    cpg -= i;
                    laddr += i << PAGESHIFT;
                    paddr += i << PAGESHIFT;

                    /*
                     * Initialize the PTE's corresponding to each virtual page
                     * to reference the physical page frames.
                     */
                    while (i--) {
                        *ppte++ = pte;
                        pte.pt_long += PT_FRAMEINCR;
                    }
                }
            }
        }
    } while (alloced);

    /*
     * Allocate linear==physical page table physical page.
     */
//  start b111111
//  loptpaddr = pgAllocArena(arenainfo, PAGESIZE, AA_LINEAR+AA_PHYSICAL+
//                      AA_SPECIFIC, 0, SSToDS(&loptladdr), INITOWNER);
//
//  /*
//   * Initialize the page directory entry for page table 0.
//   */
//  ppte = (struct pte_s *)(pgPhysPageDir + linearbias);
//  ppte->pt_long = PhysToPT(loptpaddr);
//
//  /*
//   * Initialize page tables with memory mapped linear == physical
//   * for 0 to 2mb:
//   */
//  ppte = (struct pte_s *) (loptpaddr + linearbias);
//  memset((void *) ppte, 0, NPTEPPT * sizeof(struct pte_s));
//  for (i = 0; i < 2*_1MEG; i += PAGESIZE, ppte++) {
//      ppte->pt_long = PhysToPT(i);
//  }
//
    for (j=0;j<4;j++)
    {
        loptpaddr[j]= pgAllocArena(arenainfo, PAGESIZE, AA_LINEAR+AA_PHYSICAL+
                        AA_SPECIFIC, 0, SSToDS(&loptladdr), INITOWNER);

    /*
     * Initialize the page directory entry for page table 0,1,2,3 (j).
     */
        ppte = (struct pte_s *)(pgPhysPageDir + linearbias + j*sizeof(struct pte_s));
        ppte->pt_long = PhysToPT(loptpaddr[j]);

    /*
     * Initialize page tables with memory mapped linear == physical
     * for 0 to 4mb:
     */
        ppte = (struct pte_s *) (loptpaddr[j] + linearbias);
        memset((void *) ppte, 0, NPTEPPT * sizeof(struct pte_s));
        for (i = 0; i < 4*_1MEG; i += PAGESIZE, ppte++) {
            ppte->pt_long = PhysToPT(i + (j*4*_1MEG));
        }
    }
// end b111111
    /*
     * Enable paging, but leave swapping disabled.
     */
#ifdef PMDEBUG
    /*
     * Initialize debugger to allow paging.
     */
    DebugPagingEnabled((laddr_t) pgPageToPte(debugpteladdr >> PAGESHIFT, 0),
                       debugpteladdr);
#endif
    pgEnablePaging(pgPhysPageDir);

    /*
     * Call selector manager to adjust kernel descriptor bases.
     */
    arenainfo = SELInit(arenainfo);

    /*
     * Clear linear==physical page directory entry and free its page table.
     */
//  start b111111
//  pgpPageDir->pt_long = 0;
//  pgFreeArena(arenainfo, loptpaddr);        //pd remove

    for (j=0;j<4;j++)
    {
        (pgpPageDir+j)->pt_long= 0;
        pgFreeArena(arenainfo, loptpaddr[j]);
    }
//  end b111111
    pgFlushTLB();

    /*
     * Setup pointers to the last system page table for use by the mode
     * switching code as a means of providing a temporary linear==physical
     * mapping.  Note that the real frame for MSpte is filled in by the
     * mode switch code.
     */
    MSladdr = (laddr_t)pgpSysPageTables + (MAXSYSPDE - 1) * PAGESIZE;
    MSpde.pt_long = (pgpPageDir + NPTEPPT - 1)->pt_long;
    MSpte.pt_long = PhysToPT(0);

    /*
     * Initialize the pf array.  Mark everything free, and put all pfs on
     * the free list.
     */
    pa = arenainfo;
    ppr = pgPageablePAI.pai_pprt;
    frameLastFree = pgFreeList.li_headframe;
    ppfLastFree = pgFreeList.li_headppf;
    for (i = pgPageablePAI.pai_nranges; i > 0; --i, ++ppr) {
        frame = ppr->pr_firstframe;
        ppf = pgFrameToPF(frame);

        /*
         * Connect the forward link of the last pf in the last range to the
         * first pf in this range.
         */
        FLINK(ppfLastFree) = frame;

        for (j = ppr->pr_lastframe - ppr->pr_firstframe + 1; j > 0; --j,
            ++ppf, ++frame) {
            ppf->pf_flags = PF_FREE;

            /*
             * Check the arenainfo array to determine if the page frame
             * should be marked as fast memory.
             */
            while (frame << PAGESHIFT >= (pa+1)->a_paddr) {
                ++pa;
            }
            if (pa->a_aflags & AF_FAST) {
                ppf->pf_fast = 1;
            }

            FLINK(ppf) = frame + 1;
            BLINK(ppf) = frame - 1;
        }

        /*
         * Connect the back link of the first pf in this range to the last
         * pf in the previous range.
         */
        BLINK(ppfFLINK(ppfLastFree)) = frameLastFree;
        ppfLastFree = ppf - 1;
        frameLastFree = frame - 1;
    }

    /*
     * Add pgFreeHead to the head of the list.
     */
    FLINK(ppfLastFree) = pgFreeList.li_headframe;
    BLINK(pgFreeList.li_headppf) = frameLastFree;

    /*
     *  Initialize the idle list to empty
     */
    FLINK(pgIdleList.li_headppf) = pgIdleList.li_headframe;
    BLINK(pgIdleList.li_headppf) = pgIdleList.li_headframe;

    /*
     * Initialize the vp array structures.
     */
    pgpVPBase->vp_flink = 0;
    pgpVPBase->vp_refcount = 0;
    pgpVPBase->vp_blink = &pgVPHead;
    pgVPHead.vp_refcount = 0;
    pgVPHead.vp_flink = pgpVPBase + PAGESIZE/sizeof(struct vp_s) - 1;
    pgVPHead.vp_blink = pgVPHead.vp_flink;
    pgVPHead.vp_flink->vp_flink = &pgVPHead;
    pgVPHead.vp_flink->vp_blink = pgpVPBase;
    pgpVPEnd = pgVPHead.vp_flink;
    pgVPBasePg = (ulong_t)pgpVPBase >> PAGESHIFT;
    pgVPEndPg = (ulong_t)pgpVPEnd >> PAGESHIFT;

    /*
     *  Setup pointers to temporary alias ptes
     */
    pgAliasPte = pgPagePdTopPte(pgAliasLaddr >> PAGESHIFT, &pgData);

    /*
     * Now scan the ptes for busy pages, initialize their pf's, and record
     * the page table allocated and present counts.
     */
    pgResidentPages = 0;
    pgListCount = pgPhysPages;

    ppde = pgpPageDir + (MINSYSLADDR >> PAGEDIRSHIFT);
    ppte = pgpSysPageTables;
    page = MINSYSPAGE;
    for (i = 0; i < MAXSYSSIZE >> PAGEDIRSHIFT; ++i, ++ppde, page += NPTEPPT) {
        if (ppde->pt_long == 0) {
            /*
             * Skip to the next page table.
             */
            ppte += NPTEPPT;
            continue;
        }

        /*
         * Loop over one page table.
         */
        nptes = 0;
        for (j = NPTEPPT; j > 0; --j, ++ppte) {
            if (ppte->pt_present) {
                ++nptes;

                ppf = pgFrameToPF(ppte->pt_frame);
                if (ppf->pf_flags & PF_FREE) {
                    /*
                     * We're the first to claim this pf.  Mark it resident
                     * and remove it from the free list.
                     */
                    FLINK(ppfBLINK(ppf)) = FLINK(ppf);
                    BLINK(ppfFLINK(ppf)) = BLINK(ppf);

                    LOCKS(ppf) = 0;
                    ppf->pf_pvp = (struct vp_s *) -PAGESIZE;
                    ppf->pf_refcount = 1;
                    ppf->pf_flags = 0;
                    ++pgResidentPages;
                    --pgListCount;
                } else {
                    /*
                     * It's already marked as allocated, so just bump up its
                     * reference count.
                     */
                    ++ppf->pf_refcount;
                }
            }
        }

        pgcPteAllocated[(ppde-pgpPageDir)-(MINSYSLADDR>>PAGEDIRSHIFT)] += nptes;
        pgcPtePresent[(ppde-pgpPageDir)-(MINSYSLADDR>>PAGEDIRSHIFT)] += nptes;
        pgcPteResident[(ppde-pgpPageDir)-(MINSYSLADDR>>PAGEDIRSHIFT)] += nptes;
    }

    /*
     * Bump up the page table allocated and present counts for the linear
     * range reserved for pgAliasTemp aliases and the debugger alias.
     */
    pgcPteAllocated[pgPagePdToPDI(pgAliasLaddr >> PAGESHIFT, &pgData)] +=
        2+DEBUGPTES;
    pgcPtePresent[pgPagePdToPDI(pgAliasLaddr >> PAGESHIFT, &pgData)] +=
        2+DEBUGPTES;
    pgcPteResident[pgPagePdToPDI(pgAliasLaddr >> PAGESHIFT, &pgData)] +=
        2+DEBUGPTES;

    /*
     * Mark frame 0 in use.  Note that this assumes frame 0 is currently free.
     */
    if (pft->pf_free == 0) {
        panic("frame 0 in use");
    }
    pvp = pgAllocVP();
    pvp->vp_frame = 0;
    pvp->vp_hob = INITOWNER;
    pvp->vp_obpg = 0;
    pvp->vp_flags = VP_RESIDENT|VP_PF;
    pgAssert(pvp->vp_refcount == 1);

    ppf = pft;
    FLINK(ppfBLINK(ppf)) = FLINK(ppf);
    BLINK(ppfFLINK(ppf)) = BLINK(ppf);

    LOCKS(ppf) = 0;
    ppf->pf_flags = 0;
    ppf->pf_pvp = pvp;
    ppf->pf_refcount = 1;
    ++pgResidentPages;
    --pgListCount;

    /*
     * Initialize the boundary structures for pageable memory.
     */
    pgSetBoundary(&pgPageablePAI, &pgPageablePAI.pai_1M, _1MEG >> PAGESHIFT);
    pgSetBoundary(&pgPageablePAI, &pgPageablePAI.pai_16M, _16MEG >> PAGESHIFT);
    pgPageablePAI.pai_end = pgPageablePAI.pai_16M;

    /*
     * Grab all the frames higher than 16M and put them onto a separate list.
     * They will remain there if the swap device can't handle I/O above 16M.
     */
    pgHighMemHeadppf = pgHighMemTailppf = ppfBLINK(pgFreeList.li_headppf);
    pgHighMemHeadframe = pgHighMemTailframe = BLINK(pgFreeList.li_headppf);
    while (pgHighMemHeadppf > pgPageablePAI.pai_16M.pb_ppf) {
        ++pgHighPages;
        pgHighMemHeadppf = ppfBLINK(pgHighMemHeadppf);
    }
    pgHighMemHeadframe = pgPFToFrame(pgHighMemHeadppf);
    /*
     *  Initialize the DosTable physical memory size, this call must
     *  be done BEFORE the statement: pgPhysPages-=pgHighPages (6 lines below)
     */
    pgInitDosTable();

    if (pgHighPages) {
        BLINK(pgFreeList.li_headppf) = pgHighMemHeadframe;
        pgHighMemHeadframe = FLINK(pgHighMemHeadppf);
        pgHighMemHeadppf = pgFrameToPF(pgHighMemHeadframe);
        FLINK(ppfBLINK(pgFreeList.li_headppf)) = pgFreeList.li_headframe;
        pgPhysPages -= pgHighPages;
        pgPhysMax = pgPageablePAI.pai_end.pb_frame;
    }

    /*
     * Calculate the maximum number of resident pages we will allow.
     */
    pgMaxResPages = max((pgPhysPages*7)/8, pgPhysPages - (PADPAGES+7*16));

    /*
     * Initialize the page range table for resident memory.
     */
    pgInitPrt(pgMaxResPages, pgPhysPages - pgMaxResPages);

    /*
     * Initialize the boundary structures for resident memory.
     */
    pgSetBoundary(&pgResidentPAI, &pgResidentPAI.pai_1M, _1MEG >> PAGESHIFT);
    pgSetBoundary(&pgResidentPAI, &pgResidentPAI.pai_16M, _16MEG >> PAGESHIFT);
    pgResidentPAI.pai_end = pgResidentPAI.pai_16M;

   /*
    * Sort the free list by speed.
    */
    ppf = ppfBLINK(pgFreeList.li_headppf);
    while (ppf != pgFreeList.li_headppf) {
        if (ppf->pf_fast && ppf >= pgPageablePAI.pai_1M.pb_ppf) {
            /*
             * This pf is marked fast.
             * Get the sublist of free fast pfs beginning with this pf.
             */
            ppfFastHead = ppf;
            do {
                ppf = ppfBLINK(ppf);
            } while (ppf->pf_fast);
            ppfFastTail = ppfFLINK(ppf);

            /*
             * Cut the sublist out of the free list.
             */
            BLINK(ppfFLINK(ppfFastHead)) = BLINK(ppfFastTail);
            FLINK(ppfBLINK(ppfFastTail)) = FLINK(ppfFastHead);

            /*
             * Insert the sublist at the tail of the free list.
             */
            FLINK(ppfFastHead) = pgFreeList.li_headframe;
            BLINK(ppfFastTail) = BLINK(pgFreeList.li_headppf);
            FLINK(ppfBLINK(ppfFastTail)) = pgPFToFrame(ppfFastTail);
            BLINK(pgFreeList.li_headppf) = pgPFToFrame(ppfFastHead);
        } else {
            /*
             * This ppf is not marked fast.
             * Look at the next one.
             */
            ppf = ppfBLINK(ppf);
        }
    }

    /*
     * Setup some 386 B1 errata helpers.
     *
     *  - pgErrCdMask:  page fault error code mask.  On a B1 chip we must
     *                  ignore the processor error code.
     *  - pgflPinFault: pgPin() flags in PGPageFault().
     *  - pg64Kerrata:  The infamous low 64K bits 12-15 errata.
     */
    if (CpuStepping > CPU_386B1) {
        pgErrCdMask = PER_WRITE|PER_USER;
    } else {
        pgflPinFault = PG_NOTIFYBLOCK;
        pg64Kerrata = PG_ERRATA;
    }

    /*
     *  Initialize the overcommit semaphore.
     */
    KSEMInit((HKSEM)&pgCommitSem, KRNL_MUTEXSEM, 0);

    /*
     *  Initialize the discardable zero-fill VP and pte.
     *  Also grab a page of overcommit for it.
     */
    pgpZeroFillVP = pgAllocVP();
    pgAssert(pgpZeroFillVP->vp_refcount == 1);
    pgpZeroFillVP->vp_hob = DISCARDOWNER;
    pgpZeroFillVP->vp_obpg = 0;
    pgpZeroFillVP->vp_flags = VP_RESIDENT | VP_DISCARDABLE;
    pgpZeroFillVP->vp_frame = 0;
    pgZeroFillPte = (pgpVPToVPI(pgpZeroFillVP) << PT_FRAMESHIFT) |
                    PT_RING3WRITE | PT_RING3ACCESS;
    pgDiscardablePages++;
}


/***EP  PGAllocCRPTE - Allocate Compatability Region PTE
 *
 *      PGAllocCRPTE allocates the linear address space for user page tables.
 *
 *      ENTRY   NONE
 *
 *      EXIT    Linear Address of pte's
 *
 */

laddr_t
PGAllocCRPTE(void)
{
    VMAC    ac;

// GSR ldt allocation

   /*  allocate the global shared regions LDT
    *  as the result of defect 149093 the reserve size and the commit size
    *  were the same value.  just for ease of understanding the hard coded
    *  4 was changed to GSR_NUMLDTPAGES in defect 152906.
    */
    if (VMAllocMem(GSR_NUMLDTPAGES*PAGESIZE,    /* reserve size */
                   GSR_NUMLDTPAGES*PAGESIZE,    /* commit size  152906 */
                   VMAC_ARENASYS | PG_RESIDENT | PG_R | PG_W | PG_ZEROFILL,
                   HOBNULL,                     /* current task */
                   PGCRPTEOWNER,                /* owner */
                   HOBNULL,                     /* mte */
                   0,                           /* sel flags */
                   0,                           /* block no */
                   SSToDS(&ac)) != NO_ERROR) {
            pgAssert(FALSE);
    }
    pgLDTva  = ac.ac_va;    // save the linear address
// GSR end

    /*
     *  Allocate the linear memory for the user page tables.
     *
     *  Note that the start of the object must not cross a 512K boundary
     *  (amount of memory for one processes page tables).  This is so the
     *  page tables for the page tables don't cross a page boundary.
     */
// GSR start
//  if (VMAllocMem(TKCPTDAMAX*MAXCRPDE*PAGESIZE,/* reserve size */
    if (VMAllocMem(TKCPTDAMAX*MAXCR_PDE*PAGESIZE,/* reserve size */
// GSR end
                   0,                           /* commit size */
                   VMAC_ARENASYS+VMAC_ALIGNDIR+PG_SWAPPABLE+PG_R+PG_W,
                   HOBNULL,                     /* current task */
                   PGCRPTEOWNER,                /* owner */
                   HOBNULL,                     /* mte */
                   0,                           /* sel flags */
                   0,                           /* block no */
                   SSToDS(&ac)) != NO_ERROR) {
            pgAssert(FALSE);
    }
    pgCRPtHOB = ac.ac_hob;
    (ulong_t)pgpCRPageTables = ac.ac_va;

    return(ac.ac_va);
}


/***EP  PGInitBuf - Allocate PGAllocBuf buffers
 *
 *      PGInitBuf allocates the linear address space, page tables, and
 *      selectors for the PGAllocBuf buffers used by the loader and swapper.
 *      The linear memory for the user page tables is also allocated here
 *      out of convenience.
 *
 *          - allocate linear address space for swap linear aliases
 *          - allocate GDT selectors for 16:16 alias pointers
 *
 *      ENTRY   NONE
 *      EXIT    NONE
 *
 *      allocate linear address space for PGAllocBuf buffers
 *      setup pointers
 *      for (each 64k in buffers)
 *          allocate a selector
 *          map selector to linear range
 *          store selector in pgBufSels array
 *      allocate buffer page tables with pgPinPt, and keep present by no unpin
 */

/*  The following two defines are use during pginit to fix
 *  Intel Errata #10 for the 386b1 stepping level
 */
#define PG_ERRATA10_LINADDR ((ulong_t)0x80000000l)

// no longer used since ladder 0x80000000 not in the pgcrpte region
// #define PG_ERRATA10_HELPER(pgcrpte)    \
//      ( (PG_ERRATA10_LINADDR- ((ulong_t)pgcrpte)) / (TKCPTDAMAX*MAXCRPDE) )

void
PGInitBuf()
{
    register int i;
    VMAC ac;
    laddr_t laddr;
    struct pgdata_s *ppd;
    struct vp_s *pvp;
    ulong_t ErrorLogFrame;

    // B734704 build break
    if (CheckABIOS())
    {
        for (i = 0; i < 6 ; ++i )
        {
           pgGetErrorLogFrame(i,SSToDS(&ErrorLogFrame));
           if ((ErrorLogFrame != -1) && (ErrorLogFrame <= pgPhysMax))
           {
              pgTrap2BootFix((frame_t)ErrorLogFrame);
           }
        }
    }

    /*
     *  b730502 - Allocation of linear memory for the user page tables
     *            moved to PGAllocCRPTE.
     */

    /*
     * Intel Errata #10: linear address %80000000 must be marked not preesent
     *     Since %80000000 falls in the pgcrpte arena, mark the corresponding
     *     bitmap entry as in use
     */
// no longer used since ladder 0x80000000 not in the pgcrpte region
//  now we allocate a buffer at

//  if (CpuStepping <= CPU_386B1) {
//      // assert that 0x80000000 falls within the pgcrpte arena
//
//      pgAssert(((ulong_t)pgpCRPageTables < PG_ERRATA10_LINADDR) &&
//              (((ulong_t)pgpCRPageTables+(TKCPTDAMAX*MAXCRPDE*PAGESIZE))  >
//                PG_ERRATA10_LINADDR));
//
//      pgbmContext[PG_ERRATA10_HELPER(pgpCRPageTables) / 0x20l] =
//                   1 << (PG_ERRATA10_HELPER(pgpCRPageTables) & 0x1fl);
//  }
    if (CpuStepping <= CPU_386B1) {
       ac.ac_va = PG_ERRATA10_LINADDR;
       if (VMAllocMem(PAGESIZE,                    /* reserve size */
                      0,                           /* commit size */
                      VMAC_ARENASYS+VMAC_LOCSPECIFIC+PG_RESIDENT,
                      HOBNULL,                     /* current task */
                      PGALIASOWNER,                /* owner */
                      HOBNULL,                     /* mte */
                      0,                           /* sel flags */
                      0,                           /* block no */
                      SSToDS(&ac)) != NO_ERROR) {
               pgAssert(FALSE);
       }
       pgAssert(ac.ac_va == PG_ERRATA10_LINADDR);
    }
// end errata

    /*
     *  Allocate linear address space for pgAllocBuf buffers
     */
    if (VMAllocMem(BUFSIZE,             /* ulong_t cbReserve */
        0,                              /* ulong_t cbCommit */
        VMAC_ALIGNSEL,                  /* ulong_t fl */
        HOBNULL,                        /* ushort_t hobptda */
        PGBUFOWNER,                     /* ushort_t hobowner */
        HOBNULL,                        /* ushort_t hmte */
        NA,                             /* ushort_t fsselmgr */
        NA,                             /* ulong_t blkno */
        SSToDS(&ac))) {                 /* struct vmac *pac */
            panic("PGInitBuf: VMAllocMem");
    }
    PGSegBufLaddr = laddr = (laddr_t) ac.ac_va;
    pgSwapPageBufLaddr = laddr + SEGBUFSIZE;
    pgLdrPageBufLaddr = laddr + SEGBUFSIZE+SWAPPERBUFSIZE;

    /*
     *  Tile buffers with 64k gdt selectors
     */
    for (i = 0; i < BUFSELS; i++) {
        SELAllocGDT(&pgBufSels[i]);
        SELMapVirt(laddr, _64K, pgBufSels[i]);
        laddr += _64K;
    }

    /*
     *  Allocate page tables for the buffers by pinning them and keep
     *  them present forever by not unpinning.
     */
    if (pgPinPt(PGSegBufLaddr >> PAGESHIFT,0, BUFSIZE >> PAGESHIFT,
                PG_ALLOC | PG_RESIDENT, SSToDS(&ppd))) {
        panic(("PGInitBuf: pin"));
    }

    /*
     *  Get swappable overcommit for segment buffers now, with the
     *  understanding that we will also need short-lock overcommit
     *  when we go to use the buffers.
     */
    if (pgCommit(MAXSEGBUFS*16, PG_SWAPPABLE)) {
        panic(("PGInitBuf: commit"));
    }

    /*
     *  Initialize the pte images used for the segment buffers
     */
    pgAllocBufHob = ac.ac_hob;
    for (i = 0; i < MAXSEGBUFS*16; ++i) {
        pvp = pgAllocVP();
        pgAssert(pvp->vp_refcount == 1);
        pvp->vp_hob = ac.ac_hob;
        pvp->vp_obpg = i;
        pvp->vp_flags = VP_RESIDENT | VP_PF;
        pgSegBufPte[i] = (pgpVPToVPI(pvp) << PT_FRAMESHIFT) | PT_RESIDENT |
                         PT_RING3WRITE;
    }

    /*
     *  Setup memory statistics
     */
#ifdef MEMSTAT
    pTCBCur->TCBpMemStatCur = &pTCBCur->TCBMemStat;
    pPTDACur->ptda_pmemstatcur = &pPTDACur->ptda_memstat;
#endif

    /*
     *  Attach vps to the idle and free list heads
     */
    pgIdleList.li_headppf->pf_pvp = &pgListHeadVP;
    pgIdleList.li_headppf->pf_flags = 0;
    pgFreeList.li_headppf->pf_flags = 0;
    pgListHeadVP.vp_pfidle = 1;
    pgFreeList.li_headppf->pf_free = 1;
    pgAssert(pgListHeadVP.vp_busy == 0);
}


/***EP  PGInitSwap - Inform page manager if swapping is enabled.
 *
 *      PGInitSwap informs the page manager whether swapping is enabled,
 *      whether the swapper requires virtual or physical addresses,
 *      and whether the swapper requires physical contiguity.
 *
 *          - set swap manager address type flags/swapping enabled flag
 *
 *      ENTRY   flags - enable, address type, and contiguity flags:
 *                  PIS_ADDRMASK - enable and address type field:
 *                      PIS_NOSWAP - swapping disabled
 *                      PIS_PADDR - physical addresses required
 *                      PIS_VADDR - 16:16 virtual addresses required
 *      EXIT    NONE
 *
 *      if (flags != PIS_NOSWAP)
 *          pgSwapAddrType = flags
 *          PGSwapEnabled = 1
 *          free resident overcommit used for discardable and swappable pages
 */

void
PGInitSwap(flags)
int flags;
{
    int rc;

    if (flags != PIS_NOSWAP) {
        pgSwapAddrType = flags;
        PGSwapEnabled = 1;

        /*
         *  Get resident overcommit for resident page tables that have been
         *  allocated so far.  We don't get resident commit before now
         *  since we don't want to double commit them if swapping is off.
         *
         *  Add in one extra page of request for the current thread's TSD.
         */
        rc = pgCommit(pgResPT+1, PG_RESIDENT);
        pgAssert(rc == 0);
    }
}


/***LP  pgAllocArena - Allocate physical/virtual pages from arenainfo array.
 *
 *      pgAllocArena allocates contiguous physical memory and/or virtual
 *      memory from the arenainfo array.  Physical memory is allocated by
 *      carving up the first free physical block above 1M in the array large
 *      enough to satisfy the request, and creating a new arenainfo entry by
 *      moving the following entries down to make room.
 *
 *      The arena info array is divided into two regions.  The records from
 *      the beginning of the array up to the record marked AF_PEND describe
 *      the physical arena as well as the linear memory mapped to them.
 *      Records between AF_PEND and the last record, AF_VEND, describe linear
 *      memory that is not mapped to physical memory.
 *
 *      Sparse memory can be allocated by first allocating the object
 *      specifying only linear memory.  The physical memory is then allocated
 *      by specifying physical+specific, and giving the linear address to map
 *      in laddr.
 *
 *      ENTRY   arenainfo - initial segment location information
 *              psize - size of physical region to allocate
 *              vsize - size of virtual region to allocate
 *              laddr - linear address of region (-1 if not applicable)
 *              owner - owner of the memory
 *      EXIT    physical address of allocated memory
 *
 */

paddr_t
pgAllocArena(arenainfo, size, flags, offset, pladdr, owner)
struct arena_s *arenainfo;
ulong_t size;
int flags;
ulong_t offset;
laddr_t *pladdr;
VMHOB   owner;
{
    register struct arena_s *pa, *p;
    laddr_t laddr;
    paddr_t paddr;
    ulong_t mask;

    /*
     * Make size a dword multiple, and set mask if we're to allocate at a
     * specific offset within a page.
     */
    size = (size + 3) & ~3;

    if (flags & AA_SPECIFIC) {
        mask = PAGEMASK;
    } else {
        mask = 0;
        offset = 0;
    }

    pa = arenainfo;
    if (flags & AA_PHYSICAL) {
        /*
         * Caller wants physical memory.
         *
         * Search backwards for a region that can satisfy the request.
         */
        for (pa = pa1M; pa < paPend; ++pa) {
            if ((pa->a_aflags & AF_FREE) && pa->a_size >= size) {
                /*
                 * We found a free block that's big enough, now see if it
                 * can handle the requested position.
                 *
                 * paddr is the highest address in the free block at the
                 * specified offset of the specified size.
                 */
                paddr = pa->a_paddr - offset + mask;
                paddr = (paddr & ~mask) + offset;

                if (paddr >= pa->a_paddr && paddr + size <= (pa+1)->a_paddr) {
                    /*
                     * We've found a region (we fit entirely within the free
                     * region and we didn't wrap around 4G).  Create another
                     * entry if there will be a leftover piece following our
                     * block.
                     */
                    if (paddr + size < (pa+1)->a_paddr) {
                        /*
                         * Move the following entries up to make room for
                         * another.
                         */
                        for (p = paVend; p >= pa; --p) {
                            *(p + 1) = *p;
                        }
                        (pa+1)->a_paddr = paddr + size;
                        (pa+1)->a_size = (pa+2)->a_paddr - (pa+1)->a_paddr;
                        pa->a_size = (pa+1)->a_paddr - pa->a_paddr;
                        paPend++;
                        paVend++;
                    }

                    /*
                     * If the free block is bigger than what we need, create
                     * another entry.
                     */
                    if (paddr > pa->a_paddr) {
                        /*
                         * Move the following entries up to make room for
                         * another.
                         */
                        for (p = paVend; p >= pa; --p) {
                            *(p + 1) = *p;
                        }
                        pa->a_size = paddr - pa->a_paddr;
                        (pa+1)->a_paddr = paddr;
                        (pa+1)->a_size = size;
                        paPend++;
                        paVend++;
                        pa++;
                    }
                    break;
                }
            }
        }
    }

    if (flags & AA_LINEAR) {
        /*
         * Allocate linear memory.
         */
        if (flags & AA_PHYSICAL) {
            /*
             * We've already allocated the physical memory, therefore we
             * must match the offset within the linear page to the offset
             * within the physical page.
             */
            offset = paddr & PAGEMASK;
            mask = PAGEMASK;
        } else {
            /*
             * Caller wants linear memory only.  Add a new entry on the end
             * of the arenainfo array.
             */
            pa = paVend;
            *(pa + 1) = *pa;
            ++paVend;
        }

        laddr = baseladdr - size - offset;
        laddr = (laddr & ~mask) + offset;

        baseladdr = laddr & ~PAGEMASK;
    }

    /*
     * Initialize the arena info record.
     */
    pa->a_oflags = 0;
    pa->a_sel = 0;
    pa->a_owner = owner;
    pa->a_size = size;
    if (flags & AA_LINEAR) {
        pa->a_aflags = AF_LADDR;
        pa->a_laddr = laddr;
        *pladdr = laddr;
    } else {
        pa->a_aflags = AF_PADDR;
        pa->a_laddr = *pladdr;
    }

    return(pa->a_paddr);
}


/***LP  pgFreeArena - Free a previously allocated arena entry
 *
 *      pgFreeArena marks a previously allocated arena entry free.  It
 *      is not possible to free a subset of an entry, nor an entry whose
 *      virtual size is not equal to its physical size.
 *
 *      ENTRY   arenainfo - initial segment location information
 *              paddr - physical address of allocated memory
 *      EXIT    none
 */

void
pgFreeArena(arenainfo, paddr)
struct arena_s *arenainfo;
register paddr_t paddr;
{
    register struct arena_s *pa = arenainfo;

    while (pa->a_paddr != paddr) {
        pa++;
    }
    pa->a_aflags = AF_FREE;
    pa->a_oflags = 0;
}


/***LP  pgDecommitArena - Decommit a region of committed linear address space
 *
 *      pgDecommitArena marks a region of an arena record free.  The record
 *      must be used in a sparse object, and the region must not start at
 *      the beginning or end of a record.
 *
 *      ENTRY   arenainfo - initial segment location information
 *              laddr - linear address of allocated memory
 *      EXIT    none
 */

void
pgDecommitArena(arenainfo, laddr, size)
struct arena_s *arenainfo;
register laddr_t laddr;
int size;
{
    register struct arena_s *pa = arenainfo;
    register struct arena_s *p;

    /*
     * Find the record we need to split.
     */
    while (laddr < pa->a_laddr || laddr > pa->a_laddr + pa->a_size) {
        ++pa;
    }

    /*
     * Slide the entries after the record up to make room for two new slots.
     */
    p = paVend;                                 /* end of array */
    while (p >= pa) {
        *(p + 2) = *p;
        --p;
    }
    paVend += 2;
    if (pa <= paPend) {
        paPend += 2;
    }

    /*
     * Recompute record addresses and sizes.
     */
    pa->a_size = laddr - pa->a_laddr;

    (pa+2)->a_paddr = pa->a_paddr + pa->a_size + size;
    (pa+2)->a_laddr = pa->a_laddr + pa->a_size + size;
    (pa+2)->a_size  = (pa+3)->a_paddr - (pa+2)->a_paddr;

    (pa+1)->a_paddr = pa->a_paddr + pa->a_size;
    (pa+1)->a_size  = (pa+2)->a_paddr - (pa+1)->a_paddr;
    (pa+1)->a_aflags = AF_FREE;
    (pa+1)->a_oflags = 0;
}


/***EP  PGInitHob - Add object record handles to vp structures
 *
 *      PGInitHob is called by VMInit() to add the object record handles
 *      to the vp structures associated with the kernel load objects.
 *
 *      ENTRY   page - page number of first page of object
 *              npages - size of object in pages
 *              hob - object record handle
 *      EXIT    none
 */

void
PGInitHob(page, npages, hob)
page_t page;
int npages;
VMHOB hob;
{
    register struct pte_s *ppte;
    register struct vp_s *pvp;
    struct pf_s *ppf;
    register int i;

    pgAssert(page >= MINSYSPAGE);

    /*
     *  If the caller is telling us the HOB of some object we want to remember,
     *  record it here.
     */
    if (page == (ulong_t)pgpSysPageTables >> PAGESHIFT) {
        pgSysPtHOB = hob;
    } else if (page == (ulong_t)pgpVPBase >> PAGESHIFT) {
        pgVPHob = hob;
// GSR start
    } else if (page == (ulong_t)pgpShrCRPageTables >> PAGESHIFT) {
        pgShrCRPtHOB = hob;
    }
// GSR end

    /*
     *  For each page in the object, stuff the hob into the vp
     *  If we hit an uncommitted page table, skip to the next page table.
     */
    ppte = pgPagePdTopPte(page, &pgData);
    for (i = 0; i < npages; ++i, ++ppte, ++page) {
        if (pgpPageDir[page >> (PAGEDIRSHIFT - PAGESHIFT)].pt_long == 0) {
            i += NPTEPPT - 1;
            i &= ~(NPTEPPT - 1);
        } else if (ppte->pt_long && ppte->pt_uvirt == 0) {

            pgAssert(ppte->pt_present);
            ppf = pgFrameToPF(ppte->pt_frame);
            if (ppf->pf_pvp == (struct vp_s *) -PAGESIZE) {
                pvp = pgAllocVP();
                pvp->vp_frame = ppte->pt_frame;
                ppf->pf_pvp = pvp;
                pvp->vp_refcount = ppf->pf_refcount;
                pvp->vp_flags = VP_RESIDENT|VP_PF;
                pvp->vp_obpg = i;
            }
            ppf->pf_pvp->vp_hob = hob;
        }
    }
}


/***LP  pgInitPrt - Initialize page range table for resident memory
 *
 *      pgInitPrt initializes the page range table that describes the
 *      physical memory available for resident allocation and long term
 *      lock requests.  The region of physical memory that is reserved
 *      for pageable and short term lock requests is not placed in this
 *      page range table.
 *
 *      ENTRY   cpgreslow - number of pages available for resident memory
 *                          below 16M.
 *              cpgpag -    number of pages reserved for the pageable region.
 *      EXIT    none
 */

void
pgInitPrt(cpgreslow, cpgpag)
int cpgreslow;
int cpgpag;
{
    register struct pagerange_s *ppr = pgPageablePAI.pai_pprt;
    register struct pagerange_s *pprR = pgResidentPAI.pai_pprt;
    int tpg = 0;
    int cpg;
    int i;

    /*
     * Scan from the beginning of the full page range table until we've
     * passed cpgreslow pages.
     */
    pgResidentPAI.pai_nranges = 0;
    for (i = pgPageablePAI.pai_nranges; ; --i, ++ppr, ++pprR) {
        tpg += ppr->pr_lastframe - ppr->pr_firstframe + 1;
        *pprR = *ppr;
        ++pgResidentPAI.pai_nranges;
        if (tpg >= cpgreslow) {
            /*
             * We've hit the low boundary of the pageable region.  Compute
             * how much we overflowed into the pageable region and set the
             * boundary.
             */
            cpg = tpg - cpgreslow;
            pprR->pr_lastframe -= cpg;
            pgLoResBoundfr = pprR->pr_lastframe;
            pgLoResBoundppf = pgFrameToPF(pgLoResBoundfr);
            tpg = ppr->pr_lastframe - pprR->pr_lastframe;
            ++pprR;
            break;
        }
    }

    /*
     * Scan over the table entries that lie within the pageable region.
     */
    for (;;) {
        if (tpg == cpgpag) {
            pgHiResBoundfr = ppr->pr_lastframe + 1;
            pgHiResBoundppf = pgFrameToPF(ppr->pr_lastframe) + 1;
            ++ppr;
            --i;
            break;
        } else if (tpg > cpgpag) {
            /*
             * We've hit the high boundary of the pageable region.
             */
            cpg = tpg - cpgpag;
            pprR->pr_lastframe = ppr->pr_lastframe;
            pprR->pr_firstframe = pprR->pr_lastframe - cpg;
            pgHiResBoundfr = pprR->pr_firstframe;
            pgHiResBoundppf = pgFrameToPF(pgHiResBoundfr);
            ++pgResidentPAI.pai_nranges;
            ++ppr;
            --i;
            ++pprR;
            break;
        }
        ++ppr;
        --i;
        tpg += ppr->pr_lastframe - ppr->pr_firstframe + 1;
    }

    /*
     * Scan to the end of the table.
     */
    for (; i > 0; --i, ++ppr, ++pprR) {
        ++pgResidentPAI.pai_nranges;
        *pprR = *ppr;
    }
}


/***LP  pgSetBoundary - Initialize a boundary structure.
 *
 *      pgSetBoundary initializes the fields in a boundary structure
 *      (pointer to last pf below boundary, and pointer to page range
 *      structure containing that pf).
 *
 *      ENTRY   pa - pointer to physical arena structure containing page
 *                   range table to use.
 *              pb - pointer to uninitialized boundary structure.
 *              bound - boundary frame number.
 *      EXIT    none
 */

void
pgSetBoundary(pa, pb, bound)
struct physarena_s *pa;
struct physboundary_s *pb;
frame_t bound;
{
    struct pagerange_s *ppr;
    int i;

    --bound;                            /* last frame before boundary */

    /*
     * Find range structure containing (or preceding) last frame before
     * boundary.
     */
    i = pa->pai_nranges;
    for (ppr = pa->pai_pprt; i > 0 && ppr->pr_firstframe < bound; ++ppr, --i)
        ;
    --ppr;

    /*
     * Set pr pointer to page range containing or preceding boundary.
     * Set pf pointer to last pf before boundary.
     */
    if (ppr >= pa->pai_pprt) {
        pb->pb_ppr = ppr;
        pb->pb_frame = ppr->pr_lastframe >= bound ? bound : ppr->pr_lastframe;
        pb->pb_ppf = pgFrameToPF(pb->pb_frame);
    } else {
        pb->pb_ppr = NULL;
        pb->pb_ppf = NULL;
        pb->pb_frame = -1;
    }
}


/***EP  PGInitInfoSegs - Initialize the tiled sytem and local infosegs
 *
 *      PGInitInfoSegs allocates the system and local infosegs in the
 *      shared arena and creates aliases to them in the system arena.
 *      The infosegs are allocated as swappable objects in the shared
 *      arena and aliased by a system space address.  The system space
 *      alias is PGLock'ed so they may be accessed at interrupt time.
 *
 *          - allocate infosegs in the shared arena
 *          - create aliases in the system arena
 *          - change aliases' state to UVIRT
 *
 *      ENTRY   NONE
 *      EXIT    NONE
 */

void
PGInitInfoSegs()
{
    struct vmac_s acshare;
    struct vmac_s acsys;
    extern struct infoseg_s Lis2;
    extern struct infoseg_s Sis2;
    extern struct ptda_s *pPTDACur;
    extern ulong_t LocInfoSeg;
    extern ushort_t SysInfoSeg;

    /*
     * Allocate a local infoseg in the shared arena.
     */
    acshare.ac_va = SEL_TASKINFOLADDR;
    if (VMAllocMem(LIS_LEN + TLMA_LEN,          /* reserve size */
                   LIS_LEN + TLMA_LEN,          /* commit size */
                   VMAC_LOCSPECIFIC+VMAC_ARENASHR+VMAC_ALIGNSEL+
                   VMAC_SELALLOC+PG_SWAPPABLE+PG_R+PG_U+VMAC_SHARED,
                   HOBNULL,                     /* current task */
                   INFOSEGOWNER,                /* owner */
                   HOBNULL,                     /* mte */
                   SELAL_SELMAP + (3 << SELAL_PRIVSHIFT), /* sel flags */
                   0,                           /* block no */
                  SSToDS(&acshare)) != NO_ERROR) {
            pgAssert(FALSE);
    }
    PGSetAccess(SEL_TASKINFOLADDR >> PAGESHIFT, pPTDACur, 1, PG_W | PG_INCPERM);
    Lis2.is_hob = (ushort_t)acshare.ac_hob;

    /*
     * Allocate a system space object for it.
     * Allocate it as ring 3 now, will be changed to ring 0 at end of init.
     */
    if (VMAllocMem(LIS_LEN  + TLMA_LEN,         /* reserve size */
                   0,                           /* commit sizez */
                   VMAC_ARENASYS + VMAC_SELALLOC+PG_R+PG_U+PG_W,
                   HOBNULL,                     /* current task */
                   INFOSEGOWNER,                /* owner */
                   HOBNULL,                     /* mte */
                   SELAL_SELMAP + (0 << SELAL_PRIVSHIFT), /* sel flags */
                   0,                           /* block no */
                  SSToDS(&acsys)) != NO_ERROR) {
            pgAssert(FALSE);
    }
    Lis2.is_va = (void *) acsys.ac_va;
    Lis2.is_sel = acsys.ac_sel;
    LocInfoSeg = acsys.ac_sel << 16;
    ldrMteAux[AUXORD_DOSLOCALINFO].adce_ring0 = acsys.ac_sel;

    /*
     *  Attach it to user space object
     */
    if (PGAttach(acsys.ac_va >> PAGESHIFT, 0, acshare.ac_va >> PAGESHIFT,
                 pPTDACur, (LIS_LEN+PAGEMASK) >> PAGESHIFT, PG_W+PG_U)) {
        pgAssert(FALSE);
    }

    /*
     * Lock down the system copy.
     */
    if (PGLock(acsys.ac_va >> PAGESHIFT, 0, (LIS_LEN+PAGEMASK) >> PAGESHIFT,
               PG_LONG)) {
        pgAssert(FALSE);
    }
    Lis2.is_ppf = pgFrameToPF(pgPageToPte(acsys.ac_va >> PAGESHIFT, 0)->pt_frame);
    SELSetDescInfo(SELSD_ALL, (ushort_t)D_DATA3, acsys.ac_sel, LIS_LEN,
                   (ulong_t)acsys.ac_va, acsys.ac_hob, 0, 0);


    /*
     * Allocate a system infoseg in the shared arena.
     */
    acshare.ac_va = SEL_SYSINFOLADDR;
    if (VMAllocMem(SIS_LEN,                     /* reserve size */
                   SIS_LEN,                     /* commit size */
                   VMAC_LOCSPECIFIC+VMAC_ARENASHR+VMAC_ALIGNSEL+
                   VMAC_SELALLOC+PG_SWAPPABLE+PG_R+PG_U+VMAC_SHARED,
                   HOBNULL,                     /* current task */
                   INFOSEGOWNER,                /* owner */
                   HOBNULL,                     /* mte */
                   SELAL_SELMAP + (3 << SELAL_PRIVSHIFT), /* sel flags */
                   0,                           /* block no */
                  SSToDS(&acshare)) != NO_ERROR) {
            pgAssert(FALSE);
    }
    Sis2.is_hob = (ushort_t)acshare.ac_hob;


    /*
     * Allocate the system infoseg in the system arena.
     * Allocate it as ring 3 now, will be changed to ring 0 at end of init.
     */
    if (VMAllocMem(SIS_LEN,                     /* reserve size */
                   0,                           /* commit sizez */
                   VMAC_ARENASYS + VMAC_SELALLOC+PG_R+PG_U+PG_W,
                   HOBNULL,                     /* current task */
                   INFOSEGOWNER,                /* owner */
                   HOBNULL,                     /* mte */
                   SELAL_SELMAP + (0 << SELAL_PRIVSHIFT), /* sel flags */
                   0,                           /* block no */
                  SSToDS(&acsys)) != NO_ERROR) {
            pgAssert(FALSE);
    }
    Sis2.is_va = (void *) acsys.ac_va;
    Sis2.is_sel = acsys.ac_sel;

    ldrMteAux[AUXORD_DOSGLOBALINFO].adce_ring0 = SysInfoSeg;

    /*
     *  Attach it to user space object
     */
    if (PGAttach(acsys.ac_va >> PAGESHIFT, 0, acshare.ac_va >> PAGESHIFT,
                 pPTDACur, (SIS_LEN+PAGEMASK) >> PAGESHIFT, PG_W+PG_U)) {
        pgAssert(FALSE);
    }

    /*
     * Lock down the system copy.
     */
    if (PGLock(acsys.ac_va >> PAGESHIFT, 0, (SIS_LEN+PAGEMASK) >> PAGESHIFT,
               PG_LONG)) {
        pgAssert(FALSE);
    }
    Sis2.is_ppf = pgFrameToPF(pgPageToPte(acsys.ac_va >> PAGESHIFT, 0)->pt_frame);
    SELSetDescInfo(SELSD_ALL, D_DATA3, acsys.ac_sel, SIS_LEN,
                   (ulong_t)acsys.ac_va, acsys.ac_hob, 0, 0);

    /*
     *  Convert the overcommit on the infosegs from swappable to resident
     */
    pgAssert(pgSwappablePages >= 2);
    pgSwappablePages -= 2;
}


/***EP PGInitHighMem
 *
 *      If the machine configuration supports greater than 16Mb of physical
 *      memory, reattach the disabled frames to the free list.
 *      Also recompute overcommit variables, and the number of resident
 *      and long-term locked pages allowed.
 *
 *      ENTRY   fDisabled - TRUE => leave high mem disabled
 *                          FALSE => enable high mem
 *      EXIT    none
 */

void
PGInitHighMem(fDisabled)
unsigned fDisabled;
{
    int cpgCurPag;              /* current pageable region size */
    int cpgNewPag;              /* new pageable region size */
    int cpg;
    int phys16M;                /* number of pages below 16M */
    int rc;
    VMAC ac;

    /*
     * BUGBUG: need to add a message to the message file that
     * says: "Because the device driver for the disk containing
     * your swap drive can not address memory above 16M, use of
     * that memory has been disabled."
     * We should also display the message only if the DD can't handle
     * high memory *and* such memory exists in the system.  No use
     * complaining to people about problems they don't have.
     */
// DCR 32500
    // note - pgPhysPages has already been incremented by pgHighPagesUsed
    if (pgHighPages | pgHighPagesUsed) {
// end

// d67673
//      if (!fDisabled) {  //
        // if any block device is 24 bit don't use memory above 16 meg
        // note: _iobegintransfer (diskc.c) does not lock pagebufs (_PGAllocBuf)
        //       and the page manager does not lock low (for perf. reasons)
        //       so loader reads fail with 24 bit devices

        if (!DDwith24BitDMA) {  // if any block device is 24 bit don't use
// end d67673

            phys16M = pgPhysPages;
            cpgCurPag = pgPhysPages - pgMaxResPages;

            pgPhysPages += pgHighPages;
            pgPhysMax = pgTruePhysMax;

            /*
             * Add pf's to free list.
             */
// DCR 32500
            if (pgHighPages) {
// end
                FLINK(ppfBLINK(pgFreeList.li_headppf)) = pgHighMemHeadframe;
                BLINK(pgHighMemHeadppf) = BLINK(pgFreeList.li_headppf);
                FLINK(pgHighMemTailppf) = pgFreeList.li_headframe;
                BLINK(pgFreeList.li_headppf) = pgHighMemTailframe;
            }
            /*
             * Recompute number of resident pages allowed.
             */
            pgMaxResPages =
                    max((pgPhysPages*7)/8, pgPhysPages - (PADPAGES+7*16));
            cpgNewPag = pgPhysPages - pgMaxResPages;

            /*
             * Rebuild page range table for resident memory and reset
             * boundary structures.
             */
            cpg = min(cpgNewPag / 2, cpgCurPag);
            pgInitPrt(phys16M - cpg, cpgNewPag);
            pgSetBoundary(&pgPageablePAI, &pgPageablePAI.pai_end, _1MEG);
            pgSetBoundary(&pgResidentPAI, &pgResidentPAI.pai_end, _1MEG);
                                                    /* 1M == 4G >> PAGESHIFT */
// DCR 32500
            /*  Fix counts if high memory was allocated during initialization
             *  (PGAllocHighMem uses discardable when it should use resident)
             */
            if (pgHighPagesUsed) {
               pgDiscardablePages -= pgHighPagesUsed;
               pgResidentPages    += pgHighPagesUsed;
            }
            pgHighPages=0;  // must zero out so GetSysInfo call doesn't do
                            //  double accounting
            PGGlobalFlags |= PG_HIGH_MEM_USEABLE;
// end
        } else {
            /*
             * use memory above 16 meg as swapper cache.
             */
//  BUGBUG assumming memory above 16 meg tobe contiguous

            rc = VMAllocMem(pgHighPages*PAGESIZE,
                            pgHighPages*PAGESIZE,
                            (VMAC_ALIGNPAGE | PG_UVIRT | PG_R | PG_W),
                            HOBNULL,
                            PGPRTOWNER,
                            HOBNULL,
                            0,
                            pgHighMemHeadframe << PAGESHIFT,
                            SSToDS(&ac));

            if (rc == NO_ERROR) {
                SMcInMemFile=pgHighPages;
                SMpInMemFile=ac.ac_va;
                smIncreaseBitmapForInmemFile();  /* @V3.0RV01 - Resize */
            }                                    /* swapper bitmap     */
        }
    }

    /*
     *  If this machine has less than 16meg of memory usable, always
     *  mask off requests for memory below 16meg so we don't do any special
     *  processing
     */
    if (pgPhysMax < (_16MEG >> PAGESHIFT)) {
        pgFlagMask &= ~PG_16M;
    }
    pgHighInitPending = 0;
// DCR 32500
    PGGlobalFlags |= PG_HIGH_MEM_INITED;

    FinishBufInit(); /*76643 - finish allocating fat cache if applicable*/
// end
}

/***    PGAgerInit - take over the current thread as the aging thread
 *
 *      This routine initializes the current thread as the ager and
 *      then calls PGAgeLoop(), the actual ager worker.
 *
 *      ENTRY   none
 *      EXIT    doesn't return
 *
 *      tune aging rate paramaters
 *      record position of TSDs
 *      call PGAgeLoop
 */
#define PHYSDIVISOR 0x100       /* for tuning aging parameters */

void
PGAgerInit(void)
{
    int i;
    struct agerate_s *part;

    /*
     *  Walk pgAgeRateTable and tune values to reflect amount of phys mem.
     *  Set aging thread to initial priority.
     */
    pgpAgerTCB = pTCBCur;
    part = pgAgeRateTable;
    for (i = 0; i < NRATES; part++, i++) {

        /*
         *  Tune all values but magic -1 in maxidle field of last entry.
         *  BUGBUG - the tuning formula is undoubtably sub-optimal.  Fix it.
         */
        if (i != 0) {
            part->ar_minidle += pgPhysPages / PHYSDIVISOR;
        }
        if (i != NRATES - 1) {
            part->ar_maxidle += pgPhysPages / PHYSDIVISOR;
        }
    }
    pgAgeRate(&pgAgeRateTable[NRATES-1]);       /*set initial priority to min*/

    /*
     *  If swapping is off, don't let the ager get up to aging level 1 unless
     *  the caller really wants it.
     */
    if (!PGSwapEnabled) {
        pgAgeRateTable[1].ar_minidle = 0;
    }

    /*
     *  Figure out the range of ptes that hold the TSDs.  We use that info
     *  when we idle a page so we can inform the scheduler.
     */
    pgTSDStart = pgPagePdTopPte(pageTKTSDs, &pgData);
    pgTSDEnd = pgPagePdTopPte(pageTKTSDsEnd, &pgData);

    /*
     *  Go do our job
     */
    PGAgeLoop();
    pgAssert(FALSE);    /* never returns */
}

/***    pgTrap2BootFix - fix pages that are marked bad in MVRAM
 *
 *      This routine takes a page number that was marked bad (trap2 wise)
 *      in MVRAM and either removes it from the free list or moves its
 *      contense to another page.
 *
 *      BUGBUG: currently only free pages below a 1 meg can be fixed
 *
 *      ENTRY   frame - physical frame number
 *
 *      EXIT    0 always
 *
 */
void pgTrap2BootFix(frame)
frame_t frame;
{
    laddr_t laddr;
    struct pgdata_s *ppd;
    struct vp_s *pvp;
    struct pf_s *ppfOld;
    struct pf_s *ppfNew;
    struct pte_s *ppte;      /* pointer to pte */
    VMHOB  hptda;          /* handle of ptda */
    VMHOB  hob;            /* handle of object  */
    int rc;
    page_t page;

    ppfOld = pgFrameToPF(frame);
    if (ppfOld->pf_free == 1)  {    // if pf is free
        /*
         *  Delete pf from free list and setup old pf so it won't be
         *   used any more
         */
        pgDeletePF(ppfOld);
        ppfOld->pf_flags &= ~(PF_FREE|PF_BUSY);
        ppfOld->pf_llock = 1;                         // make it long locked
        ppfOld->pf_refcount = 1;                      // dummy ref count of 1
        ppfOld->pf_pvp = (struct vp_s *)(pgVPBasePg << PAGESHIFT);
                                                   // set pvp to vp[0]
#ifdef PGSTRICT
        ppfOld->pf_block = -1;
#endif
    } else {                     // pf is inuse
       if (ppfOld->pf_busy == 0  && frame > 0xff) {   // bugbug 0xff
            /*
             *  Get linear address  vp->mo->ma->la+np
             */
            hob = ppfOld->pf_pvp->vp_hob;
            if (VMObjHandleInfo(hob, SSToDS(&laddr),SSToDS(&hptda)))
                return;   // return if error

            /*
             *  Compute page number
             */
            page = (laddr >> PAGESHIFT) + ppfOld->pf_pvp->vp_obpg;

            /*
             *  Compute pointer to corresponding pte
             */
            ppte = pgpSysPageTables + (page - MINSYSPAGE);

            /*
             *  Get new page for transfer
             */
            if ( pgGetPF(page, PG_16M, SSToDS(&ppfNew)) )
              return;   // return if error

            /*
             *  Copy page and pf info
             */
            memcpy((void *)pgAliasTemp(pgPFToFrame(ppfNew), 0),
                          (void *)(page << PAGESHIFT), PAGESIZE);
            memcpy(ppfNew, ppfOld, sizeof(struct pf_s));

            /*
             *  Set frame field of new page in ptr
             */
            ppte->pt_frame = pgPFToFrame(ppfNew);

            /*
             *  Setup old pf sp it won't be used any more
             */
            ppfOld->pf_flags &= ~(PF_FREE|PF_BUSY);
            ppfOld->pf_llock = 1;                 // make it long locked
            ppfOld->pf_refcount = 1;              // dummy ref count of 1
            ppfOld->pf_pvp = (struct vp_s *)(pgVPBasePg << PAGESHIFT);
#ifdef PGSTRICT
        ppfOld->pf_block = -1;
#endif
       }
    }
    return;
}

#pragma code_seg()
