/*static char *SCCSID = "@(#)pgmap.c    6.6 92/02/22";*/
#define SCCSID	"@(#)pgmap.c    6.6 92/02/22"

/*
 *	IBM/Microsoft Confidential
 *
 *	Copyright (c) IBM Corporation 1987, 1989
 *	Copyright (c) Microsoft Corporation 1987, 1989
 *
 *	All Rights Reserved
 */

#define FNO_NUM  FNO_PGMAP_C
#include <fnomem.h>

#define INCL_ERRORS
#include <bseerr.h>
#include <osconst.h>
#include <ostypes.h>
#include <osmac.h>
#include <mi.h>
#include <dossymc.h>
#include <sigdef.h>
#include <except.h>
#include <pgexport.h>
#include <protmode.h>
#include <vmexport.h>
#include <smexport.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 <seldesc.h>
#include <vmowner.h>
#include <pglocal.h>
#include <kutil.h>
#include <stdlib.h>
#include <string.h>

#define INCL_TYPES
#include <basedef.h>
#include <bsetib.h>

#define FP_SEG16(fp) (*((unsigned short *)&(fp) + 1))
#define FP_OFF16(fp) (*((unsigned short *)&(fp)))

void	*pKStackBase;
struct	pte_s	*ppteKStackBase;
extern	ulong_t CpuStepping;		/* CPU stepping */
extern	ulong_t FPEMDataSize;

/***EP	PGPageToPhys - Convert a virtual page into a physical address.
 *
 *	Converts a virtual page number to a physical address.  The
 *	page must be locked or system uvirt.
 *
 *	    - translate virtual page to pte pointer
 *	    - verify page is marked present and locked or uvirt
 *	    - extract physical address from pte
 *
 *	ENTRY	page - virtual page
 *		pptda - pointer to ptda
 *		ppaddr - point to ulong to return physical page address
 *	EXIT	0 if success, else error
 *
 *	Don't use the 'far' keyword (FS and GS must be preserved)
 *
 *	ppte = pointer to page's pte
 *	if the page is no present or not (locked or uvirt)
 *		return error
 *	*ppaddr = physical page from ppte->pt_frame
 */

int
PGPageToPhys(page, pptda, ppaddr)
page_t page;
struct ptda_s *pptda;
ulong_t *ppaddr;
{
    register struct pte_s *ppte;
    struct pgdata_s *ppd;

    /*
     *	Get the pgdata structure and verify we aren't in no-man's land
     */
    if (pgPageToPd(page, pptda, SSToDS(&ppd)) == 0) {
	/*
	 *  Verify page table is present
	 */
	if (ppd->pd_pcpresent[pgPagePdToPDI(page, ppd)]) {

	    /*
	     *	Verify page is present
	     */
	    ppte = pgPagePdTopPte(page, ppd);
	    if (ppte->pt_present) {
		*ppaddr = ppte->pt_frame << PAGESHIFT;
		return(0);
	    }
	}
    }
    return(ERROR_INVALID_ADDRESS);
}


/***EP	PGMapVirt - Maps a range of ptes to physical memory
 *
 *	Maps a range of physical memory to a range of virtual
 *	pages.	This very quick but very dumb.
 *
 *	This routine may block when operating on pages outside the
 *	system arena, but may be called at interupt time for system
 *	addresses.
 *
 *	    - For npages at page, set up uvirt ptes as requested
 *
 *	Don't use the 'far' keyword (FS and GS must be preserved)
 *
 *	ENTRY:	page - virtual page of reserved region
 *		paddr - base physical address
 *		npages - number of pages to map
 *		flags - page permissions (PG_*) or -1 if permission should
 *			remain unchanged.
 *	EXIT:	NONE
 *r
 *	ppte = pgPageToPte(page, pptda)
 *	pte.pt_present = 1
 *	pte.pt_state = PTSB_UVIRT
 *	pte.pt_frame = paddr >> PAGESHIFT
 *	while (npages--)
 *	    *(ppte++) = pte
 *	    pte.pt_frame++
 *	flush the TLB
 */
void
PGMapVirt (page, paddr, npages, flags)
page_t page;
paddr_t paddr;
int npages;
int flags;
{
    register ulong_t pte;
    register struct pte_s *ppte;

    pgAssert(page >= MINSYSPAGE);  /* need to pin user page tables */

    ppte = pgpSysPageTables + (page - MINSYSPAGE);
    pte = (paddr & PT_FRAME);

    /*
     *	Keep the current permissions
     */
    if (flags == -1) {
	for (; npages > 0; --npages, ++ppte) {
	    pgAssert((ppte->pt_long & (PT_PRESENT | PT_UVIRT | PT_RESIDENT)) ==
		     (PT_PRESENT | PT_UVIRT | PT_RESIDENT));
	    ppte->pt_frame = 0;
	    ppte->pt_long |= pte;
	    pte += PT_FRAMEINCR;
	}

    /*
     *	put in new permissions
     */
    } else {
	pte |= PT_UVIRT | (flags & (PG_W | PG_U)) | PT_PRESENT | PT_RESIDENT;
	for (; npages > 0; --npages, ++ppte) {
	    pgAssert((ppte->pt_long & (PT_PRESENT | PT_UVIRT)) ==
		     (PT_PRESENT | PT_UVIRT));
	    ppte->pt_long = pte;
	    pte += PT_FRAMEINCR;
	}
    }
    pgFlushTLB();
}



/***EP	PGLinToPageList - translate linear range to physical page list
 *
 *	Given a linear address and count of bytes, this routine will
 *	build an array of pagelist_s structures which will describe the
 *	physical pages mapped by the linear range.
 *
 *	This routine may be called at interrupt time.
 *
 *	Don't use the 'far' keyword (FS and GS must be preserved)
 *
 *	Warning:  The caller is trusted to pass in linear addresses that
 *		  are valid and locked or fixed.  If the linear range is
 *		  not present, we could get a fatal fault in the kernel.
 *
 *		  The caller is also trusted to pass in a big enough
 *		  PageList buffer.
 *
 *	ENTRY:	laddr - starting virtual address
 *		pptda - for the context
 *		cb - count of bytes in linear range
 *		ppl - return pointer to page list array
 *	EXIT:	Number of entries in resulting page list array
 *		or -1 if error
 *
 *	if (linear range doesn't begin on a page boundary)
 *	    build the entry for the first range
 *
 *	while (we haven't mapped cb bytes)
 *	    if (this page isn't physically contiguous with the previous one)
 *		start a new entry
 *		fill in base physical address
 *	    pl_cb += min(PAGESIZE, bytes remaining to be mapped)
 */
int
PGLinToPageList (laddr, pptda, cb, ppl)
laddr_t laddr;
struct ptda_s *pptda;
ulong_t cb;
register struct pagelist_s *ppl;
{
    paddr_t lastframe;
    paddr_t thisframe;
    ulong_t slop;
    int entries = 1;
    register struct pte_s *ppte;
    struct pgdata_s *ppd;

    /*
     *	Verify that the page table and page for the starting address are valid
     */
    if (pgPageToPd(laddr >> PAGESHIFT, pptda, SSToDS(&ppd)) != 0 ||
	ppd->pd_pcpresent[pgPagePdToPDI(laddr >> PAGESHIFT, ppd)] == 0 ||
	(ppte = pgPagePdTopPte(laddr >> PAGESHIFT, ppd))->pt_present == 0) {
	return(-1);
    }
    thisframe = ppte->pt_frame << PT_FRAMESHIFT;

    /*
     *	If the linear address is not page aligned, special case the first
     *	entry so we don't have to worry about alignment during the main
     *	loop.
     */
    if (laddr & PAGEMASK) {
	slop = PAGESIZE - (laddr & PAGEMASK);
	ppl->pl_cb = min(slop, cb);
	ppl->pl_paddr = thisframe + (laddr & PAGEMASK);
	cb -= slop;

    /*
     *	If the linear address is page aligned, jump into the main
     *	loop to register it.
     */
    } else {
	goto aligned;
    }

    /*
     *	Loop through the pages in the range, having already accounted for
     *	any partial page at the beginning of the range.
     */
    while ((int) cb > 0) {
	ppte++;
	if (ppte->pt_present == 0) {
	    return(-1);
	}
	lastframe = thisframe;
	thisframe = ppte->pt_frame << PT_FRAMESHIFT;

	if ((thisframe - PAGESIZE) != lastframe) {
	    /*
	     *	This page is not contiguous with the previous one.  Set up a
	     *	new array entry.
	     */
	    ppl++;
	    entries++;
  aligned:
	    ppl->pl_cb = 0;
	    ppl->pl_paddr = thisframe;
	}
	ppl->pl_cb += min(PAGESIZE, cb);
	cb -= PAGESIZE;
    }
    return(entries);
}



/***EP	PGPageListToLin - build a linear mapping of a page list
 *
 *	This routine maps the physical pages described in the page list array
 *	*ppl to a linear address range.  The ptes for the linear range must
 *	be already allocated as UVIRT.
 *
 *	This routine may be called at interrupt time.
 *
 *	Don't use the 'far' keyword (FS and GS must be preserved)
 *
 *	ENTRY:	ppl - pointer to page list array to map
 *		cb - sum of pl_cb fields in *ppl
 *		page - virtual page number to begin linear mapping
 *		flags - page permissions for the range (PAM_*) or -1
 *			if the current permissions should be retained.
 *		psize - on entry - maximum number of linear bytes to map
 *			on exit  - filled with number of bytes in mapping
 *	EXIT:	0 if success, else error code
 */
int
PGPageListToLin (ppl, cb, page, flags, psize)
register struct pagelist_s *ppl;
ulong_t cb;
page_t page;
ulong_t flags;
ulong_t *psize;
{
    register struct pte_s *ppte;
    struct pte_s pteand;
    register struct pte_s pteor;
    int cbtmp;
    int size = 0;
    int maxpages=(*psize + (ppl->pl_paddr & PAGEMASK) + PAGEMASK) >> PAGESHIFT;
    struct pagelist_s *pplbase = ppl;

    pgAssert(page >= MINSYSPAGE);
    ppte = pgpSysPageTables + (page - MINSYSPAGE);

    /*
     *	Setup the pte and and or masks to reflect requested permissions
     */
    if (flags == -1) {
	pteand.pt_long = ~PT_FRAME;
	pteor.pt_long = PT_PRESENT | PT_UVIRT;
    } else {
	pteand.pt_long = ~(PT_FRAME | PT_RING3WRITE | PT_RING3ACCESS);
	pteor.pt_long = PT_PRESENT | PT_UVIRT | (flags & (PG_W | PG_U));
    }

    /*
     *	Loop over each page list entry and map it.
     */
    for (;(int) cb > 0; ppl++) {
	cbtmp = ppl->pl_cb;
	cb -= cbtmp;
	pteor.pt_frame = ppl->pl_paddr >> PT_FRAMESHIFT;

	/*
	 *  Within a particular page list entry, loop over each page within it.
	 *  Round cbtmp up so a partial first page doesn't make our range
	 *  look smaller than it is (and possibly skip a partial last page).
	 */
	cbtmp += (ppl->pl_paddr & PAGEMASK);
	for (;cbtmp > 0; ppte++, cbtmp -= PAGESIZE) {
	    if (--maxpages < 0) {	    /* if map is too big, error */
		return(ERROR_INVALID_PARAMETER);
	    }
	    size += PAGESIZE;
	    ppte->pt_long &= pteand.pt_long;
	    ppte->pt_long |= pteor.pt_long;
	    pteor.pt_long += PT_FRAMEINCR;
	}
    }
    pgFlushTLB();
    size += cbtmp - (pplbase->pl_paddr & PAGEMASK);
    pgAssert(size > 0);
    if (size <= *psize) {
	*psize = size;
	return(0);
    }
    return(ERROR_INVALID_PARAMETER);
}


/***EP	PGPhysToHOB - given a physical address, return HOB
 *
 *	ENTRY:	paddr - physical address to query
 *		phob - pointer to location to return HOB
 *	EXIT:	0 if success, else error code
 *
 *	if (the physical address is valid and resident and has a HOB)
 *	    *phob = HOB
 *	else
 *	    return error
 */
int
PGPhysToHOB (paddr, phob)
paddr_t paddr;
VMHOB *phob;
{
    int i = pgPageablePAI.pai_nranges;
    register struct pagerange_s *ppr = pgPageablePAI.pai_pprt;
    ulong_t frame = paddr >> PAGESHIFT;

    if (frame <= pgPhysMax) {
	while (ppr->pr_lastframe < frame && --i > 0) {
	    ppr++;
	}
	if (i > 0 && frame >= ppr->pr_firstframe && pft[frame].pf_free == 0) {
	    *phob = pft[frame].pf_pvp->vp_hob;
	    return(0);
	}
    }
    return(ERROR_INVALID_ADDRESS);
}



/***LP	pgPageToPte - Convert a virtual page into a pte pointer.
 *
 *	This routine converts a virtual page into a pte pointer.
 *	The returned pointer is only valid if the page tables are pinned.
 *	If the page lies within a vdm page directory alias this
 *	routine will internal error to prevent any unapproved page manager
 *	functions from operating on the aliases (routines that modify
 *	the present and allocated pte counts are a no-no).  Callers
 *	that need to operate on linear addresses that may be VDM aliases
 *	should first convert the alias address to the region it maps.
 *
 *	    - determine proper arena
 *	    - use the page and page table base to form a pointer
 *
 *	ENTRY	page - virtual page
 *		pptda - pointer to ptda
 *	EXIT	pte pointer
 *r
 *	if (page < MAXCRPAGE)
 *	    ppd = &pptda->ptda_pgdata
 *	else if (page >= MINSYSPAGE)
 *	    ppd = &pgData
 *	    assert address not in reserved range
 *	else
 *	    panic
 *	return((struct pte_s *) &ppd->pd_ppte[page - ppd->pd_base])
 */

struct pte_s *
pgPageToPte(page, pptda)
page_t page;
struct ptda_s *pptda;
{
    register struct pgdata_s *ppd;

    /*
     *	If page is within compitibility region, use ppdata structure from ptda
     */
//GSR start
    if (page < MINGLOBALSHRPAGE) {
	pgAssert(pptda->ptda_signature[0] == 'T' &&
		 pptda->ptda_signature[1]=='D');     /* bad ptda signature */
	ppd = (struct pgdata_s *) &pptda->ptda_pgdata;

    /*
     *	test for global shared page tables
     */
    } else if (page < MAXGLOBALSHRPAGE) {
	ppd = &pgShrData;
// GSR end
    /*
     *	Use system page table if in system area and not in vdm alias
     */
    } else if (page >= MINSYSPAGE) {
	pgAssert(page < MAXSYSPAGE);	/* page number past end of memory */
	ppd = &pgData;

    /*
     *	If in an invalid area, panic
     */
    } else {
	pgAssert(FALSE);
    }

    return((struct pte_s *) &ppd->pd_ppte[page - ppd->pd_base]);
}


/***LP	pgAliasTemp - Map a temporary linear alias to a physical page.
 *
 *	pgAliasTemp maps a *very* temporary linear alias to a physical page.
 *	The linear alias is valid until the next call or until a yield.
 *
 *	    - map linear alias
 *	    - flush TLB
 *
 *	ENTRY	frame - physical page frame
 *		f - flag to select alias linear address
 *		    0 -> first laddr
 *		    1 -> second laddr
 *	EXIT	linear address of temporary alias
 *r
 *	laddr = pgAliasLaddr + f*PAGESIZE
 *	get pte pointer from laddr
 *	set frame in pte
 *	pgFlushTLB()
 *	return(pgAliasLaddr)
 */

laddr_t
pgAliasTemp(frame, f)
frame_t frame;
int f;
{
    laddr_t laddr;

    pgAssert((unsigned) f <= 1);

    laddr = pgAliasLaddr + (f << PAGESHIFT);
    pgAliasPte[f].pt_long = (frame << PT_FRAMESHIFT) |
		PT_PRESENT | PT_UVIRT | PT_RING3WRITE;
    pgFlushTLB();
    return(laddr);
}


/***EP	PGMapTmpAlias - Map a temporary linear alias to a linear page.
 *
 *	PGMapTmpAlias maps a *very* temporary linear alias to a linear page
 *	(and following linear page, if it is present) that is in the system
 *	arena or current context.  The linear alias is valid until the next
 *	call or until a yield.
 *
 *	The specified page must be present, and the following page may
 *	optionally be present (it must be present if it is to be mapped).
 *
 *	    - map linear alias
 *	    - flush TLB
 *
 *	ENTRY	page - page number to alias
 *	EXIT	page of temporary alias
 *
 *	get page's physical frame number
 *	map pgAliasLaddr to frame
 *	pgFlushTLB()
 *	return(pgAliasLaddr)
 */

page_t
PGMapTmpAlias(page)
page_t page;
{
    register struct pte_s *pptesrc;

    pgAssert(page >= MINSYSPAGE);
    pptesrc = pgpSysPageTables + (page - MINSYSPAGE);

    /*
     *	Verify the page and its page table are present.
     */
    pgAssert(pgpPageDir[page >> (PAGEDIRSHIFT - PAGESHIFT)].pt_long &&
	     pptesrc->pt_present);

    /*
     * Map the requested page.
     */
    pgAliasPte->pt_long = (pptesrc->pt_long & PT_FRAME) | PT_PRESENT |
			  PT_UVIRT | PT_RING3WRITE;

    ++page;
    ++pptesrc;
#ifdef PGSTRICT
    pgAliasPte[1].pt_long = 0;		    /* assume second page is invalid */
#endif

    /*
     * Map the following page if it's present and valid.
     */
    if (pgpPageDir[page >> (PAGEDIRSHIFT - PAGESHIFT)].pt_long &&
	pptesrc->pt_present) {

	pgAliasPte[1].pt_long = (pptesrc->pt_long & PT_FRAME) | PT_PRESENT |
				PT_UVIRT | PT_RING3WRITE;
    }

    pgFlushTLB();
    return(pgAliasLaddr >> PAGESHIFT);
}


/***EP	PGAllocBuf - allocate a demand load buffer.
 *
 *	PGAllocBuf allocates a demand load buffer for a segment load
 *	or a page load.  If a physical frame is passed in, a page
 *	buffer is used, otherwise a segment buffer is used.
 *	If a segment buffer is used the returned offset will always be zero.
 *	The page buffers are merely a uvirt map of a physical page that
 *	must be resident.  Segment buffers requests cause the allocation
 *	of resident pages.
 *
 *	The page tables used to map the buffers are allocated during
 *	PGInit and are not paged, nor are their various page table
 *	counts (present, allocated, resident/locked) incremented or
 *	decremented.  Also, althought the pages are marked resident so
 *	they will not be pageed, they are given swappable overcommit at init
 *	time and short-term lock commit when allocated. Cuz the bufs have these
 *	special properties, not all page manager functions are allowed
 *	to operate on them.  A PGFree, for example would decrement the
 *	the page table pte counts and free overcommit that would never be
 *	re-requested.  Safe procedures to call are:  PGPreload,
 *	and PGFreeBuf.
 *
 *	    - if frame != -1, make uvirt mapping of frame from page buffer
 *	    - else allocate physical memory and use segment buffer
 *
 *	ENTRY	npages - when PG_REMAP is set, number of bytes in requested
 *			 segment, else number of pages
 *		flags -
 *		    For segment buffers, the following flags are used:
 *
 *		      PG_REMAP - "frame" contains pointer to an array of
 *				pagelist_s structs to map segment to
 *		      if remap is not specified, allocate physical memory
 *		      with the following attributes:
 *
 *			PG_NOBLOCK -> don't block for pgSegBufWanted, rtn err
 *			       Note: used only be SMCompact
 *			PG_CONTIG -> physically contiguous
 *			PG_1M -> physical addresses below 1 Meg
 *			PG_16M -> physical addresses below 16 Meg
 *
 *		    For page buffers, the following flags are used:
 *
 *		      PG_SWAPPABLE -> i/o will be through swapper
 *		      PG_DISCARDABLE -> i/o will be through loaderr
 *
 *		frame - preallocated frame for page buffer, pointer to
 *			pagelist array, or -1
 *		pladdr - pointer to unitialized linear address
 *		psaddr - pointer to unitialized 16:16 GDT segmented address
 *		page - page this memory will ultimately map
 *	EXIT	0 on success
 *		    *pladdr = linear address
 *		    *psaddr = segmented address
 *		else error code
 */

int
PGAllocBuf(npages, flags, frame, pladdr, psaddr, page)
int npages;
int flags;
frame_t frame;
laddr_t *pladdr;
ulong_t *psaddr;
page_t page;
{
    register int i;
    register int offset;
    struct pte_s *ppte;
    struct pte_s *pptetmp;
    ulong_t saddr;
    laddr_t laddr;
    ulong_t j;

    pgAssert(npages != 0);

    /*
     *	If caller passes in a single physical frame, make a uvirt map of it
     *	through a page buffer
     */
    if (frame != -1 && (flags & PG_REMAP) == 0) {
	/*
	 *  Use a separate bitmap depending on the whether this is a swapper
	 *  fault or loader fault.
	 */
	if (flags & PG_DISCARDABLE) {
	    while ((i = pgAllocBit(pgbmLdrPageBuf, MAXLDRFAULTS)) == 0) {
		pgLdrBufWanted = 1;
		if (i = TKSleep(&pgLdrBufWanted, -1, 0, 0)) {
		    pgAssert(i == ERROR_INTERRUPT);
		    return(i);
		}
	    }
	    offset = SEGBUFSIZE+SWAPPERBUFSIZE;
	    pgAssert(PGSegBufLaddr < pgSwapPageBufLaddr &&
		     pgSwapPageBufLaddr < pgLdrPageBufLaddr);
#ifdef PGSTRICT
	    pgLdrBufOwner[i-1] = pTCBCur->TCBNumber;
#endif
	} else {
	    while ((i = pgAllocBit(pgbmSwapPageBuf, MAXSWAPPERFAULTS)) == 0) {
		pgSwapBufWanted = 1;
		if (i = TKSleep(&pgSwapBufWanted, -1, 0, 0)) {
		    pgAssert(i == ERROR_INTERRUPT);
		    return(i);
		}
	    }
	    offset = SEGBUFSIZE;
#ifdef PGSTRICT
	    pgSwapBufOwner[i-1] = pTCBCur->TCBNumber;
#endif
	}

	/*
	 *  We got a bit.  Whack the corresponding pte
	 */
	offset += (i - 1) * PAGESIZE;
	laddr = PGSegBufLaddr + offset;
	pgpSysPageTables[(laddr - MINSYSLADDR) >> PAGESHIFT].pt_long =
			(PT_UVIRT | (frame << PT_FRAMESHIFT) | PT_RING3WRITE |
			PT_PRESENT | PT_RESIDENT);

    /*
     *	Here the caller needs a segment buffer.  Either allocate the
     *	pages for him or use the pagelist he passed in.
     */
    } else {
	/*
	 *  If he wants us to allocate the memory, get short term-lock
	 *  overcommit for it (we already have swappable commit we got
	 *  at init-time).  We have to get commit before the bit to
	 *  avoid deadlock (since some people load segments inside of lock).
	 */
	if (i = pgShortLockCommit(_64K >> PAGESHIFT, 0)) {
	    return(i);
	}

	/*
	 *  Get a buffer from the bit map.  Block until we find one.
	 */
	while ((i = pgAllocBit(pgbmSegBuf, MAXSEGBUFS)) == 0) {
	    if ((flags & PG_NOBLOCK) == 0) {
		for (i=0; i < MAXSEGBUFS; i++) {
		    TKDeclareInversion(pgSegBufOwner[i]);
		}
		pgSegBufWanted = 1;
		if (i = TKSleep(&pgSegBufWanted, -1, 1, 0)) {
		  segerror:
		    pgShortLockCommitFree(_64K >> PAGESHIFT);
		    return(i);
		}
	    } else {
	       return(ERROR_BLOCKED);	// currently used only by SMCompact
	    }
	}
	--i;
	pgSegBufOwner[i] = pTCBCur->TCBNumber;
	TKEnterInversion(pTCBCur);
	offset = i * _64K;
	laddr = PGSegBufLaddr + offset;

	/*
	 *  If this is a request to map already allocated memory, call
	 *  PGLinToPageList to do this.
	 */
	if (flags & PG_REMAP) {
	    j = 60*1024;
	    pgAssert(npages <= j); /* size limit for now */
	    i = PGPageListToLin((struct pagelist_s *)frame, npages,
				laddr >> PAGESHIFT, PG_W, SSToDS(&j));
	    pgAssert(i == 0);	/* loader gave us bogus pagelist or npages */

	    /*
	     *	Since the physical memory in the pagelist might not start
	     *	on a page boundary, we have to skip over any initial page
	     *	fragment
	     */
	    i = (((struct pagelist_s *)frame)->pl_paddr & PAGEMASK);
	    offset += i;
	    laddr += i;

	/*
	 *  The caller wants us to allocate the memory
	 */
	} else {
	    ppte = pgPagePdTopPte(laddr >> PAGESHIFT, &pgData);
	    j = i * 16;       /* index into pte image array */
	    for (pptetmp = ppte, i = npages; i > 0; pptetmp++, i--, j++) {
		pptetmp->pt_long = pgSegBufPte[j];
	    }

	    /* alloc physical memory */
	    if (i = pgAllocPF(ppte, page, npages, flags, 0)) {
		pgFreeBit(pgbmSegBuf, ((laddr - PGSegBufLaddr) / _64K) + 1);
		goto segerror;
	    }
	}
    }
    FP_SEG16(saddr) = pgBufSels[offset / _64K];
    FP_OFF16(saddr) = (ushort_t) (offset % _64K);
    *psaddr = saddr;
    *pladdr = laddr;
    return(0);
}


/***EP	PGFreeBuf - release a demand load buffer.
 *
 *	PGFreeBuf frees a demand load buffer for a segment load
 *	or a page load.  The phsical memory is freed for segment
 *	buffers.
 *
 *	    - verify alignment and position of laddr
 *	    - if laddr is within page buffer zero pte and return
 *	    - else free physical memory, reduce overcommit, zero ptes
 *
 *	ENTRY	laddr - linear address of buffer to be freed
 *	EXIT	NONE
 */

void
PGFreeBuf(laddr)
laddr_t laddr;
{
    register int i;
    struct pte_s *ppte;

#ifdef PGSTRICT
    if (PGSegBufLaddr > pgSwapPageBufLaddr ||
	pgSwapPageBufLaddr > pgLdrPageBufLaddr ||
	laddr < PGSegBufLaddr ||
	laddr > PGSegBufLaddr + BUFSIZE) {
	panic(("PGFreeBuf: addr"));
    }
#endif
    ppte = pgPagePdTopPte(laddr >> PAGESHIFT, &pgData);

    /*
     *	If within page buffers, just zero pte in the appropriate buffer,
     *	free bit, wakeup anyone waiting, and return
     */
    if (laddr >= pgLdrPageBufLaddr) {
	pgAssert((ppte->pt_long & (PT_PRESENT | PT_RESIDENT | PT_UVIRT)) ==
				       (PT_RESIDENT | PT_PRESENT | PT_UVIRT));
	ppte->pt_long = 0;
	i = (laddr - pgLdrPageBufLaddr)/PAGESIZE;
	pgFreeBit(pgbmLdrPageBuf, i + 1);
#ifdef PGSTRICT
	pgLdrBufOwner[i] = 0;
#endif
	if (pgLdrBufWanted) {
	    pgLdrBufWanted = 0;
	    TKWakeup(&pgLdrBufWanted, 0, NULL);
	}
	pgFlushTLB();
	return;

    } else  if (laddr >= pgSwapPageBufLaddr) {
	pgAssert((ppte->pt_long & (PT_PRESENT | PT_RESIDENT | PT_UVIRT)) ==
				       (PT_RESIDENT | PT_PRESENT | PT_UVIRT));
	ppte->pt_long = 0;
	i = (laddr - pgSwapPageBufLaddr)/PAGESIZE;
	pgFreeBit(pgbmSwapPageBuf,i + 1);
#ifdef PGSTRICT
	pgSwapBufOwner[i] = 0;
#endif
	if (pgSwapBufWanted) {
	    pgSwapBufWanted = 0;
	    TKWakeup(&pgSwapBufWanted, 0, NULL);
	}
	pgFlushTLB();
	return;
    }

    /*
     *	If the free is within the segment buffer also free the physical
     *	memory.
     */
    for (i = _64K/PAGESIZE; i--; ppte++) {
	if ((ppte->pt_long & (PT_PRESENT | PT_RESIDENT | PT_UVIRT)) ==
					 (PT_PRESENT | PT_RESIDENT)) {
	    pgFreePF(pgFrameToPF(ppte->pt_frame), ppte->pt_frame);
	    ppte->pt_long = 0;

#ifdef PGSTRICT
	/* page must be resident or free or uvirt */
	} else if (ppte->pt_long && ppte->pt_uvirt == 0) {
	    panic(("pgFreeBuf: seg state"));
	} else {
	    ppte->pt_long = 0;
#endif
	}
    }

    pgShortLockCommitFree(_64K >> PAGESHIFT);

    /*
     *	Free the bit in the bit map and get out
     */
    i = (laddr - PGSegBufLaddr) / _64K;
    pgFreeBit(pgbmSegBuf, i + 1);
    pgSegBufOwner[i] = 0;
    TKExitInversion();
    if (pgSegBufWanted) {
	pgSegBufWanted = 0;
	TKWakeup(&pgSegBufWanted, 0, NULL);
    }
    pgFlushTLB();
}


/***	pgGetKStackPte - Get kernel stack PTE contents
 *
 *	ENTRY	p	source virtual address for pte contents
 *		cp	count of pages to skip from source virtual address
 *		flUser	PT_RING3ACCESS bit
 *			if ((CpuStepping <= CPU_386B1) and (R0 Stack page))
 *				user = PT_RING3ACCESS;
 *			else
 *				user = 0;
 *
 *	EXIT	ulong_t
 *		pte contents for a kernel stack pte
 */

#define pgGetKStackPte(p,cp,flUser) (((*(ulong_t *) \
	pgPageToPte(((ulong_t)(p) >> PAGESHIFT) + (cp), 0)) & PT_FRAME) | \
		(PT_UVIRT | PT_PRESENT | PT_RING3WRITE | (flUser)))


/***EP	PGKStackMap - Precalculate TCB variables for context switching.
 *
 *	PGKStackMap precalculates the values in the TCB required for
 *	context switching.
 *
 *	PGKStackMap depends on the value stored in pTCB-> TCBpPTDA
 *	to calculate these values.
 *
 *	WARNING : PGKStackMap assumes that the physical address of
 *	the PTDA and TCB never changes!  Always use CBPTDA!  Never
 *	use sizeof(ptda_t)!  CBPTDA is rounded to the appropriate
 *	boundary; sizeof(ptda_t) is not.
 *
 *	ENTRY	pTCB		pointer to TCB to set up.
 *
 *	EXIT	void
 *		The following variables are set up :
 *
 *	TCBpteKStackTCB0;	// KStack page 0 of TCB (always present)
 *	TCBpteKStackTCB1;	// KStack page 1 of TCB (pos. not present)
 *	TCBpteKStackTSD;	// KStack page of TSD (swappable)
 *	TCBpteKStackPTDA0;	// KStack page 0 of PTDA (always present)
 *	TCBpteKStackPTDA1;	// KStack page 1 of PTDA (pos. not present)
 *	TCBdescKStackSS;	// SS descriptor
 *	TCBCurrTCB;		// SS-relative offset of Current TCB
 *	TCBCurrTSD;		// SS-relative offset of Current TSD
 *	TCBBiasTCB;		// stack-to-flat TCB conversion value
 *	TCBBiasTSD;		// stack-to-flat TSD conversion value
 *
 *	The Kernel Stack (KStack) region looks like this :
 *
 *	Stack Segment	 KStack Region	       Notes
 *	=============	================	=====
 *
 *		     10 -----------------
 *			|		|
 *	FFFF	----->	| +-----------+ |  pte: TCBpteKStackPTDA2
 *		|	| |	      | |
 *		|     f - |   PTDA    | -
 *		|	| |	      | |
 *		|	| |	      | |  pte: TCBpteKStackPTDA1
 *		|	| |	      | |
 *		|     e - |	      | -
 *	TKOPTDA ----->	| +-----------+ |  pte: TCBpteKStackPTDA0
 *		|	|		|
 *		|	| [Prev. PTDA]	|	[ PTDA danger zone ]
 *		|     d -		-
 *		|	| +...........+ |
 *		|	| .	      . |
 *		|	| .	      . |
 *		|     c - .	      . -
 *		|	| . INVALID   . |	These invalid pages are
 *		|	| .  PAGES    . |	here to fault on stray
 *		|	| .	      . |	pointers.
 *		|     b - .	      . -
 *		|	| .	      . |
 *		|	| .	      . |
 *		|	| +...........+ |
 *		|     a -		-
 *		|	|  [Next TCB]	|	[ TCB danger zone ]
 *		|	| +-----------+ |  pte: TCBpteKStackTCB1
 *		|	| |	      | |
 *		|     9 - |   TCB     | -
 *		|	| |	      | |
 *	CurrTCB ----->	| +-----------+ |  pte: TCBpteKStackTCB0
 *		.	|		|	[ stack overrun zone ]
 *		.     8 -		- <---- pKStackBase +
 *	INVALID .	| +...........+ |	(CPAGETCBSKIP * PAGESIZE)
 *	STACK	.	| .	      . |
 *	AREA	.	| .	      . |
 *		.  6..7 - .	      . -
 *		.	| .  INVALID  . |	These invalid pages are
 *		.	| .   PAGES   . |	here to fault on stray
 *		.	| .	      . |	flat pointers.
 *		.     5 - +-----------+ -
 *		.	| .	      . |	This is the Thread Swappable
 *		.	| .    TSD    . |	Data block which includes the
 *		.	| .	      . |	kernel stack.
 *		.  2..4 - +-----------+ -	pte: TCBpteKStackTSD
 *		.	| .	      . |
 *		.	| .  INVALID  . |	These invalid pages are
 *		.	| .   PAGES   . |	here to fault on stray
 *		.     1 - .	      . -	flat pointers.
 *		.	| .	      . |
 *		.	| .	      . |
 *		.	| .	      . |	[ null pointer zone ]
 *		.     0 - .	      . - <---- pKStackBase
 *		.	| .	      . |
 *	0 ----> +	| .	      . | <---- pKStackBase - CBPTDA
 *			| +...........+ |
 *			+---------------+ <---- underflow page
 *
 *	Calulation Rules
 *	================
 *	The last byte of the PTDA resides in page f.
 *	The first byte of the TCB resides in the page given by CPAGETCBSKIP.
 *	The limit of the expand-down SS segment is TCBCurrTSD.
 *	Pages not used are not present.  The invalid pages are
 *	  reserved to catch stray pointers.
 *
 */

#define KSTACK_ACCESS	(D_PRES+D_DPL0+D_SEG+D_DATA+D_EXPDN+D_W)
#define KSTACK_ATTR	(D_UVIRT)

void
PGKStackMap(pTCB)
register struct tcb_s *pTCB;
{
	ulong_t cbToPtdaTop;
	ulong_t pSSBase;
	ulong_t fpgRing3;
	ulong_t PageTopOfPTDA;
	ulong_t PageBaseOfPTDA;
	register struct desctab *pDesc;
	tsd_t *ptsd;
	ulong_t ldtaddr;

	// Distance from top of PTDA to top of KStack region.

	cbToPtdaTop = (PAGESIZE -
		       ((ulong_t) (pTCB->TCBpPTDA) + CBPTDA)) & PAGEMASK;

	pSSBase = (ulong_t) pKStackBase - (ulong_t) cbToPtdaTop;

	pTCB->TCBCurrTCB = (CPAGETCBSKIP * PAGESIZE) +
			    ((ulong_t) pTCB & PAGEMASK) +
			    cbToPtdaTop;

	pgAssert(pTCB->TCBCurrTCB <= TKOTCB1);

	pTCB->TCBCurrTSD = CPAGETSDSKIP*PAGESIZE + cbToPtdaTop;
					// Note that the TSD is page-aligned

	ptsd = pTCB->TCBpTSD;

	// Compute SS-relative-to-flat conversion values
	pTCB->TCBBiasTCB = (ulong_t) pTCB - (ulong_t) pTCB->TCBCurrTCB;
	pTCB->TCBBiasTSD = (ulong_t) ptsd - (ulong_t) pTCB->TCBCurrTSD;

	//	At this point, we have the following :
	//	SS selector base  : pSSBase
	//	SS selector limit : pTCB->TCBCurrTSD + TSDSTART - 1
	//
	//	We now construct and save the SS descriptor.

	pDesc = (struct desctab *) &ptsd->TSDdescKStackSS;
	pDesc-> d_limit   = (ushort_t)(pTCB->TCBCurrTSD + TSDSTART - 1);
	pDesc-> d_loaddr  = (ushort_t) pSSBase;
	pDesc-> d_hiaddr  = (unsigned char) (pSSBase >> 16);
	pDesc-> d_access  = (unsigned char) KSTACK_ACCESS;
	pDesc-> d_attr	  = (unsigned char) KSTACK_ATTR;
	pDesc-> d_extaddr = (unsigned char) (pSSBase >> 24);

	/*
	 *  While we are at it, also construct the LDT table descriptor
	 *  image.
	 */
	pDesc = (struct desctab *) &ptsd->TSDdescLDT;
	ldtaddr = pTCB->TCBpPTDA->ptda_ldtaddr;
	pDesc-> d_limit   = 0xffff;
	pDesc-> d_loaddr  = (ushort_t) ldtaddr;
	pDesc-> d_hiaddr  = (unsigned char) (ldtaddr >> 16);
	pDesc-> d_access  = (unsigned char) D_LDT0;
	pDesc-> d_attr	  = (unsigned char) 0;
	pDesc-> d_extaddr = (unsigned char) (ldtaddr >> 24);


	/*
	 *  If the FP emulator is installed, set up this thread's
	 *  emulator data area.
	 */
	if ((ldtaddr = (ulong_t) pTCB->TCB_NPX_Buffer) != NULL) {
	    pDesc = (struct desctab *) &ptsd->TSDdescFPEM;
	    pDesc-> d_limit   = (ushort_t) (FPEMDataSize - 1);
	    pDesc-> d_loaddr  = (ushort_t) ldtaddr;
	    pDesc-> d_hiaddr  = (unsigned char) (ldtaddr >> 16);
	    pDesc-> d_access  = (unsigned char) D_DATA3;
	    pDesc-> d_attr    = (unsigned char) 0;
	    pDesc-> d_extaddr = (unsigned char) (ldtaddr >> 24);
	}

	//	Now we need to get the kernel stack page table entries.
	//
	//	The zeroth page of the TCB and the second page of the
	//	PTDA are always valid.	The first page of the TCB and
	//	the zeroth and first pages of the PTDA are optional.
	//	All unused pages are made invalid (0).
	//
	//	Pseudocode:
	//
	//	set TCBpteKStackTCB0 to the zeroth pte of the TCB;
	//
	//	If the TCB spans a page,
	//		set TCBpteKStackTCB1 to the first pte of the TCB;
	//	else
	//		invalidate TCBpteKStackTCB1;

	fpgRing3 = (CpuStepping <= CPU_386B1)? PT_RING3ACCESS: 0;

	pTCB->TCBpteKStackTCB0 = pgGetKStackPte(pTCB, 0, fpgRing3);

	if ((((ulong_t) (pTCB+1) - 1) >> PAGESHIFT) !=
	    ((ulong_t) pTCB >> PAGESHIFT)) {
		pTCB->TCBpteKStackTCB1 = pgGetKStackPte(pTCB, 1, fpgRing3);
	} else {
		pTCB->TCBpteKStackTCB1 = 0;
	}

	pTCB->TCBpteKStackTSD =
	    pgGetKStackPte(ptsd,TSDSTART >> PAGESHIFT,fpgRing3);
					// Get TSD's PTE

	//	Remember - the PTDA is validated from the top down.  The
	//	top pte (TCBpteKStackPTDA2) is always valid!
	//
	//	Pseudocode:
	//
	//	set TCBpteKStackPTDA2 to the zeroth pte of the PTDA;
	//	invalidate TCBpteKStackPTDA1;
	//	invalidate TCBpteKStackPTDA0;
	//
	//	If the PTDA spans (at least) two pages,
	//		TCBpteKStackPTDA1 = TCBpteKStackPTDA2;
	//		set TCBpteKStackPTDA2 to the first pte of the PTDA;
	//
	//	If the PTDA spans exactly three pages,
	//		TCBpteKStackPTDA0 = TCBpteKStackPTDA1;
	//		TCBpteKStackPTDA1 = TCBpteKStackPTDA2;
	//		set TCBpteKStackPTDA2 to the second pte of the PTDA;

	pTCB->TCBpteKStackPTDA2 = pgGetKStackPte(pTCB->TCBpPTDA, 0,0);
	pTCB->TCBpteKStackPTDA1 = 0;
	pTCB->TCBpteKStackPTDA0 = 0;

	PageTopOfPTDA  = ((ulong_t) ((pTCB->TCBpPTDA)+1) - 1) >> PAGESHIFT;
	PageBaseOfPTDA = ((ulong_t) (pTCB->TCBpPTDA)) >> PAGESHIFT;

	if (PageTopOfPTDA != PageBaseOfPTDA) {
		pTCB->TCBpteKStackPTDA1 = pTCB->TCBpteKStackPTDA2;
		pTCB->TCBpteKStackPTDA2 = pgGetKStackPte(pTCB->TCBpPTDA, 1,0);
	}

	if (PageTopOfPTDA == PageBaseOfPTDA+2) {
		pTCB->TCBpteKStackPTDA0 = pTCB->TCBpteKStackPTDA1;
		pTCB->TCBpteKStackPTDA1 = pTCB->TCBpteKStackPTDA2;
		pTCB->TCBpteKStackPTDA2 = pgGetKStackPte(pTCB->TCBpPTDA, 2,0);
	}
}


/***EP	PGKStackRemap - recalculate thread variables for context switching.
 *
 *	PGKStackMap recalculates the values in the TCB required for
 *	context switching.  Only those values which could change
 *	after the thread is created need to be recomputed.
 *
 *	ENTRY	ptcb - pointer to TCB to set up.
 *	RETURN	NONE
 */

void
PGKStackRemap(ptcb)
tcb_t *ptcb;
{
    /*
     * Get TSD's PTE
     */

    ptcb->TCBpteKStackTSD =
	pgGetKStackPte(ptcb->TCBpTSD,
		       TSDSTART >> PAGESHIFT,
		       (CpuStepping <= CPU_386B1)? PT_RING3ACCESS: 0);
}



int pgPageToPd(page, pptda, pppd)
page_t page;
struct ptda_s	*pptda;
struct pgdata_s **pppd;
{
//   _asm int 3
// KDH - Delete 0x10 since this would map to wrong page directory....
   if (page < MAXCR_PAGE) {
      *pppd = (struct pgdata_s *)&pptda->ptda_pgdata;
   } else {
// KDH - Delete 0x10 since this would map to wrong page directory....
      if (page < MAXGLOBALSHRPAGE) {
	  *pppd = &pgShrData;
       } else {
	 if (page < MINSYSPAGE || page >= MAXSYSPAGE) {
	    return(ERROR_ACCESS_DENIED);
	 } else {
	    *pppd = &pgData;
	 }
      }
   }
   return(0);
}



