| plan 9 kernel history: overview | file list | diff list |
1991/1005/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/1004 | * leave a map entry for a user area. | |
| 1991/0716 | */ | |
| 1991/0719 | /* allocate and fill low level page tables for kernel mem */ | |
| 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/0718 | kpt[i] = (i<<PGSHIFT) | PTEVALID | PTEKERNEL | PTEWRITE; | |
| 1991/0821 | print("%d low level pte's, %d high level pte's\n", npage, nkpt); | |
| 1991/0717 | /* allocate page table for u-> */ upt = ialloc(BY2PG, 1); /* allocate top level table and put pointers to lower tables in it */ | |
| 1991/1004 | top = ialloc(BY2PG, 1); ktoppg.va = (ulong)top; ktoppg.pa = ktoppg.va & ~KZERO; | |
| 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/0718 | x = TOPOFF(USERADDR); | |
| 1991/0717 | y = ((ulong)upt)&~KZERO; | |
| 1991/1004 | top[x] = y | PTEVALID | PTEKERNEL | PTEWRITE; 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"); } | |