1*25210b06SDavid du Colombier /*
2*25210b06SDavid du Colombier * Memory mappings. Life was easier when 2G of memory was enough.
3*25210b06SDavid du Colombier *
4*25210b06SDavid du Colombier * The kernel memory starts at KZERO, with the text loaded at KZERO+1M
5*25210b06SDavid du Colombier * (9load sits under 1M during the load). The memory from KZERO to the
6*25210b06SDavid du Colombier * top of memory is mapped 1-1 with physical memory, starting at physical
7*25210b06SDavid du Colombier * address 0. All kernel memory and data structures (i.e., the entries stored
8*25210b06SDavid du Colombier * into conf.mem) must sit in this physical range: if KZERO is at 0xF0000000,
9*25210b06SDavid du Colombier * then the kernel can only have 256MB of memory for itself.
10*25210b06SDavid du Colombier *
11*25210b06SDavid du Colombier * The 256M below KZERO comprises three parts. The lowest 4M is the
12*25210b06SDavid du Colombier * virtual page table, a virtual address representation of the current
13*25210b06SDavid du Colombier * page table tree. The second 4M is used for temporary per-process
14*25210b06SDavid du Colombier * mappings managed by kmap and kunmap. The remaining 248M is used
15*25210b06SDavid du Colombier * for global (shared by all procs and all processors) device memory
16*25210b06SDavid du Colombier * mappings and managed by vmap and vunmap. The total amount (256M)
17*25210b06SDavid du Colombier * could probably be reduced somewhat if desired. The largest device
18*25210b06SDavid du Colombier * mapping is that of the video card, and even though modern video cards
19*25210b06SDavid du Colombier * have embarrassing amounts of memory, the video drivers only use one
20*25210b06SDavid du Colombier * frame buffer worth (at most 16M). Each is described in more detail below.
21*25210b06SDavid du Colombier *
22*25210b06SDavid du Colombier * The VPT is a 4M frame constructed by inserting the pdb into itself.
23*25210b06SDavid du Colombier * This short-circuits one level of the page tables, with the result that
24*25210b06SDavid du Colombier * the contents of second-level page tables can be accessed at VPT.
25*25210b06SDavid du Colombier * We use the VPT to edit the page tables (see mmu) after inserting them
26*25210b06SDavid du Colombier * into the page directory. It is a convenient mechanism for mapping what
27*25210b06SDavid du Colombier * might be otherwise-inaccessible pages. The idea was borrowed from
28*25210b06SDavid du Colombier * the Exokernel.
29*25210b06SDavid du Colombier *
30*25210b06SDavid du Colombier * The VPT doesn't solve all our problems, because we still need to
31*25210b06SDavid du Colombier * prepare page directories before we can install them. For that, we
32*25210b06SDavid du Colombier * use tmpmap/tmpunmap, which map a single page at TMPADDR.
33*25210b06SDavid du Colombier */
34*25210b06SDavid du Colombier
35*25210b06SDavid du Colombier #include "u.h"
36*25210b06SDavid du Colombier #include "../port/lib.h"
37*25210b06SDavid du Colombier #include "mem.h"
38*25210b06SDavid du Colombier #include "dat.h"
39*25210b06SDavid du Colombier #include "fns.h"
40*25210b06SDavid du Colombier #include "io.h"
41*25210b06SDavid du Colombier
42*25210b06SDavid du Colombier /*
43*25210b06SDavid du Colombier * Simple segment descriptors with no translation.
44*25210b06SDavid du Colombier */
45*25210b06SDavid du Colombier #define DATASEGM(p) { 0xFFFF, SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(p)|SEGDATA|SEGW }
46*25210b06SDavid du Colombier #define EXECSEGM(p) { 0xFFFF, SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR }
47*25210b06SDavid du Colombier #define EXEC16SEGM(p) { 0xFFFF, SEGG|(0xF<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR }
48*25210b06SDavid du Colombier #define TSSSEGM(b,p) { ((b)<<16)|sizeof(Tss),\
49*25210b06SDavid du Colombier ((b)&0xFF000000)|(((b)>>16)&0xFF)|SEGTSS|SEGPL(p)|SEGP }
50*25210b06SDavid du Colombier
51*25210b06SDavid du Colombier void realmodeintrinst(void);
52*25210b06SDavid du Colombier void _stop32pg(void);
53*25210b06SDavid du Colombier
54*25210b06SDavid du Colombier Segdesc gdt[NGDT] =
55*25210b06SDavid du Colombier {
56*25210b06SDavid du Colombier [NULLSEG] { 0, 0}, /* null descriptor */
57*25210b06SDavid du Colombier [KDSEG] DATASEGM(0), /* kernel data/stack */
58*25210b06SDavid du Colombier [KESEG] EXECSEGM(0), /* kernel code */
59*25210b06SDavid du Colombier [UDSEG] DATASEGM(3), /* user data/stack */
60*25210b06SDavid du Colombier [UESEG] EXECSEGM(3), /* user code */
61*25210b06SDavid du Colombier [TSSSEG] TSSSEGM(0,0), /* tss segment */
62*25210b06SDavid du Colombier [KESEG16] EXEC16SEGM(0), /* kernel code 16-bit */
63*25210b06SDavid du Colombier };
64*25210b06SDavid du Colombier
65*25210b06SDavid du Colombier static int didmmuinit;
66*25210b06SDavid du Colombier static void taskswitch(ulong, ulong);
67*25210b06SDavid du Colombier static void memglobal(void);
68*25210b06SDavid du Colombier
69*25210b06SDavid du Colombier #define vpt ((ulong*)VPT)
70*25210b06SDavid du Colombier #define VPTX(va) (((ulong)(va))>>12)
71*25210b06SDavid du Colombier #define vpd (vpt+VPTX(VPT))
72*25210b06SDavid du Colombier
73*25210b06SDavid du Colombier void
mmuinit0(void)74*25210b06SDavid du Colombier mmuinit0(void)
75*25210b06SDavid du Colombier {
76*25210b06SDavid du Colombier memmove(m->gdt, gdt, sizeof gdt);
77*25210b06SDavid du Colombier }
78*25210b06SDavid du Colombier
79*25210b06SDavid du Colombier void
mmuinit(void)80*25210b06SDavid du Colombier mmuinit(void)
81*25210b06SDavid du Colombier {
82*25210b06SDavid du Colombier ulong x, *p;
83*25210b06SDavid du Colombier ushort ptr[3];
84*25210b06SDavid du Colombier
85*25210b06SDavid du Colombier didmmuinit = 1;
86*25210b06SDavid du Colombier
87*25210b06SDavid du Colombier if(0) print("vpt=%#.8ux vpd=%#p kmap=%#.8ux\n",
88*25210b06SDavid du Colombier VPT, vpd, KMAP);
89*25210b06SDavid du Colombier
90*25210b06SDavid du Colombier memglobal();
91*25210b06SDavid du Colombier m->pdb[PDX(VPT)] = PADDR(m->pdb)|PTEWRITE|PTEVALID;
92*25210b06SDavid du Colombier
93*25210b06SDavid du Colombier m->tss = malloc(sizeof(Tss));
94*25210b06SDavid du Colombier if(m->tss == nil)
95*25210b06SDavid du Colombier panic("mmuinit: no memory");
96*25210b06SDavid du Colombier memset(m->tss, 0, sizeof(Tss));
97*25210b06SDavid du Colombier m->tss->iomap = 0xDFFF<<16;
98*25210b06SDavid du Colombier
99*25210b06SDavid du Colombier /*
100*25210b06SDavid du Colombier * We used to keep the GDT in the Mach structure, but it
101*25210b06SDavid du Colombier * turns out that that slows down access to the rest of the
102*25210b06SDavid du Colombier * page. Since the Mach structure is accessed quite often,
103*25210b06SDavid du Colombier * it pays off anywhere from a factor of 1.25 to 2 on real
104*25210b06SDavid du Colombier * hardware to separate them (the AMDs are more sensitive
105*25210b06SDavid du Colombier * than Intels in this regard). Under VMware it pays off
106*25210b06SDavid du Colombier * a factor of about 10 to 100.
107*25210b06SDavid du Colombier */
108*25210b06SDavid du Colombier memmove(m->gdt, gdt, sizeof gdt);
109*25210b06SDavid du Colombier x = (ulong)m->tss;
110*25210b06SDavid du Colombier m->gdt[TSSSEG].d0 = (x<<16)|sizeof(Tss);
111*25210b06SDavid du Colombier m->gdt[TSSSEG].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGTSS|SEGPL(0)|SEGP;
112*25210b06SDavid du Colombier
113*25210b06SDavid du Colombier ptr[0] = sizeof(gdt)-1;
114*25210b06SDavid du Colombier x = (ulong)m->gdt;
115*25210b06SDavid du Colombier ptr[1] = x & 0xFFFF;
116*25210b06SDavid du Colombier ptr[2] = (x>>16) & 0xFFFF;
117*25210b06SDavid du Colombier lgdt(ptr);
118*25210b06SDavid du Colombier
119*25210b06SDavid du Colombier ptr[0] = sizeof(Segdesc)*256-1;
120*25210b06SDavid du Colombier x = IDTADDR;
121*25210b06SDavid du Colombier ptr[1] = x & 0xFFFF;
122*25210b06SDavid du Colombier ptr[2] = (x>>16) & 0xFFFF;
123*25210b06SDavid du Colombier lidt(ptr);
124*25210b06SDavid du Colombier
125*25210b06SDavid du Colombier /*
126*25210b06SDavid du Colombier * this kills 9load but not 9boot. 9load dies at the taskswitch.
127*25210b06SDavid du Colombier * should track down exactly why some day.
128*25210b06SDavid du Colombier */
129*25210b06SDavid du Colombier /* make most kernel text unwritable */
130*25210b06SDavid du Colombier if(0) for(x = PGROUND((ulong)_stop32pg); x < (ulong)etext; x += BY2PG){
131*25210b06SDavid du Colombier if (x == (ulong)realmodeintrinst & ~(BY2PG-1))
132*25210b06SDavid du Colombier continue;
133*25210b06SDavid du Colombier p = mmuwalk(m->pdb, x, 2, 0);
134*25210b06SDavid du Colombier if(p == nil)
135*25210b06SDavid du Colombier panic("mmuinit");
136*25210b06SDavid du Colombier *p &= ~PTEWRITE;
137*25210b06SDavid du Colombier }
138*25210b06SDavid du Colombier
139*25210b06SDavid du Colombier taskswitch(PADDR(m->pdb), (ulong)m + MACHSIZE);
140*25210b06SDavid du Colombier ltr(TSSSEL);
141*25210b06SDavid du Colombier }
142*25210b06SDavid du Colombier
143*25210b06SDavid du Colombier /*
144*25210b06SDavid du Colombier * On processors that support it, we set the PTEGLOBAL bit in
145*25210b06SDavid du Colombier * page table and page directory entries that map kernel memory.
146*25210b06SDavid du Colombier * Doing this tells the processor not to bother flushing them
147*25210b06SDavid du Colombier * from the TLB when doing the TLB flush associated with a
148*25210b06SDavid du Colombier * context switch (write to CR3). Since kernel memory mappings
149*25210b06SDavid du Colombier * are never removed, this is safe. (If we ever remove kernel memory
150*25210b06SDavid du Colombier * mappings, we can do a full flush by turning off the PGE bit in CR4,
151*25210b06SDavid du Colombier * writing to CR3, and then turning the PGE bit back on.)
152*25210b06SDavid du Colombier *
153*25210b06SDavid du Colombier * See also mmukmap below.
154*25210b06SDavid du Colombier *
155*25210b06SDavid du Colombier * Processor support for the PTEGLOBAL bit is enabled in devarch.c.
156*25210b06SDavid du Colombier */
157*25210b06SDavid du Colombier static void
memglobal(void)158*25210b06SDavid du Colombier memglobal(void)
159*25210b06SDavid du Colombier {
160*25210b06SDavid du Colombier int i, j;
161*25210b06SDavid du Colombier ulong *pde, *pte;
162*25210b06SDavid du Colombier
163*25210b06SDavid du Colombier /* only need to do this once, on bootstrap processor */
164*25210b06SDavid du Colombier if(m->machno != 0)
165*25210b06SDavid du Colombier return;
166*25210b06SDavid du Colombier
167*25210b06SDavid du Colombier if(!m->havepge)
168*25210b06SDavid du Colombier return;
169*25210b06SDavid du Colombier
170*25210b06SDavid du Colombier pde = m->pdb;
171*25210b06SDavid du Colombier for(i=PDX(KZERO); i<1024; i++){
172*25210b06SDavid du Colombier if(pde[i] & PTEVALID){
173*25210b06SDavid du Colombier pde[i] |= PTEGLOBAL;
174*25210b06SDavid du Colombier if(!(pde[i] & PTESIZE)){
175*25210b06SDavid du Colombier pte = KADDR(pde[i]&~(BY2PG-1));
176*25210b06SDavid du Colombier for(j=0; j<1024; j++)
177*25210b06SDavid du Colombier if(pte[j] & PTEVALID)
178*25210b06SDavid du Colombier pte[j] |= PTEGLOBAL;
179*25210b06SDavid du Colombier }
180*25210b06SDavid du Colombier }
181*25210b06SDavid du Colombier }
182*25210b06SDavid du Colombier }
183*25210b06SDavid du Colombier
184*25210b06SDavid du Colombier /*
185*25210b06SDavid du Colombier * Flush all the user-space and device-mapping mmu info
186*25210b06SDavid du Colombier * for this process, because something has been deleted.
187*25210b06SDavid du Colombier * It will be paged back in on demand.
188*25210b06SDavid du Colombier */
189*25210b06SDavid du Colombier void
flushmmu(void)190*25210b06SDavid du Colombier flushmmu(void)
191*25210b06SDavid du Colombier {
192*25210b06SDavid du Colombier int s;
193*25210b06SDavid du Colombier
194*25210b06SDavid du Colombier s = splhi();
195*25210b06SDavid du Colombier up->newtlb = 1;
196*25210b06SDavid du Colombier mmuswitch(up);
197*25210b06SDavid du Colombier splx(s);
198*25210b06SDavid du Colombier }
199*25210b06SDavid du Colombier
200*25210b06SDavid du Colombier /*
201*25210b06SDavid du Colombier * Flush a single page mapping from the tlb.
202*25210b06SDavid du Colombier */
203*25210b06SDavid du Colombier void
flushpg(ulong va)204*25210b06SDavid du Colombier flushpg(ulong va)
205*25210b06SDavid du Colombier {
206*25210b06SDavid du Colombier if(X86FAMILY(m->cpuidax) >= 4)
207*25210b06SDavid du Colombier invlpg(va);
208*25210b06SDavid du Colombier else
209*25210b06SDavid du Colombier putcr3(getcr3());
210*25210b06SDavid du Colombier }
211*25210b06SDavid du Colombier
212*25210b06SDavid du Colombier /*
213*25210b06SDavid du Colombier * Allocate a new page for a page directory.
214*25210b06SDavid du Colombier * We keep a small cache of pre-initialized
215*25210b06SDavid du Colombier * page directories in each mach.
216*25210b06SDavid du Colombier */
217*25210b06SDavid du Colombier static Page*
mmupdballoc(void)218*25210b06SDavid du Colombier mmupdballoc(void)
219*25210b06SDavid du Colombier {
220*25210b06SDavid du Colombier int s;
221*25210b06SDavid du Colombier Page *page;
222*25210b06SDavid du Colombier ulong *pdb;
223*25210b06SDavid du Colombier
224*25210b06SDavid du Colombier s = splhi();
225*25210b06SDavid du Colombier m->pdballoc++;
226*25210b06SDavid du Colombier if(m->pdbpool == 0){
227*25210b06SDavid du Colombier spllo();
228*25210b06SDavid du Colombier page = newpage(0, 0, 0);
229*25210b06SDavid du Colombier page->va = (ulong)vpd;
230*25210b06SDavid du Colombier splhi();
231*25210b06SDavid du Colombier pdb = tmpmap(page);
232*25210b06SDavid du Colombier memmove(pdb, m->pdb, BY2PG);
233*25210b06SDavid du Colombier pdb[PDX(VPT)] = page->pa|PTEWRITE|PTEVALID; /* set up VPT */
234*25210b06SDavid du Colombier tmpunmap(pdb);
235*25210b06SDavid du Colombier }else{
236*25210b06SDavid du Colombier page = m->pdbpool;
237*25210b06SDavid du Colombier m->pdbpool = page->next;
238*25210b06SDavid du Colombier m->pdbcnt--;
239*25210b06SDavid du Colombier }
240*25210b06SDavid du Colombier splx(s);
241*25210b06SDavid du Colombier return page;
242*25210b06SDavid du Colombier }
243*25210b06SDavid du Colombier
244*25210b06SDavid du Colombier static void
mmupdbfree(Proc * proc,Page * p)245*25210b06SDavid du Colombier mmupdbfree(Proc *proc, Page *p)
246*25210b06SDavid du Colombier {
247*25210b06SDavid du Colombier if(islo())
248*25210b06SDavid du Colombier panic("mmupdbfree: islo");
249*25210b06SDavid du Colombier m->pdbfree++;
250*25210b06SDavid du Colombier if(m->pdbcnt >= 10){
251*25210b06SDavid du Colombier p->next = proc->mmufree;
252*25210b06SDavid du Colombier proc->mmufree = p;
253*25210b06SDavid du Colombier }else{
254*25210b06SDavid du Colombier p->next = m->pdbpool;
255*25210b06SDavid du Colombier m->pdbpool = p;
256*25210b06SDavid du Colombier m->pdbcnt++;
257*25210b06SDavid du Colombier }
258*25210b06SDavid du Colombier }
259*25210b06SDavid du Colombier
260*25210b06SDavid du Colombier /*
261*25210b06SDavid du Colombier * A user-space memory segment has been deleted, or the
262*25210b06SDavid du Colombier * process is exiting. Clear all the pde entries for user-space
263*25210b06SDavid du Colombier * memory mappings and device mappings. Any entries that
264*25210b06SDavid du Colombier * are needed will be paged back in as necessary.
265*25210b06SDavid du Colombier */
266*25210b06SDavid du Colombier static void
mmuptefree(Proc * proc)267*25210b06SDavid du Colombier mmuptefree(Proc* proc)
268*25210b06SDavid du Colombier {
269*25210b06SDavid du Colombier int s;
270*25210b06SDavid du Colombier ulong *pdb;
271*25210b06SDavid du Colombier Page **last, *page;
272*25210b06SDavid du Colombier
273*25210b06SDavid du Colombier if(proc->mmupdb == nil || proc->mmuused == nil)
274*25210b06SDavid du Colombier return;
275*25210b06SDavid du Colombier s = splhi();
276*25210b06SDavid du Colombier pdb = tmpmap(proc->mmupdb);
277*25210b06SDavid du Colombier last = &proc->mmuused;
278*25210b06SDavid du Colombier for(page = *last; page; page = page->next){
279*25210b06SDavid du Colombier pdb[page->daddr] = 0;
280*25210b06SDavid du Colombier last = &page->next;
281*25210b06SDavid du Colombier }
282*25210b06SDavid du Colombier tmpunmap(pdb);
283*25210b06SDavid du Colombier splx(s);
284*25210b06SDavid du Colombier *last = proc->mmufree;
285*25210b06SDavid du Colombier proc->mmufree = proc->mmuused;
286*25210b06SDavid du Colombier proc->mmuused = 0;
287*25210b06SDavid du Colombier }
288*25210b06SDavid du Colombier
289*25210b06SDavid du Colombier static void
taskswitch(ulong pdb,ulong stack)290*25210b06SDavid du Colombier taskswitch(ulong pdb, ulong stack)
291*25210b06SDavid du Colombier {
292*25210b06SDavid du Colombier Tss *tss;
293*25210b06SDavid du Colombier
294*25210b06SDavid du Colombier tss = m->tss;
295*25210b06SDavid du Colombier tss->ss0 = KDSEL;
296*25210b06SDavid du Colombier tss->esp0 = stack;
297*25210b06SDavid du Colombier tss->ss1 = KDSEL;
298*25210b06SDavid du Colombier tss->esp1 = stack;
299*25210b06SDavid du Colombier tss->ss2 = KDSEL;
300*25210b06SDavid du Colombier tss->esp2 = stack;
301*25210b06SDavid du Colombier putcr3(pdb);
302*25210b06SDavid du Colombier }
303*25210b06SDavid du Colombier
304*25210b06SDavid du Colombier void
mmuswitch(Proc * proc)305*25210b06SDavid du Colombier mmuswitch(Proc* proc)
306*25210b06SDavid du Colombier {
307*25210b06SDavid du Colombier ulong *pdb;
308*25210b06SDavid du Colombier
309*25210b06SDavid du Colombier if(proc->newtlb){
310*25210b06SDavid du Colombier mmuptefree(proc);
311*25210b06SDavid du Colombier proc->newtlb = 0;
312*25210b06SDavid du Colombier }
313*25210b06SDavid du Colombier
314*25210b06SDavid du Colombier if(proc->mmupdb){
315*25210b06SDavid du Colombier pdb = tmpmap(proc->mmupdb);
316*25210b06SDavid du Colombier pdb[PDX(MACHADDR)] = m->pdb[PDX(MACHADDR)];
317*25210b06SDavid du Colombier tmpunmap(pdb);
318*25210b06SDavid du Colombier taskswitch(proc->mmupdb->pa, (ulong)(proc->kstack+KSTACK));
319*25210b06SDavid du Colombier }else
320*25210b06SDavid du Colombier taskswitch(PADDR(m->pdb), (ulong)(proc->kstack+KSTACK));
321*25210b06SDavid du Colombier }
322*25210b06SDavid du Colombier
323*25210b06SDavid du Colombier /*
324*25210b06SDavid du Colombier * Release any pages allocated for a page directory base or page-tables
325*25210b06SDavid du Colombier * for this process:
326*25210b06SDavid du Colombier * switch to the prototype pdb for this processor (m->pdb);
327*25210b06SDavid du Colombier * call mmuptefree() to place all pages used for page-tables (proc->mmuused)
328*25210b06SDavid du Colombier * onto the process' free list (proc->mmufree). This has the side-effect of
329*25210b06SDavid du Colombier * cleaning any user entries in the pdb (proc->mmupdb);
330*25210b06SDavid du Colombier * if there's a pdb put it in the cache of pre-initialised pdb's
331*25210b06SDavid du Colombier * for this processor (m->pdbpool) or on the process' free list;
332*25210b06SDavid du Colombier * finally, place any pages freed back into the free pool (palloc).
333*25210b06SDavid du Colombier * This routine is only called from schedinit() with palloc locked.
334*25210b06SDavid du Colombier */
335*25210b06SDavid du Colombier void
mmurelease(Proc * proc)336*25210b06SDavid du Colombier mmurelease(Proc* proc)
337*25210b06SDavid du Colombier {
338*25210b06SDavid du Colombier Page *page, *next;
339*25210b06SDavid du Colombier ulong *pdb;
340*25210b06SDavid du Colombier
341*25210b06SDavid du Colombier if(islo())
342*25210b06SDavid du Colombier panic("mmurelease: islo");
343*25210b06SDavid du Colombier taskswitch(PADDR(m->pdb), (ulong)m + BY2PG);
344*25210b06SDavid du Colombier if(proc->kmaptable){
345*25210b06SDavid du Colombier if(proc->mmupdb == nil)
346*25210b06SDavid du Colombier panic("mmurelease: no mmupdb");
347*25210b06SDavid du Colombier if(--proc->kmaptable->ref)
348*25210b06SDavid du Colombier panic("mmurelease: kmap ref %d", proc->kmaptable->ref);
349*25210b06SDavid du Colombier if(proc->nkmap)
350*25210b06SDavid du Colombier panic("mmurelease: nkmap %d", proc->nkmap);
351*25210b06SDavid du Colombier /*
352*25210b06SDavid du Colombier * remove kmaptable from pdb before putting pdb up for reuse.
353*25210b06SDavid du Colombier */
354*25210b06SDavid du Colombier pdb = tmpmap(proc->mmupdb);
355*25210b06SDavid du Colombier if(PPN(pdb[PDX(KMAP)]) != proc->kmaptable->pa)
356*25210b06SDavid du Colombier panic("mmurelease: bad kmap pde %#.8lux kmap %#.8lux",
357*25210b06SDavid du Colombier pdb[PDX(KMAP)], proc->kmaptable->pa);
358*25210b06SDavid du Colombier pdb[PDX(KMAP)] = 0;
359*25210b06SDavid du Colombier tmpunmap(pdb);
360*25210b06SDavid du Colombier /*
361*25210b06SDavid du Colombier * move kmaptable to free list.
362*25210b06SDavid du Colombier */
363*25210b06SDavid du Colombier pagechainhead(proc->kmaptable);
364*25210b06SDavid du Colombier proc->kmaptable = 0;
365*25210b06SDavid du Colombier }
366*25210b06SDavid du Colombier if(proc->mmupdb){
367*25210b06SDavid du Colombier mmuptefree(proc);
368*25210b06SDavid du Colombier mmupdbfree(proc, proc->mmupdb);
369*25210b06SDavid du Colombier proc->mmupdb = 0;
370*25210b06SDavid du Colombier }
371*25210b06SDavid du Colombier for(page = proc->mmufree; page; page = next){
372*25210b06SDavid du Colombier next = page->next;
373*25210b06SDavid du Colombier if(--page->ref)
374*25210b06SDavid du Colombier panic("mmurelease: page->ref %d", page->ref);
375*25210b06SDavid du Colombier pagechainhead(page);
376*25210b06SDavid du Colombier }
377*25210b06SDavid du Colombier if(proc->mmufree && palloc.r.p)
378*25210b06SDavid du Colombier wakeup(&palloc.r);
379*25210b06SDavid du Colombier proc->mmufree = 0;
380*25210b06SDavid du Colombier }
381*25210b06SDavid du Colombier
382*25210b06SDavid du Colombier /*
383*25210b06SDavid du Colombier * Allocate and install pdb for the current process.
384*25210b06SDavid du Colombier */
385*25210b06SDavid du Colombier static void
upallocpdb(void)386*25210b06SDavid du Colombier upallocpdb(void)
387*25210b06SDavid du Colombier {
388*25210b06SDavid du Colombier int s;
389*25210b06SDavid du Colombier ulong *pdb;
390*25210b06SDavid du Colombier Page *page;
391*25210b06SDavid du Colombier
392*25210b06SDavid du Colombier if(up->mmupdb != nil)
393*25210b06SDavid du Colombier return;
394*25210b06SDavid du Colombier page = mmupdballoc();
395*25210b06SDavid du Colombier s = splhi();
396*25210b06SDavid du Colombier if(up->mmupdb != nil){
397*25210b06SDavid du Colombier /*
398*25210b06SDavid du Colombier * Perhaps we got an interrupt while
399*25210b06SDavid du Colombier * mmupdballoc was sleeping and that
400*25210b06SDavid du Colombier * interrupt allocated an mmupdb?
401*25210b06SDavid du Colombier * Seems unlikely.
402*25210b06SDavid du Colombier */
403*25210b06SDavid du Colombier mmupdbfree(up, page);
404*25210b06SDavid du Colombier splx(s);
405*25210b06SDavid du Colombier return;
406*25210b06SDavid du Colombier }
407*25210b06SDavid du Colombier pdb = tmpmap(page);
408*25210b06SDavid du Colombier pdb[PDX(MACHADDR)] = m->pdb[PDX(MACHADDR)];
409*25210b06SDavid du Colombier tmpunmap(pdb);
410*25210b06SDavid du Colombier up->mmupdb = page;
411*25210b06SDavid du Colombier putcr3(up->mmupdb->pa);
412*25210b06SDavid du Colombier splx(s);
413*25210b06SDavid du Colombier }
414*25210b06SDavid du Colombier
415*25210b06SDavid du Colombier /*
416*25210b06SDavid du Colombier * Update the mmu in response to a user fault. pa may have PTEWRITE set.
417*25210b06SDavid du Colombier */
418*25210b06SDavid du Colombier void
putmmu(ulong va,ulong pa,Page *)419*25210b06SDavid du Colombier putmmu(ulong va, ulong pa, Page*)
420*25210b06SDavid du Colombier {
421*25210b06SDavid du Colombier int old, s;
422*25210b06SDavid du Colombier Page *page;
423*25210b06SDavid du Colombier
424*25210b06SDavid du Colombier if(up->mmupdb == nil)
425*25210b06SDavid du Colombier upallocpdb();
426*25210b06SDavid du Colombier
427*25210b06SDavid du Colombier /*
428*25210b06SDavid du Colombier * We should be able to get through this with interrupts
429*25210b06SDavid du Colombier * turned on (if we get interrupted we'll just pick up
430*25210b06SDavid du Colombier * where we left off) but we get many faults accessing
431*25210b06SDavid du Colombier * vpt[] near the end of this function, and they always happen
432*25210b06SDavid du Colombier * after the process has been switched out and then
433*25210b06SDavid du Colombier * switched back, usually many times in a row (perhaps
434*25210b06SDavid du Colombier * it cannot switch back successfully for some reason).
435*25210b06SDavid du Colombier *
436*25210b06SDavid du Colombier * In any event, I'm tired of searching for this bug.
437*25210b06SDavid du Colombier * Turn off interrupts during putmmu even though
438*25210b06SDavid du Colombier * we shouldn't need to. - rsc
439*25210b06SDavid du Colombier */
440*25210b06SDavid du Colombier
441*25210b06SDavid du Colombier s = splhi();
442*25210b06SDavid du Colombier if(!(vpd[PDX(va)]&PTEVALID)){
443*25210b06SDavid du Colombier if(up->mmufree == 0){
444*25210b06SDavid du Colombier spllo();
445*25210b06SDavid du Colombier page = newpage(0, 0, 0);
446*25210b06SDavid du Colombier splhi();
447*25210b06SDavid du Colombier }
448*25210b06SDavid du Colombier else{
449*25210b06SDavid du Colombier page = up->mmufree;
450*25210b06SDavid du Colombier up->mmufree = page->next;
451*25210b06SDavid du Colombier }
452*25210b06SDavid du Colombier vpd[PDX(va)] = PPN(page->pa)|PTEUSER|PTEWRITE|PTEVALID;
453*25210b06SDavid du Colombier /* page is now mapped into the VPT - clear it */
454*25210b06SDavid du Colombier memset((void*)(VPT+PDX(va)*BY2PG), 0, BY2PG);
455*25210b06SDavid du Colombier page->daddr = PDX(va);
456*25210b06SDavid du Colombier page->next = up->mmuused;
457*25210b06SDavid du Colombier up->mmuused = page;
458*25210b06SDavid du Colombier }
459*25210b06SDavid du Colombier old = vpt[VPTX(va)];
460*25210b06SDavid du Colombier vpt[VPTX(va)] = pa|PTEUSER|PTEVALID;
461*25210b06SDavid du Colombier if(old&PTEVALID)
462*25210b06SDavid du Colombier flushpg(va);
463*25210b06SDavid du Colombier if(getcr3() != up->mmupdb->pa)
464*25210b06SDavid du Colombier print("bad cr3 %#.8lux %#.8lux\n", getcr3(), up->mmupdb->pa);
465*25210b06SDavid du Colombier splx(s);
466*25210b06SDavid du Colombier }
467*25210b06SDavid du Colombier
468*25210b06SDavid du Colombier /*
469*25210b06SDavid du Colombier * Double-check the user MMU.
470*25210b06SDavid du Colombier * Error checking only.
471*25210b06SDavid du Colombier */
472*25210b06SDavid du Colombier void
checkmmu(ulong va,ulong pa)473*25210b06SDavid du Colombier checkmmu(ulong va, ulong pa)
474*25210b06SDavid du Colombier {
475*25210b06SDavid du Colombier if(up->mmupdb == 0)
476*25210b06SDavid du Colombier return;
477*25210b06SDavid du Colombier if(!(vpd[PDX(va)]&PTEVALID) || !(vpt[VPTX(va)]&PTEVALID))
478*25210b06SDavid du Colombier return;
479*25210b06SDavid du Colombier if(PPN(vpt[VPTX(va)]) != pa)
480*25210b06SDavid du Colombier print("%ld %s: va=%#08lux pa=%#08lux pte=%#08lux\n",
481*25210b06SDavid du Colombier up->pid, up->text,
482*25210b06SDavid du Colombier va, pa, vpt[VPTX(va)]);
483*25210b06SDavid du Colombier }
484*25210b06SDavid du Colombier
485*25210b06SDavid du Colombier /*
486*25210b06SDavid du Colombier * Walk the page-table pointed to by pdb and return a pointer
487*25210b06SDavid du Colombier * to the entry for virtual address va at the requested level.
488*25210b06SDavid du Colombier * If the entry is invalid and create isn't requested then bail
489*25210b06SDavid du Colombier * out early. Otherwise, for the 2nd level walk, allocate a new
490*25210b06SDavid du Colombier * page-table page and register it in the 1st level. This is used
491*25210b06SDavid du Colombier * only to edit kernel mappings, which use pages from kernel memory,
492*25210b06SDavid du Colombier * so it's okay to use KADDR to look at the tables.
493*25210b06SDavid du Colombier */
494*25210b06SDavid du Colombier ulong*
mmuwalk(ulong * pdb,ulong va,int level,int create)495*25210b06SDavid du Colombier mmuwalk(ulong* pdb, ulong va, int level, int create)
496*25210b06SDavid du Colombier {
497*25210b06SDavid du Colombier ulong *table;
498*25210b06SDavid du Colombier void *map;
499*25210b06SDavid du Colombier
500*25210b06SDavid du Colombier table = &pdb[PDX(va)];
501*25210b06SDavid du Colombier if(!(*table & PTEVALID) && create == 0)
502*25210b06SDavid du Colombier return 0;
503*25210b06SDavid du Colombier
504*25210b06SDavid du Colombier switch(level){
505*25210b06SDavid du Colombier
506*25210b06SDavid du Colombier default:
507*25210b06SDavid du Colombier return 0;
508*25210b06SDavid du Colombier
509*25210b06SDavid du Colombier case 1:
510*25210b06SDavid du Colombier return table;
511*25210b06SDavid du Colombier
512*25210b06SDavid du Colombier case 2:
513*25210b06SDavid du Colombier if(*table & PTESIZE)
514*25210b06SDavid du Colombier panic("mmuwalk2: va %luX entry %luX", va, *table);
515*25210b06SDavid du Colombier if(!(*table & PTEVALID)){
516*25210b06SDavid du Colombier /*
517*25210b06SDavid du Colombier * Have to call low-level allocator from
518*25210b06SDavid du Colombier * memory.c if we haven't set up the xalloc
519*25210b06SDavid du Colombier * tables yet.
520*25210b06SDavid du Colombier */
521*25210b06SDavid du Colombier if(didmmuinit)
522*25210b06SDavid du Colombier map = xspanalloc(BY2PG, BY2PG, 0);
523*25210b06SDavid du Colombier else
524*25210b06SDavid du Colombier map = rampage();
525*25210b06SDavid du Colombier if(map == nil)
526*25210b06SDavid du Colombier panic("mmuwalk xspanalloc failed");
527*25210b06SDavid du Colombier *table = PADDR(map)|PTEWRITE|PTEVALID;
528*25210b06SDavid du Colombier }
529*25210b06SDavid du Colombier table = KADDR(PPN(*table));
530*25210b06SDavid du Colombier return &table[PTX(va)];
531*25210b06SDavid du Colombier }
532*25210b06SDavid du Colombier }
533*25210b06SDavid du Colombier
534*25210b06SDavid du Colombier /*
535*25210b06SDavid du Colombier * Device mappings are shared by all procs and processors and
536*25210b06SDavid du Colombier * live in the virtual range VMAP to VMAP+VMAPSIZE. The master
537*25210b06SDavid du Colombier * copy of the mappings is stored in mach0->pdb, and they are
538*25210b06SDavid du Colombier * paged in from there as necessary by vmapsync during faults.
539*25210b06SDavid du Colombier */
540*25210b06SDavid du Colombier
541*25210b06SDavid du Colombier static Lock vmaplock;
542*25210b06SDavid du Colombier
543*25210b06SDavid du Colombier static int findhole(ulong *a, int n, int count);
544*25210b06SDavid du Colombier static ulong vmapalloc(ulong size);
545*25210b06SDavid du Colombier static void pdbunmap(ulong*, ulong, int);
546*25210b06SDavid du Colombier
547*25210b06SDavid du Colombier /*
548*25210b06SDavid du Colombier * Add a device mapping to the vmap range.
549*25210b06SDavid du Colombier */
550*25210b06SDavid du Colombier void*
vmap(ulong pa,int size)551*25210b06SDavid du Colombier vmap(ulong pa, int size)
552*25210b06SDavid du Colombier {
553*25210b06SDavid du Colombier int osize;
554*25210b06SDavid du Colombier ulong o, va;
555*25210b06SDavid du Colombier
556*25210b06SDavid du Colombier /*
557*25210b06SDavid du Colombier * might be asking for less than a page.
558*25210b06SDavid du Colombier */
559*25210b06SDavid du Colombier osize = size;
560*25210b06SDavid du Colombier o = pa & (BY2PG-1);
561*25210b06SDavid du Colombier pa -= o;
562*25210b06SDavid du Colombier size += o;
563*25210b06SDavid du Colombier
564*25210b06SDavid du Colombier size = ROUND(size, BY2PG);
565*25210b06SDavid du Colombier if(pa == 0){
566*25210b06SDavid du Colombier print("vmap pa=0 pc=%#p\n", getcallerpc(&pa));
567*25210b06SDavid du Colombier return nil;
568*25210b06SDavid du Colombier }
569*25210b06SDavid du Colombier ilock(&vmaplock);
570*25210b06SDavid du Colombier if((va = vmapalloc(size)) == 0
571*25210b06SDavid du Colombier || pdbmap(MACHP(0)->pdb, pa|PTEUNCACHED|PTEWRITE, va, size) < 0){
572*25210b06SDavid du Colombier iunlock(&vmaplock);
573*25210b06SDavid du Colombier return 0;
574*25210b06SDavid du Colombier }
575*25210b06SDavid du Colombier iunlock(&vmaplock);
576*25210b06SDavid du Colombier /* avoid trap on local processor
577*25210b06SDavid du Colombier for(i=0; i<size; i+=4*MB)
578*25210b06SDavid du Colombier vmapsync(va+i);
579*25210b06SDavid du Colombier */
580*25210b06SDavid du Colombier USED(osize);
581*25210b06SDavid du Colombier // print(" vmap %#.8lux %d => %#.8lux\n", pa+o, osize, va+o);
582*25210b06SDavid du Colombier return (void*)(va + o);
583*25210b06SDavid du Colombier }
584*25210b06SDavid du Colombier
585*25210b06SDavid du Colombier static int
findhole(ulong * a,int n,int count)586*25210b06SDavid du Colombier findhole(ulong *a, int n, int count)
587*25210b06SDavid du Colombier {
588*25210b06SDavid du Colombier int have, i;
589*25210b06SDavid du Colombier
590*25210b06SDavid du Colombier have = 0;
591*25210b06SDavid du Colombier for(i=0; i<n; i++){
592*25210b06SDavid du Colombier if(a[i] == 0)
593*25210b06SDavid du Colombier have++;
594*25210b06SDavid du Colombier else
595*25210b06SDavid du Colombier have = 0;
596*25210b06SDavid du Colombier if(have >= count)
597*25210b06SDavid du Colombier return i+1 - have;
598*25210b06SDavid du Colombier }
599*25210b06SDavid du Colombier return -1;
600*25210b06SDavid du Colombier }
601*25210b06SDavid du Colombier
602*25210b06SDavid du Colombier /*
603*25210b06SDavid du Colombier * Look for free space in the vmap.
604*25210b06SDavid du Colombier */
605*25210b06SDavid du Colombier static ulong
vmapalloc(ulong size)606*25210b06SDavid du Colombier vmapalloc(ulong size)
607*25210b06SDavid du Colombier {
608*25210b06SDavid du Colombier int i, n, o;
609*25210b06SDavid du Colombier ulong *vpdb;
610*25210b06SDavid du Colombier int vpdbsize;
611*25210b06SDavid du Colombier
612*25210b06SDavid du Colombier vpdb = &MACHP(0)->pdb[PDX(VMAP)];
613*25210b06SDavid du Colombier vpdbsize = VMAPSIZE/(4*MB);
614*25210b06SDavid du Colombier
615*25210b06SDavid du Colombier if(size >= 4*MB){
616*25210b06SDavid du Colombier n = (size+4*MB-1) / (4*MB);
617*25210b06SDavid du Colombier if((o = findhole(vpdb, vpdbsize, n)) != -1)
618*25210b06SDavid du Colombier return VMAP + o*4*MB;
619*25210b06SDavid du Colombier return 0;
620*25210b06SDavid du Colombier }
621*25210b06SDavid du Colombier n = (size+BY2PG-1) / BY2PG;
622*25210b06SDavid du Colombier for(i=0; i<vpdbsize; i++)
623*25210b06SDavid du Colombier if((vpdb[i]&PTEVALID) && !(vpdb[i]&PTESIZE))
624*25210b06SDavid du Colombier if((o = findhole(KADDR(PPN(vpdb[i])), WD2PG, n)) != -1)
625*25210b06SDavid du Colombier return VMAP + i*4*MB + o*BY2PG;
626*25210b06SDavid du Colombier if((o = findhole(vpdb, vpdbsize, 1)) != -1)
627*25210b06SDavid du Colombier return VMAP + o*4*MB;
628*25210b06SDavid du Colombier
629*25210b06SDavid du Colombier /*
630*25210b06SDavid du Colombier * could span page directory entries, but not worth the trouble.
631*25210b06SDavid du Colombier * not going to be very much contention.
632*25210b06SDavid du Colombier */
633*25210b06SDavid du Colombier return 0;
634*25210b06SDavid du Colombier }
635*25210b06SDavid du Colombier
636*25210b06SDavid du Colombier /*
637*25210b06SDavid du Colombier * Remove a device mapping from the vmap range.
638*25210b06SDavid du Colombier * Since pdbunmap does not remove page tables, just entries,
639*25210b06SDavid du Colombier * the call need not be interlocked with vmap.
640*25210b06SDavid du Colombier */
641*25210b06SDavid du Colombier void
vunmap(void * v,int size)642*25210b06SDavid du Colombier vunmap(void *v, int size)
643*25210b06SDavid du Colombier {
644*25210b06SDavid du Colombier int i;
645*25210b06SDavid du Colombier ulong va, o;
646*25210b06SDavid du Colombier Mach *nm;
647*25210b06SDavid du Colombier Proc *p;
648*25210b06SDavid du Colombier
649*25210b06SDavid du Colombier /*
650*25210b06SDavid du Colombier * might not be aligned
651*25210b06SDavid du Colombier */
652*25210b06SDavid du Colombier va = (ulong)v;
653*25210b06SDavid du Colombier o = va&(BY2PG-1);
654*25210b06SDavid du Colombier va -= o;
655*25210b06SDavid du Colombier size += o;
656*25210b06SDavid du Colombier size = ROUND(size, BY2PG);
657*25210b06SDavid du Colombier
658*25210b06SDavid du Colombier if(size < 0 || va < VMAP || va+size > VMAP+VMAPSIZE)
659*25210b06SDavid du Colombier panic("vunmap va=%#.8lux size=%#x pc=%#.8lux",
660*25210b06SDavid du Colombier va, size, getcallerpc(&v));
661*25210b06SDavid du Colombier
662*25210b06SDavid du Colombier pdbunmap(MACHP(0)->pdb, va, size);
663*25210b06SDavid du Colombier
664*25210b06SDavid du Colombier /*
665*25210b06SDavid du Colombier * Flush mapping from all the tlbs and copied pdbs.
666*25210b06SDavid du Colombier * This can be (and is) slow, since it is called only rarely.
667*25210b06SDavid du Colombier * It is possible for vunmap to be called with up == nil,
668*25210b06SDavid du Colombier * e.g. from the reset/init driver routines during system
669*25210b06SDavid du Colombier * boot. In that case it suffices to flush the MACH(0) TLB
670*25210b06SDavid du Colombier * and return.
671*25210b06SDavid du Colombier */
672*25210b06SDavid du Colombier if(!active.thunderbirdsarego){
673*25210b06SDavid du Colombier if(MACHP(0)->pdb == 0)
674*25210b06SDavid du Colombier panic("vunmap: nil m->pdb pc=%#p", getcallerpc(&v));
675*25210b06SDavid du Colombier if(PADDR(MACHP(0)->pdb) == 0)
676*25210b06SDavid du Colombier panic("vunmap: nil PADDR(m->pdb)");
677*25210b06SDavid du Colombier putcr3(PADDR(MACHP(0)->pdb));
678*25210b06SDavid du Colombier return;
679*25210b06SDavid du Colombier }
680*25210b06SDavid du Colombier for(i=0; i<conf.nproc; i++){
681*25210b06SDavid du Colombier p = proctab(i);
682*25210b06SDavid du Colombier if(p->state == Dead)
683*25210b06SDavid du Colombier continue;
684*25210b06SDavid du Colombier if(p != up)
685*25210b06SDavid du Colombier p->newtlb = 1;
686*25210b06SDavid du Colombier }
687*25210b06SDavid du Colombier for(i=0; i<conf.nmach; i++){
688*25210b06SDavid du Colombier nm = MACHP(i);
689*25210b06SDavid du Colombier if(nm != m)
690*25210b06SDavid du Colombier nm->flushmmu = 1;
691*25210b06SDavid du Colombier }
692*25210b06SDavid du Colombier flushmmu();
693*25210b06SDavid du Colombier for(i=0; i<conf.nmach; i++){
694*25210b06SDavid du Colombier nm = MACHP(i);
695*25210b06SDavid du Colombier if(nm != m)
696*25210b06SDavid du Colombier while((active.machs&(1<<nm->machno)) && nm->flushmmu)
697*25210b06SDavid du Colombier ;
698*25210b06SDavid du Colombier }
699*25210b06SDavid du Colombier }
700*25210b06SDavid du Colombier
701*25210b06SDavid du Colombier /*
702*25210b06SDavid du Colombier * Add kernel mappings for va -> pa for a section of size bytes.
703*25210b06SDavid du Colombier */
704*25210b06SDavid du Colombier int
pdbmap(ulong * pdb,ulong pa,ulong va,int size)705*25210b06SDavid du Colombier pdbmap(ulong *pdb, ulong pa, ulong va, int size)
706*25210b06SDavid du Colombier {
707*25210b06SDavid du Colombier int pse;
708*25210b06SDavid du Colombier ulong pgsz, *pte, *table;
709*25210b06SDavid du Colombier ulong flag, off;
710*25210b06SDavid du Colombier
711*25210b06SDavid du Colombier flag = pa&0xFFF;
712*25210b06SDavid du Colombier pa &= ~0xFFF;
713*25210b06SDavid du Colombier
714*25210b06SDavid du Colombier if((MACHP(0)->cpuiddx & 0x08) && (getcr4() & 0x10))
715*25210b06SDavid du Colombier pse = 1;
716*25210b06SDavid du Colombier else
717*25210b06SDavid du Colombier pse = 0;
718*25210b06SDavid du Colombier
719*25210b06SDavid du Colombier for(off=0; off<size; off+=pgsz){
720*25210b06SDavid du Colombier table = &pdb[PDX(va+off)];
721*25210b06SDavid du Colombier if((*table&PTEVALID) && (*table&PTESIZE))
722*25210b06SDavid du Colombier panic("vmap: pdb pte valid and big page: "
723*25210b06SDavid du Colombier "va=%#.8lux pa=%#.8lux pde=%#.8lux",
724*25210b06SDavid du Colombier va+off, pa+off, *table);
725*25210b06SDavid du Colombier
726*25210b06SDavid du Colombier /*
727*25210b06SDavid du Colombier * Check if it can be mapped using a 4MB page:
728*25210b06SDavid du Colombier * va, pa aligned and size >= 4MB and processor can do it.
729*25210b06SDavid du Colombier */
730*25210b06SDavid du Colombier if(pse && (pa+off)%(4*MB) == 0 && (va+off)%(4*MB) == 0 &&
731*25210b06SDavid du Colombier (size-off) >= 4*MB){
732*25210b06SDavid du Colombier *table = (pa+off)|flag|PTESIZE|PTEVALID;
733*25210b06SDavid du Colombier pgsz = 4*MB;
734*25210b06SDavid du Colombier }else{
735*25210b06SDavid du Colombier pte = mmuwalk(pdb, va+off, 2, 1);
736*25210b06SDavid du Colombier if(*pte&PTEVALID)
737*25210b06SDavid du Colombier panic("vmap: va=%#.8lux pa=%#.8lux pte=%#.8lux",
738*25210b06SDavid du Colombier va+off, pa+off, *pte);
739*25210b06SDavid du Colombier *pte = (pa+off)|flag|PTEVALID;
740*25210b06SDavid du Colombier pgsz = BY2PG;
741*25210b06SDavid du Colombier }
742*25210b06SDavid du Colombier }
743*25210b06SDavid du Colombier return 0;
744*25210b06SDavid du Colombier }
745*25210b06SDavid du Colombier
746*25210b06SDavid du Colombier /*
747*25210b06SDavid du Colombier * Remove mappings. Must already exist, for sanity.
748*25210b06SDavid du Colombier * Only used for kernel mappings, so okay to use KADDR.
749*25210b06SDavid du Colombier */
750*25210b06SDavid du Colombier static void
pdbunmap(ulong * pdb,ulong va,int size)751*25210b06SDavid du Colombier pdbunmap(ulong *pdb, ulong va, int size)
752*25210b06SDavid du Colombier {
753*25210b06SDavid du Colombier ulong vae;
754*25210b06SDavid du Colombier ulong *table;
755*25210b06SDavid du Colombier
756*25210b06SDavid du Colombier vae = va+size;
757*25210b06SDavid du Colombier while(va < vae){
758*25210b06SDavid du Colombier table = &pdb[PDX(va)];
759*25210b06SDavid du Colombier if(!(*table & PTEVALID)){
760*25210b06SDavid du Colombier panic("vunmap: not mapped");
761*25210b06SDavid du Colombier /*
762*25210b06SDavid du Colombier va = (va+4*MB-1) & ~(4*MB-1);
763*25210b06SDavid du Colombier continue;
764*25210b06SDavid du Colombier */
765*25210b06SDavid du Colombier }
766*25210b06SDavid du Colombier if(*table & PTESIZE){
767*25210b06SDavid du Colombier *table = 0;
768*25210b06SDavid du Colombier va = (va+4*MB-1) & ~(4*MB-1);
769*25210b06SDavid du Colombier continue;
770*25210b06SDavid du Colombier }
771*25210b06SDavid du Colombier table = KADDR(PPN(*table));
772*25210b06SDavid du Colombier if(!(table[PTX(va)] & PTEVALID))
773*25210b06SDavid du Colombier panic("vunmap: not mapped");
774*25210b06SDavid du Colombier table[PTX(va)] = 0;
775*25210b06SDavid du Colombier va += BY2PG;
776*25210b06SDavid du Colombier }
777*25210b06SDavid du Colombier }
778*25210b06SDavid du Colombier
779*25210b06SDavid du Colombier /*
780*25210b06SDavid du Colombier * Handle a fault by bringing vmap up to date.
781*25210b06SDavid du Colombier * Only copy pdb entries and they never go away,
782*25210b06SDavid du Colombier * so no locking needed.
783*25210b06SDavid du Colombier */
784*25210b06SDavid du Colombier int
vmapsync(ulong va)785*25210b06SDavid du Colombier vmapsync(ulong va)
786*25210b06SDavid du Colombier {
787*25210b06SDavid du Colombier ulong entry, *table;
788*25210b06SDavid du Colombier
789*25210b06SDavid du Colombier if(va < VMAP || va >= VMAP+VMAPSIZE)
790*25210b06SDavid du Colombier return 0;
791*25210b06SDavid du Colombier
792*25210b06SDavid du Colombier entry = MACHP(0)->pdb[PDX(va)];
793*25210b06SDavid du Colombier if(!(entry&PTEVALID))
794*25210b06SDavid du Colombier return 0;
795*25210b06SDavid du Colombier if(!(entry&PTESIZE)){
796*25210b06SDavid du Colombier /* make sure entry will help the fault */
797*25210b06SDavid du Colombier table = KADDR(PPN(entry));
798*25210b06SDavid du Colombier if(!(table[PTX(va)]&PTEVALID))
799*25210b06SDavid du Colombier return 0;
800*25210b06SDavid du Colombier }
801*25210b06SDavid du Colombier vpd[PDX(va)] = entry;
802*25210b06SDavid du Colombier /*
803*25210b06SDavid du Colombier * TLB doesn't cache negative results, so no flush needed.
804*25210b06SDavid du Colombier */
805*25210b06SDavid du Colombier return 1;
806*25210b06SDavid du Colombier }
807*25210b06SDavid du Colombier
808*25210b06SDavid du Colombier
809*25210b06SDavid du Colombier /*
810*25210b06SDavid du Colombier * KMap is used to map individual pages into virtual memory.
811*25210b06SDavid du Colombier * It is rare to have more than a few KMaps at a time (in the
812*25210b06SDavid du Colombier * absence of interrupts, only two at a time are ever used,
813*25210b06SDavid du Colombier * but interrupts can stack). The mappings are local to a process,
814*25210b06SDavid du Colombier * so we can use the same range of virtual address space for
815*25210b06SDavid du Colombier * all processes without any coordination.
816*25210b06SDavid du Colombier */
817*25210b06SDavid du Colombier #define kpt (vpt+VPTX(KMAP))
818*25210b06SDavid du Colombier #define NKPT (KMAPSIZE/BY2PG)
819*25210b06SDavid du Colombier
820*25210b06SDavid du Colombier KMap*
kmap(Page * page)821*25210b06SDavid du Colombier kmap(Page *page)
822*25210b06SDavid du Colombier {
823*25210b06SDavid du Colombier int i, o, s;
824*25210b06SDavid du Colombier
825*25210b06SDavid du Colombier if(up == nil)
826*25210b06SDavid du Colombier panic("kmap: up=0 pc=%#.8lux", getcallerpc(&page));
827*25210b06SDavid du Colombier if(up->mmupdb == nil)
828*25210b06SDavid du Colombier upallocpdb();
829*25210b06SDavid du Colombier if(up->nkmap < 0)
830*25210b06SDavid du Colombier panic("kmap %lud %s: nkmap=%d", up->pid, up->text, up->nkmap);
831*25210b06SDavid du Colombier
832*25210b06SDavid du Colombier /*
833*25210b06SDavid du Colombier * Splhi shouldn't be necessary here, but paranoia reigns.
834*25210b06SDavid du Colombier * See comment in putmmu above.
835*25210b06SDavid du Colombier */
836*25210b06SDavid du Colombier s = splhi();
837*25210b06SDavid du Colombier up->nkmap++;
838*25210b06SDavid du Colombier if(!(vpd[PDX(KMAP)]&PTEVALID)){
839*25210b06SDavid du Colombier /* allocate page directory */
840*25210b06SDavid du Colombier if(KMAPSIZE > BY2XPG)
841*25210b06SDavid du Colombier panic("bad kmapsize");
842*25210b06SDavid du Colombier if(up->kmaptable != nil)
843*25210b06SDavid du Colombier panic("kmaptable");
844*25210b06SDavid du Colombier spllo();
845*25210b06SDavid du Colombier up->kmaptable = newpage(0, 0, 0);
846*25210b06SDavid du Colombier splhi();
847*25210b06SDavid du Colombier vpd[PDX(KMAP)] = up->kmaptable->pa|PTEWRITE|PTEVALID;
848*25210b06SDavid du Colombier flushpg((ulong)kpt);
849*25210b06SDavid du Colombier memset(kpt, 0, BY2PG);
850*25210b06SDavid du Colombier kpt[0] = page->pa|PTEWRITE|PTEVALID;
851*25210b06SDavid du Colombier up->lastkmap = 0;
852*25210b06SDavid du Colombier splx(s);
853*25210b06SDavid du Colombier return (KMap*)KMAP;
854*25210b06SDavid du Colombier }
855*25210b06SDavid du Colombier if(up->kmaptable == nil)
856*25210b06SDavid du Colombier panic("no kmaptable");
857*25210b06SDavid du Colombier o = up->lastkmap+1;
858*25210b06SDavid du Colombier for(i=0; i<NKPT; i++){
859*25210b06SDavid du Colombier if(kpt[(i+o)%NKPT] == 0){
860*25210b06SDavid du Colombier o = (i+o)%NKPT;
861*25210b06SDavid du Colombier kpt[o] = page->pa|PTEWRITE|PTEVALID;
862*25210b06SDavid du Colombier up->lastkmap = o;
863*25210b06SDavid du Colombier splx(s);
864*25210b06SDavid du Colombier return (KMap*)(KMAP+o*BY2PG);
865*25210b06SDavid du Colombier }
866*25210b06SDavid du Colombier }
867*25210b06SDavid du Colombier panic("out of kmap");
868*25210b06SDavid du Colombier return nil;
869*25210b06SDavid du Colombier }
870*25210b06SDavid du Colombier
871*25210b06SDavid du Colombier void
kunmap(KMap * k)872*25210b06SDavid du Colombier kunmap(KMap *k)
873*25210b06SDavid du Colombier {
874*25210b06SDavid du Colombier ulong va;
875*25210b06SDavid du Colombier
876*25210b06SDavid du Colombier va = (ulong)k;
877*25210b06SDavid du Colombier if(up->mmupdb == nil || !(vpd[PDX(KMAP)]&PTEVALID))
878*25210b06SDavid du Colombier panic("kunmap: no kmaps");
879*25210b06SDavid du Colombier if(va < KMAP || va >= KMAP+KMAPSIZE)
880*25210b06SDavid du Colombier panic("kunmap: bad address %#.8lux pc=%#p", va, getcallerpc(&k));
881*25210b06SDavid du Colombier if(!(vpt[VPTX(va)]&PTEVALID))
882*25210b06SDavid du Colombier panic("kunmap: not mapped %#.8lux pc=%#p", va, getcallerpc(&k));
883*25210b06SDavid du Colombier up->nkmap--;
884*25210b06SDavid du Colombier if(up->nkmap < 0)
885*25210b06SDavid du Colombier panic("kunmap %lud %s: nkmap=%d", up->pid, up->text, up->nkmap);
886*25210b06SDavid du Colombier vpt[VPTX(va)] = 0;
887*25210b06SDavid du Colombier flushpg(va);
888*25210b06SDavid du Colombier }
889*25210b06SDavid du Colombier
890*25210b06SDavid du Colombier /*
891*25210b06SDavid du Colombier * Temporary one-page mapping used to edit page directories.
892*25210b06SDavid du Colombier *
893*25210b06SDavid du Colombier * The fasttmp #define controls whether the code optimizes
894*25210b06SDavid du Colombier * the case where the page is already mapped in the physical
895*25210b06SDavid du Colombier * memory window.
896*25210b06SDavid du Colombier */
897*25210b06SDavid du Colombier #define fasttmp 1
898*25210b06SDavid du Colombier
899*25210b06SDavid du Colombier void*
tmpmap(Page * p)900*25210b06SDavid du Colombier tmpmap(Page *p)
901*25210b06SDavid du Colombier {
902*25210b06SDavid du Colombier ulong i;
903*25210b06SDavid du Colombier ulong *entry;
904*25210b06SDavid du Colombier
905*25210b06SDavid du Colombier if(islo())
906*25210b06SDavid du Colombier panic("tmpaddr: islo");
907*25210b06SDavid du Colombier
908*25210b06SDavid du Colombier if(fasttmp && p->pa < -KZERO)
909*25210b06SDavid du Colombier return KADDR(p->pa);
910*25210b06SDavid du Colombier
911*25210b06SDavid du Colombier /*
912*25210b06SDavid du Colombier * PDX(TMPADDR) == PDX(MACHADDR), so this
913*25210b06SDavid du Colombier * entry is private to the processor and shared
914*25210b06SDavid du Colombier * between up->mmupdb (if any) and m->pdb.
915*25210b06SDavid du Colombier */
916*25210b06SDavid du Colombier entry = &vpt[VPTX(TMPADDR)];
917*25210b06SDavid du Colombier if(!(*entry&PTEVALID)){
918*25210b06SDavid du Colombier for(i=KZERO; i<=CPU0MACH; i+=BY2PG)
919*25210b06SDavid du Colombier print("%#p: *%#p=%#p (vpt=%#p index=%#p)\n", i, &vpt[VPTX(i)], vpt[VPTX(i)], vpt, VPTX(i));
920*25210b06SDavid du Colombier panic("tmpmap: no entry");
921*25210b06SDavid du Colombier }
922*25210b06SDavid du Colombier if(PPN(*entry) != PPN(TMPADDR-KZERO))
923*25210b06SDavid du Colombier panic("tmpmap: already mapped entry=%#.8lux", *entry);
924*25210b06SDavid du Colombier *entry = p->pa|PTEWRITE|PTEVALID;
925*25210b06SDavid du Colombier flushpg(TMPADDR);
926*25210b06SDavid du Colombier return (void*)TMPADDR;
927*25210b06SDavid du Colombier }
928*25210b06SDavid du Colombier
929*25210b06SDavid du Colombier void
tmpunmap(void * v)930*25210b06SDavid du Colombier tmpunmap(void *v)
931*25210b06SDavid du Colombier {
932*25210b06SDavid du Colombier ulong *entry;
933*25210b06SDavid du Colombier
934*25210b06SDavid du Colombier if(islo())
935*25210b06SDavid du Colombier panic("tmpaddr: islo");
936*25210b06SDavid du Colombier if(fasttmp && (ulong)v >= KZERO && v != (void*)TMPADDR)
937*25210b06SDavid du Colombier return;
938*25210b06SDavid du Colombier if(v != (void*)TMPADDR)
939*25210b06SDavid du Colombier panic("tmpunmap: bad address");
940*25210b06SDavid du Colombier entry = &vpt[VPTX(TMPADDR)];
941*25210b06SDavid du Colombier if(!(*entry&PTEVALID) || PPN(*entry) == PPN(PADDR(TMPADDR)))
942*25210b06SDavid du Colombier panic("tmpmap: not mapped entry=%#.8lux", *entry);
943*25210b06SDavid du Colombier *entry = PPN(TMPADDR-KZERO)|PTEWRITE|PTEVALID;
944*25210b06SDavid du Colombier flushpg(TMPADDR);
945*25210b06SDavid du Colombier }
946*25210b06SDavid du Colombier
947*25210b06SDavid du Colombier /*
948*25210b06SDavid du Colombier * These could go back to being macros once the kernel is debugged,
949*25210b06SDavid du Colombier * but the extra checking is nice to have.
950*25210b06SDavid du Colombier */
951*25210b06SDavid du Colombier void*
kaddr(ulong pa)952*25210b06SDavid du Colombier kaddr(ulong pa)
953*25210b06SDavid du Colombier {
954*25210b06SDavid du Colombier if(pa > (ulong)-KZERO)
955*25210b06SDavid du Colombier panic("kaddr: pa=%#.8lux > -KZERO pc=%#p", pa, getcallerpc(&pa));
956*25210b06SDavid du Colombier return (void*)(pa | KZERO);
957*25210b06SDavid du Colombier }
958*25210b06SDavid du Colombier
959*25210b06SDavid du Colombier ulong
paddr(void * v)960*25210b06SDavid du Colombier paddr(void *v)
961*25210b06SDavid du Colombier {
962*25210b06SDavid du Colombier ulong va;
963*25210b06SDavid du Colombier
964*25210b06SDavid du Colombier va = (ulong)v;
965*25210b06SDavid du Colombier if(va < KZERO)
966*25210b06SDavid du Colombier panic("paddr: va=%#.8lux < KZERO pc=%#p", va, getcallerpc(&v));
967*25210b06SDavid du Colombier return va & ~KSEGM;
968*25210b06SDavid du Colombier }
969*25210b06SDavid du Colombier
970*25210b06SDavid du Colombier /*
971*25210b06SDavid du Colombier * More debugging.
972*25210b06SDavid du Colombier */
973*25210b06SDavid du Colombier void
countpagerefs(ulong * ref,int print)974*25210b06SDavid du Colombier countpagerefs(ulong *ref, int print)
975*25210b06SDavid du Colombier {
976*25210b06SDavid du Colombier USED(ref, print);
977*25210b06SDavid du Colombier }
978*25210b06SDavid du Colombier
979*25210b06SDavid du Colombier void
checkfault(ulong,ulong)980*25210b06SDavid du Colombier checkfault(ulong, ulong)
981*25210b06SDavid du Colombier {
982*25210b06SDavid du Colombier }
983*25210b06SDavid du Colombier
984*25210b06SDavid du Colombier /*
985*25210b06SDavid du Colombier * Return the number of bytes that can be accessed via KADDR(pa).
986*25210b06SDavid du Colombier * If pa is not a valid argument to KADDR, return 0.
987*25210b06SDavid du Colombier */
988*25210b06SDavid du Colombier ulong
cankaddr(ulong pa)989*25210b06SDavid du Colombier cankaddr(ulong pa)
990*25210b06SDavid du Colombier {
991*25210b06SDavid du Colombier if(pa >= -KZERO)
992*25210b06SDavid du Colombier return 0;
993*25210b06SDavid du Colombier return -KZERO - pa;
994*25210b06SDavid du Colombier }
995*25210b06SDavid du Colombier
996