plan 9 kernel history: overview | file list | diff list

1991/1210/pc/mmu.c (diff list | history)

pc/mmu.c on 1991/0612
1991/0612    
#include	"u.h" 
#include	"lib.h" 
#include	"mem.h" 
#include	"dat.h" 
#include	"fns.h" 
#include	"io.h" 
 
1991/0613    
/* 
1991/0625    
 *  task state segment.  Plan 9 ignores all the task switching goo and just 
 *  uses the tss for esp0 and ss0 on gate's into the kernel, interrupts, 
 *  and exceptions.  The rest is completely ignored. 
 * 
 *  This means that we only need one tss in the whole system. 
 */ 
typedef struct Tss	Tss; 
struct Tss 
{ 
	ulong	backlink;	/* unused */ 
1991/0717    
	ulong	sp0;		/* pl0 stack pointer */ 
1991/0625    
	ulong	ss0;		/* pl0 stack selector */ 
1991/0717    
	ulong	sp1;		/* pl1 stack pointer */ 
1991/0625    
	ulong	ss1;		/* pl1 stack selector */ 
1991/0717    
	ulong	sp2;		/* pl2 stack pointer */ 
1991/0625    
	ulong	ss2;		/* pl2 stack selector */ 
	ulong	cr3;		/* page table descriptor */ 
	ulong	eip;		/* instruction pointer */ 
	ulong	eflags;		/* processor flags */ 
	ulong	eax;		/* general (hah?) registers */ 
	ulong 	ecx; 
	ulong	edx; 
	ulong	ebx; 
	ulong	esp; 
	ulong	ebp; 
	ulong	esi; 
	ulong	edi; 
	ulong	es;		/* segment selectors */ 
	ulong	cs; 
	ulong	ss; 
	ulong	ds; 
	ulong	fs; 
	ulong	gs; 
	ulong	ldt;		/* local descriptor table */ 
	ulong	iomap;		/* io map base */ 
}; 
1991/0717    
Tss tss; 
1991/0625    
 
/* 
1991/0703    
 *  segment descriptor initializers 
 */ 
1991/0717    
#define	DATASEGM(p) 	{ 0xFFFF, SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(p)|SEGDATA|SEGW } 
#define	EXECSEGM(p) 	{ 0xFFFF, SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR } 
#define CALLGATE(s,o,p)	{ ((o)&0xFFFF)|((s)<<16), (o)&0xFFFF0000|SEGP|SEGPL(p)|SEGCG } 
#define	D16SEGM(p) 	{ 0xFFFF, (0x0<<16)|SEGP|SEGPL(p)|SEGDATA|SEGW } 
#define	E16SEGM(p) 	{ 0xFFFF, (0x0<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR } 
#define	TSSSEGM(b,p)	{ ((b)<<16)|sizeof(Tss),\ 
			  ((b)&0xFF000000)|(((b)<<16)&0xFF)|SEGTSS|SEGPL(p)|SEGP } 
1991/0703    
 
/* 
1991/0613    
 *  global descriptor table describing all segments 
 */ 
1991/0706    
Segdesc gdt[] = 
1991/0612    
{ 
1991/0613    
[NULLSEG]	{ 0, 0},		/* null descriptor */ 
1991/0717    
[KDSEG]		DATASEGM(0),		/* kernel data/stack */ 
[KESEG]		EXECSEGM(0),		/* kernel code */ 
[UDSEG]		DATASEGM(3),		/* user data/stack */ 
[UESEG]		EXECSEGM(3),		/* user code */ 
1991/0703    
[SYSGATE]	CALLGATE(KESEL,0,3),	/* call gate for system calls */ 
1991/0717    
[TSSSEG]	TSSSEGM(0,0),		/* tss segment */ 
1991/0613    
}; 
1991/0703    
 
1991/1004    
static Page	ktoppg;		/* prototype top level page table 
				 * containing kernel mappings  */ 
static ulong	*kpt;		/* 2nd level page tables for kernel mem */ 
static ulong	*upt;		/* 2nd level page table for struct User */ 
1991/0711    
 
1991/0717    
#define ROUNDUP(s,v)	(((s)+(v-1))&~(v-1)) 
1991/0718    
/* 
 *  offset of virtual address into 
 *  top level page table 
 */ 
#define TOPOFF(v)	((v)>>(2*PGSHIFT-2)) 
1991/0717    
 
1991/0718    
/* 
 *  offset of virtual address into 
 *  bottom level page table 
 */ 
1991/0719    
#define BTMOFF(v)	(((v)>>(PGSHIFT))&(WD2PG-1)) 
1991/0718    
 
1991/0703    
void 
1991/0719    
mmudump(void) 
{ 
	int i; 
	ulong *z; 
	z = (ulong*)gdt; 
	for(i = 0; i < sizeof(gdt)/4; i+=2) 
		print("%8.8lux %8.8lux\n", *z++, *z++); 
	print("UESEL %lux UDSEL %lux\n", UESEL, UDSEL); 
	print("KESEL %lux KDSEL %lux\n", KESEL, KDSEL); 
	panic("done"); 
} 
 
1991/1004    
/* 
 *  Create a prototype page map that maps all of memory into 
 *  kernel (KZERO) space.  This is the default map.  It is used 
 *  whenever the processor not running a process or whenever running 
 *  a process which does not yet have its own map. 
 */ 
1991/0719    
void 
1991/0703    
mmuinit(void) 
{ 
1991/0821    
	int i, nkpt, npage, nbytes; 
1991/0717    
	ulong x; 
	ulong y; 
1991/1004    
	ulong *top; 
1991/0717    
 
1991/0716    
	/* 
	 *  set up the global descriptor table 
	 */ 
1991/0717    
	x = (ulong)systrap; 
	gdt[SYSGATE].d0 = (x&0xFFFF)|(KESEL<<16); 
	gdt[SYSGATE].d1 = (x&0xFFFF0000)|SEGP|SEGPL(3)|SEGCG; 
1991/0718    
	x = (ulong)&tss; 
1991/0717    
	gdt[TSSSEG].d0 = (x<<16)|sizeof(Tss); 
	gdt[TSSSEG].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGTSS|SEGPL(0)|SEGP; 
1991/0718    
	putgdt(gdt, sizeof gdt); 
1991/0716    
 
	/* 
1991/0717    
	 *  set up system page tables. 
	 *  map all of physical memory to start at KZERO. 
1991/1210    
	 *  map ROM BIOS at the usual place (F0000000). 
1991/1004    
	 *  leave a map entry for a user area. 
1991/0716    
	 */ 
 
1991/1210    
	/*  allocate top level table */ 
	top = ialloc(BY2PG, 1); 
	ktoppg.va = (ulong)top; 
	ktoppg.pa = ktoppg.va & ~KZERO; 
 
	/*  map all memory to KZERO */ 
1991/0827    
	npage = conf.base1/BY2PG + conf.npage1; 
1991/0821    
	nbytes = PGROUND(npage*BY2WD);		/* words of page map */ 
	nkpt = nbytes/BY2PG;			/* pages of page map */ 
	kpt = ialloc(nbytes, 1); 
	for(i = 0; i < npage; i++) 
1991/1210    
		kpt[i] = (0+i*BY2PG) | PTEVALID | PTEKERNEL | PTEWRITE; 
1991/0718    
	x = TOPOFF(KZERO); 
1991/0717    
	y = ((ulong)kpt)&~KZERO; 
1991/0718    
	for(i = 0; i < nkpt; i++) 
1991/1004    
		top[x+i] = (y+i*BY2PG) | PTEVALID | PTEKERNEL | PTEWRITE; 
1991/1210    
 
	/*  page table for u-> */ 
	upt = ialloc(BY2PG, 1); 
1991/0718    
	x = TOPOFF(USERADDR); 
1991/0717    
	y = ((ulong)upt)&~KZERO; 
1991/1004    
	top[x] = y | PTEVALID | PTEKERNEL | PTEWRITE; 
1991/1210    
 
1991/1004    
	putcr3(ktoppg.pa); 
1991/0717    
 
1991/0716    
	/* 
	 *  set up the task segment 
	 */ 
1991/0718    
	tss.sp0 = USERADDR+BY2PG; 
1991/0717    
	tss.ss0 = KDSEL; 
1991/1004    
	tss.cr3 = ktoppg.pa; 
1991/0718    
	puttr(TSSSEL); 
1991/0711    
} 
 
1991/1004    
/* 
 *  Get a page for a process's page map. 
 * 
 *  Each process maintains its own free list of page 
 *  table pages.  All page table pages are put on 
 *  this list in flushmmu().  flushmmu() doesn't 
 *  putpage() the pages since the process will soon need 
 *  them back.  Also, this avoids worrying about deadlocks 
 *  twixt flushmmu() and putpage(). 
 * 
 *  mmurelease() will give back the pages when the process 
 *  exits. 
 */ 
static Page* 
mmugetpage(int clear) 
{ 
	Proc *p = u->p; 
	Page *pg; 
1991/1003    
 
1991/1004    
	if(p->mmufree){ 
		pg = p->mmufree; 
		p->mmufree = pg->next; 
		if(clear) 
			memset((void*)pg->va, 0, BY2PG); 
	} else { 
		pg = newpage(clear, 0, 0); 
		pg->va = VA(kmap(pg)); 
	} 
	return pg; 
} 
 
/* 
 *  Put all page map pages on the process's free list and 
 *  call mapstack to set up the prototype page map.  This 
 *  effectively forgets all of the process's mappings. 
 */ 
1991/0711    
void 
1991/1004    
flushmmu(void) 
{ 
	int s; 
	Proc *p; 
	Page *pg; 
 
	if(u == 0) 
		return; 
 
	p = u->p; 
	s = splhi(); 
	if(p->mmutop){ 
		p->mmutop->next = p->mmufree; 
		p->mmufree = p->mmutop; 
		for(pg = p->mmufree; pg->next; pg = pg->next) 
			; 
		pg->next = p->mmuused; 
		p->mmutop = 0; 
		p->mmuused = 0; 
	} 
	mapstack(u->p); 
	splx(s); 
} 
 
/* 
 *  Switch to a process's memory map.  If the process doesn't 
 *  have a map yet, just use the prototype one that contains 
 *  mappings for only the kernel and the User struct. 
 */ 
void 
1991/0711    
mapstack(Proc *p) 
{ 
1991/0718    
	ulong tlbphys; 
	int i; 
1991/1004    
	Page *pg; 
1991/0718    
 
1991/0928    
	if(p->upage->va != (USERADDR|(p->pid&0xFFFF)) && p->pid != 0) 
1991/0718    
		panic("mapstack %d 0x%lux 0x%lux", p->pid, p->upage->pa, p->upage->va); 
 
1991/1004    
	if(p->mmutop) 
		pg = p->mmutop; 
	else 
		pg = &ktoppg; 
1991/0718    
 
	/* map in u area */ 
	upt[0] = PPN(p->upage->pa) | PTEVALID | PTEKERNEL | PTEWRITE; 
 
1991/1004    
	/* tell processor about new page table (flushes cached entries) */ 
	putcr3(pg->pa); 
1991/0718    
 
	u = (User*)USERADDR; 
1991/0711    
} 
 
1991/1004    
/* 
 *  give all page table pages back to the free pool.  This is called in sched() 
 *  with palloc locked. 
 */ 
1991/0711    
void 
1991/1004    
mmurelease(Proc *p) 
1991/0711    
{ 
1991/1004    
	Page *pg; 
	Page *next; 
1991/0718    
 
1991/1004    
	/* point 386 to protoype page map */ 
	putcr3(ktoppg.pa); 
1991/0718    
 
1991/1004    
	/* give away page table pages */ 
	for(pg = p->mmufree; pg; pg = next){ 
		next = pg->next; 
		simpleputpage(pg); 
	} 
	p->mmufree = 0; 
	for(pg = p->mmuused; pg; pg = next){ 
		next = pg->next; 
		simpleputpage(pg); 
	} 
	p->mmuused = 0; 
	if(p->mmutop) 
		simpleputpage(p->mmutop); 
	p->mmutop = 0; 
1991/0711    
} 
 
1991/1004    
/* 
 *  Add an entry into the mmu. 
 */ 
#define FOURMEG (4*1024*1024) 
1991/0711    
void 
1991/0718    
putmmu(ulong va, ulong pa, Page *pg) 
1991/0711    
{ 
1991/0718    
	int topoff; 
1991/1004    
	ulong *top; 
1991/0718    
	ulong *pt; 
	Proc *p; 
1991/1004    
	char err[64]; 
1991/1005    
	int x; 
1991/0718    
 
	if(u==0) 
		panic("putmmu"); 
1991/1004    
 
1991/0718    
	p = u->p; 
 
1991/1004    
	if(va >= USERADDR && va < USERADDR + FOURMEG) 
		print("putmmu in USERADDR page table 0x%lux\n", va); 
	if((va & 0xF0000000) == KZERO) 
		print("putmmu in kernel page table 0x%lux\n", va); 
 
1991/0718    
	/* 
1991/1004    
	 *  if no top level page, allocate one and copy the prototype 
	 *  into it. 
1991/0718    
	 */ 
1991/1004    
	if(p->mmutop == 0){ 
1991/1005    
		/* 
		 *  N.B. The assignment to pg is neccessary. 
		 *  We can't assign to p->mmutop until after 
		 *  copying ktoppg into the new page since we might 
		 *  get scheded in this code and p->mmutop will be 
		 *  pointing to a bad map. 
		 */ 
		pg = mmugetpage(0); 
		memmove((void*)pg->va, (void*)ktoppg.va, BY2PG); 
		p->mmutop = pg; 
1991/1004    
	} 
	top = (ulong*)p->mmutop->va; 
1991/0718    
 
	/* 
1991/1004    
	 *  if bottom level page table missing, allocate one and point 
	 *  the top level page at it. 
1991/0718    
	 */ 
1991/1004    
	topoff = TOPOFF(va); 
	if(top[topoff] == 0){ 
		pg = mmugetpage(1); 
		top[topoff] = PPN(pg->pa) | PTEVALID | PTEUSER | PTEWRITE; 
		pg->next = p->mmuused; 
		p->mmuused = pg; 
1991/0718    
	} 
 
	/* 
1991/1004    
	 *  put in new mmu entry 
1991/0718    
	 */ 
1991/1004    
	pt = (ulong*)(PPN(top[topoff])|KZERO); 
1991/0718    
	pt[BTMOFF(va)] = pa | PTEUSER; 
 
	/* flush cached mmu entries */ 
1991/1004    
	putcr3(p->mmutop->pa); 
1991/0711    
} 
 
void 
invalidateu(void) 
{ 
1991/0718    
	/* unmap u area */ 
	upt[0] = 0; 
 
	/* flush cached mmu entries */ 
1991/1004    
	putcr3(ktoppg.pa); 
1991/0703    
} 
 
void 
systrap(void) 
{ 
	panic("system trap from user"); 
} 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)