File:  [MW Coherent from dump] / coherent / b / kernel / i386 / mmu.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:37 2019 UTC (7 years ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/*
 * MMU dependent code for Coherent 386
 *
 * Copyright (c) Ciaran O'Donnell, Bievres (FRANCE), 1991
 */

#include <sys/coherent.h>
#include <sys/clist.h>
#include <errno.h>
#include <sys/inode.h>
#include <sys/seg.h>
#include <signal.h>
#include <sys/buf.h>
#include <sys/alloc.h>
#include <l.out.h>
#include <ieeefp.h>

/* These defines belong somewhere else:  */
#define LOMEM	0x15	/* CMOS address of size in K of memory below 1MB.  */
#define EXTMEM	0x17	/* CMOS address of size in K of memory above 1MB.  */
#define ONE_K	1024
#define ONE_MEG	1048576
#define USE_NDATA	1

/*
 * DMA will not work to memory above 16M, so limit the amount of memory
 * above 1M to 15M.  A much cleverer scheme should be implemented.
 */
int HACK_LIMIT = (15*ONE_MEG);

/*
 * For 0 < i < 64, buddysize[i] is log(base 2) of nearest power of two
 * which is greater than or equal to i.
 */
char	buddysize[64] = {
	-1, 0, 1, 2, 2, 3, 3, 3,
	3, 4, 4, 4, 4, 4, 4, 4,
	4, 5, 5, 5, 5, 5, 5, 5,
	5, 5, 5, 5, 5, 5, 5, 5,
	5, 6, 6, 6, 6, 6, 6, 6,
	6, 6, 6, 6, 6, 6, 6, 6,
	6, 6, 6, 6, 6, 6, 6, 6,
	6, 6, 6, 6, 6, 6, 6, 6 };

#define	min(a, b)	((a) < (b) ? (a) : (b))

/*
 * Functions.
 *	Import.
 *	Export.
 *	Local.
 */
void		areacheck();
void		areafree();
void		areainit();
BLOCKLIST *	arealloc();
int		areasize();
cseg_t *	c_alloc();
cseg_t *	c_extend(); 
void		c_free();
int		c_grow();
int		countsize();
void		doload();
char *		getPhysMem();
void		i8086();
void		idtinit();
void		init_phy_seg();
void		mchinit();
void		msigend();
void		msigstart();
void		physMemInit();
SR		*loaded();
unsigned int	read16_cmos();
void		segload();
void		sunload();
void		unload();
void		valloc();

#define		zero_fill(from, len)	memset(from, 0, len)

/*
 * "load" a handle "hp"  to a segment into the space tree for a process
 */
void
doload(srp)
register SR	*srp;
{
	register int	n;
	register	cseg_t *pp;
	register int	base1, flags;
	register int	akey;

	pp = srp->sr_segp->s_vmem;
	flags = srp->sr_segp->s_flags;
	base1 = btocrd(srp->sr_base);
	n = btoc(srp->sr_size);

	/*
	 * we load all pages
	 */
	 /* a shm segment ref may be Read-Write or Read-Only */
	if (srp->sr_flag & SRFRODT)
		akey = SEG_RO;
	else {
		switch (flags&(SFSYST|SFTEXT)) {
		case SFTEXT:	akey = SEG_RO;  break;
		case SFSYST:	akey = SEG_SRW; break;
		default:	akey = SEG_RW;  break;
		}
	}

	do
		ptable1_v[base1++] = (*pp++ & ~SEG_NPL) | akey;
	while (--n);
	mmuupd();
}

/*
 * unload a handle key "key" to a segment from the MMU hardware
 */
void
unload(srp)
register SR *srp;
{
	register int	n, base1;

	base1 = btocrd(srp->sr_base);
	
	n = btoc(srp->sr_size);
	do {
		ptable1_v[base1++] = SEG_ILL;
	} while (--n);
	mmuupd();
}

/*
 * Allocate 'clicks_wanted' clicks of core space.
 * Returns physical segment descriptor if success, else NULL.
 * The physical segment descriptor is a table of page table entries
 * suitable for insertion into a page table.
 */
cseg_t *
c_alloc(clicks_wanted)
unsigned	clicks_wanted;
{
	unsigned	pno;
	cseg_t *pp;
	register cseg_t *qp;

	/* Do we have enough free physical clicks for this request?  */
	if (clicks_wanted > allocno())
		goto no_c_alloc;

	/* Allocate some space for the table to return.  */
	if ((pp = (cseg_t *)arealloc(clicks_wanted)) == 0)
		goto no_c_alloc;
	qp = pp;

	/* fill in entries in the requested table */
	do {
		pno = *--sysmem.pfree;
		if (!pvalid(pno))
			panic("c_alloc");
		*qp++ = (clickseg(pno) & ~SEG_BITS) | SEG_PRE;
	} while (--clicks_wanted);
	return pp;

no_c_alloc:
	return 0;
}

/*
 * Given an array "pp" containing "numClicks" click descriptors,
 *   if "pp" is the click list for a user segment currently loaded
 *     invalidate click entries for "pp" in the current page table
 *   return each click in "pp" to the sysmem pool, if it came from there.
 *   return the array "pp" to the buddy pool.
 */
void
c_free(pp, numClicks)
cseg_t	*pp;
unsigned	numClicks;
{
	unsigned	pno;
	register cseg_t *qp;
	register int	sz;
	SR		*srp;

	if (srp = loaded(pp)) {
		unload(srp);
		srp->sr_segp = 0;
	}
	sz = numClicks;
	if (&sysmem.pfree[sz] > sysmem.efree)
		panic("c_free - nalloc");
	qp = pp;
	do {
		if ((*qp & SEG_NPL) == 0) {
			pno = segclick(*qp);
			if (!pvalid(pno))
				panic("c_free");
			*sysmem.pfree++ = pno;
		} else {
			T_HAL(0x40000, printf("c_free NPL %x ", *qp));
		}
		qp++;
	} while (--sz);
	areafree((BLOCKLIST *)pp, numClicks);
}

/*
 * Given a user virtual address, a physical address, and a byte
 * count, map the specified virtual address into the user data
 * page table for the current process.
 *
 * This is meant to be called from the console ioctl, KDMAPDISP.
 * The user virtual address must be click aligned.
 * The range of physical addresses must lie outside installed RAM
 * or within the "PHYS_MEM" pool.
 *
 * Return 1 on success, else 0.
 */
int
mapPhysUser(virtAddr, physAddr, numBytes)
{
	int ret = 0;
	SR * srp = u.u_segl + SIPDATA;
	SEG * sp = srp->sr_segp;
	cseg_t * pp = sp->s_vmem, * qp;
	int pno, clickOffset, numClicks, i;

	/* Check alignment. */
	if ((virtAddr & (NBPC-1)) || (physAddr & (NBPC-1))) {
		T_HAL(0x40000, printf("mPU: failed alignment "));
		goto mPUdone;
	}

	/* Check validity of range of virtual addresses. */
	if (virtAddr < srp->sr_base ||
	  (virtAddr + numBytes) >= (srp->sr_base + srp->sr_size)) {
		T_HAL(0x40000, printf("mPU: bad vaddr "));
		goto mPUdone;
	}

	/* Check validity of range of physical addresses. */
	/* if not in PHYS_MEM pool... */
	if (!physValid(physAddr, numBytes)) {

		/* get installed RAM physical addresses */
		unsigned int physLow = ctob((read16_cmos(LOMEM) + 3) >> 2);
		unsigned int physHigh = ctob((read16_cmos(EXTMEM) + 3) >> 2)
		  + ONE_MEG;

		T_HAL(0x40000, printf("physLow=%x physHigh=%x ",
		  physLow, physHigh));

		/* Fail if physical range overlaps installed base RAM. */
		if (physAddr < physLow) {
			T_HAL(0x40000, printf("mPU: overlap base RAM "));
			goto mPUdone;
		}

		/* Fail if physical range overlaps installed extended RAM. */
		if (physAddr < physHigh && (physAddr + numBytes) >= ONE_MEG) {
			T_HAL(0x40000, printf("mPU: overlap extended RAM "));
			goto mPUdone;
		}
	}

	/*
	 * For each click in user data segment which is to be remapped
	 *   if current click was taken from sysmem pool
	 *     return current click to sysmem pool
	 *   write new physical address into current click entry
	 *   mark current click as not coming from sysmem pool
	 *   map current click into page table
	 */
	clickOffset = btocrd(virtAddr - srp->sr_base);
	numClicks = numBytes >> BPCSHIFT;
	for (qp = pp + clickOffset, i = 0; i < numClicks; i++, qp++) {
		if ((*qp & SEG_NPL) == 0) {
			pno = segclick(*qp);
			if (!pvalid(pno)) {
				T_HAL(0x40000, printf("mPU: bad release "));
			} else {
				*sysmem.pfree++ = pno;
				T_HAL(0x40000,
				  printf("mPU: freeing virtual click %x ",
				  virtAddr + ctob(i)));
			}
		} else {
			T_HAL(0x40000,
			  printf("mPU: rewriting virtual NPL click %x ",
			  virtAddr + ctob(i)));
		}
		*qp = (physAddr + ctob(i)) | (SEG_RW | SEG_NPL);
		ptable1_v[btocrd(virtAddr) + i] = *qp;
	}
	mmuupd();
	ret = 1;

mPUdone:
	return ret;
}

/*
 * Add a click to a segment.
 * Enlarge buddy table for segment, if needed.
 *
 * Arguments:
 *	pp points to segment reference table (segp->s_vmem, e.g.)
 *	osz is old segment size, in clicks
 *
 * Return pointer to enlarged segment reference table, or NULL if failed.
 */
cseg_t *
c_extend(pp, osz) 
register cseg_t *pp;
int osz;
{
	register	cseg_t *pp1;
	register unsigned	pno;
	register int	i;
	SR		*srp;

	/* Fail if no more free clicks available. */
	if (sysmem.pfree < &sysmem.tfree[1])
		goto no_c_extend;

	/* Don't grow segment beyond hardware segment size (4 megabytes). */
	if (osz >= (NBPS/NBPC))
		goto no_c_extend;

	if (srp = loaded(pp)) {
		unload(srp);
		srp->sr_segp = 0;
	}

	/*
	 * If the old size was a power of 2, it has used up an entire
	 * buddy area, so we will need to allocate more space.
	 */
	if (IS_POW2(osz)) {
		if ((pp1 = (cseg_t*) arealloc(osz+1))==0)
			goto no_c_extend;
		for (i=0; i < osz; i++)
			pp1[i] = pp[i];
		areafree(pp, osz);
		pp = pp1;
	}

	for (i=osz; --i >= 0;)
		pp[i+1] = pp[i];

	pno = *--sysmem.pfree;
	if (!pvalid(pno))
		panic("c_extend");
	pp[0] = clickseg(pno) | SEG_RW;
	return pp;

no_c_extend:
	return 0;
}

/*
 * Given segment size in bytes, estimate total space needed
 * to keep track of the segment (I think - hws).
 *
 * return value is num_bytes plus some overhead...
 */
int
countsize(num_bytes)
int num_bytes;
{
	int ret;

	if (num_bytes <= NBPC/sizeof(long))
		ret = num_bytes+1;
	else
		ret = num_bytes
		  + ((num_bytes + NBPC/sizeof(long) - 1) >> BPC1SHIFT) + 1;
	return ret;
}

/*
 * buddy allocation 
 */

/*
 * Deallocate a segment descriptor area.
 * "sp" is not really a BLOCKLIST*, rather a cseg_t *.
 * "numClicks" is the number of clicks referenced in the area.
 */
void
areafree(sp, numClicks)
BLOCKLIST *sp;
int numClicks;
{
	register int	n;	/* adresse du buddy, taille du reste */
	register int	ix, nx;
	register	BLOCKLIST *buddy;

	areacheck(2, sp);

	/*
	 * Pointer "sp" points to an element in the sysmem table of
	 * free clicks.
	 * Integer "ix" is the index of "sp" into that table.
	 * Will use "ix" to index into one or more buddy tables.
	 */
	ix = sp - sysmem.u.budtab;
	n = areasize(numClicks);
	do {
		/* "nx" is index of buddy element to the one at "ix". */
		nx = BUDDY(ix, n);
		if (sysmem.budfree[nx>>WSHIFT] & 1<<(nx&(WCOUNT-1))) {
			/* coalesce two buddies */
			buddy = sysmem.u.budtab + nx; 
			if (buddy->kval != n)
				break;
			sysmem.budfree[nx>>WSHIFT] &= ~ (1<<(nx & (WCOUNT-1)));
			DELETE2(buddy);
			if (nx < ix) 
				ix = nx;
		} else
			break;
	} while (++n < NBUDDY);
	sysmem.budfree[ix>>WSHIFT] |= 1 << (ix & (WCOUNT-1));
	buddy = sysmem.u.budtab + ix;
	INSERT2(BLOCKLIST, buddy, &sysmem.bfree[n]);
	buddy->kval = n;
	areacheck(3, buddy);
}

/*
 * arealloc()
 *
 * Given size in "clicks" of a segment to manage,
 * return pointer to an array of enough descriptors.
 * If not enough free descriptors available, return 0.
 */
BLOCKLIST *
arealloc(clicks)
register int clicks;
{
	register	BLOCKLIST *sp;
	register	BLOCKLIST *p, *q;
	register int	size;
	BLOCKLIST	*rsp;
	register int	nx;

	areacheck(0, 0);
	size = areasize(clicks);
	/*
	 * 1. Find little end, bloc p, free >= size
	 */
	for (q = p = sysmem.bfree + size;p->forw == p; size++, p++)
		if (p >= sysmem.bfree + NBUDDY - 1) {
			return(0);	/* y en a pas */
		}

	rsp = p->forw;
	DELETE2(rsp);
	nx = rsp - sysmem.u.budtab;
	sysmem.budfree[nx>>WSHIFT] &= ~(1 << (nx & (WCOUNT-1)));
	size = 1<<size;
	sp = rsp + size; /* buddy address */
	while (p-- != q) {
		/*
		 * 2.1 The block is too big, uncouple & free buddy
		 */
		sp -= (size >>= 1);
		nx = sp - sysmem.u.budtab;
		sysmem.budfree[nx>>WSHIFT] |= 1 << (nx & (WCOUNT-1));
		INSERT2(BLOCKLIST, sp, p);
		sp->kval = p - sysmem.bfree;
	}
	areacheck(1, rsp);
	return rsp;
}

void
areainit(n)
{
	extern char __end[];
	register int i;

	for (i=0; i < (1<<(NBUDDY-WSHIFT)); i++)
		sysmem.budfree[i] = 0;
	for (i=0; i<NBUDDY; i++)
		INIT2(&sysmem.bfree[i]);
	sysmem.u.budtab = (BLOCKLIST *)__end;
	n /= sizeof(BLOCKLIST);
	if (n > (1 << NBUDDY))
		panic("areainit");
	for (i=0; i<n; i++)
		areafree(&sysmem.u.budtab[i], sizeof(BLOCKLIST)/sizeof(long));
}

/*
 * areasize()
 *
 * Do a log(base 2) calculation on n.
 * If n is zero, return -1.
 *
 * Else, consider the nearest power of two which is greater than or
 * equal to n
 *	p/2 < n <= p
 * Then set p = 4 * (2**x).  Note BLKSZ is 2.
 * Return max(x,0).
 *
 * If n is too large (more than 3F00), we will go beyond the limits of
 * table buddysize[].
 *
 * In practice, n is the total number of clicks needed in a segment,
 * and the return value will be used to access a buddy system list.
 */
int
areasize(n)
register unsigned int	n;
{
	register int m;
#ifdef FROTZ
	int ret, oldn = n;
#endif

	if (n > 0x3F00)
		panic("areasize");

	n = (n + (1 << BLKSZ) - 1) >> BLKSZ;
	m = n & 0x3F;
#ifdef FROTZ
	if ((n >>= 6) == 0)
		ret = buddysize[m];
	else {
		int index;

		index = n;
		if (m)
			index++;
		ret = buddysize[index] + 6;
	}
	return ret;
#else
	if ((n >>= 6) == 0)
		return buddysize[m];
	return buddysize[n + ((m!=0)?1:0)] + 6;
#endif
}

#define	MAXBUDDY	2048
#define	CHECK(p) ((p>=&sysmem.bfree[0] && p<&sysmem.bfree[NBUDDY]) || \
		(p>=sysmem.u.budtab && p<&sysmem.u.budtab[1<<NBUDDY]))
void
areacheck(flag, sp)
register	BLOCKLIST *sp;
{
	register	BLOCKLIST *next, *start;
	register int i, nx;

	if (sp) {
		if (&sysmem.u.budtab[sp-sysmem.u.budtab] != sp)
		  printf("*check* %d %x %x\n", flag, sp, sysmem.u.budtab);
	}
		
	for (i=0; i<NBUDDY; i++) {
		start = next = &sysmem.bfree[i];
		do {
			next = next->forw;
			if (!CHECK(next))
				printf("next = %x (%d)\n", next, flag);
			if (next->back != start)
				printf("%x->forw->back != %x\n", next, start);
			if (next != &sysmem.bfree[i]) {
				if (next->kval != i)
					printf("bad kval %x, %d (%d)\n",
						next, next->kval, flag);
				nx = next - sysmem.u.budtab;
				if ((sysmem.budfree[nx>>WSHIFT] & (1 << (nx & (WCOUNT-1)))) == 0)
					printf("in bfree but not budfree %x (%d)\n", next, flag);
			}
			start = next;
		} while (next != &sysmem.bfree[i]);
	}
}

MAKESR(physMem, _physMem);
int	PHYS_MEM = 0;		/* Number of bytes of contiguous RAM needed */

/*
 * A block of contiguous physical memory has been allocated for special
 * i/o devices.
 * Problem: clicks of physical memory are in reverse order in the
 * page table.
 * This routine reverses the page table entries for the pages
 * involved.  It relies *heavily* on all pages having virtual addresses
 * in the FFCx xxxx segment.
 *
 * If all goes well, assign physAvailStart to the virtual address of
 * the beginning of the region, and physAvailBytes to the number of bytes
 * in the region.  Otherwise, leave physAvailStart and physAvailBytes at 0.
 *
 * As memory is allocated, physAvailStart advances to point to the next
 * available byte of contiguous memory, physAvailBytes is decremented,
 * and physPoolStart remains set to the virtual address of the start of
 * the contiguous pool.
 */
static int	physPoolStart;	/* start of contiguous memory area */
static int	physAvailStart;	/* next free byte in contiguous memory area */
static int	physAvailBytes;	/* number of bytes in contiguous memory area */

/*
 * Check whether a range of physical addresses lies within the
 * pool of contiguous physical memory.
 */
int
physValid(base, numBytes)
unsigned int base, numBytes;
{
	int vpool;
	int ret = 0;

	if (PHYS_MEM) {
		vpool = vtop(physPoolStart);
		T_HAL(0x40000, printf("PHYS_MEM phys addrs %x to %x  ",
		  vpool, vpool + PHYS_MEM));
		if (base >= vpool && (base + numBytes) <= (vpool + PHYS_MEM))
			ret = 1;
	} else {
		T_HAL(0x40000, printf("No PHYS_MEM "));
	}

	T_HAL(0x40000, printf("physValid(%x, %x) = %d ", base, numBytes, ret));
	return ret;
}

void
physMemInit()
{
	int m, vaddr;
	int err = 0, num_clicks = btoc(PHYS_MEM);
	int prevPaddr, paddr;

	/*
	 * Going half way into page table for physMem
	 *   If entry and its complementary entry aren't both in top segment
	 *     Error exit (no phys mem will be available).
	 *   Get page table entries and swap them.
	 */
	for (m = 0; m < num_clicks/2; m++) {
		int m2 = num_clicks - 1 - m;	/* complementary index */

		/* compute virtual addresses */
		int lo_addr = physMem.sr_base + ctob(m);
		int hi_addr = physMem.sr_base + ctob(m2);

		/* compute indices into page table (ptable1_v) */
		int lo_p1ix = btocrd(lo_addr);
		int hi_p1ix = btocrd(hi_addr);

		/* fetch physical addresses from page table */
		int lo_paddr = ptable1_v[lo_p1ix];
		int hi_paddr = ptable1_v[hi_p1ix];

		/* abort if either address is not in top segment */
		if (btosrd(lo_addr) != 0x3FF) {
			err = 1;
			break;
		}
		if (btosrd(hi_addr) != 0x3FF) {
			err = 1;
			break;
		}

		/* exchange page table entries */
		ptable1_v[lo_p1ix] = hi_paddr;
		ptable1_v[hi_p1ix] = lo_paddr;
	}

	/*
	 * Final sanity check.
	 * In case someone gets creative with startup code, check
	 * again here that the memory is actually contiguous.
	 */
	prevPaddr = vtop(physMem.sr_base);
	for (m = 0; m < num_clicks - 1; m++) {
		paddr = vtop(physMem.sr_base + ctob(m + 1));
		if (paddr - prevPaddr != NBPC) {
			err = 1;
			break;
		}
		prevPaddr = paddr;
	}

	if (!err) {
		physPoolStart = physAvailStart = physMem.sr_base;
		physAvailBytes = PHYS_MEM;
	}
}

/*
 * Return virtual address of block of contiguous physical memory.
 * If request cannot be granted, return 0.
 *
 * Expect physMem resource to be granted during load routine of device
 * drivers.  Once allocated, memory is not returned to the physMem pool.
 */
char *
getPhysMem(numBytes)
unsigned int numBytes;
{
	char * ret = NULL;

	if (numBytes <= physAvailBytes) {
		ret = (char *)physAvailStart;
		physAvailStart += numBytes;
		physAvailBytes -= numBytes;
	} else
		printf("getPhysMem failed - %d additional bytes "
		  "PHYS_MEM needed\n", physAvailBytes - numBytes);
	return ret;
}

/*
 * Return virtual address of aligned block of contiguous physical memory.
 * Mainly for devices using the stupid Intel DMA hardware without
 *   scatter/gather.
 * If request cannot be granted, return 0.
 *
 * Argument "align" says what physical boundary we need alignment on.
 * It must be a power of 2.
 * For 4k alignment, align = 4k, etc.
 * Sorry, but will throw away memory to get to the next acceptable address.
 *
 * Once allocated, memory is not returned to the physMem pool.
 */
char *
getDmaMem(numBytes, align)
unsigned int numBytes;
unsigned int align;
{
	char * ret = NULL;
	int wastedBytes, neededBytes;

	if (align == 0) {
		printf("getDmaMem(0) (?)\n");
		goto getDmaMemDone;
	}

	if (!IS_POW2(align)) {
		printf("getDmaMem(%x) (?)\n", align);
		goto getDmaMemDone;
	}

	/*
	 * Waste RAM from bottom of pool up to physical
	 * address with desired alignment.
	 */
	wastedBytes = align - (vtop(physAvailStart) % align);
	neededBytes = numBytes + wastedBytes;

	if (neededBytes <= physAvailBytes) {
		ret = (char *)physAvailStart + wastedBytes;
		physAvailStart += neededBytes;
		physAvailBytes -= neededBytes;
	} else
		printf("getDmaMem failed - %d additional bytes "
		  "PHYS_MEM needed\n", physAvailBytes - neededBytes);

getDmaMemDone:
	return ret;
}
/***************/

#undef	ptable1_v

/*
 * pageDir is the physical address of the click in use for the page
 * directory, offset by ctob(SBASE - PBASE)
 */
#define	pageDir		((long *)(&stext[ctob(-1)]))

int total_clicks;	/* How many clicks did we start with?  */

void
mchinit()
{
	extern char __end[], __end_data[], stext[], __end_text[], sdata[];
	extern int RAM0, RAMSIZE;

	int lo;		/* Number of bytes of physical memory below 640K.  */
	int hi;		/* Number of bytes of physical memory above 1M.  */
	register char *pe; 
	register int zero = 0;
	register int	i;
	register	long *ptable1_v;
	register unsigned short	base;
	int	sysseg, codeseg, stackseg, ramseg, ptable1;
	int	ptoff;	/* An offset into pageDir[]  */
#if USE_NDATA
	int	dataseg[NDATA];
#else
	int	dataseg;
#endif
	int	nalloc;
	extern char	digtab[];
	static	SEG	uinit;
	int	budArenaBytes;	/* number of bytes in buddy pool */
	int	kerBytes;	/* number of bytes in kernel text and data */

	/*
	 * 1.
	 *   a. Relocate the data on a page boundary (4K bytes) the
	 *      bootstrap relocates it on a paragraph boundary (16 bytes)
	 *
	 *   b. Verify that the data has been relocated correctly 
	 */
	pe = __end_data;					/* 1.a */
	i = (((unsigned)__end_text+15) & ~15) - (unsigned)sdata;
	do {
		pe--;
		pe[0] = pe[i];
	} while (pe != sdata);					/* 1.b */

	/*
	 * Can now access the .data segment from C.
	 * If not, next loop will hang the kernel.
	 */
	CHIRP('A');
	while (digtab[0]!='0');
	CHIRP('*');

	/* Zero the bss. */
	pe = __end_data;
	do
		*pe++ = zero;
	while (pe != __end);

	/*
	 * Zero the level 0 page directory, which occupies the click
	 * of virtual space immediately below kernel text.
	 */
	pe = (char *) pageDir;
	do
		*pe++ = zero;
	while (pe != stext);

	CHIRP('2');

	/*
	 * 3. Calculate total system memory.
	 *    Count the space used by the system and the page
	 *    descriptors, the interrupt stack, and the refresh work area
	 *
	 * a. initialize allocation area and adjust system size
	 *    to take allocation area and free page area into account
	 */

	/*
	 * btoc(__end) - SBASE is the number of clicks in kernel text
	 * plus data, rounded up.
	 * PBASE is the starting physical click number of the kernel.
	 *
	 * Set sysmem.lo to the physical click address just past the kernel.
	 */
	DV(__end);

	kerBytes = __end - ((SBASE - PBASE)<<BPCSHIFT);
	DV(kerBytes);

	sysmem.lo = btoc(kerBytes);
	DV(sysmem.lo);

	/*
	 * lo is the size in bytes of memory between the end of the kernel
	 *	and the end of memory below 640K.
	 * hi is the size in bytes of memory over 1 Megabyte (Extended memory).
	 *
	 * Round the sizes from the CMOS down to the next click.  This
	 * compensates for systems where the CMOS reports sizes that are
	 * not multiples of 4K.
	 */
	DV(read16_cmos(LOMEM));
	lo = ctob(read16_cmos(LOMEM) >> 2) - ctob(sysmem.lo);
	DV(lo);

	DV(read16_cmos(EXTMEM));
	hi = ctob(read16_cmos(EXTMEM) >> 2);
	DV(hi);

	/*
	 * Sometimes, we die horribly if there is too much memory.
	 * Artificially limit hi to HACK_LIMIT.
	 */
	if (hi > HACK_LIMIT)
		hi = HACK_LIMIT;

	/* clear base memory above the kernel */
	CHIRP('z');
	zero_fill(ctob(sysmem.lo+SBASE-PBASE), lo);
	CHIRP('Z');

	/* clear extended memory */
	zero_fill(ONE_MEG+ctob(SBASE-PBASE), hi);
	CHIRP('Y');
	
	/* Record total memory for later use.  */
	total_mem = ctob(sysmem.lo) + lo + hi;
	DV(total_mem);

	/*
	 * sysmem.pfree and relatives will keep track of a pool of 4k pages
	 * assigned to processes, hereinafter known as the sysmem pool.
	 * How many clicks can go into this pool?  nalloc.
	 * Allow NBPC for the click itself, a short for the sysmem pointer,
	 * and SPLASH*sizeof(long) for buddy system overhead.
	 */
	nalloc = (lo+hi) / (sizeof(short) + SPLASH*sizeof(long) + NBPC);
	DV(nalloc);

	/*
	 * ASSERT:
	 * For the moment we want only to assure that the
	 * BUDDY arena and the stack of free pages will fit below
	 * 640K.
	 */
	budArenaBytes = SPLASH*nalloc*sizeof(long);
	DV(budArenaBytes);

#define SIZEOF_FREE_PAGES ((btoc(hi) + btoc(lo))* sizeof(short))
	T_PIGGY(0x800, {
		if (budArenaBytes + SIZEOF_FREE_PAGES >= lo) {
			panic("Too much memory");
		}
	});

	/*
	 * Initialize the buddy system arena.  This memory is used
	 * for the compressed page tables.
	 */
	areainit(budArenaBytes);

	/*
	 * Initialize the stack of free pages.
	 * __end is the virtual address just past kernel data
	 * Point sysmem.tfree to the lowest virtual address just above
	 * the buddy pool, and initialize sysmem.pfree there.
	 */
	sysmem.tfree = sysmem.pfree = 
	  (unsigned short *)(__end + budArenaBytes);
	DV(sysmem.tfree);

	/* sysmem.hi is the physical click number just past high RAM */
	sysmem.hi = btoc(hi+ONE_MEG);
	DV(sysmem.hi);

	/* base is the physical click number just past base RAM */
	base = sysmem.lo + (lo>>BPCSHIFT);
	DV(base);

	/*
	 * Adjust sysmem.lo to be the physical click number just above
	 * not just the kernel, but above sysmem overhead as well.
	 */
	sysmem.lo = btoc(kerBytes + budArenaBytes + nalloc*sizeof(short));
	DV(sysmem.lo);

	/*
	 * sysmem.vaddre is the virtual address of the next click after the
	 * kernel.
	 */
	sysmem.vaddre = ctob(sysmem.lo+SBASE-PBASE);
	DV(sysmem.vaddre);

	/* include in system area pages for arena, free area */

	CHIRP('3');

	/*
	 * 4.
	 *  Free the memory from [end, 640) kilobytes
	 *  Free the memory from [1024, 16*1024) kilobytes
	 *
	 *  We are building a stack of free pages bounded below
	 *  by sysmem.tfree and above by sysmem.efree.  sysmem.pfree
	 *  is the top of the stack.  The stack grows upwards.
	 */
	total_clicks = 0;

	/*
	 * Initialize the sysmem table (phase 1 - base RAM).
	 * Put base RAM above the kernel and sysmem overhead area into
	 * sysmem pool.
	 */
	while (base > sysmem.lo) {
		*sysmem.pfree++ = --base;
		++total_clicks;
	}

	/*
	 * Initialize the sysmem table (phase 2 - extended RAM).
	 * Put all extended RAM into the sysmem pool.
	 */
	base = btoc(ONE_MEG);
	while (base < sysmem.hi && total_clicks < nalloc) {
		*sysmem.pfree++ = base++;
		++total_clicks;
	}
	DV(total_clicks);

	/*
	 * Roundoff error may have made nalloc smaller than necessary.
	 */
	while(base < sysmem.hi) {
		if (sysmem.pfree + 1 >= sysmem.vaddre)
			break;
		*sysmem.pfree++ = base++;
		++total_clicks;
		nalloc++;
	}
	DV(total_clicks);
	DV(nalloc);

	/*
	 * sysmem.efree points just past the last pointer in the sysmem
	 * table.
	 */
	sysmem.efree = sysmem.pfree;
	DV(sysmem.efree);
	DV(allocno());

	T_PIGGY(0x800, {
		/*
		 * ASSERT:  The stack of free pages should end within a click
		 * of the lowest available memory.
		 */
		if ((cseg_t *)ctob(sysmem.lo+SBASE-PBASE) < sysmem.efree) {
			panic("sysmem.lo is too low");
		}

		if (sysmem.efree < (cseg_t *)ctob(sysmem.lo+SBASE-PBASE - 1)){
			panic("sysmem.efree is too low");
		}

		/*
		 * ASSERT:  There should be nalloc total_clicks.
		 */
		if (nalloc != total_clicks) {
			panic("nalloc != total_clicks ");
		}
	});

	CHIRP('4');

	/*
	 * 5. allocate page entries and initialize level 0 ^'s
	 * a. [ 00000000 .. 003FFFFF)		user code segment
	 * b. [ 00400000 .. 007FFFFF)		user data & bss
	 * c. [ 7FC00000 .. 7FFFFFFF)		user stack
	 *c.i.[ 80000000 .. 80FFFFFF)		ram disk
	 * d. [ FF800000 .. FFBFFFFF)		pointers to level 1 page table
	 * e. [ FFC00000 .. FFFFFFFF)		system process addresses
	 */
	codeseg = clickseg(*--sysmem.pfree);		/* 5.a */
	pageDir[0x000] = codeseg  | DIR_RW; 

#if USE_NDATA
	for (i = 0; i < NDATA; i++) {
		dataseg[i] = clickseg(*--sysmem.pfree);	/* 5.b */
		pageDir[0x001+i] = dataseg[i] | DIR_RW;
	}
#else
	dataseg = clickseg(*--sysmem.pfree);		/* 5.b */
	pageDir[0x001] = dataseg | DIR_RW;
#endif

	stackseg = clickseg(*--sysmem.pfree);		/* 5.c */
	pageDir[0x1FF] = stackseg  | DIR_RW; 

	/*
	 * ptable1 is a handle for the click containing page table
	 * entries for the page table.
	 *
	 * allocate a click for ptable1
	 * Then point at it from the page directory.
	 */
	ptable1 = clickseg(*--sysmem.pfree);		/* 5.d */
	pageDir[0x3FE] = ptable1 | DIR_RW; 

	sysseg = clickseg(*--sysmem.pfree);		/* 5.e */
	pageDir[0x3FF] = sysseg  | DIR_RW;

	CHIRP('5');

	/*
	 * 6. initialize  level 2 ^'s to [5.d]
	 */

	ptable1_v  = (long *)(ptable1 + ctob(SBASE-PBASE));
	DV(pageDir);
	DV(ptable1_v);
	ptable1_v[0x000] = codeseg | SEG_SRW;
#if USE_NDATA
	for (i = 0; i < NDATA; i++)
		ptable1_v[0x001+i] = dataseg[i] | SEG_SRW;
#else
	ptable1_v[0x001] = dataseg | SEG_SRW;
#endif
	ptable1_v[0x1FF] = stackseg| SEG_SRW;

	/*
	 * This ram disk stuff should go away once the scheme
	 * for allocating pieces of virtual memory space is in place.
	 */
	for (ptoff = btosrd(RAM0) & 0x3ff;
	  ptoff < (btosrd(RAM0 + 2 * RAMSIZE) & 0x3ff); ++ptoff) {
		ramseg =  clickseg(*--sysmem.pfree);		/* 5.c.i */
		pageDir[ptoff] = ramseg  | DIR_RW; 
		ptable1_v[ptoff] = ramseg | SEG_SRW;
	}

	ptable1_v[0x3FF] = sysseg  | SEG_SRW;

	CHIRP('6');

	/*
	 * 7.
	 * b. map kernel code and data
	 * 	map ^ to:
	 * c. 	level 0 page table
	 * d. 	level 1 page table
	 * e. 	I/O segments (video RAM, ...) 
	 */ 

	ptable1_v  = (long *)(sysseg + ctob(SBASE-PBASE));	/* 7.b */
	DV(ptable1_v);
	for (i = PBASE; i <sysmem.lo; i++)
		ptable1_v[i-PBASE] = clickseg(i) | SEG_SRW;

	ptable1_v[0x3FE] = clickseg(PTABLE0_P) | SEG_SRW;	/* 7.c */
	ptable1_v[0x3FD] = ptable1 | SEG_SRW;			/* 7.d */

	init_phy_seg(ptable1_v, ROM-SBASE,   0x0000F0000);	/* 7.e. */
	init_phy_seg(ptable1_v, VIDEOa-SBASE,0x0000B0000);
	init_phy_seg(ptable1_v, VIDEOb-SBASE,0x0000B8000);

	CHIRP('7');

	/*
	 * 8. allocate and map U area
	 */

	uinit.s_flags = SFSYST|SFCORE;
	uinit.s_size = UPASIZE;
	uinit.s_vmem = c_alloc(btoc(UPASIZE));
	ptable1_v[0x3FF] = *uinit.s_vmem | SEG_SRW;
	procq.p_segp[SIUSERP] = &uinit;

	CHIRP('8');

	/*
	 * 9. make FFC00000 and 00002000 map to the same address
	 * to prevent the prefetch after the instruction turning on
	 * paging from causing a page fault
	 */
	ptable1_v  = (long *)(codeseg + ctob(SBASE-PBASE));
	DV(ptable1_v);
	ptable1_v[PBASE] = clickseg(PBASE) | SEG_SRW;

	CHIRP('9');

	/*
	 * 10. load page table base address into MMU
	 *	fix up the interrupt vectors
	 */
	mmuupdnR0();
	CHIRP('U');
	idtinit();
	CHIRP('I');
}

typedef struct
{
	unsigned short	off_lo;
	unsigned short	seg;
	unsigned short	flags;
	unsigned short	off_hi;
} IDT;

/*
 * ldtinit()
 *
 * Fix up descriptors which are hard to create properly at compile/link time.
 * Apply to idt and ldt.
 *
 * Swap 16-bit words at descriptor+2, descriptor+6.
 */
void
idtinit()
{
	extern IDT	idt[], idtend[];
	extern IDT	ldt[], ldtend[];
	extern IDT	gdtFixBegin[], gdtFixEnd[];

	register IDT *ip;
	register unsigned short tmp;

	for (ip = idt; ip < idtend; ip++) {
		tmp = ip->off_hi;
		ip->off_hi = ip->seg;
		ip->seg = tmp;
	}

	for (ip = ldt; ip < ldtend; ip++) {
		tmp = ip->off_hi;
		ip->off_hi = ip->seg;
		ip->seg = tmp;
	}

	for (ip = gdtFixBegin; ip < gdtFixEnd; ip++) {
		tmp = ip->off_hi;
		ip->off_hi = ip->seg;
		ip->seg = tmp;
	}
}

void
init_phy_seg(ptable1_v, addr, base)
long	*ptable1_v;
{
	register int i;

	for (i=0; i<btoc(0x10000); i++) {
		ptable1_v[addr+i] = base | SEG_SRW; 
		base += NBPC;
	}
}

/*
 * Load up segmentation registers.
 */
SR	ugmtab[NUSEG];

void
segload()
{
	register int i;
	register	SR *start;

	/*
	 * 1. unprogram the currently active UGM user segments
	 *    reset ugmtab
	 */
	for (start = &ugmtab[1]; start < &ugmtab[NUSEG]; start++) {
		if (start->sr_segp)
			unload(start);
		start->sr_segp = 0;
	}

	/*
	 * 2. Load each segment in the p->p_region list into the MMU
	 *    Remember values in ugmtab.
	 */
	start = &ugmtab[1];
	for (i = 1; i < NUSEG; i++) {
		if (u.u_segl[i].sr_segp) {
			*start = u.u_segl[i];
			switch (i) {
			case SIPDATA:
				if (u.u_segl[SISTACK].sr_base)
					start->sr_size = min(start->sr_size,
					  (long)u.u_segl[SISTACK].sr_base-
					  u.u_segl[SISTACK].sr_size);
				break;
			case SISTACK:
				start->sr_base -= start->sr_size;
				break;
			}

			start->sr_segp = 0;
			if (SELF->p_segp[i]) {
				start->sr_segp = SELF->p_segp[i];
				doload(start);
			}
			start++;
		}
	}

	/* 3.  Update shm segment information. */
	shmLoad();
}

SR *
loaded(pp)
register cseg_t *pp;
{
	register SR	*start;

	for (start = ugmtab; start < ugmtab + NUSEG; start++) {
		if (start->sr_segp && start->sr_segp->s_vmem == pp) {
			return start;
		}
	}
	return 0;
}

MAKESR(r0stk, _r0stk);
extern int tss_sp0;

/*
 * General initialization
 */
void
i8086()
{
	unsigned	csize, isize, ssize, allsize;
	caddr_t	base;
	unsigned int	calc_mem, boost;

	/* This is the first C code executed after paging is turned on. */

	workPoolInit();

	/*
	 * Allocate contiguous physical memory if PHYS_MEM is patched
	 * to a nonzero value.
	 */
	if (PHYS_MEM) {
		physMem.sr_size = (PHYS_MEM+NBPC-1)&~(NBPC-1);
		valloc(&physMem);
		physMemInit();
	}

	/*
	 * Allocate a click for ring 0 stack.
	 */
	r0stk.sr_size = NBPC;
	valloc(&r0stk);
	tss_sp0 = r0stk.sr_base + NBPC;

	/*
	 * calc_mem is used for autosizing buffer cache and kalloc pool.
	 * It is total_mem, limited below by 1 meg and above by 12 meg.
	 * The upper limit is a temporary move to allow booting on 16 Meg
	 * systems.
	 *
	 * "boost" is used in autosizing buffer cache and kalloc pool.
	 * It is the number of megabytes of calc_mem above 1 meg, i.e.,
	 * a number between 0 and 11.
	 */
	if (total_mem < ONE_MEG)
		calc_mem = ONE_MEG;
	else if (total_mem > 12 * ONE_MEG)
		calc_mem = 12 * ONE_MEG;
	else
		calc_mem = total_mem;

	boost = (calc_mem - ONE_MEG) / ONE_MEG;

	/*
	 * If the number of cache buffers was not explicitly set (i.e., !0)
	 * then calculate the number of buffers using the simple heuristic:
	 *     128 minimum + 400 per MB of available RAM (i.e., after 1MB)
	 */
	if (NBUF == 0)
		NBUF = 128 + (400 * boost);

	/*
	 * If the amount of kalloc() space was not explicitly set (i.e., !0)
	 * then calculate using the simple heuristic:
	 *     64k minimum + 32k per MB of available RAM (i.e., after 1MB)
	 */
	if (ALLSIZE == 0)
		ALLSIZE = 65536 + (32768 * boost);

	blockp.sr_size = NBUF*BSIZE;
	valloc(&blockp);

	allocp.sr_size= allsize = NBUF*sizeof(BUF) + ALLSIZE;
#if USE_SLOT
	allocp.sr_size += ssize = NSLOT * (sizeof(int) + slotsz);
#else
	ssize = 0;
#endif
	allocp.sr_size += isize = NINODE* sizeof(INODE);
	allocp.sr_size += csize = NCLIST* sizeof(CLIST);
	valloc(&allocp);
	base = allocp.sr_base;
	allkp = setarena(base, allsize);
	base += allsize;
#if USE_SLOT
	slotp = (int *)base;
	base += ssize;
#endif
	inodep = (INODE*) base;
	base += isize;
	clistp = (paddr_t)base;
}

/*
 * Allocate srp->sr_size bytes of physical memory, and map it into
 * virtual memory space.  At the end, the struct at srp will describe
 * the new segment.
 */
void
valloc(srp)
SR	*srp;
{
	register int npage;

	/*
	 * If we've run out of virtual memory space, panic().
	 *
	 * A more graceful solution is needed, but valloc() does
	 * not provide a return value.
	 */
	if (sysmem.vaddre + srp->sr_size > MAX_VADDR) {
		panic("valloc: out of virtual memory space");
	}

	npage = btoc(srp->sr_size);

	srp->sr_base = sysmem.vaddre;
	srp->sr_segp->s_size = srp->sr_size;
	srp->sr_segp->s_vmem = c_alloc(npage);
	srp->sr_segp->s_flags = SFSYST|SFCORE;
	doload(srp);

	sysmem.vaddre += ctob(npage);
}

/*
 * See if the given process may fit in core.
 */
int
testcore(pp)
register PROC *pp;
{
	return 1;
}

/*
 * Calculate segmentation for a
 * new program. If there is a stack segment
 * present merge it into the data segment and
 * relocate the argument list.
 * Make sure that the changes are reflected in the u.u_segl array
 * which sproto sets up.
 */
int
mproto()
{
	return 1;
}

int
accdata(base, count)
unsigned	base, count;
{
	SR *srp;

	srp = &u.u_segl[SIPDATA];
	return base>=srp->sr_base && base+count <= srp->sr_base+srp->sr_size;
}

int
accstack(base, count)
unsigned	base;
{
	SR *srp;

	srp = &u.u_segl[SISTACK];
	return base>=srp->sr_base-srp->sr_size && base+count<=srp->sr_base;
}

int
acctext(base, count)
unsigned	base;
{
	SR *srp;

	srp = &u.u_segl[SISTEXT];
	return base>=srp->sr_base && base+count <= srp->sr_base+srp->sr_size;
}

printhex(v, max)
unsigned long v;
{
	register int i;

	for (i = max-1; i>=0; --i) 
		putchar(digtab[(v >> (i*4)) & 0xF]);
}

/* Read a 16 byte number from the CMOS.  */
unsigned int
read16_cmos(addr)
unsigned int addr;
{
        unsigned char read_cmos();
	
	return((read_cmos(addr+1)<<8) + read_cmos(addr));
} /* read16_cmos() */

int
c_grow(sp, new_bytes)
SEG *sp;
int new_bytes;
{
	register int	i;
	register cseg_t *pp;
	int		new_clicks, pno, nsize, old_clicks;
	SR		*srp;

	T_PIGGY(0x8000000, printf("c_grow(sp: %x, new: %x)", sp, new_bytes););

	new_clicks = btoc(new_bytes);
	old_clicks = btoc(sp->s_size);

	if (new_clicks == old_clicks) {
		goto ok_c_grow;
	}

	if (new_clicks < old_clicks) {
		printf("%s:can't contract segment\n",u.u_comm);
		goto no_c_grow;
	}

	if (new_clicks - old_clicks > allocno()) {
		goto no_c_grow;
	}

	T_PIGGY(0x8000000, printf("nc: %x, oc: %x,",new_clicks,old_clicks););

	/*
	 * Allocate a new descriptor vector if necessary.
	 * pp is the element corresponding to the virtual address
	 * "0"(sr_base)
	 */
	pp = sp->s_vmem;
	nsize = areasize(new_clicks);
	if (nsize != areasize(old_clicks)
	  && !(pp = (cseg_t*)arealloc(new_clicks))) {
		T_PIGGY(0x8000000,
			 printf("Can not allocate new descriptor."););
		goto no_c_grow;
	}

	T_PIGGY(0x8000000, printf("new pp: %x", pp););

	if (0 != (srp = loaded(sp->s_vmem))) {
		T_PIGGY(0x8000000, printf("unloading srp: %x, ", srp););
		unload(srp);
		srp->sr_segp = 0;
	}

	/*
	 * Allocate new descriptors.
	 */
	T_PIGGY(0x8000000, printf("new desc: ["););
	for (i = old_clicks; i < new_clicks; i++) {
		pno = *--sysmem.pfree;
		pp[i] = clickseg(pno) | SEG_RW;
		T_PIGGY(0x8000000, printf("%x, ", pp[i]););
	}
	T_PIGGY(0x8000000, printf("]"););

	/*
	 * Copy unchanged descriptors and free old vector if necessary.
	 */
	if (pp != sp->s_vmem) {
		T_PIGGY(0x8000000, printf("old desc: ["););
		for (i = 0; i < old_clicks; i++) {
			pp[i] = sp->s_vmem[i];
			T_PIGGY(0x8000000, printf("%x, ", pp[i]););
		}
		T_PIGGY(0x8000000, printf("]"););
		areafree((BLOCKLIST*)sp->s_vmem, old_clicks);
	}

	sp->s_vmem = pp;

	/*
	 * clear the added clicks
	 *
	 * MAPIO macro - convert array of page descriptors, offset
	 *   into system global address.
	 */
	T_PIGGY(0x8000000, printf("dmaclear(%x, %x, 0)", 
				ctob(new_clicks - old_clicks),
				MAPIO(sp->s_vmem, ctob(old_clicks))
			   );
	); /* T_PIGGY() */

	dmaclear(ctob(new_clicks - old_clicks),
	  MAPIO(sp->s_vmem, ctob(old_clicks)), 0);

ok_c_grow:
	return 0;

no_c_grow:
	return -1;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.