Coherent4.2.10/i386/mchinit.c

/*
 * mchinit.c - protected, nonpaged startup for COHERENT.
 *
 */

#define	_KERNEL		1

#include <common/ccompat.h>
#include <kernel/reg.h>
#include <kernel/trace.h>
#include <sys/mmu.h>
#include <sys/proc.h>
#include <limits.h>


#define	PTABLE0_P	0x00001	/* Page directory physical address.	*/


unsigned total_mem;		/* Total physical memory in bytes.  */

#define	SPLASH	3
#define	NDATA	4	/* process data segments			*/
#define	BLKSZ	2	/* log2 sizeof(BLOCKLIST)/sizeof(cseg_t)	*/

/* 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

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

/*
 * Used to print hex numbers, and to verify data relocation.
 */

extern char digtab [];

/*
 * "__end" is end of kernel (i.e., end of BSS).
 */

extern char __end [];

/*
 * stext
 *
 *   When mchinit runs, bootstrap code assures us that stext corresponds
 *   to physical address 0x2000.
 *
 *   The kernel is generated so that symbol stext has value 0xFFC0_0000.
 *   When mchinit runs, the data segment selector (SEG_386_ID) has an
 *   offset of 0x0040_2000.  So, reference to stext in mchinit () code
 *   also refers to physical address 0x2000.
 */

extern char stext [];

extern char __end_data [], __end_text [], sdata [];
extern int RAM0, RAMSIZE;


/*
 * 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;

/*
 * This is a bitmap of 4mb regions for which we should create 1-1 logical-
 * physical mappings in the page tables.
 */

extern	unsigned long	physical_mapping_map;

/*
 * pageDir
 *
 *  pageDir is the virtual address where the page directory is kept,
 *  in virtual space immediately below kernel text.
 *  As of 94/06/15, pagedir is 0xFFBF_F000.
 * 
 *  Assembly-language startup points CR3 (page directory register)
 *  at *physical* address 0x1000, the second page from the start of
 *  low RAM.  (The lowest page of physical RAM is left unchanged from
 *  boot-up, and contains the BIOS RAM area.)
 */

#define	pageDir		((long *) (stext - NBPC))

int total_pages;	/* How many pages did we start with?  */


/****************************************************************
 * init_phy_seg ()
 *
 ***************************************************************/

#if __USE_PROTO__
void init_phy_seg (long * ptab1_v, int addr, int base)
#else
void
init_phy_seg (ptab1_v, addr, base)
long	* ptab1_v;
#endif
{
	register int i;

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

/****************************************************************
 * idtinit ()
 *
 * 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.
 ***************************************************************/

#if __USE_PROTO__
void idtinit (void)
#else
void
idtinit ()
#endif
{
	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;
	}
}

/****************************************************************
 * mchinit ()
 *
 * Set up page tables just before entering paged mode.
 *
 * mchinit() is the last thing to be done before paging is turned on.
 * It executes in protected mode, and has a valid data segment.
 *
 * At compile-time, data segment symbols are based at 0xFFC0_0000.
 *
 * At run time for mchinit (), the data segment has an offset of
 * 0x0040_2000 in the CPU descriptor tables - selector SEG_386_ID.
 ***************************************************************/

#if __USE_PROTO__
void mchinit (void)
#else
void
mchinit ()
#endif
{
	int lo;		/* Number of bytes of physical memory below 640K.  */
	int hi;		/* Number of bytes of physical memory above 1M.  */
	char * pe; 
	int	i;
	long * ptab1_v;
	unsigned short	base;
	int	codeseg, sysseg, ptable1;
	int	ptoff;	/* An offset into pageDir []  */
	int	nalloc;
	static	SEG	uinit;
	int	budArenaBytes;	/* number of bytes in buddy pool */
	int	kerBytes;	/* number of bytes in kernel text and data */

	__paddr_t	ua_paddr;

	/*
	 * 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')
		/* DO NOTHING */ ;
	CHIRP ('*');

	/* Zero the bss. */
	memset (__end_data, 0, __end - __end_data);

	/*
	 * Zero the page directory, which occupies the page
	 * of virtual space immediately below kernel text.
	 */

	memset (pageDir, 0, NBPC);


	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
	 */

	/*
	 * btocru (__end) - SBASE is the number of pages in kernel text
	 * plus data, rounded up.
	 * PBASE is the starting physical page number of the kernel.
	 *
	 * Set sysmem.lo to the physical page address just past the kernel.
	 */

	kerBytes = (int)(__end - ((SBASE - PBASE) << BPCSHIFT));
	sysmem.lo = btocru (kerBytes);


	/*
	 * 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 page.  This
	 * compensates for systems where the CMOS reports sizes that are
	 * not multiples of 4K.
	 */

	lo = ctob (read16_cmos (LOMEM) >> 2) - ctob (sysmem.lo);
	hi = ctob (read16_cmos (EXTMEM) >> 2);


	/*
	 * Sometimes, we die horribly if there is too much memory.
	 * Artificially limit hi to HACK_LIMIT.
	 */

	if (hi > HACK_LIMIT)
		hi = HACK_LIMIT;
	
	/* Record total memory for later use.  */
	total_mem = ctob (sysmem.lo) + lo + hi;

#if 0
	/*
	 * Clear base memory above the kernel.  93/12/09 - hal
	 *
	 * This is in hopes of eradicating some compatibility issues
	 * that arose at startup with 4.2.05, which did not clear memory
	 * above the kernel.
	 * For example, is the U area, including u.u_rdir, initially NULL
	 * as pfork() expects it to be?
	 */
	CHIRP('z');
	memset (ctob (sysmem.lo + SBASE - PBASE), 0, lo);
	CHIRP('Z');

	/* clear extended memory */
	memset (ONE_MEG + ctob (SBASE - PBASE), 0, hi);
	CHIRP('Y');
#endif

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

	nalloc = (lo + hi) / (sizeof (short) + SPLASH * sizeof (long) + NBPC);


	/*
	 * 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);

	/*
	 * 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);


	/* sysmem.hi is the physical page number just past high RAM */
	sysmem.hi = btocru (hi + ONE_MEG);


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


	/*
	 * Adjust sysmem.lo to be the physical page number just above
	 * not just the kernel, but above sysmem overhead as well.
	 */

	sysmem.lo = btocru (kerBytes + budArenaBytes + nalloc * sizeof (short));


	/*
	 * sysmem.vaddre is the virtual address of the next page after the
	 * kernel.
	 */

	sysmem.vaddre = (caddr_t)(ctob (sysmem.lo + SBASE-PBASE));


	/* 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_pages = 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_pages;
	}

	/*
	 * Initialize the sysmem table (phase 2 - extended RAM).
	 * Put all extended RAM into the sysmem pool.
	 */

	base = btocru (ONE_MEG);
	while (base < sysmem.hi && total_pages < nalloc) {
		* sysmem.pfree ++ = base ++;
		++ total_pages;
	}


	/*
	 * Roundoff error may have made nalloc smaller than necessary.
	 */

	while (base < sysmem.hi) {
		if ((unsigned int)sysmem.pfree + 1
		  >= (unsigned int)sysmem.vaddre)
			break;
		* sysmem.pfree ++ = base ++;
		++ total_pages;
		nalloc ++;
	}


	/*
	 * sysmem.efree points just past the last pointer in the sysmem
	 * table.
	 */

	sysmem.efree = sysmem.pfree;

	/*
	 * Grab a free page of physical memory.
	 *
	 * It will hold the page table entries for the second-from-top
	 * 4 MByte segment of virtual space.
	 * Point the second-to-last page directory entry at this
	 * physical page.
	 *
	 * ptable1 is the physical address of page just grabbed.
	 *
	 * ptab1_v holds the physical address of this page, relocated
	 * to compensate for the current data segment offset.
	 * So, ptab1_v [i] will contain the physical address (and
	 * some mode bits) for the 4KByte page of memory mapped to
	 * virtual address 0xFF80_0000 + (i * NBPC).
	 *
	 * Each nonzero entry in ptab1_v [] points to a 4 KByte
	 * page table.
	 *
	 * The corresponding 4 MByte region of virtual space,
	 * 0xFF80_0000 - (0xFFC0_0000-1), will hold page tables for
	 * various parts of the 4 GByte address space.
	 *
	 * Number 4 MByte segments of virtual space 0,1,..,3FF.
	 *
	 * Suppose we want to access memory in segment i.
	 * Then a 4 KByte page table must be allocated for segment i,
	 * say with physical address p.
	 *
	 * And, page directory entry i must point to the physical
	 * address of the page table entry.
	 *	pageDir [i] = p with mode bits
	 *
	 * If we expect to change which pages of physical memory will
	 * be mapped to segment i, the page table must itself be
	 * accessible, so we give it a virtual address in ptab1_v []:
	 *	ptab1_v [i] = p with mode bits
	 */

	ptable1 = ctob (* -- sysmem.pfree);		/* 5.d */
	pageDir [0x3FE] = ptable1 | SEG_SRW; 

	ptab1_v = (long *) (ptable1 + ctob (SBASE - PBASE));

	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
	 *
	 * F0000000 -> F7FFFFFF		128Mb of 1-1 mapped physical memory.
	 */

	/* 5.a */
	codeseg = ctob (* -- sysmem.pfree);
	ptab1_v [0] = pageDir [0] = codeseg | SEG_RW;

	/* 5.b */
	for (i = 0; i < NDATA; i ++)
		ptab1_v [i + 1] = pageDir [i + 1] =
				ctob (* -- sysmem.pfree) | SEG_RW;

	/* 5.c */
	ptab1_v [0x1FF] = pageDir [0x1FF] =
			ctob (* -- sysmem.pfree) | SEG_RW;

	/* 5.e */
	sysseg = ctob (* -- sysmem.pfree);
	ptab1_v [0x3FF] = pageDir [0x3FF] = sysseg | SEG_SRW;


	/*
	 * Create page directory and page table entries for the space that
	 * will hold the space mapped 1-1 with physical memory.
	 *
	 * The initial mapping includes whatever areas we are told are needed
	 * by the configuration process, as well as enough space for all the
	 * RAM we are told this machine has.
	 */

	physical_mapping_map |= (1 << btosru (hi + 0x100000UL)) - 1;

	for (i = 0 ; i < sizeof (physical_mapping_map) * CHAR_BIT ; i ++) {
	     	unsigned long	physpage;
	     	unsigned long *	phystable;
	     	int		j;

	     	/*
	     	 * Set up page directory to point to table table entries for
	     	 * this space, and also the part of the page-table area that
	     	 * maps itself.
	     	 */

	     	if ((physical_mapping_map & (1 << i)) == 0)
	     		continue;

	     	physpage = ctob (* -- sysmem.pfree);
		ptab1_v [i + btosrd (__PHYSICAL_MAP_BASE)] =
			pageDir [i + btosrd (__PHYSICAL_MAP_BASE)] =
				physpage | SEG_SRW;

		phystable = (unsigned long *)(physpage + ctob (SBASE - PBASE));
		physpage = stob (i);

		for (j = 0 ; j < ctob (1) / sizeof (cseg_t) ; j ++) {
			phystable [j] = physpage | SEG_SRW;
			physpage += NBPC;
		}
	}

	CHIRP ('5');


	/*
	 * 6. initialize  level 2 ^'s to [5.d]
	 *
	 * This ram disk stuff should go away once the scheme
	 * for allocating pieces of virtual memory space is in place.
	 */

	/* 5.c.i */
	for (ptoff = btosrd (RAM0) & 0x3ff;
	     ptoff < (btosrd (RAM0 + 2 * RAMSIZE) & 0x3ff); ++ ptoff) {
		ptab1_v [ptoff] = pageDir [ptoff] =
				ctob (* -- sysmem.pfree) | 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, ...) 
	 */ 

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

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

	init_phy_seg (ptab1_v, ROM - SBASE,   0x0000F0000);	/* 7.e. */
	init_phy_seg (ptab1_v, VIDEOa - SBASE, 0x0000B0000);
	init_phy_seg (ptab1_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 (btocru (UPASIZE));
	ptab1_v [0x3FF] = * uinit.s_vmem | SEG_SRW;
	procq.p_segl [SIUSERP].sr_segp = & uinit;

	/* Clear the U area. */
	ua_paddr = ptab1_v[0x3ff] &~(NBPC - 1);
	memset((caddr_t)(ua_paddr + ctob(SBASE - PBASE)), 0, NBPC);

	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
	 */

	((long *) (codeseg + ctob (SBASE - PBASE))) [PBASE] =
			ctob (PBASE) | SEG_SRW;

	CHIRP ('9');

	/*
	 * 10. load page table base address into MMU
	 *	fix up the interrupt vectors
	 */

	mmuupdnR0();
	CHIRP ('U');
	idtinit ();
	CHIRP ('I');
}