14de34a7eSDavid du Colombier /*
24de34a7eSDavid du Colombier * Memory mappings. Life was easier when 2G of memory was enough.
34de34a7eSDavid du Colombier *
44de34a7eSDavid du Colombier * The kernel memory starts at KZERO, with the text loaded at KZERO+1M
54de34a7eSDavid du Colombier * (9load sits under 1M during the load). The memory from KZERO to the
64de34a7eSDavid du Colombier * top of memory is mapped 1-1 with physical memory, starting at physical
74de34a7eSDavid du Colombier * address 0. All kernel memory and data structures (i.e., the entries stored
84de34a7eSDavid du Colombier * into conf.mem) must sit in this physical range: if KZERO is at 0xF0000000,
94de34a7eSDavid du Colombier * then the kernel can only have 256MB of memory for itself.
104de34a7eSDavid du Colombier *
114de34a7eSDavid du Colombier * The 256M below KZERO comprises three parts. The lowest 4M is the
124de34a7eSDavid du Colombier * virtual page table, a virtual address representation of the current
134de34a7eSDavid du Colombier * page table tree. The second 4M is used for temporary per-process
144de34a7eSDavid du Colombier * mappings managed by kmap and kunmap. The remaining 248M is used
154de34a7eSDavid du Colombier * for global (shared by all procs and all processors) device memory
164de34a7eSDavid du Colombier * mappings and managed by vmap and vunmap. The total amount (256M)
174de34a7eSDavid du Colombier * could probably be reduced somewhat if desired. The largest device
184de34a7eSDavid du Colombier * mapping is that of the video card, and even though modern video cards
194de34a7eSDavid du Colombier * have embarrassing amounts of memory, the video drivers only use one
204de34a7eSDavid du Colombier * frame buffer worth (at most 16M). Each is described in more detail below.
214de34a7eSDavid du Colombier *
224de34a7eSDavid du Colombier * The VPT is a 4M frame constructed by inserting the pdb into itself.
234de34a7eSDavid du Colombier * This short-circuits one level of the page tables, with the result that
244de34a7eSDavid du Colombier * the contents of second-level page tables can be accessed at VPT.
254de34a7eSDavid du Colombier * We use the VPT to edit the page tables (see mmu) after inserting them
264de34a7eSDavid du Colombier * into the page directory. It is a convenient mechanism for mapping what
274de34a7eSDavid du Colombier * might be otherwise-inaccessible pages. The idea was borrowed from
284de34a7eSDavid du Colombier * the Exokernel.
294de34a7eSDavid du Colombier *
304de34a7eSDavid du Colombier * The VPT doesn't solve all our problems, because we still need to
314de34a7eSDavid du Colombier * prepare page directories before we can install them. For that, we
324de34a7eSDavid du Colombier * use tmpmap/tmpunmap, which map a single page at TMPADDR.
334de34a7eSDavid du Colombier */
344de34a7eSDavid du Colombier
353e12c5d1SDavid du Colombier #include "u.h"
363e12c5d1SDavid du Colombier #include "../port/lib.h"
373e12c5d1SDavid du Colombier #include "mem.h"
383e12c5d1SDavid du Colombier #include "dat.h"
393e12c5d1SDavid du Colombier #include "fns.h"
403e12c5d1SDavid du Colombier #include "io.h"
413e12c5d1SDavid du Colombier
424de34a7eSDavid du Colombier /*
434de34a7eSDavid du Colombier * Simple segment descriptors with no translation.
444de34a7eSDavid du Colombier */
457dd7cddfSDavid du Colombier #define DATASEGM(p) { 0xFFFF, SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(p)|SEGDATA|SEGW }
467dd7cddfSDavid du Colombier #define EXECSEGM(p) { 0xFFFF, SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR }
474de34a7eSDavid du Colombier #define EXEC16SEGM(p) { 0xFFFF, SEGG|(0xF<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR }
487dd7cddfSDavid du Colombier #define TSSSEGM(b,p) { ((b)<<16)|sizeof(Tss),\
493e12c5d1SDavid du Colombier ((b)&0xFF000000)|(((b)>>16)&0xFF)|SEGTSS|SEGPL(p)|SEGP }
503e12c5d1SDavid du Colombier
519a747e4fSDavid du Colombier Segdesc gdt[NGDT] =
527dd7cddfSDavid du Colombier {
537dd7cddfSDavid du Colombier [NULLSEG] { 0, 0}, /* null descriptor */
547dd7cddfSDavid du Colombier [KDSEG] DATASEGM(0), /* kernel data/stack */
557dd7cddfSDavid du Colombier [KESEG] EXECSEGM(0), /* kernel code */
567dd7cddfSDavid du Colombier [UDSEG] DATASEGM(3), /* user data/stack */
577dd7cddfSDavid du Colombier [UESEG] EXECSEGM(3), /* user code */
587dd7cddfSDavid du Colombier [TSSSEG] TSSSEGM(0,0), /* tss segment */
594de34a7eSDavid du Colombier [KESEG16] EXEC16SEGM(0), /* kernel code 16-bit */
60219b2ee8SDavid du Colombier };
617dd7cddfSDavid du Colombier
624de34a7eSDavid du Colombier static int didmmuinit;
634de34a7eSDavid du Colombier static void taskswitch(ulong, ulong);
644de34a7eSDavid du Colombier static void memglobal(void);
65219b2ee8SDavid du Colombier
664de34a7eSDavid du Colombier #define vpt ((ulong*)VPT)
674de34a7eSDavid du Colombier #define VPTX(va) (((ulong)(va))>>12)
684de34a7eSDavid du Colombier #define vpd (vpt+VPTX(VPT))
694de34a7eSDavid du Colombier
704de34a7eSDavid du Colombier void
mmuinit0(void)714de34a7eSDavid du Colombier mmuinit0(void)
724de34a7eSDavid du Colombier {
734de34a7eSDavid du Colombier memmove(m->gdt, gdt, sizeof gdt);
744de34a7eSDavid du Colombier }
754de34a7eSDavid du Colombier
764de34a7eSDavid du Colombier void
mmuinit(void)774de34a7eSDavid du Colombier mmuinit(void)
784de34a7eSDavid du Colombier {
794de34a7eSDavid du Colombier ulong x, *p;
804de34a7eSDavid du Colombier ushort ptr[3];
814de34a7eSDavid du Colombier
824de34a7eSDavid du Colombier didmmuinit = 1;
834de34a7eSDavid du Colombier
84567483c8SDavid du Colombier if(0) print("vpt=%#.8ux vpd=%#p kmap=%#.8ux\n",
85567483c8SDavid du Colombier VPT, vpd, KMAP);
864de34a7eSDavid du Colombier
874de34a7eSDavid du Colombier memglobal();
884de34a7eSDavid du Colombier m->pdb[PDX(VPT)] = PADDR(m->pdb)|PTEWRITE|PTEVALID;
894de34a7eSDavid du Colombier
904de34a7eSDavid du Colombier m->tss = malloc(sizeof(Tss));
91aa72973aSDavid du Colombier if(m->tss == nil)
92aa72973aSDavid du Colombier panic("mmuinit: no memory");
934de34a7eSDavid du Colombier memset(m->tss, 0, sizeof(Tss));
944de34a7eSDavid du Colombier m->tss->iomap = 0xDFFF<<16;
954de34a7eSDavid du Colombier
964de34a7eSDavid du Colombier /*
974de34a7eSDavid du Colombier * We used to keep the GDT in the Mach structure, but it
984de34a7eSDavid du Colombier * turns out that that slows down access to the rest of the
994de34a7eSDavid du Colombier * page. Since the Mach structure is accessed quite often,
1004de34a7eSDavid du Colombier * it pays off anywhere from a factor of 1.25 to 2 on real
1014de34a7eSDavid du Colombier * hardware to separate them (the AMDs are more sensitive
1024de34a7eSDavid du Colombier * than Intels in this regard). Under VMware it pays off
1034de34a7eSDavid du Colombier * a factor of about 10 to 100.
1044de34a7eSDavid du Colombier */
1054de34a7eSDavid du Colombier memmove(m->gdt, gdt, sizeof gdt);
1064de34a7eSDavid du Colombier x = (ulong)m->tss;
1074de34a7eSDavid du Colombier m->gdt[TSSSEG].d0 = (x<<16)|sizeof(Tss);
1084de34a7eSDavid du Colombier m->gdt[TSSSEG].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGTSS|SEGPL(0)|SEGP;
1094de34a7eSDavid du Colombier
1104de34a7eSDavid du Colombier ptr[0] = sizeof(gdt)-1;
1114de34a7eSDavid du Colombier x = (ulong)m->gdt;
1124de34a7eSDavid du Colombier ptr[1] = x & 0xFFFF;
1134de34a7eSDavid du Colombier ptr[2] = (x>>16) & 0xFFFF;
1144de34a7eSDavid du Colombier lgdt(ptr);
1154de34a7eSDavid du Colombier
1164de34a7eSDavid du Colombier ptr[0] = sizeof(Segdesc)*256-1;
1174de34a7eSDavid du Colombier x = IDTADDR;
1184de34a7eSDavid du Colombier ptr[1] = x & 0xFFFF;
1194de34a7eSDavid du Colombier ptr[2] = (x>>16) & 0xFFFF;
1204de34a7eSDavid du Colombier lidt(ptr);
1214de34a7eSDavid du Colombier
1224de34a7eSDavid du Colombier /* make kernel text unwritable */
1234de34a7eSDavid du Colombier for(x = KTZERO; x < (ulong)etext; x += BY2PG){
1244de34a7eSDavid du Colombier p = mmuwalk(m->pdb, x, 2, 0);
1254de34a7eSDavid du Colombier if(p == nil)
1264de34a7eSDavid du Colombier panic("mmuinit");
1274de34a7eSDavid du Colombier *p &= ~PTEWRITE;
1284de34a7eSDavid du Colombier }
1294de34a7eSDavid du Colombier
1304de34a7eSDavid du Colombier taskswitch(PADDR(m->pdb), (ulong)m + BY2PG);
1314de34a7eSDavid du Colombier ltr(TSSSEL);
132219b2ee8SDavid du Colombier }
1333e12c5d1SDavid du Colombier
1349a747e4fSDavid du Colombier /*
1359a747e4fSDavid du Colombier * On processors that support it, we set the PTEGLOBAL bit in
1369a747e4fSDavid du Colombier * page table and page directory entries that map kernel memory.
1379a747e4fSDavid du Colombier * Doing this tells the processor not to bother flushing them
1389a747e4fSDavid du Colombier * from the TLB when doing the TLB flush associated with a
1399a747e4fSDavid du Colombier * context switch (write to CR3). Since kernel memory mappings
1409a747e4fSDavid du Colombier * are never removed, this is safe. (If we ever remove kernel memory
1419a747e4fSDavid du Colombier * mappings, we can do a full flush by turning off the PGE bit in CR4,
1429a747e4fSDavid du Colombier * writing to CR3, and then turning the PGE bit back on.)
1439a747e4fSDavid du Colombier *
1449a747e4fSDavid du Colombier * See also mmukmap below.
1459a747e4fSDavid du Colombier *
1469a747e4fSDavid du Colombier * Processor support for the PTEGLOBAL bit is enabled in devarch.c.
1479a747e4fSDavid du Colombier */
1489a747e4fSDavid du Colombier static void
memglobal(void)1499a747e4fSDavid du Colombier memglobal(void)
1509a747e4fSDavid du Colombier {
1519a747e4fSDavid du Colombier int i, j;
1529a747e4fSDavid du Colombier ulong *pde, *pte;
1539a747e4fSDavid du Colombier
1549a747e4fSDavid du Colombier /* only need to do this once, on bootstrap processor */
1559a747e4fSDavid du Colombier if(m->machno != 0)
1569a747e4fSDavid du Colombier return;
1579a747e4fSDavid du Colombier
1589a747e4fSDavid du Colombier if(!m->havepge)
1599a747e4fSDavid du Colombier return;
1609a747e4fSDavid du Colombier
1619a747e4fSDavid du Colombier pde = m->pdb;
1624de34a7eSDavid du Colombier for(i=PDX(KZERO); i<1024; i++){
1639a747e4fSDavid du Colombier if(pde[i] & PTEVALID){
1649a747e4fSDavid du Colombier pde[i] |= PTEGLOBAL;
1659a747e4fSDavid du Colombier if(!(pde[i] & PTESIZE)){
1669a747e4fSDavid du Colombier pte = KADDR(pde[i]&~(BY2PG-1));
1679a747e4fSDavid du Colombier for(j=0; j<1024; j++)
1689a747e4fSDavid du Colombier if(pte[j] & PTEVALID)
1699a747e4fSDavid du Colombier pte[j] |= PTEGLOBAL;
1709a747e4fSDavid du Colombier }
1719a747e4fSDavid du Colombier }
1729a747e4fSDavid du Colombier }
1739a747e4fSDavid du Colombier }
1749a747e4fSDavid du Colombier
1759a747e4fSDavid du Colombier /*
1764de34a7eSDavid du Colombier * Flush all the user-space and device-mapping mmu info
1774de34a7eSDavid du Colombier * for this process, because something has been deleted.
1784de34a7eSDavid du Colombier * It will be paged back in on demand.
1799a747e4fSDavid du Colombier */
1803e12c5d1SDavid du Colombier void
flushmmu(void)1813e12c5d1SDavid du Colombier flushmmu(void)
1823e12c5d1SDavid du Colombier {
1833e12c5d1SDavid du Colombier int s;
1843e12c5d1SDavid du Colombier
1853e12c5d1SDavid du Colombier s = splhi();
1867dd7cddfSDavid du Colombier up->newtlb = 1;
1877dd7cddfSDavid du Colombier mmuswitch(up);
1883e12c5d1SDavid du Colombier splx(s);
1893e12c5d1SDavid du Colombier }
1903e12c5d1SDavid du Colombier
1914de34a7eSDavid du Colombier /*
1924de34a7eSDavid du Colombier * Flush a single page mapping from the tlb.
1934de34a7eSDavid du Colombier */
1944de34a7eSDavid du Colombier void
flushpg(ulong va)1954de34a7eSDavid du Colombier flushpg(ulong va)
1964de34a7eSDavid du Colombier {
1974de34a7eSDavid du Colombier if(X86FAMILY(m->cpuidax) >= 4)
1984de34a7eSDavid du Colombier invlpg(va);
1994de34a7eSDavid du Colombier else
20025910e17SDavid du Colombier putcr3(getcr3());
2014de34a7eSDavid du Colombier }
2024de34a7eSDavid du Colombier
2034de34a7eSDavid du Colombier /*
2044de34a7eSDavid du Colombier * Allocate a new page for a page directory.
2054de34a7eSDavid du Colombier * We keep a small cache of pre-initialized
2064de34a7eSDavid du Colombier * page directories in each mach.
2074de34a7eSDavid du Colombier */
2084de34a7eSDavid du Colombier static Page*
mmupdballoc(void)2094de34a7eSDavid du Colombier mmupdballoc(void)
2104de34a7eSDavid du Colombier {
2114de34a7eSDavid du Colombier int s;
2124de34a7eSDavid du Colombier Page *page;
2134de34a7eSDavid du Colombier ulong *pdb;
2144de34a7eSDavid du Colombier
2154de34a7eSDavid du Colombier s = splhi();
2161bd28109SDavid du Colombier m->pdballoc++;
2174de34a7eSDavid du Colombier if(m->pdbpool == 0){
2184de34a7eSDavid du Colombier spllo();
2194de34a7eSDavid du Colombier page = newpage(0, 0, 0);
2204de34a7eSDavid du Colombier page->va = (ulong)vpd;
2214de34a7eSDavid du Colombier splhi();
2224de34a7eSDavid du Colombier pdb = tmpmap(page);
2234de34a7eSDavid du Colombier memmove(pdb, m->pdb, BY2PG);
2244de34a7eSDavid du Colombier pdb[PDX(VPT)] = page->pa|PTEWRITE|PTEVALID; /* set up VPT */
2254de34a7eSDavid du Colombier tmpunmap(pdb);
2264de34a7eSDavid du Colombier }else{
2274de34a7eSDavid du Colombier page = m->pdbpool;
2284de34a7eSDavid du Colombier m->pdbpool = page->next;
2294de34a7eSDavid du Colombier m->pdbcnt--;
2304de34a7eSDavid du Colombier }
2314de34a7eSDavid du Colombier splx(s);
2324de34a7eSDavid du Colombier return page;
2334de34a7eSDavid du Colombier }
2344de34a7eSDavid du Colombier
2354de34a7eSDavid du Colombier static void
mmupdbfree(Proc * proc,Page * p)2364de34a7eSDavid du Colombier mmupdbfree(Proc *proc, Page *p)
2374de34a7eSDavid du Colombier {
2384de34a7eSDavid du Colombier if(islo())
2394de34a7eSDavid du Colombier panic("mmupdbfree: islo");
2401bd28109SDavid du Colombier m->pdbfree++;
2414de34a7eSDavid du Colombier if(m->pdbcnt >= 10){
2424de34a7eSDavid du Colombier p->next = proc->mmufree;
2434de34a7eSDavid du Colombier proc->mmufree = p;
2444de34a7eSDavid du Colombier }else{
2454de34a7eSDavid du Colombier p->next = m->pdbpool;
2464de34a7eSDavid du Colombier m->pdbpool = p;
2471bd28109SDavid du Colombier m->pdbcnt++;
2484de34a7eSDavid du Colombier }
2494de34a7eSDavid du Colombier }
2504de34a7eSDavid du Colombier
2514de34a7eSDavid du Colombier /*
2524de34a7eSDavid du Colombier * A user-space memory segment has been deleted, or the
2534de34a7eSDavid du Colombier * process is exiting. Clear all the pde entries for user-space
2544de34a7eSDavid du Colombier * memory mappings and device mappings. Any entries that
2554de34a7eSDavid du Colombier * are needed will be paged back in as necessary.
2564de34a7eSDavid du Colombier */
2577dd7cddfSDavid du Colombier static void
mmuptefree(Proc * proc)2587dd7cddfSDavid du Colombier mmuptefree(Proc* proc)
2593e12c5d1SDavid du Colombier {
2604de34a7eSDavid du Colombier int s;
2617dd7cddfSDavid du Colombier ulong *pdb;
2627dd7cddfSDavid du Colombier Page **last, *page;
2633e12c5d1SDavid du Colombier
2644de34a7eSDavid du Colombier if(proc->mmupdb == nil || proc->mmuused == nil)
2654de34a7eSDavid du Colombier return;
2664de34a7eSDavid du Colombier s = splhi();
2674de34a7eSDavid du Colombier pdb = tmpmap(proc->mmupdb);
2687dd7cddfSDavid du Colombier last = &proc->mmuused;
2697dd7cddfSDavid du Colombier for(page = *last; page; page = page->next){
2707dd7cddfSDavid du Colombier pdb[page->daddr] = 0;
2717dd7cddfSDavid du Colombier last = &page->next;
2723e12c5d1SDavid du Colombier }
2734de34a7eSDavid du Colombier tmpunmap(pdb);
2744de34a7eSDavid du Colombier splx(s);
2757dd7cddfSDavid du Colombier *last = proc->mmufree;
2767dd7cddfSDavid du Colombier proc->mmufree = proc->mmuused;
2777dd7cddfSDavid du Colombier proc->mmuused = 0;
2787dd7cddfSDavid du Colombier }
2794de34a7eSDavid du Colombier
2804de34a7eSDavid du Colombier static void
taskswitch(ulong pdb,ulong stack)2814de34a7eSDavid du Colombier taskswitch(ulong pdb, ulong stack)
2824de34a7eSDavid du Colombier {
2834de34a7eSDavid du Colombier Tss *tss;
2844de34a7eSDavid du Colombier
2854de34a7eSDavid du Colombier tss = m->tss;
2864de34a7eSDavid du Colombier tss->ss0 = KDSEL;
2874de34a7eSDavid du Colombier tss->esp0 = stack;
2884de34a7eSDavid du Colombier tss->ss1 = KDSEL;
2894de34a7eSDavid du Colombier tss->esp1 = stack;
2904de34a7eSDavid du Colombier tss->ss2 = KDSEL;
2914de34a7eSDavid du Colombier tss->esp2 = stack;
2924de34a7eSDavid du Colombier putcr3(pdb);
2933e12c5d1SDavid du Colombier }
2943e12c5d1SDavid du Colombier
2957dd7cddfSDavid du Colombier void
mmuswitch(Proc * proc)2967dd7cddfSDavid du Colombier mmuswitch(Proc* proc)
2977dd7cddfSDavid du Colombier {
2987dd7cddfSDavid du Colombier ulong *pdb;
2993e12c5d1SDavid du Colombier
3007dd7cddfSDavid du Colombier if(proc->newtlb){
3017dd7cddfSDavid du Colombier mmuptefree(proc);
3027dd7cddfSDavid du Colombier proc->newtlb = 0;
3037dd7cddfSDavid du Colombier }
3047dd7cddfSDavid du Colombier
3057dd7cddfSDavid du Colombier if(proc->mmupdb){
3064de34a7eSDavid du Colombier pdb = tmpmap(proc->mmupdb);
3077dd7cddfSDavid du Colombier pdb[PDX(MACHADDR)] = m->pdb[PDX(MACHADDR)];
3084de34a7eSDavid du Colombier tmpunmap(pdb);
3097dd7cddfSDavid du Colombier taskswitch(proc->mmupdb->pa, (ulong)(proc->kstack+KSTACK));
3104de34a7eSDavid du Colombier }else
3117dd7cddfSDavid du Colombier taskswitch(PADDR(m->pdb), (ulong)(proc->kstack+KSTACK));
3123e12c5d1SDavid du Colombier }
3133e12c5d1SDavid du Colombier
3143e12c5d1SDavid du Colombier /*
3157dd7cddfSDavid du Colombier * Release any pages allocated for a page directory base or page-tables
3167dd7cddfSDavid du Colombier * for this process:
3177dd7cddfSDavid du Colombier * switch to the prototype pdb for this processor (m->pdb);
3187dd7cddfSDavid du Colombier * call mmuptefree() to place all pages used for page-tables (proc->mmuused)
3197dd7cddfSDavid du Colombier * onto the process' free list (proc->mmufree). This has the side-effect of
3207dd7cddfSDavid du Colombier * cleaning any user entries in the pdb (proc->mmupdb);
3217dd7cddfSDavid du Colombier * if there's a pdb put it in the cache of pre-initialised pdb's
3227dd7cddfSDavid du Colombier * for this processor (m->pdbpool) or on the process' free list;
3237dd7cddfSDavid du Colombier * finally, place any pages freed back into the free pool (palloc).
3245f61b043SDavid du Colombier * This routine is only called from schedinit() with palloc locked.
3253e12c5d1SDavid du Colombier */
3264de34a7eSDavid du Colombier void
mmurelease(Proc * proc)3274de34a7eSDavid du Colombier mmurelease(Proc* proc)
3284de34a7eSDavid du Colombier {
3294de34a7eSDavid du Colombier Page *page, *next;
3305f61b043SDavid du Colombier ulong *pdb;
3317dd7cddfSDavid du Colombier
3321bd28109SDavid du Colombier if(islo())
3331bd28109SDavid du Colombier panic("mmurelease: islo");
3344de34a7eSDavid du Colombier taskswitch(PADDR(m->pdb), (ulong)m + BY2PG);
3355f61b043SDavid du Colombier if(proc->kmaptable){
3365f61b043SDavid du Colombier if(proc->mmupdb == nil)
3375f61b043SDavid du Colombier panic("mmurelease: no mmupdb");
3385f61b043SDavid du Colombier if(--proc->kmaptable->ref)
339c9b6d007SDavid du Colombier panic("mmurelease: kmap ref %d", proc->kmaptable->ref);
34025910e17SDavid du Colombier if(proc->nkmap)
341c9b6d007SDavid du Colombier panic("mmurelease: nkmap %d", proc->nkmap);
3425f61b043SDavid du Colombier /*
3435f61b043SDavid du Colombier * remove kmaptable from pdb before putting pdb up for reuse.
3445f61b043SDavid du Colombier */
3455f61b043SDavid du Colombier pdb = tmpmap(proc->mmupdb);
3465f61b043SDavid du Colombier if(PPN(pdb[PDX(KMAP)]) != proc->kmaptable->pa)
3475f61b043SDavid du Colombier panic("mmurelease: bad kmap pde %#.8lux kmap %#.8lux",
3485f61b043SDavid du Colombier pdb[PDX(KMAP)], proc->kmaptable->pa);
3495f61b043SDavid du Colombier pdb[PDX(KMAP)] = 0;
3505f61b043SDavid du Colombier tmpunmap(pdb);
3515f61b043SDavid du Colombier /*
3525f61b043SDavid du Colombier * move kmaptable to free list.
3535f61b043SDavid du Colombier */
3545f61b043SDavid du Colombier pagechainhead(proc->kmaptable);
3555f61b043SDavid du Colombier proc->kmaptable = 0;
3565f61b043SDavid du Colombier }
3577dd7cddfSDavid du Colombier if(proc->mmupdb){
3584de34a7eSDavid du Colombier mmuptefree(proc);
3594de34a7eSDavid du Colombier mmupdbfree(proc, proc->mmupdb);
3607dd7cddfSDavid du Colombier proc->mmupdb = 0;
3617dd7cddfSDavid du Colombier }
3627dd7cddfSDavid du Colombier for(page = proc->mmufree; page; page = next){
3637dd7cddfSDavid du Colombier next = page->next;
3647dd7cddfSDavid du Colombier if(--page->ref)
365c9b6d007SDavid du Colombier panic("mmurelease: page->ref %d", page->ref);
3667dd7cddfSDavid du Colombier pagechainhead(page);
3677dd7cddfSDavid du Colombier }
3687dd7cddfSDavid du Colombier if(proc->mmufree && palloc.r.p)
3697dd7cddfSDavid du Colombier wakeup(&palloc.r);
3707dd7cddfSDavid du Colombier proc->mmufree = 0;
3717dd7cddfSDavid du Colombier }
3727dd7cddfSDavid du Colombier
3734de34a7eSDavid du Colombier /*
3744de34a7eSDavid du Colombier * Allocate and install pdb for the current process.
3754de34a7eSDavid du Colombier */
3764de34a7eSDavid du Colombier static void
upallocpdb(void)3774de34a7eSDavid du Colombier upallocpdb(void)
3783e12c5d1SDavid du Colombier {
3797dd7cddfSDavid du Colombier int s;
3804de34a7eSDavid du Colombier ulong *pdb;
3817dd7cddfSDavid du Colombier Page *page;
3823e12c5d1SDavid du Colombier
38325910e17SDavid du Colombier if(up->mmupdb != nil)
38425910e17SDavid du Colombier return;
3854de34a7eSDavid du Colombier page = mmupdballoc();
3867dd7cddfSDavid du Colombier s = splhi();
38725910e17SDavid du Colombier if(up->mmupdb != nil){
38825910e17SDavid du Colombier /*
38925910e17SDavid du Colombier * Perhaps we got an interrupt while
39025910e17SDavid du Colombier * mmupdballoc was sleeping and that
39125910e17SDavid du Colombier * interrupt allocated an mmupdb?
39225910e17SDavid du Colombier * Seems unlikely.
39325910e17SDavid du Colombier */
39425910e17SDavid du Colombier mmupdbfree(up, page);
39525910e17SDavid du Colombier splx(s);
39625910e17SDavid du Colombier return;
39725910e17SDavid du Colombier }
3984de34a7eSDavid du Colombier pdb = tmpmap(page);
3997dd7cddfSDavid du Colombier pdb[PDX(MACHADDR)] = m->pdb[PDX(MACHADDR)];
4004de34a7eSDavid du Colombier tmpunmap(pdb);
4014de34a7eSDavid du Colombier up->mmupdb = page;
4021bd28109SDavid du Colombier putcr3(up->mmupdb->pa);
4033e12c5d1SDavid du Colombier splx(s);
4043e12c5d1SDavid du Colombier }
4053e12c5d1SDavid du Colombier
4064de34a7eSDavid du Colombier /*
4074de34a7eSDavid du Colombier * Update the mmu in response to a user fault. pa may have PTEWRITE set.
4084de34a7eSDavid du Colombier */
4094de34a7eSDavid du Colombier void
putmmu(ulong va,ulong pa,Page *)4104de34a7eSDavid du Colombier putmmu(ulong va, ulong pa, Page*)
4113e12c5d1SDavid du Colombier {
41225910e17SDavid du Colombier int old, s;
4134de34a7eSDavid du Colombier Page *page;
4144de34a7eSDavid du Colombier
4154de34a7eSDavid du Colombier if(up->mmupdb == nil)
4164de34a7eSDavid du Colombier upallocpdb();
4174de34a7eSDavid du Colombier
41825910e17SDavid du Colombier /*
41925910e17SDavid du Colombier * We should be able to get through this with interrupts
42025910e17SDavid du Colombier * turned on (if we get interrupted we'll just pick up
42125910e17SDavid du Colombier * where we left off) but we get many faults accessing
42225910e17SDavid du Colombier * vpt[] near the end of this function, and they always happen
42325910e17SDavid du Colombier * after the process has been switched out and then
42425910e17SDavid du Colombier * switched back, usually many times in a row (perhaps
42525910e17SDavid du Colombier * it cannot switch back successfully for some reason).
42625910e17SDavid du Colombier *
42725910e17SDavid du Colombier * In any event, I'm tired of searching for this bug.
42825910e17SDavid du Colombier * Turn off interrupts during putmmu even though
42925910e17SDavid du Colombier * we shouldn't need to. - rsc
43025910e17SDavid du Colombier */
43125910e17SDavid du Colombier
43225910e17SDavid du Colombier s = splhi();
4334de34a7eSDavid du Colombier if(!(vpd[PDX(va)]&PTEVALID)){
4345979f962SDavid du Colombier if(up->mmufree == 0){
4355979f962SDavid du Colombier spllo();
4364de34a7eSDavid du Colombier page = newpage(0, 0, 0);
4375979f962SDavid du Colombier splhi();
4385979f962SDavid du Colombier }
4394de34a7eSDavid du Colombier else{
4404de34a7eSDavid du Colombier page = up->mmufree;
4414de34a7eSDavid du Colombier up->mmufree = page->next;
4424de34a7eSDavid du Colombier }
4434de34a7eSDavid du Colombier vpd[PDX(va)] = PPN(page->pa)|PTEUSER|PTEWRITE|PTEVALID;
4444de34a7eSDavid du Colombier /* page is now mapped into the VPT - clear it */
4454de34a7eSDavid du Colombier memset((void*)(VPT+PDX(va)*BY2PG), 0, BY2PG);
4464de34a7eSDavid du Colombier page->daddr = PDX(va);
4474de34a7eSDavid du Colombier page->next = up->mmuused;
4484de34a7eSDavid du Colombier up->mmuused = page;
4494de34a7eSDavid du Colombier }
4504de34a7eSDavid du Colombier old = vpt[VPTX(va)];
4514de34a7eSDavid du Colombier vpt[VPTX(va)] = pa|PTEUSER|PTEVALID;
4524de34a7eSDavid du Colombier if(old&PTEVALID)
4534de34a7eSDavid du Colombier flushpg(va);
4541bd28109SDavid du Colombier if(getcr3() != up->mmupdb->pa)
455567483c8SDavid du Colombier print("bad cr3 %#.8lux %#.8lux\n", getcr3(), up->mmupdb->pa);
45625910e17SDavid du Colombier splx(s);
4574de34a7eSDavid du Colombier }
4584de34a7eSDavid du Colombier
4594de34a7eSDavid du Colombier /*
4604de34a7eSDavid du Colombier * Double-check the user MMU.
4614de34a7eSDavid du Colombier * Error checking only.
4624de34a7eSDavid du Colombier */
4634de34a7eSDavid du Colombier void
checkmmu(ulong va,ulong pa)4644de34a7eSDavid du Colombier checkmmu(ulong va, ulong pa)
4654de34a7eSDavid du Colombier {
4664de34a7eSDavid du Colombier if(up->mmupdb == 0)
4674de34a7eSDavid du Colombier return;
4684de34a7eSDavid du Colombier if(!(vpd[PDX(va)]&PTEVALID) || !(vpt[VPTX(va)]&PTEVALID))
4694de34a7eSDavid du Colombier return;
4704de34a7eSDavid du Colombier if(PPN(vpt[VPTX(va)]) != pa)
471567483c8SDavid du Colombier print("%ld %s: va=%#08lux pa=%#08lux pte=%#08lux\n",
4724de34a7eSDavid du Colombier up->pid, up->text,
4734de34a7eSDavid du Colombier va, pa, vpt[VPTX(va)]);
4744de34a7eSDavid du Colombier }
475bd389b36SDavid du Colombier
476bd389b36SDavid du Colombier /*
4777dd7cddfSDavid du Colombier * Walk the page-table pointed to by pdb and return a pointer
4787dd7cddfSDavid du Colombier * to the entry for virtual address va at the requested level.
4797dd7cddfSDavid du Colombier * If the entry is invalid and create isn't requested then bail
4807dd7cddfSDavid du Colombier * out early. Otherwise, for the 2nd level walk, allocate a new
4814de34a7eSDavid du Colombier * page-table page and register it in the 1st level. This is used
4824de34a7eSDavid du Colombier * only to edit kernel mappings, which use pages from kernel memory,
4834de34a7eSDavid du Colombier * so it's okay to use KADDR to look at the tables.
484219b2ee8SDavid du Colombier */
4854de34a7eSDavid du Colombier ulong*
mmuwalk(ulong * pdb,ulong va,int level,int create)4864de34a7eSDavid du Colombier mmuwalk(ulong* pdb, ulong va, int level, int create)
4874de34a7eSDavid du Colombier {
4884de34a7eSDavid du Colombier ulong *table;
4894de34a7eSDavid du Colombier void *map;
4904de34a7eSDavid du Colombier
4917dd7cddfSDavid du Colombier table = &pdb[PDX(va)];
4927dd7cddfSDavid du Colombier if(!(*table & PTEVALID) && create == 0)
4937dd7cddfSDavid du Colombier return 0;
4947dd7cddfSDavid du Colombier
4957dd7cddfSDavid du Colombier switch(level){
4967dd7cddfSDavid du Colombier
4977dd7cddfSDavid du Colombier default:
4987dd7cddfSDavid du Colombier return 0;
4997dd7cddfSDavid du Colombier
5007dd7cddfSDavid du Colombier case 1:
5017dd7cddfSDavid du Colombier return table;
5027dd7cddfSDavid du Colombier
5037dd7cddfSDavid du Colombier case 2:
5047dd7cddfSDavid du Colombier if(*table & PTESIZE)
505c9b6d007SDavid du Colombier panic("mmuwalk2: va %luX entry %luX", va, *table);
5067dd7cddfSDavid du Colombier if(!(*table & PTEVALID)){
5074de34a7eSDavid du Colombier /*
5084de34a7eSDavid du Colombier * Have to call low-level allocator from
5094de34a7eSDavid du Colombier * memory.c if we haven't set up the xalloc
5104de34a7eSDavid du Colombier * tables yet.
5114de34a7eSDavid du Colombier */
5124de34a7eSDavid du Colombier if(didmmuinit)
5134de34a7eSDavid du Colombier map = xspanalloc(BY2PG, BY2PG, 0);
5144de34a7eSDavid du Colombier else
5154de34a7eSDavid du Colombier map = rampage();
5164de34a7eSDavid du Colombier if(map == nil)
5174de34a7eSDavid du Colombier panic("mmuwalk xspanalloc failed");
5184de34a7eSDavid du Colombier *table = PADDR(map)|PTEWRITE|PTEVALID;
5197dd7cddfSDavid du Colombier }
5207dd7cddfSDavid du Colombier table = KADDR(PPN(*table));
5217dd7cddfSDavid du Colombier return &table[PTX(va)];
5227dd7cddfSDavid du Colombier }
5237dd7cddfSDavid du Colombier }
5247dd7cddfSDavid du Colombier
5254de34a7eSDavid du Colombier /*
5264de34a7eSDavid du Colombier * Device mappings are shared by all procs and processors and
5274de34a7eSDavid du Colombier * live in the virtual range VMAP to VMAP+VMAPSIZE. The master
5284de34a7eSDavid du Colombier * copy of the mappings is stored in mach0->pdb, and they are
5294de34a7eSDavid du Colombier * paged in from there as necessary by vmapsync during faults.
5304de34a7eSDavid du Colombier */
5317dd7cddfSDavid du Colombier
5324de34a7eSDavid du Colombier static Lock vmaplock;
5334de34a7eSDavid du Colombier
5344de34a7eSDavid du Colombier static int findhole(ulong *a, int n, int count);
5354de34a7eSDavid du Colombier static ulong vmapalloc(ulong size);
5364de34a7eSDavid du Colombier static void pdbunmap(ulong*, ulong, int);
5374de34a7eSDavid du Colombier
5384de34a7eSDavid du Colombier /*
5394de34a7eSDavid du Colombier * Add a device mapping to the vmap range.
5404de34a7eSDavid du Colombier */
5414de34a7eSDavid du Colombier void*
vmap(ulong pa,int size)5424de34a7eSDavid du Colombier vmap(ulong pa, int size)
543219b2ee8SDavid du Colombier {
5444de34a7eSDavid du Colombier int osize;
5454de34a7eSDavid du Colombier ulong o, va;
546219b2ee8SDavid du Colombier
5474de34a7eSDavid du Colombier /*
5484de34a7eSDavid du Colombier * might be asking for less than a page.
5494de34a7eSDavid du Colombier */
5504de34a7eSDavid du Colombier osize = size;
5514de34a7eSDavid du Colombier o = pa & (BY2PG-1);
5524de34a7eSDavid du Colombier pa -= o;
5534de34a7eSDavid du Colombier size += o;
554219b2ee8SDavid du Colombier
5554de34a7eSDavid du Colombier size = ROUND(size, BY2PG);
5564de34a7eSDavid du Colombier if(pa == 0){
557567483c8SDavid du Colombier print("vmap pa=0 pc=%#p\n", getcallerpc(&pa));
5584de34a7eSDavid du Colombier return nil;
5594de34a7eSDavid du Colombier }
5604de34a7eSDavid du Colombier ilock(&vmaplock);
5614de34a7eSDavid du Colombier if((va = vmapalloc(size)) == 0
5624de34a7eSDavid du Colombier || pdbmap(MACHP(0)->pdb, pa|PTEUNCACHED|PTEWRITE, va, size) < 0){
5634de34a7eSDavid du Colombier iunlock(&vmaplock);
564219b2ee8SDavid du Colombier return 0;
565219b2ee8SDavid du Colombier }
5664de34a7eSDavid du Colombier iunlock(&vmaplock);
5674de34a7eSDavid du Colombier /* avoid trap on local processor
5684de34a7eSDavid du Colombier for(i=0; i<size; i+=4*MB)
5694de34a7eSDavid du Colombier vmapsync(va+i);
5704de34a7eSDavid du Colombier */
5714de34a7eSDavid du Colombier USED(osize);
5724de34a7eSDavid du Colombier // print(" vmap %#.8lux %d => %#.8lux\n", pa+o, osize, va+o);
5734de34a7eSDavid du Colombier return (void*)(va + o);
5747dd7cddfSDavid du Colombier }
575219b2ee8SDavid du Colombier
5764de34a7eSDavid du Colombier static int
findhole(ulong * a,int n,int count)5774de34a7eSDavid du Colombier findhole(ulong *a, int n, int count)
5784de34a7eSDavid du Colombier {
5794de34a7eSDavid du Colombier int have, i;
580219b2ee8SDavid du Colombier
5814de34a7eSDavid du Colombier have = 0;
5824de34a7eSDavid du Colombier for(i=0; i<n; i++){
5834de34a7eSDavid du Colombier if(a[i] == 0)
5844de34a7eSDavid du Colombier have++;
5857dd7cddfSDavid du Colombier else
5864de34a7eSDavid du Colombier have = 0;
5874de34a7eSDavid du Colombier if(have >= count)
5884de34a7eSDavid du Colombier return i+1 - have;
5894de34a7eSDavid du Colombier }
5904de34a7eSDavid du Colombier return -1;
5917dd7cddfSDavid du Colombier }
5927dd7cddfSDavid du Colombier
5934de34a7eSDavid du Colombier /*
5944de34a7eSDavid du Colombier * Look for free space in the vmap.
5954de34a7eSDavid du Colombier */
5964de34a7eSDavid du Colombier static ulong
vmapalloc(ulong size)5974de34a7eSDavid du Colombier vmapalloc(ulong size)
5987dd7cddfSDavid du Colombier {
5994de34a7eSDavid du Colombier int i, n, o;
6004de34a7eSDavid du Colombier ulong *vpdb;
6014de34a7eSDavid du Colombier int vpdbsize;
6027dd7cddfSDavid du Colombier
6034de34a7eSDavid du Colombier vpdb = &MACHP(0)->pdb[PDX(VMAP)];
6044de34a7eSDavid du Colombier vpdbsize = VMAPSIZE/(4*MB);
6054de34a7eSDavid du Colombier
6064de34a7eSDavid du Colombier if(size >= 4*MB){
6074de34a7eSDavid du Colombier n = (size+4*MB-1) / (4*MB);
6084de34a7eSDavid du Colombier if((o = findhole(vpdb, vpdbsize, n)) != -1)
6094de34a7eSDavid du Colombier return VMAP + o*4*MB;
6101bd28109SDavid du Colombier return 0;
6114de34a7eSDavid du Colombier }
6124de34a7eSDavid du Colombier n = (size+BY2PG-1) / BY2PG;
6134de34a7eSDavid du Colombier for(i=0; i<vpdbsize; i++)
6144de34a7eSDavid du Colombier if((vpdb[i]&PTEVALID) && !(vpdb[i]&PTESIZE))
6154de34a7eSDavid du Colombier if((o = findhole(KADDR(PPN(vpdb[i])), WD2PG, n)) != -1)
6164de34a7eSDavid du Colombier return VMAP + i*4*MB + o*BY2PG;
6174de34a7eSDavid du Colombier if((o = findhole(vpdb, vpdbsize, 1)) != -1)
6184de34a7eSDavid du Colombier return VMAP + o*4*MB;
6194de34a7eSDavid du Colombier
6204de34a7eSDavid du Colombier /*
6214de34a7eSDavid du Colombier * could span page directory entries, but not worth the trouble.
6224de34a7eSDavid du Colombier * not going to be very much contention.
6234de34a7eSDavid du Colombier */
6244de34a7eSDavid du Colombier return 0;
6254de34a7eSDavid du Colombier }
6264de34a7eSDavid du Colombier
6274de34a7eSDavid du Colombier /*
6284de34a7eSDavid du Colombier * Remove a device mapping from the vmap range.
6294de34a7eSDavid du Colombier * Since pdbunmap does not remove page tables, just entries,
6304de34a7eSDavid du Colombier * the call need not be interlocked with vmap.
6314de34a7eSDavid du Colombier */
6324de34a7eSDavid du Colombier void
vunmap(void * v,int size)6334de34a7eSDavid du Colombier vunmap(void *v, int size)
6344de34a7eSDavid du Colombier {
6354de34a7eSDavid du Colombier int i;
6364de34a7eSDavid du Colombier ulong va, o;
6374de34a7eSDavid du Colombier Mach *nm;
6384de34a7eSDavid du Colombier Proc *p;
6394de34a7eSDavid du Colombier
6404de34a7eSDavid du Colombier /*
6414de34a7eSDavid du Colombier * might not be aligned
6424de34a7eSDavid du Colombier */
6434de34a7eSDavid du Colombier va = (ulong)v;
6444de34a7eSDavid du Colombier o = va&(BY2PG-1);
6454de34a7eSDavid du Colombier va -= o;
6464de34a7eSDavid du Colombier size += o;
6474de34a7eSDavid du Colombier size = ROUND(size, BY2PG);
6484de34a7eSDavid du Colombier
6494de34a7eSDavid du Colombier if(size < 0 || va < VMAP || va+size > VMAP+VMAPSIZE)
650c9b6d007SDavid du Colombier panic("vunmap va=%#.8lux size=%#x pc=%#.8lux",
65172061b92SDavid du Colombier va, size, getcallerpc(&v));
6524de34a7eSDavid du Colombier
6534de34a7eSDavid du Colombier pdbunmap(MACHP(0)->pdb, va, size);
6544de34a7eSDavid du Colombier
6554de34a7eSDavid du Colombier /*
6564de34a7eSDavid du Colombier * Flush mapping from all the tlbs and copied pdbs.
6574de34a7eSDavid du Colombier * This can be (and is) slow, since it is called only rarely.
65897bff0f0SDavid du Colombier * It is possible for vunmap to be called with up == nil,
65997bff0f0SDavid du Colombier * e.g. from the reset/init driver routines during system
66097bff0f0SDavid du Colombier * boot. In that case it suffices to flush the MACH(0) TLB
66197bff0f0SDavid du Colombier * and return.
6624de34a7eSDavid du Colombier */
66397bff0f0SDavid du Colombier if(!active.thunderbirdsarego){
66497bff0f0SDavid du Colombier putcr3(PADDR(MACHP(0)->pdb));
66597bff0f0SDavid du Colombier return;
66697bff0f0SDavid du Colombier }
6674de34a7eSDavid du Colombier for(i=0; i<conf.nproc; i++){
6684de34a7eSDavid du Colombier p = proctab(i);
6694de34a7eSDavid du Colombier if(p->state == Dead)
6704de34a7eSDavid du Colombier continue;
6714de34a7eSDavid du Colombier if(p != up)
6724de34a7eSDavid du Colombier p->newtlb = 1;
6734de34a7eSDavid du Colombier }
674c2c68342SDavid du Colombier /*
675c2c68342SDavid du Colombier * since the 386 is short of registers, m always contains the constant
676c2c68342SDavid du Colombier * MACHADDR, not MACHP(m->machno); see ../pc/dat.h. so we can't just
677c2c68342SDavid du Colombier * compare addresses with m.
678c2c68342SDavid du Colombier */
679c2c68342SDavid du Colombier for(i=0; i<conf.nmach; i++)
680c2c68342SDavid du Colombier if(i != m->machno)
681c2c68342SDavid du Colombier MACHP(i)->flushmmu = 1;
6824de34a7eSDavid du Colombier flushmmu();
683c2c68342SDavid du Colombier for(i=0; i<conf.nmach; i++)
684c2c68342SDavid du Colombier if(i != m->machno) {
6854de34a7eSDavid du Colombier nm = MACHP(i);
686*405829f1SDavid du Colombier while(iscpuactive(nm->machno) && nm->flushmmu)
6874de34a7eSDavid du Colombier ;
6884de34a7eSDavid du Colombier }
6894de34a7eSDavid du Colombier }
6904de34a7eSDavid du Colombier
6914de34a7eSDavid du Colombier /*
6924de34a7eSDavid du Colombier * Add kernel mappings for pa -> va for a section of size bytes.
6934de34a7eSDavid du Colombier */
6944de34a7eSDavid du Colombier int
pdbmap(ulong * pdb,ulong pa,ulong va,int size)6954de34a7eSDavid du Colombier pdbmap(ulong *pdb, ulong pa, ulong va, int size)
6964de34a7eSDavid du Colombier {
6974de34a7eSDavid du Colombier int pse;
69825910e17SDavid du Colombier ulong pgsz, *pte, *table;
69925910e17SDavid du Colombier ulong flag, off;
7004de34a7eSDavid du Colombier
7014de34a7eSDavid du Colombier flag = pa&0xFFF;
7024de34a7eSDavid du Colombier pa &= ~0xFFF;
7034de34a7eSDavid du Colombier
70461fd6f66SDavid du Colombier if((MACHP(0)->cpuiddx & Pse) && (getcr4() & 0x10))
7057dd7cddfSDavid du Colombier pse = 1;
7067dd7cddfSDavid du Colombier else
7077dd7cddfSDavid du Colombier pse = 0;
7087dd7cddfSDavid du Colombier
70925910e17SDavid du Colombier for(off=0; off<size; off+=pgsz){
71025910e17SDavid du Colombier table = &pdb[PDX(va+off)];
7114de34a7eSDavid du Colombier if((*table&PTEVALID) && (*table&PTESIZE))
7124de34a7eSDavid du Colombier panic("vmap: va=%#.8lux pa=%#.8lux pde=%#.8lux",
71325910e17SDavid du Colombier va+off, pa+off, *table);
7147dd7cddfSDavid du Colombier
7157dd7cddfSDavid du Colombier /*
7164de34a7eSDavid du Colombier * Check if it can be mapped using a 4MB page:
7174de34a7eSDavid du Colombier * va, pa aligned and size >= 4MB and processor can do it.
7187dd7cddfSDavid du Colombier */
71925910e17SDavid du Colombier if(pse && (pa+off)%(4*MB) == 0 && (va+off)%(4*MB) == 0 && (size-off) >= 4*MB){
72025910e17SDavid du Colombier *table = (pa+off)|flag|PTESIZE|PTEVALID;
7217dd7cddfSDavid du Colombier pgsz = 4*MB;
7224de34a7eSDavid du Colombier }else{
72325910e17SDavid du Colombier pte = mmuwalk(pdb, va+off, 2, 1);
7244de34a7eSDavid du Colombier if(*pte&PTEVALID)
7254de34a7eSDavid du Colombier panic("vmap: va=%#.8lux pa=%#.8lux pte=%#.8lux",
72625910e17SDavid du Colombier va+off, pa+off, *pte);
72725910e17SDavid du Colombier *pte = (pa+off)|flag|PTEVALID;
7287dd7cddfSDavid du Colombier pgsz = BY2PG;
7297dd7cddfSDavid du Colombier }
7307dd7cddfSDavid du Colombier }
7314de34a7eSDavid du Colombier return 0;
7324de34a7eSDavid du Colombier }
7337dd7cddfSDavid du Colombier
7347dd7cddfSDavid du Colombier /*
7354de34a7eSDavid du Colombier * Remove mappings. Must already exist, for sanity.
7364de34a7eSDavid du Colombier * Only used for kernel mappings, so okay to use KADDR.
7377dd7cddfSDavid du Colombier */
7384de34a7eSDavid du Colombier static void
pdbunmap(ulong * pdb,ulong va,int size)7394de34a7eSDavid du Colombier pdbunmap(ulong *pdb, ulong va, int size)
7404de34a7eSDavid du Colombier {
7414de34a7eSDavid du Colombier ulong vae;
7424de34a7eSDavid du Colombier ulong *table;
7437dd7cddfSDavid du Colombier
7444de34a7eSDavid du Colombier vae = va+size;
7454de34a7eSDavid du Colombier while(va < vae){
7464de34a7eSDavid du Colombier table = &pdb[PDX(va)];
7474de34a7eSDavid du Colombier if(!(*table & PTEVALID)){
7484de34a7eSDavid du Colombier panic("vunmap: not mapped");
7494de34a7eSDavid du Colombier /*
7504de34a7eSDavid du Colombier va = (va+4*MB-1) & ~(4*MB-1);
7514de34a7eSDavid du Colombier continue;
7524de34a7eSDavid du Colombier */
7534de34a7eSDavid du Colombier }
7544de34a7eSDavid du Colombier if(*table & PTESIZE){
7554de34a7eSDavid du Colombier *table = 0;
7564de34a7eSDavid du Colombier va = (va+4*MB-1) & ~(4*MB-1);
7574de34a7eSDavid du Colombier continue;
7584de34a7eSDavid du Colombier }
7594de34a7eSDavid du Colombier table = KADDR(PPN(*table));
7604de34a7eSDavid du Colombier if(!(table[PTX(va)] & PTEVALID))
7614de34a7eSDavid du Colombier panic("vunmap: not mapped");
7624de34a7eSDavid du Colombier table[PTX(va)] = 0;
7634de34a7eSDavid du Colombier va += BY2PG;
7644de34a7eSDavid du Colombier }
7654de34a7eSDavid du Colombier }
7664de34a7eSDavid du Colombier
7674de34a7eSDavid du Colombier /*
7684de34a7eSDavid du Colombier * Handle a fault by bringing vmap up to date.
7694de34a7eSDavid du Colombier * Only copy pdb entries and they never go away,
7704de34a7eSDavid du Colombier * so no locking needed.
7714de34a7eSDavid du Colombier */
7724de34a7eSDavid du Colombier int
vmapsync(ulong va)7734de34a7eSDavid du Colombier vmapsync(ulong va)
7744de34a7eSDavid du Colombier {
7754de34a7eSDavid du Colombier ulong entry, *table;
7764de34a7eSDavid du Colombier
7774de34a7eSDavid du Colombier if(va < VMAP || va >= VMAP+VMAPSIZE)
7784de34a7eSDavid du Colombier return 0;
7794de34a7eSDavid du Colombier
7804de34a7eSDavid du Colombier entry = MACHP(0)->pdb[PDX(va)];
7814de34a7eSDavid du Colombier if(!(entry&PTEVALID))
7824de34a7eSDavid du Colombier return 0;
7834de34a7eSDavid du Colombier if(!(entry&PTESIZE)){
7844de34a7eSDavid du Colombier /* make sure entry will help the fault */
7854de34a7eSDavid du Colombier table = KADDR(PPN(entry));
7864de34a7eSDavid du Colombier if(!(table[PTX(va)]&PTEVALID))
7874de34a7eSDavid du Colombier return 0;
7884de34a7eSDavid du Colombier }
7894de34a7eSDavid du Colombier vpd[PDX(va)] = entry;
7904de34a7eSDavid du Colombier /*
7914de34a7eSDavid du Colombier * TLB doesn't cache negative results, so no flush needed.
7924de34a7eSDavid du Colombier */
7934de34a7eSDavid du Colombier return 1;
7944de34a7eSDavid du Colombier }
7954de34a7eSDavid du Colombier
7964de34a7eSDavid du Colombier
7974de34a7eSDavid du Colombier /*
7984de34a7eSDavid du Colombier * KMap is used to map individual pages into virtual memory.
7994de34a7eSDavid du Colombier * It is rare to have more than a few KMaps at a time (in the
8004de34a7eSDavid du Colombier * absence of interrupts, only two at a time are ever used,
8014de34a7eSDavid du Colombier * but interrupts can stack). The mappings are local to a process,
8024de34a7eSDavid du Colombier * so we can use the same range of virtual address space for
8034de34a7eSDavid du Colombier * all processes without any coordination.
8044de34a7eSDavid du Colombier */
8054de34a7eSDavid du Colombier #define kpt (vpt+VPTX(KMAP))
8064de34a7eSDavid du Colombier #define NKPT (KMAPSIZE/BY2PG)
8074de34a7eSDavid du Colombier
8084de34a7eSDavid du Colombier KMap*
kmap(Page * page)8094de34a7eSDavid du Colombier kmap(Page *page)
8104de34a7eSDavid du Colombier {
8114de34a7eSDavid du Colombier int i, o, s;
8124de34a7eSDavid du Colombier
8134de34a7eSDavid du Colombier if(up == nil)
8144de34a7eSDavid du Colombier panic("kmap: up=0 pc=%#.8lux", getcallerpc(&page));
8154de34a7eSDavid du Colombier if(up->mmupdb == nil)
8164de34a7eSDavid du Colombier upallocpdb();
81725910e17SDavid du Colombier if(up->nkmap < 0)
81825910e17SDavid du Colombier panic("kmap %lud %s: nkmap=%d", up->pid, up->text, up->nkmap);
81925910e17SDavid du Colombier
82025910e17SDavid du Colombier /*
82125910e17SDavid du Colombier * Splhi shouldn't be necessary here, but paranoia reigns.
82225910e17SDavid du Colombier * See comment in putmmu above.
82325910e17SDavid du Colombier */
82425910e17SDavid du Colombier s = splhi();
8255f61b043SDavid du Colombier up->nkmap++;
8264de34a7eSDavid du Colombier if(!(vpd[PDX(KMAP)]&PTEVALID)){
8274de34a7eSDavid du Colombier /* allocate page directory */
8284de34a7eSDavid du Colombier if(KMAPSIZE > BY2XPG)
8294de34a7eSDavid du Colombier panic("bad kmapsize");
8305f61b043SDavid du Colombier if(up->kmaptable != nil)
8315f61b043SDavid du Colombier panic("kmaptable");
83225910e17SDavid du Colombier spllo();
8335f61b043SDavid du Colombier up->kmaptable = newpage(0, 0, 0);
83425910e17SDavid du Colombier splhi();
8355f61b043SDavid du Colombier vpd[PDX(KMAP)] = up->kmaptable->pa|PTEWRITE|PTEVALID;
83625910e17SDavid du Colombier flushpg((ulong)kpt);
8374de34a7eSDavid du Colombier memset(kpt, 0, BY2PG);
8384de34a7eSDavid du Colombier kpt[0] = page->pa|PTEWRITE|PTEVALID;
8394de34a7eSDavid du Colombier up->lastkmap = 0;
84025910e17SDavid du Colombier splx(s);
8414de34a7eSDavid du Colombier return (KMap*)KMAP;
8424de34a7eSDavid du Colombier }
8435f61b043SDavid du Colombier if(up->kmaptable == nil)
8445f61b043SDavid du Colombier panic("no kmaptable");
8454de34a7eSDavid du Colombier o = up->lastkmap+1;
8464de34a7eSDavid du Colombier for(i=0; i<NKPT; i++){
8474de34a7eSDavid du Colombier if(kpt[(i+o)%NKPT] == 0){
8484de34a7eSDavid du Colombier o = (i+o)%NKPT;
8494de34a7eSDavid du Colombier kpt[o] = page->pa|PTEWRITE|PTEVALID;
8504de34a7eSDavid du Colombier up->lastkmap = o;
85125910e17SDavid du Colombier splx(s);
8524de34a7eSDavid du Colombier return (KMap*)(KMAP+o*BY2PG);
8534de34a7eSDavid du Colombier }
8544de34a7eSDavid du Colombier }
8554de34a7eSDavid du Colombier panic("out of kmap");
8564de34a7eSDavid du Colombier return nil;
8574de34a7eSDavid du Colombier }
8584de34a7eSDavid du Colombier
8594de34a7eSDavid du Colombier void
kunmap(KMap * k)8604de34a7eSDavid du Colombier kunmap(KMap *k)
8614de34a7eSDavid du Colombier {
8624de34a7eSDavid du Colombier ulong va;
8634de34a7eSDavid du Colombier
8644de34a7eSDavid du Colombier va = (ulong)k;
8654de34a7eSDavid du Colombier if(up->mmupdb == nil || !(vpd[PDX(KMAP)]&PTEVALID))
8664de34a7eSDavid du Colombier panic("kunmap: no kmaps");
8674de34a7eSDavid du Colombier if(va < KMAP || va >= KMAP+KMAPSIZE)
868567483c8SDavid du Colombier panic("kunmap: bad address %#.8lux pc=%#p", va, getcallerpc(&k));
8694de34a7eSDavid du Colombier if(!(vpt[VPTX(va)]&PTEVALID))
870567483c8SDavid du Colombier panic("kunmap: not mapped %#.8lux pc=%#p", va, getcallerpc(&k));
8715f61b043SDavid du Colombier up->nkmap--;
87225910e17SDavid du Colombier if(up->nkmap < 0)
87325910e17SDavid du Colombier panic("kunmap %lud %s: nkmap=%d", up->pid, up->text, up->nkmap);
8744de34a7eSDavid du Colombier vpt[VPTX(va)] = 0;
8754de34a7eSDavid du Colombier flushpg(va);
8764de34a7eSDavid du Colombier }
8774de34a7eSDavid du Colombier
8784de34a7eSDavid du Colombier /*
8794de34a7eSDavid du Colombier * Temporary one-page mapping used to edit page directories.
8804de34a7eSDavid du Colombier *
8814de34a7eSDavid du Colombier * The fasttmp #define controls whether the code optimizes
8824de34a7eSDavid du Colombier * the case where the page is already mapped in the physical
8834de34a7eSDavid du Colombier * memory window.
8844de34a7eSDavid du Colombier */
8854de34a7eSDavid du Colombier #define fasttmp 1
8864de34a7eSDavid du Colombier
8874de34a7eSDavid du Colombier void*
tmpmap(Page * p)8884de34a7eSDavid du Colombier tmpmap(Page *p)
8894de34a7eSDavid du Colombier {
8904de34a7eSDavid du Colombier ulong i;
8914de34a7eSDavid du Colombier ulong *entry;
8924de34a7eSDavid du Colombier
8934de34a7eSDavid du Colombier if(islo())
8944de34a7eSDavid du Colombier panic("tmpaddr: islo");
8954de34a7eSDavid du Colombier
8964de34a7eSDavid du Colombier if(fasttmp && p->pa < -KZERO)
8974de34a7eSDavid du Colombier return KADDR(p->pa);
8984de34a7eSDavid du Colombier
8994de34a7eSDavid du Colombier /*
9004de34a7eSDavid du Colombier * PDX(TMPADDR) == PDX(MACHADDR), so this
9014de34a7eSDavid du Colombier * entry is private to the processor and shared
9024de34a7eSDavid du Colombier * between up->mmupdb (if any) and m->pdb.
9034de34a7eSDavid du Colombier */
9044de34a7eSDavid du Colombier entry = &vpt[VPTX(TMPADDR)];
9054de34a7eSDavid du Colombier if(!(*entry&PTEVALID)){
9064de34a7eSDavid du Colombier for(i=KZERO; i<=CPU0MACH; i+=BY2PG)
907567483c8SDavid du Colombier print("%#p: *%#p=%#p (vpt=%#p index=%#p)\n", i, &vpt[VPTX(i)], vpt[VPTX(i)], vpt, VPTX(i));
9084de34a7eSDavid du Colombier panic("tmpmap: no entry");
9094de34a7eSDavid du Colombier }
9104de34a7eSDavid du Colombier if(PPN(*entry) != PPN(TMPADDR-KZERO))
9114de34a7eSDavid du Colombier panic("tmpmap: already mapped entry=%#.8lux", *entry);
9124de34a7eSDavid du Colombier *entry = p->pa|PTEWRITE|PTEVALID;
9134de34a7eSDavid du Colombier flushpg(TMPADDR);
9144de34a7eSDavid du Colombier return (void*)TMPADDR;
9154de34a7eSDavid du Colombier }
9164de34a7eSDavid du Colombier
9174de34a7eSDavid du Colombier void
tmpunmap(void * v)9184de34a7eSDavid du Colombier tmpunmap(void *v)
9194de34a7eSDavid du Colombier {
9204de34a7eSDavid du Colombier ulong *entry;
9214de34a7eSDavid du Colombier
9224de34a7eSDavid du Colombier if(islo())
9234de34a7eSDavid du Colombier panic("tmpaddr: islo");
9244de34a7eSDavid du Colombier if(fasttmp && (ulong)v >= KZERO && v != (void*)TMPADDR)
9254de34a7eSDavid du Colombier return;
9264de34a7eSDavid du Colombier if(v != (void*)TMPADDR)
9274de34a7eSDavid du Colombier panic("tmpunmap: bad address");
9284de34a7eSDavid du Colombier entry = &vpt[VPTX(TMPADDR)];
9294de34a7eSDavid du Colombier if(!(*entry&PTEVALID) || PPN(*entry) == PPN(PADDR(TMPADDR)))
9304de34a7eSDavid du Colombier panic("tmpmap: not mapped entry=%#.8lux", *entry);
9314de34a7eSDavid du Colombier *entry = PPN(TMPADDR-KZERO)|PTEWRITE|PTEVALID;
9324de34a7eSDavid du Colombier flushpg(TMPADDR);
9334de34a7eSDavid du Colombier }
9344de34a7eSDavid du Colombier
9354de34a7eSDavid du Colombier /*
9364de34a7eSDavid du Colombier * These could go back to being macros once the kernel is debugged,
9374de34a7eSDavid du Colombier * but the extra checking is nice to have.
9384de34a7eSDavid du Colombier */
9394de34a7eSDavid du Colombier void*
kaddr(ulong pa)9404de34a7eSDavid du Colombier kaddr(ulong pa)
9414de34a7eSDavid du Colombier {
9424de34a7eSDavid du Colombier if(pa > (ulong)-KZERO)
9434de34a7eSDavid du Colombier panic("kaddr: pa=%#.8lux", pa);
9444de34a7eSDavid du Colombier return (void*)(pa+KZERO);
9454de34a7eSDavid du Colombier }
9464de34a7eSDavid du Colombier
9474de34a7eSDavid du Colombier ulong
paddr(void * v)9484de34a7eSDavid du Colombier paddr(void *v)
9494de34a7eSDavid du Colombier {
9504de34a7eSDavid du Colombier ulong va;
9514de34a7eSDavid du Colombier
9524de34a7eSDavid du Colombier va = (ulong)v;
9534de34a7eSDavid du Colombier if(va < KZERO)
954567483c8SDavid du Colombier panic("paddr: va=%#.8lux pc=%#p", va, getcallerpc(&v));
9554de34a7eSDavid du Colombier return va-KZERO;
956bd389b36SDavid du Colombier }
9575f61b043SDavid du Colombier
9585f61b043SDavid du Colombier /*
9595f61b043SDavid du Colombier * More debugging.
9605f61b043SDavid du Colombier */
9615f61b043SDavid du Colombier void
countpagerefs(ulong * ref,int print)9625f61b043SDavid du Colombier countpagerefs(ulong *ref, int print)
9635f61b043SDavid du Colombier {
9645f61b043SDavid du Colombier int i, n;
9655f61b043SDavid du Colombier Mach *mm;
9665f61b043SDavid du Colombier Page *pg;
9675f61b043SDavid du Colombier Proc *p;
9685f61b043SDavid du Colombier
9695f61b043SDavid du Colombier n = 0;
9705f61b043SDavid du Colombier for(i=0; i<conf.nproc; i++){
9715f61b043SDavid du Colombier p = proctab(i);
9725f61b043SDavid du Colombier if(p->mmupdb){
9735f61b043SDavid du Colombier if(print){
9745f61b043SDavid du Colombier if(ref[pagenumber(p->mmupdb)])
9755f61b043SDavid du Colombier iprint("page %#.8lux is proc %d (pid %lud) pdb\n",
9765f61b043SDavid du Colombier p->mmupdb->pa, i, p->pid);
9775f61b043SDavid du Colombier continue;
9785f61b043SDavid du Colombier }
9795f61b043SDavid du Colombier if(ref[pagenumber(p->mmupdb)]++ == 0)
9805f61b043SDavid du Colombier n++;
9815f61b043SDavid du Colombier else
9825f61b043SDavid du Colombier iprint("page %#.8lux is proc %d (pid %lud) pdb but has other refs!\n",
9835f61b043SDavid du Colombier p->mmupdb->pa, i, p->pid);
9845f61b043SDavid du Colombier }
9855f61b043SDavid du Colombier if(p->kmaptable){
9865f61b043SDavid du Colombier if(print){
9875f61b043SDavid du Colombier if(ref[pagenumber(p->kmaptable)])
9885f61b043SDavid du Colombier iprint("page %#.8lux is proc %d (pid %lud) kmaptable\n",
9895f61b043SDavid du Colombier p->kmaptable->pa, i, p->pid);
9905f61b043SDavid du Colombier continue;
9915f61b043SDavid du Colombier }
9925f61b043SDavid du Colombier if(ref[pagenumber(p->kmaptable)]++ == 0)
9935f61b043SDavid du Colombier n++;
9945f61b043SDavid du Colombier else
9955f61b043SDavid du Colombier iprint("page %#.8lux is proc %d (pid %lud) kmaptable but has other refs!\n",
9965f61b043SDavid du Colombier p->kmaptable->pa, i, p->pid);
9975f61b043SDavid du Colombier }
9985f61b043SDavid du Colombier for(pg=p->mmuused; pg; pg=pg->next){
9995f61b043SDavid du Colombier if(print){
10005f61b043SDavid du Colombier if(ref[pagenumber(pg)])
10015f61b043SDavid du Colombier iprint("page %#.8lux is on proc %d (pid %lud) mmuused\n",
10025f61b043SDavid du Colombier pg->pa, i, p->pid);
10035f61b043SDavid du Colombier continue;
10045f61b043SDavid du Colombier }
10055f61b043SDavid du Colombier if(ref[pagenumber(pg)]++ == 0)
10065f61b043SDavid du Colombier n++;
10075f61b043SDavid du Colombier else
10085f61b043SDavid du Colombier iprint("page %#.8lux is on proc %d (pid %lud) mmuused but has other refs!\n",
10095f61b043SDavid du Colombier pg->pa, i, p->pid);
10105f61b043SDavid du Colombier }
10115f61b043SDavid du Colombier for(pg=p->mmufree; pg; pg=pg->next){
10125f61b043SDavid du Colombier if(print){
10135f61b043SDavid du Colombier if(ref[pagenumber(pg)])
10145f61b043SDavid du Colombier iprint("page %#.8lux is on proc %d (pid %lud) mmufree\n",
10155f61b043SDavid du Colombier pg->pa, i, p->pid);
10165f61b043SDavid du Colombier continue;
10175f61b043SDavid du Colombier }
10185f61b043SDavid du Colombier if(ref[pagenumber(pg)]++ == 0)
10195f61b043SDavid du Colombier n++;
10205f61b043SDavid du Colombier else
10215f61b043SDavid du Colombier iprint("page %#.8lux is on proc %d (pid %lud) mmufree but has other refs!\n",
10225f61b043SDavid du Colombier pg->pa, i, p->pid);
10235f61b043SDavid du Colombier }
10245f61b043SDavid du Colombier }
10255f61b043SDavid du Colombier if(!print)
10265f61b043SDavid du Colombier iprint("%d pages in proc mmu\n", n);
10275f61b043SDavid du Colombier n = 0;
10285f61b043SDavid du Colombier for(i=0; i<conf.nmach; i++){
10295f61b043SDavid du Colombier mm = MACHP(i);
10305f61b043SDavid du Colombier for(pg=mm->pdbpool; pg; pg=pg->next){
10315f61b043SDavid du Colombier if(print){
10325f61b043SDavid du Colombier if(ref[pagenumber(pg)])
10335f61b043SDavid du Colombier iprint("page %#.8lux is in cpu%d pdbpool\n",
10345f61b043SDavid du Colombier pg->pa, i);
10355f61b043SDavid du Colombier continue;
10365f61b043SDavid du Colombier }
10375f61b043SDavid du Colombier if(ref[pagenumber(pg)]++ == 0)
10385f61b043SDavid du Colombier n++;
10395f61b043SDavid du Colombier else
10405f61b043SDavid du Colombier iprint("page %#.8lux is in cpu%d pdbpool but has other refs!\n",
10415f61b043SDavid du Colombier pg->pa, i);
10425f61b043SDavid du Colombier }
10435f61b043SDavid du Colombier }
10441bd28109SDavid du Colombier if(!print){
10455f61b043SDavid du Colombier iprint("%d pages in mach pdbpools\n", n);
10461bd28109SDavid du Colombier for(i=0; i<conf.nmach; i++)
10471bd28109SDavid du Colombier iprint("cpu%d: %d pdballoc, %d pdbfree\n",
10481bd28109SDavid du Colombier i, MACHP(i)->pdballoc, MACHP(i)->pdbfree);
10491bd28109SDavid du Colombier }
10505f61b043SDavid du Colombier }
1051be704722SDavid du Colombier
1052be704722SDavid du Colombier void
checkfault(ulong,ulong)1053d04cc87cSDavid du Colombier checkfault(ulong, ulong)
1054be704722SDavid du Colombier {
1055d04cc87cSDavid du Colombier }
1056be704722SDavid du Colombier
1057208510e1SDavid du Colombier /*
1058208510e1SDavid du Colombier * Return the number of bytes that can be accessed via KADDR(pa).
1059208510e1SDavid du Colombier * If pa is not a valid argument to KADDR, return 0.
1060208510e1SDavid du Colombier */
1061208510e1SDavid du Colombier ulong
cankaddr(ulong pa)1062208510e1SDavid du Colombier cankaddr(ulong pa)
1063208510e1SDavid du Colombier {
1064208510e1SDavid du Colombier if(pa >= -KZERO)
1065208510e1SDavid du Colombier return 0;
1066208510e1SDavid du Colombier return -KZERO - pa;
1067208510e1SDavid du Colombier }
1068be704722SDavid du Colombier
1069