|
|
Plan 9 NeXT
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
/*
* segment descriptor initializers
*/
#define DATASEGM(p) (Segdesc){ 0xFFFF,\
SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(p)|SEGDATA|SEGW }
#define EXECSEGM(p) (Segdesc){ 0xFFFF,\
SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR }
#define CALLGATE(s,o,p) (Segdesc){ ((o)&0xFFFF)|((s)<<16),\
(o)&0xFFFF0000|SEGP|SEGPL(p)|SEGCG }
#define D16SEGM(p) (Segdesc){ 0xFFFF,\
(0x0<<16)|SEGP|SEGPL(p)|SEGDATA|SEGW }
#define E16SEGM(p) (Segdesc){ 0xFFFF,\
(0x0<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR }
#define TSSSEGM(b,p) (Segdesc){ ((b)<<16)|sizeof(Tss),\
((b)&0xFF000000)|(((b)>>16)&0xFF)|SEGTSS|SEGPL(p)|SEGP }
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 */
#define ROUNDUP(s,v) (((s)+(v-1))&~(v-1))
/*
* offset of virtual address into
* top level page table
*/
#define TOPOFF(v) (((ulong)(v))>>(2*PGSHIFT-2))
/*
* offset of virtual address into
* bottom level page table
*/
#define BTMOFF(v) ((((ulong)(v))>>(PGSHIFT))&(WD2PG-1))
#define MAXUMEG 64 /* maximum memory per user process in megabytes */
#define ONEMEG (1024*1024)
enum {
Nisa= 256,
};
struct
{
Lock;
ulong s[Nisa];
ulong e[Nisa];
} isaalloc;
/*
* setup mmu for a cpu assuming we've already created the kernel
* page tables.
*/
void
setupmmu(void)
{
ulong x;
/*
* set up the global descriptor table. we make the tss entry here
* since it requires arithmetic on an address and hence cannot
* be a compile or link time constant.
*/
x = (ulong)&m->tss;
m->gdt[NULLSEG] = (Segdesc){0, 0};
m->gdt[TSSSEG] = TSSSEGM(x, 0);
m->gdt[KDSEG] = DATASEGM(0); /* kernel data/stack */
m->gdt[KESEG] = EXECSEGM(0); /* kernel code */
m->gdt[UDSEG] = DATASEGM(3); /* user data/stack */
m->gdt[UESEG] = EXECSEGM(3); /* user code */
putgdt(m->gdt, sizeof(m->gdt));
/*
* point to kernel page table
*/
putcr3(ktoppg.pa);
/*
* set up the task segment
*/
memset(&m->tss, 0, sizeof(m->tss));
m->tss.sp0 = USERADDR+BY2PG;
m->tss.ss0 = KDSEL;
m->tss.cr3 = ktoppg.pa;
puttr(TSSSEL);
}
/*
* 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.
*/
void
mmuinit(void)
{
int i, nkpt, npage, nbytes;
ulong x;
ulong y;
ulong *top;
/*
* set up system page tables.
* map all of physical memory to start at KZERO.
* leave a map entry for a user area.
*/
/* allocate top level table */
top = xspanalloc(BY2PG, BY2PG, 0);
ktoppg.va = (ulong)top;
ktoppg.pa = ktoppg.va & ~KZERO;
/* map all memory to KZERO */
npage = 128*MB/BY2PG;
nbytes = PGROUND(npage*BY2WD); /* words of page map */
nkpt = nbytes/BY2PG; /* pages of page map */
kpt = xspanalloc(nbytes, BY2PG, 0);
for(i = 0; i < npage; i++)
kpt[i] = (0+i*BY2PG) | PTEVALID | PTEKERNEL | PTEWRITE;
x = TOPOFF(KZERO);
y = ((ulong)kpt)&~KZERO;
for(i = 0; i < nkpt; i++)
top[x+i] = (y+i*BY2PG) | PTEVALID | PTEKERNEL | PTEWRITE;
/* page table for u-> */
upt = xspanalloc(BY2PG, BY2PG, 0);
x = TOPOFF(USERADDR);
y = ((ulong)upt)&~KZERO;
top[x] = y | PTEVALID | PTEKERNEL | PTEWRITE;
setupmmu();
}
/*
* Mark the mmu and tlb as inconsistent and call mapstack to fix it up.
*/
void
flushmmu(void)
{
int s;
s = splhi();
if(u){
u->p->newtlb = 1;
mapstack(u->p);
} else
putcr3(ktoppg.pa);
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
mapstack(Proc *p)
{
Page *pg;
ulong *top;
if(p->upage->va != (USERADDR|(p->pid&0xFFFF)) && p->pid != 0)
panic("mapstack %d 0x%lux 0x%lux", p->pid, p->upage->pa, p->upage->va);
if(p->newtlb){
/*
* newtlb set means that they are inconsistent
* with the segment.c data structures.
*
* bin the current second level page tables and
* the pointers to them in the top level page.
* pg->daddr is used by putmmu to save the offset into
* the top level page.
*/
if(p->mmutop && p->mmuused){
top = (ulong*)p->mmutop->va;
for(pg = p->mmuused; pg->next; pg = pg->next)
ilputl(&top[pg->daddr], 0);
ilputl(&top[pg->daddr], 0);
pg->next = p->mmufree;
p->mmufree = p->mmuused;
p->mmuused = 0;
}
p->newtlb = 0;
}
/* map in u area */
upt[0] = PPN(p->upage->pa) | PTEVALID | PTEKERNEL | PTEWRITE;
/* tell processor about new page table (flushes cached entries) */
if(p->mmutop)
pg = p->mmutop;
else
pg = &ktoppg;
putcr3(pg->pa);
u = (User*)USERADDR;
}
/*
* give all page table pages back to the free pool. This is called in sched()
* with palloc locked.
*/
void
mmurelease(Proc *p)
{
Page *pg;
Page *next;
/* point 386 to protoype page map */
putcr3(ktoppg.pa);
/* 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;
}
/*
* Add an entry into the mmu.
*/
void
putmmu(ulong va, ulong pa, Page *pg)
{
int topoff;
ulong *top;
ulong *pt;
Proc *p;
int s;
if(u==0)
panic("putmmu");
p = u->p;
/*
* create a top level page if we don't already have one.
* copy the kernel top level page into it for kernel mappings.
*/
if(p->mmutop == 0){
pg = newpage(0, 0, 0);
pg->va = VA(kmap(pg));
memmove((void*)pg->va, (void*)ktoppg.va, BY2PG);
p->mmutop = pg;
}
top = (ulong*)p->mmutop->va;
topoff = TOPOFF(va);
/*
* if bottom level page table missing, allocate one
* and point the top level page at it.
*/
s = splhi();
if(PPN(top[topoff]) == 0){
if(p->mmufree == 0){
spllo();
pg = newpage(1, 0, 0);
pg->va = VA(kmap(pg));
splhi();
} else {
pg = p->mmufree;
p->mmufree = pg->next;
memset((void*)pg->va, 0, BY2PG);
}
ilputl(&top[topoff], PPN(pg->pa) | PTEVALID | PTEUSER | PTEWRITE);
pg->daddr = topoff;
pg->next = p->mmuused;
p->mmuused = pg;
}
/*
* put in new mmu entry
*/
pt = (ulong*)(PPN(top[topoff])|KZERO);
ilputl(&pt[BTMOFF(va)], pa | PTEUSER);
/* flush cached mmu entries */
putcr3(p->mmutop->pa);
splx(s);
}
void
invalidateu(void)
{
/* unmap u area */
upt[0] = 0;
/* flush cached mmu entries */
putcr3(ktoppg.pa);
}
/*
* used to map a page into 16 meg - BY2PG for confinit(). tpt is the temporary
* page table set up by l.s.
*/
long*
mapaddr(ulong addr)
{
ulong base;
ulong off;
static ulong *pte, top;
extern ulong tpt[];
if(pte == 0){
top = (((ulong)tpt)+(BY2PG-1))&~(BY2PG-1);
pte = (ulong*)top;
top &= ~KZERO;
top += BY2PG;
pte += (4*1024*1024-BY2PG)>>PGSHIFT;
}
base = off = addr;
base &= ~(KZERO|(BY2PG-1));
off &= BY2PG-1;
*pte = base|PTEVALID|PTEKERNEL|PTEWRITE; /**/
putcr3((ulong)top);
return (long*)(KZERO | 4*1024*1024-BY2PG | off);
}
/*
* make isa address space available
*/
void
putisa(ulong addr, int len)
{
ulong e;
int i, hole;
addr &= ~KZERO;
e = addr + len;
lock(&isaalloc);
hole = -1;
for(i = 0; i < Nisa; i++){
if(isaalloc.s[i] == e){
isaalloc.s[i] = addr;
break;
}
if(isaalloc.e[i] == addr){
isaalloc.e[i] = e;
break;
}
if(isaalloc.s[i] == 0)
hole = i;
}
if(i >= Nisa && hole >= 0){
isaalloc.s[hole] = addr;
isaalloc.e[hole] = e;
}
unlock(&isaalloc);
}
/*
* allocate some address space (already mapped into the kernel)
* for ISA bus memory.
*/
ulong
getisa(ulong addr, int len, int align)
{
int i;
long os, s, e;
lock(&isaalloc);
os = s = e = 0;
for(i = 0; i < Nisa; i++){
s = os = isaalloc.s[i];
if(s == 0)
continue;
e = isaalloc.e[i];
if(addr && addr >= s && addr < e)
break;
if(align > 0)
s = ((s + align - 1)/align)*align;
if(e - s >= len)
break;
}
if(i >= Nisa){
unlock(&isaalloc);
return 0;
}
/* remove */
isaalloc.s[i] = 0;
unlock(&isaalloc);
/* give back edges */
if(s != os)
putisa(os, s - os);
os = s + len;
if(os != e)
putisa(os, e - os);
return KZERO|s;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.