Commit | Line | Data |
---|---|---|
1b68e56d WJ |
1 | /* |
2 | * Copyright (c) 1991 Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * the Systems Programming Group of the University of Utah Computer | |
7 | * Science Department and William Jolitz of UUNET Technologies Inc. | |
8 | * | |
9 | * Redistribution and use in source and binary forms, with or without | |
10 | * modification, are permitted provided that the following conditions | |
11 | * are met: | |
12 | * 1. Redistributions of source code must retain the above copyright | |
13 | * notice, this list of conditions and the following disclaimer. | |
14 | * 2. Redistributions in binary form must reproduce the above copyright | |
15 | * notice, this list of conditions and the following disclaimer in the | |
16 | * documentation and/or other materials provided with the distribution. | |
17 | * 3. All advertising materials mentioning features or use of this software | |
18 | * must display the following acknowledgement: | |
19 | * This product includes software developed by the University of | |
20 | * California, Berkeley and its contributors. | |
21 | * 4. Neither the name of the University nor the names of its contributors | |
22 | * may be used to endorse or promote products derived from this software | |
23 | * without specific prior written permission. | |
24 | * | |
25 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
26 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
27 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
28 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
29 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
30 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
31 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
32 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
33 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
34 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
35 | * SUCH DAMAGE. | |
36 | * | |
37 | * @(#)pmap.c 7.7 (Berkeley) 5/12/91 | |
58c5bdc7 PHK |
38 | * |
39 | * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE | |
40 | * -------------------- ----- ---------------------- | |
41 | * CURRENT PATCH LEVEL: 1 00063 | |
42 | * -------------------- ----- ---------------------- | |
43 | * | |
44 | * 28 Nov 1991 Poul-Henning Kamp Speedup processing. | |
1b68e56d WJ |
45 | */ |
46 | static char rcsid[] = "$Header: /usr/src/sys.386bsd/i386/i386/RCS/pmap.c,v 1.3 92/01/21 14:26:44 william Exp Locker: root $"; | |
47 | ||
48 | /* | |
49 | * Derived from hp300 version by Mike Hibler, this version by William | |
50 | * Jolitz uses a recursive map [a pde points to the page directory] to | |
51 | * map the page tables using the pagetables themselves. This is done to | |
52 | * reduce the impact on kernel virtual memory for lots of sparse address | |
53 | * space, and to reduce the cost of memory to each process. | |
54 | * | |
55 | * Derived from: hp300/@(#)pmap.c 7.1 (Berkeley) 12/5/90 | |
56 | */ | |
57 | ||
58 | /* | |
59 | * Reno i386 version, from Mike Hibler's hp300 version. | |
60 | */ | |
61 | ||
62 | /* | |
63 | * Manages physical address maps. | |
64 | * | |
65 | * In addition to hardware address maps, this | |
66 | * module is called upon to provide software-use-only | |
67 | * maps which may or may not be stored in the same | |
68 | * form as hardware maps. These pseudo-maps are | |
69 | * used to store intermediate results from copy | |
70 | * operations to and from address spaces. | |
71 | * | |
72 | * Since the information managed by this module is | |
73 | * also stored by the logical address mapping module, | |
74 | * this module may throw away valid virtual-to-physical | |
75 | * mappings at almost any time. However, invalidations | |
76 | * of virtual-to-physical mappings must be done as | |
77 | * requested. | |
78 | * | |
79 | * In order to cope with hardware architectures which | |
80 | * make virtual-to-physical map invalidates expensive, | |
81 | * this module may delay invalidate or reduced protection | |
82 | * operations until such time as they are actually | |
83 | * necessary. This module is given full information as | |
84 | * to which processors are currently using which maps, | |
85 | * and to when physical maps must be made correct. | |
86 | */ | |
87 | ||
88 | #include "param.h" | |
89 | #include "proc.h" | |
90 | #include "malloc.h" | |
91 | #include "user.h" | |
92 | ||
93 | #include "vm/vm.h" | |
94 | #include "vm/vm_kern.h" | |
95 | #include "vm/vm_page.h" | |
96 | /*#include "vm/vm_pageout.h"*/ | |
97 | ||
98 | #include "i386/isa/isa.h" | |
99 | ||
100 | /* | |
101 | * Allocate various and sundry SYSMAPs used in the days of old VM | |
102 | * and not yet converted. XXX. | |
103 | */ | |
104 | #define BSDVM_COMPAT 1 | |
105 | ||
106 | #ifdef DEBUG | |
107 | struct { | |
108 | int kernel; /* entering kernel mapping */ | |
109 | int user; /* entering user mapping */ | |
110 | int ptpneeded; /* needed to allocate a PT page */ | |
111 | int pwchange; /* no mapping change, just wiring or protection */ | |
112 | int wchange; /* no mapping change, just wiring */ | |
113 | int mchange; /* was mapped but mapping to different page */ | |
114 | int managed; /* a managed page */ | |
115 | int firstpv; /* first mapping for this PA */ | |
116 | int secondpv; /* second mapping for this PA */ | |
117 | int ci; /* cache inhibited */ | |
118 | int unmanaged; /* not a managed page */ | |
119 | int flushes; /* cache flushes */ | |
120 | } enter_stats; | |
121 | struct { | |
122 | int calls; | |
123 | int removes; | |
124 | int pvfirst; | |
125 | int pvsearch; | |
126 | int ptinvalid; | |
127 | int uflushes; | |
128 | int sflushes; | |
129 | } remove_stats; | |
130 | ||
131 | int debugmap = 0; | |
132 | int pmapdebug = 0 /* 0xffff */; | |
133 | #define PDB_FOLLOW 0x0001 | |
134 | #define PDB_INIT 0x0002 | |
135 | #define PDB_ENTER 0x0004 | |
136 | #define PDB_REMOVE 0x0008 | |
137 | #define PDB_CREATE 0x0010 | |
138 | #define PDB_PTPAGE 0x0020 | |
139 | #define PDB_CACHE 0x0040 | |
140 | #define PDB_BITS 0x0080 | |
141 | #define PDB_COLLECT 0x0100 | |
142 | #define PDB_PROTECT 0x0200 | |
143 | #define PDB_PDRTAB 0x0400 | |
144 | #define PDB_PARANOIA 0x2000 | |
145 | #define PDB_WIRING 0x4000 | |
146 | #define PDB_PVDUMP 0x8000 | |
147 | ||
148 | int pmapvacflush = 0; | |
149 | #define PVF_ENTER 0x01 | |
150 | #define PVF_REMOVE 0x02 | |
151 | #define PVF_PROTECT 0x04 | |
152 | #define PVF_TOTAL 0x80 | |
153 | #endif | |
154 | ||
155 | /* | |
156 | * Get PDEs and PTEs for user/kernel address space | |
157 | */ | |
158 | #define pmap_pde(m, v) (&((m)->pm_pdir[((vm_offset_t)(v) >> PD_SHIFT)&1023])) | |
159 | ||
160 | #define pmap_pte_pa(pte) (*(int *)(pte) & PG_FRAME) | |
161 | ||
162 | #define pmap_pde_v(pte) ((pte)->pd_v) | |
163 | #define pmap_pte_w(pte) ((pte)->pg_w) | |
164 | /* #define pmap_pte_ci(pte) ((pte)->pg_ci) */ | |
165 | #define pmap_pte_m(pte) ((pte)->pg_m) | |
166 | #define pmap_pte_u(pte) ((pte)->pg_u) | |
167 | #define pmap_pte_v(pte) ((pte)->pg_v) | |
168 | #define pmap_pte_set_w(pte, v) ((pte)->pg_w = (v)) | |
169 | #define pmap_pte_set_prot(pte, v) ((pte)->pg_prot = (v)) | |
170 | ||
171 | /* | |
172 | * Given a map and a machine independent protection code, | |
173 | * convert to a vax protection code. | |
174 | */ | |
175 | #define pte_prot(m, p) (protection_codes[p]) | |
176 | int protection_codes[8]; | |
177 | ||
178 | struct pmap kernel_pmap_store; | |
179 | pmap_t kernel_pmap; | |
180 | ||
181 | vm_offset_t avail_start; /* PA of first available physical page */ | |
182 | vm_offset_t avail_end; /* PA of last available physical page */ | |
183 | vm_size_t mem_size; /* memory size in bytes */ | |
184 | vm_offset_t virtual_avail; /* VA of first avail page (after kernel bss)*/ | |
185 | vm_offset_t virtual_end; /* VA of last avail page (end of kernel AS) */ | |
186 | vm_offset_t vm_first_phys; /* PA of first managed page */ | |
187 | vm_offset_t vm_last_phys; /* PA just past last managed page */ | |
188 | int i386pagesperpage; /* PAGE_SIZE / I386_PAGE_SIZE */ | |
189 | boolean_t pmap_initialized = FALSE; /* Has pmap_init completed? */ | |
190 | char *pmap_attributes; /* reference and modify bits */ | |
191 | ||
192 | boolean_t pmap_testbit(); | |
193 | void pmap_clear_modify(); | |
194 | ||
195 | #if BSDVM_COMPAT | |
196 | #include "msgbuf.h" | |
197 | ||
198 | /* | |
199 | * All those kernel PT submaps that BSD is so fond of | |
200 | */ | |
201 | struct pte *CMAP1, *CMAP2, *mmap; | |
202 | caddr_t CADDR1, CADDR2, vmmap; | |
203 | struct pte *msgbufmap; | |
204 | struct msgbuf *msgbufp; | |
205 | #endif | |
206 | ||
207 | /* | |
208 | * Bootstrap the system enough to run with virtual memory. | |
209 | * Map the kernel's code and data, and allocate the system page table. | |
210 | * | |
211 | * On the I386 this is called after mapping has already been enabled | |
212 | * and just syncs the pmap module with what has already been done. | |
213 | * [We can't call it easily with mapping off since the kernel is not | |
214 | * mapped with PA == VA, hence we would have to relocate every address | |
215 | * from the linked base (virtual) address 0xFE000000 to the actual | |
216 | * (physical) address starting relative to 0] | |
217 | */ | |
218 | struct pte *pmap_pte(); | |
219 | ||
220 | void | |
221 | pmap_bootstrap(firstaddr, loadaddr) | |
222 | vm_offset_t firstaddr; | |
223 | vm_offset_t loadaddr; | |
224 | { | |
225 | #if BSDVM_COMPAT | |
226 | vm_offset_t va; | |
227 | struct pte *pte; | |
228 | #endif | |
229 | extern vm_offset_t maxmem, physmem; | |
230 | extern int IdlePTD; | |
231 | ||
232 | avail_start = firstaddr; | |
233 | avail_end = maxmem << PG_SHIFT; | |
234 | ||
235 | /* XXX: allow for msgbuf */ | |
236 | avail_end -= i386_round_page(sizeof(struct msgbuf)); | |
237 | ||
238 | mem_size = physmem << PG_SHIFT; | |
239 | virtual_avail = (vm_offset_t)atdevbase + 0x100000 - 0xa0000 + 10*NBPG; | |
240 | virtual_end = VM_MAX_KERNEL_ADDRESS; | |
241 | i386pagesperpage = PAGE_SIZE / I386_PAGE_SIZE; | |
242 | ||
243 | /* | |
244 | * Initialize protection array. | |
245 | */ | |
246 | i386_protection_init(); | |
247 | ||
248 | /* | |
249 | * The kernel's pmap is statically allocated so we don't | |
250 | * have to use pmap_create, which is unlikely to work | |
251 | * correctly at this part of the boot sequence. | |
252 | */ | |
253 | kernel_pmap = &kernel_pmap_store; | |
254 | ||
255 | #ifdef notdef | |
256 | /* | |
257 | * Create Kernel page directory table and page maps. | |
258 | * [ currently done in locore. i have wild and crazy ideas -wfj ] | |
259 | */ | |
260 | bzero(firstaddr, 4*NBPG); | |
261 | kernel_pmap->pm_pdir = firstaddr + VM_MIN_KERNEL_ADDRESS; | |
262 | kernel_pmap->pm_ptab = firstaddr + VM_MIN_KERNEL_ADDRESS + NBPG; | |
263 | ||
264 | firstaddr += NBPG; | |
265 | for (x = i386_btod(VM_MIN_KERNEL_ADDRESS); | |
266 | x < i386_btod(VM_MIN_KERNEL_ADDRESS)+3; x++) { | |
267 | struct pde *pde; | |
268 | pde = kernel_pmap->pm_pdir + x; | |
269 | *(int *)pde = firstaddr + x*NBPG | PG_V | PG_KW; | |
270 | } | |
271 | #else | |
272 | kernel_pmap->pm_pdir = (pd_entry_t *)(0xfe000000 + IdlePTD); | |
273 | #endif | |
274 | ||
275 | ||
276 | simple_lock_init(&kernel_pmap->pm_lock); | |
277 | kernel_pmap->pm_count = 1; | |
278 | ||
279 | #if BSDVM_COMPAT | |
280 | /* | |
281 | * Allocate all the submaps we need | |
282 | */ | |
283 | #define SYSMAP(c, p, v, n) \ | |
284 | v = (c)va; va += ((n)*I386_PAGE_SIZE); p = pte; pte += (n); | |
285 | ||
286 | va = virtual_avail; | |
287 | pte = pmap_pte(kernel_pmap, va); | |
288 | ||
289 | SYSMAP(caddr_t ,CMAP1 ,CADDR1 ,1 ) | |
290 | SYSMAP(caddr_t ,CMAP2 ,CADDR2 ,1 ) | |
291 | SYSMAP(caddr_t ,mmap ,vmmap ,1 ) | |
292 | SYSMAP(struct msgbuf * ,msgbufmap ,msgbufp ,1 ) | |
293 | virtual_avail = va; | |
294 | #endif | |
295 | /* | |
296 | * reserve special hunk of memory for use by bus dma as a bounce | |
297 | * buffer (contiguous virtual *and* physical memory). for now, | |
298 | * assume vm does not use memory beneath hole, and we know that | |
299 | * the bootstrap uses top 32k of base memory. -wfj | |
300 | */ | |
301 | { | |
302 | extern vm_offset_t isaphysmem; | |
303 | isaphysmem = va; | |
304 | ||
305 | virtual_avail = pmap_map(va, 0xa0000 - 32*1024, 0xa0000, VM_PROT_ALL); | |
306 | } | |
307 | ||
308 | *(int *)PTD = 0; | |
309 | load_cr3(rcr3()); | |
310 | ||
311 | } | |
312 | ||
313 | /* | |
314 | * Initialize the pmap module. | |
315 | * Called by vm_init, to initialize any structures that the pmap | |
316 | * system needs to map virtual memory. | |
317 | */ | |
318 | void | |
319 | pmap_init(phys_start, phys_end) | |
320 | vm_offset_t phys_start, phys_end; | |
321 | { | |
322 | vm_offset_t addr, addr2; | |
323 | vm_size_t npg, s; | |
324 | int rv; | |
325 | extern int KPTphys; | |
326 | ||
327 | #ifdef DEBUG | |
328 | if (pmapdebug & PDB_FOLLOW) | |
329 | printf("pmap_init(%x, %x)\n", phys_start, phys_end); | |
330 | #endif | |
331 | /* | |
332 | * Now that kernel map has been allocated, we can mark as | |
333 | * unavailable regions which we have mapped in locore. | |
334 | */ | |
335 | addr = atdevbase; | |
336 | (void) vm_map_find(kernel_map, NULL, (vm_offset_t) 0, | |
337 | &addr, (0x100000-0xa0000), FALSE); | |
338 | ||
339 | addr = (vm_offset_t) 0xfe000000+KPTphys/* *NBPG */; | |
340 | vm_object_reference(kernel_object); | |
341 | (void) vm_map_find(kernel_map, kernel_object, addr, | |
342 | &addr, 2*NBPG, FALSE); | |
343 | ||
344 | /* | |
345 | * Allocate memory for random pmap data structures. Includes the | |
346 | * pv_head_table and pmap_attributes. | |
347 | */ | |
348 | npg = atop(phys_end - phys_start); | |
349 | s = (vm_size_t) (sizeof(struct pv_entry) * npg + npg); | |
350 | s = round_page(s); | |
351 | addr = (vm_offset_t) kmem_alloc(kernel_map, s); | |
352 | pv_table = (pv_entry_t) addr; | |
353 | addr += sizeof(struct pv_entry) * npg; | |
354 | pmap_attributes = (char *) addr; | |
355 | #ifdef DEBUG | |
356 | if (pmapdebug & PDB_INIT) | |
357 | printf("pmap_init: %x bytes (%x pgs): tbl %x attr %x\n", | |
358 | s, npg, pv_table, pmap_attributes); | |
359 | #endif | |
360 | ||
361 | /* | |
362 | * Now it is safe to enable pv_table recording. | |
363 | */ | |
364 | vm_first_phys = phys_start; | |
365 | vm_last_phys = phys_end; | |
366 | pmap_initialized = TRUE; | |
367 | } | |
368 | ||
369 | /* | |
370 | * Used to map a range of physical addresses into kernel | |
371 | * virtual address space. | |
372 | * | |
373 | * For now, VM is already on, we only need to map the | |
374 | * specified memory. | |
375 | */ | |
376 | vm_offset_t | |
377 | pmap_map(virt, start, end, prot) | |
378 | vm_offset_t virt; | |
379 | vm_offset_t start; | |
380 | vm_offset_t end; | |
381 | int prot; | |
382 | { | |
383 | #ifdef DEBUG | |
384 | if (pmapdebug & PDB_FOLLOW) | |
385 | printf("pmap_map(%x, %x, %x, %x)\n", virt, start, end, prot); | |
386 | #endif | |
387 | while (start < end) { | |
388 | pmap_enter(kernel_pmap, virt, start, prot, FALSE); | |
389 | virt += PAGE_SIZE; | |
390 | start += PAGE_SIZE; | |
391 | } | |
392 | return(virt); | |
393 | } | |
394 | ||
395 | /* | |
396 | * Create and return a physical map. | |
397 | * | |
398 | * If the size specified for the map | |
399 | * is zero, the map is an actual physical | |
400 | * map, and may be referenced by the | |
401 | * hardware. | |
402 | * | |
403 | * If the size specified is non-zero, | |
404 | * the map will be used in software only, and | |
405 | * is bounded by that size. | |
406 | * | |
407 | * [ just allocate a ptd and mark it uninitialize -- should we track | |
408 | * with a table which process has which ptd? -wfj ] | |
409 | */ | |
410 | ||
411 | pmap_t | |
412 | pmap_create(size) | |
413 | vm_size_t size; | |
414 | { | |
415 | register pmap_t pmap; | |
416 | ||
417 | #ifdef DEBUG | |
418 | if (pmapdebug & (PDB_FOLLOW|PDB_CREATE)) | |
419 | printf("pmap_create(%x)\n", size); | |
420 | #endif | |
421 | /* | |
422 | * Software use map does not need a pmap | |
423 | */ | |
424 | if (size) | |
425 | return(NULL); | |
426 | ||
427 | /* XXX: is it ok to wait here? */ | |
428 | pmap = (pmap_t) malloc(sizeof *pmap, M_VMPMAP, M_WAITOK); | |
429 | #ifdef notifwewait | |
430 | if (pmap == NULL) | |
431 | panic("pmap_create: cannot allocate a pmap"); | |
432 | #endif | |
433 | bzero(pmap, sizeof(*pmap)); | |
434 | pmap_pinit(pmap); | |
435 | return (pmap); | |
436 | } | |
437 | ||
438 | /* | |
439 | * Initialize a preallocated and zeroed pmap structure, | |
440 | * such as one in a vmspace structure. | |
441 | */ | |
442 | void | |
443 | pmap_pinit(pmap) | |
444 | register struct pmap *pmap; | |
445 | { | |
446 | ||
447 | #ifdef DEBUG | |
448 | if (pmapdebug & (PDB_FOLLOW|PDB_CREATE)) | |
449 | pg("pmap_pinit(%x)\n", pmap); | |
450 | #endif | |
451 | ||
452 | /* | |
453 | * No need to allocate page table space yet but we do need a | |
454 | * valid page directory table. | |
455 | */ | |
456 | pmap->pm_pdir = (pd_entry_t *) kmem_alloc(kernel_map, NBPG); | |
457 | ||
458 | /* wire in kernel global address entries */ | |
459 | bcopy(PTD+KPTDI_FIRST, pmap->pm_pdir+KPTDI_FIRST, | |
460 | (KPTDI_LAST-KPTDI_FIRST+1)*4); | |
461 | ||
462 | /* install self-referential address mapping entry */ | |
463 | *(int *)(pmap->pm_pdir+PTDPTDI) = | |
464 | (int)pmap_extract(kernel_pmap, pmap->pm_pdir) | PG_V | PG_URKW; | |
465 | ||
466 | pmap->pm_count = 1; | |
467 | simple_lock_init(&pmap->pm_lock); | |
468 | } | |
469 | ||
470 | /* | |
471 | * Retire the given physical map from service. | |
472 | * Should only be called if the map contains | |
473 | * no valid mappings. | |
474 | */ | |
475 | void | |
476 | pmap_destroy(pmap) | |
477 | register pmap_t pmap; | |
478 | { | |
479 | int count; | |
480 | ||
481 | #ifdef DEBUG | |
482 | if (pmapdebug & PDB_FOLLOW) | |
483 | printf("pmap_destroy(%x)\n", pmap); | |
484 | #endif | |
485 | if (pmap == NULL) | |
486 | return; | |
487 | ||
488 | simple_lock(&pmap->pm_lock); | |
489 | count = --pmap->pm_count; | |
490 | simple_unlock(&pmap->pm_lock); | |
491 | if (count == 0) { | |
492 | pmap_release(pmap); | |
493 | free((caddr_t)pmap, M_VMPMAP); | |
494 | } | |
495 | } | |
496 | ||
497 | /* | |
498 | * Release any resources held by the given physical map. | |
499 | * Called when a pmap initialized by pmap_pinit is being released. | |
500 | * Should only be called if the map contains no valid mappings. | |
501 | */ | |
502 | void | |
503 | pmap_release(pmap) | |
504 | register struct pmap *pmap; | |
505 | { | |
506 | ||
507 | #ifdef DEBUG | |
508 | if (pmapdebug & PDB_FOLLOW) | |
509 | pg("pmap_release(%x)\n", pmap); | |
510 | #endif | |
511 | #ifdef notdef /* DIAGNOSTIC */ | |
512 | /* count would be 0 from pmap_destroy... */ | |
513 | simple_lock(&pmap->pm_lock); | |
514 | if (pmap->pm_count != 1) | |
515 | panic("pmap_release count"); | |
516 | #endif | |
517 | kmem_free(kernel_map, (vm_offset_t)pmap->pm_pdir, NBPG); | |
518 | } | |
519 | ||
520 | /* | |
521 | * Add a reference to the specified pmap. | |
522 | */ | |
523 | void | |
524 | pmap_reference(pmap) | |
525 | pmap_t pmap; | |
526 | { | |
527 | #ifdef DEBUG | |
528 | if (pmapdebug & PDB_FOLLOW) | |
529 | printf("pmap_reference(%x)", pmap); | |
530 | #endif | |
531 | if (pmap != NULL) { | |
532 | simple_lock(&pmap->pm_lock); | |
533 | pmap->pm_count++; | |
534 | simple_unlock(&pmap->pm_lock); | |
535 | } | |
536 | } | |
537 | ||
538 | /* | |
539 | * Remove the given range of addresses from the specified map. | |
540 | * | |
541 | * It is assumed that the start and end are properly | |
542 | * rounded to the page size. | |
543 | */ | |
544 | void | |
545 | pmap_remove(pmap, sva, eva) | |
58c5bdc7 PHK |
546 | struct pmap *pmap; |
547 | register vm_offset_t sva; | |
548 | register vm_offset_t eva; | |
1b68e56d | 549 | { |
58c5bdc7 PHK |
550 | register pt_entry_t *ptp,*ptq; |
551 | vm_offset_t va; | |
552 | vm_offset_t pa; | |
553 | pt_entry_t *pte; | |
554 | pv_entry_t pv, npv; | |
555 | int ix; | |
556 | int s, bits; | |
1b68e56d WJ |
557 | #ifdef DEBUG |
558 | pt_entry_t opte; | |
559 | ||
560 | if (pmapdebug & (PDB_FOLLOW|PDB_REMOVE|PDB_PROTECT)) | |
561 | pg("pmap_remove(%x, %x, %x)", pmap, sva, eva); | |
562 | #endif | |
563 | ||
564 | if (pmap == NULL) | |
565 | return; | |
566 | ||
58c5bdc7 PHK |
567 | /* are we current address space or kernel? */ |
568 | if (pmap->pm_pdir[PTDPTDI].pd_pfnum == PTDpde.pd_pfnum | |
569 | || pmap == kernel_pmap) | |
570 | ptp=PTmap; | |
571 | ||
572 | /* otherwise, we are alternate address space */ | |
573 | else { | |
574 | if (pmap->pm_pdir[PTDPTDI].pd_pfnum | |
575 | != APTDpde.pd_pfnum) { | |
576 | APTDpde = pmap->pm_pdir[PTDPTDI]; | |
577 | tlbflush(); | |
578 | } | |
579 | ptp=APTmap; | |
580 | } | |
1b68e56d WJ |
581 | #ifdef DEBUG |
582 | remove_stats.calls++; | |
583 | #endif | |
58c5bdc7 PHK |
584 | |
585 | /* this is essential since we must check the PDE(sva) for precense */ | |
586 | while (sva <= eva && !pmap_pde_v(pmap_pde(pmap, sva))) | |
587 | sva = (sva & PD_MASK) + (1<<PD_SHIFT); | |
588 | sva = i386_btop(sva); | |
589 | eva = i386_btop(eva); | |
590 | ||
591 | for (; sva < eva; sva++) { | |
1b68e56d WJ |
592 | /* |
593 | * Weed out invalid mappings. | |
594 | * Note: we assume that the page directory table is | |
595 | * always allocated, and in kernel virtual. | |
596 | */ | |
58c5bdc7 PHK |
597 | ptq=ptp+sva; |
598 | while((sva & 0x3ff) && !pmap_pte_pa(ptq)) | |
599 | { | |
600 | if(++sva >= eva) | |
601 | return; | |
602 | ptq++; | |
603 | } | |
604 | ||
605 | ||
606 | if(!(sva & 0x3ff)) /* Only check once in a while */ | |
607 | { | |
608 | if (!pmap_pde_v(pmap_pde(pmap, i386_ptob(sva)))) | |
609 | { | |
610 | /* We can race ahead here, straight to next pde.. */ | |
611 | sva = (sva & 0xffc00) + (1<<10) -1 ; | |
1b68e56d | 612 | continue; |
58c5bdc7 PHK |
613 | } |
614 | } | |
615 | if(!pmap_pte_pa(ptp+sva)) | |
616 | continue; | |
1b68e56d | 617 | |
58c5bdc7 | 618 | pte = ptp + sva; |
1b68e56d | 619 | pa = pmap_pte_pa(pte); |
58c5bdc7 | 620 | va = i386_ptob(sva); |
1b68e56d WJ |
621 | #ifdef DEBUG |
622 | opte = *pte; | |
623 | remove_stats.removes++; | |
624 | #endif | |
625 | /* | |
626 | * Update statistics | |
627 | */ | |
628 | if (pmap_pte_w(pte)) | |
629 | pmap->pm_stats.wired_count--; | |
630 | pmap->pm_stats.resident_count--; | |
631 | ||
632 | /* | |
633 | * Invalidate the PTEs. | |
634 | * XXX: should cluster them up and invalidate as many | |
635 | * as possible at once. | |
636 | */ | |
637 | #ifdef DEBUG | |
638 | if (pmapdebug & PDB_REMOVE) | |
639 | printf("remove: inv %x ptes at %x(%x) ", | |
640 | i386pagesperpage, pte, *(int *)pte); | |
641 | #endif | |
642 | bits = ix = 0; | |
643 | do { | |
644 | bits |= *(int *)pte & (PG_U|PG_M); | |
645 | *(int *)pte++ = 0; | |
646 | /*TBIS(va + ix * I386_PAGE_SIZE);*/ | |
647 | } while (++ix != i386pagesperpage); | |
648 | if (curproc && pmap == &curproc->p_vmspace->vm_pmap) | |
649 | pmap_activate(pmap, (struct pcb *)curproc->p_addr); | |
650 | /* are we current address space or kernel? */ | |
651 | /*if (pmap->pm_pdir[PTDPTDI].pd_pfnum == PTDpde.pd_pfnum | |
652 | || pmap == kernel_pmap) | |
653 | load_cr3(curpcb->pcb_ptd);*/ | |
654 | tlbflush(); | |
655 | ||
656 | #ifdef needednotdone | |
657 | reduce wiring count on page table pages as references drop | |
658 | #endif | |
659 | ||
660 | /* | |
661 | * Remove from the PV table (raise IPL since we | |
662 | * may be called at interrupt time). | |
663 | */ | |
664 | if (pa < vm_first_phys || pa >= vm_last_phys) | |
665 | continue; | |
666 | pv = pa_to_pvh(pa); | |
667 | s = splimp(); | |
668 | /* | |
669 | * If it is the first entry on the list, it is actually | |
670 | * in the header and we must copy the following entry up | |
671 | * to the header. Otherwise we must search the list for | |
672 | * the entry. In either case we free the now unused entry. | |
673 | */ | |
674 | if (pmap == pv->pv_pmap && va == pv->pv_va) { | |
675 | npv = pv->pv_next; | |
676 | if (npv) { | |
677 | *pv = *npv; | |
678 | free((caddr_t)npv, M_VMPVENT); | |
679 | } else | |
680 | pv->pv_pmap = NULL; | |
681 | #ifdef DEBUG | |
682 | remove_stats.pvfirst++; | |
683 | #endif | |
684 | } else { | |
685 | for (npv = pv->pv_next; npv; npv = npv->pv_next) { | |
686 | #ifdef DEBUG | |
687 | remove_stats.pvsearch++; | |
688 | #endif | |
689 | if (pmap == npv->pv_pmap && va == npv->pv_va) | |
690 | break; | |
691 | pv = npv; | |
692 | } | |
693 | #ifdef DEBUG | |
694 | if (npv == NULL) | |
695 | panic("pmap_remove: PA not in pv_tab"); | |
696 | #endif | |
697 | pv->pv_next = npv->pv_next; | |
698 | free((caddr_t)npv, M_VMPVENT); | |
699 | pv = pa_to_pvh(pa); | |
700 | } | |
701 | ||
702 | #ifdef notdef | |
703 | [tally number of pagetable pages, if sharing of ptpages adjust here] | |
704 | #endif | |
705 | /* | |
706 | * Update saved attributes for managed page | |
707 | */ | |
708 | pmap_attributes[pa_index(pa)] |= bits; | |
709 | splx(s); | |
710 | } | |
711 | #ifdef notdef | |
712 | [cache and tlb flushing, if needed] | |
713 | #endif | |
714 | } | |
715 | ||
716 | /* | |
717 | * Routine: pmap_remove_all | |
718 | * Function: | |
719 | * Removes this physical page from | |
720 | * all physical maps in which it resides. | |
721 | * Reflects back modify bits to the pager. | |
722 | */ | |
723 | void | |
724 | pmap_remove_all(pa) | |
725 | vm_offset_t pa; | |
726 | { | |
727 | register pv_entry_t pv; | |
728 | int s; | |
729 | ||
730 | #ifdef DEBUG | |
731 | if (pmapdebug & (PDB_FOLLOW|PDB_REMOVE|PDB_PROTECT)) | |
732 | printf("pmap_remove_all(%x)", pa); | |
733 | /*pmap_pvdump(pa);*/ | |
734 | #endif | |
735 | /* | |
736 | * Not one of ours | |
737 | */ | |
738 | if (pa < vm_first_phys || pa >= vm_last_phys) | |
739 | return; | |
740 | ||
741 | pv = pa_to_pvh(pa); | |
742 | s = splimp(); | |
743 | /* | |
744 | * Do it the easy way for now | |
745 | */ | |
746 | while (pv->pv_pmap != NULL) { | |
747 | #ifdef DEBUG | |
748 | if (!pmap_pde_v(pmap_pde(pv->pv_pmap, pv->pv_va)) || | |
749 | pmap_pte_pa(pmap_pte(pv->pv_pmap, pv->pv_va)) != pa) | |
750 | panic("pmap_remove_all: bad mapping"); | |
751 | #endif | |
752 | pmap_remove(pv->pv_pmap, pv->pv_va, pv->pv_va + PAGE_SIZE); | |
753 | } | |
754 | splx(s); | |
755 | } | |
756 | ||
757 | /* | |
758 | * Routine: pmap_copy_on_write | |
759 | * Function: | |
760 | * Remove write privileges from all | |
761 | * physical maps for this physical page. | |
762 | */ | |
763 | void | |
764 | pmap_copy_on_write(pa) | |
765 | vm_offset_t pa; | |
766 | { | |
767 | #ifdef DEBUG | |
768 | if (pmapdebug & (PDB_FOLLOW|PDB_PROTECT)) | |
769 | printf("pmap_copy_on_write(%x)", pa); | |
770 | #endif | |
771 | pmap_changebit(pa, PG_RO, TRUE); | |
772 | } | |
773 | ||
774 | /* | |
775 | * Set the physical protection on the | |
776 | * specified range of this map as requested. | |
777 | */ | |
778 | void | |
779 | pmap_protect(pmap, sva, eva, prot) | |
780 | register pmap_t pmap; | |
781 | vm_offset_t sva, eva; | |
782 | vm_prot_t prot; | |
783 | { | |
784 | register pt_entry_t *pte; | |
785 | register vm_offset_t va; | |
786 | register int ix; | |
787 | int i386prot; | |
788 | boolean_t firstpage = TRUE; | |
58c5bdc7 | 789 | register pt_entry_t *ptp; |
1b68e56d WJ |
790 | |
791 | #ifdef DEBUG | |
792 | if (pmapdebug & (PDB_FOLLOW|PDB_PROTECT)) | |
793 | printf("pmap_protect(%x, %x, %x, %x)", pmap, sva, eva, prot); | |
794 | #endif | |
795 | if (pmap == NULL) | |
796 | return; | |
797 | ||
798 | if ((prot & VM_PROT_READ) == VM_PROT_NONE) { | |
799 | pmap_remove(pmap, sva, eva); | |
800 | return; | |
801 | } | |
802 | if (prot & VM_PROT_WRITE) | |
803 | return; | |
804 | ||
58c5bdc7 PHK |
805 | /* are we current address space or kernel? */ |
806 | if (pmap->pm_pdir[PTDPTDI].pd_pfnum == PTDpde.pd_pfnum | |
807 | || pmap == kernel_pmap) | |
808 | ptp=PTmap; | |
809 | ||
810 | /* otherwise, we are alternate address space */ | |
811 | else { | |
812 | if (pmap->pm_pdir[PTDPTDI].pd_pfnum | |
813 | != APTDpde.pd_pfnum) { | |
814 | APTDpde = pmap->pm_pdir[PTDPTDI]; | |
815 | tlbflush(); | |
816 | } | |
817 | ptp=APTmap; | |
818 | } | |
1b68e56d WJ |
819 | for (va = sva; va < eva; va += PAGE_SIZE) { |
820 | /* | |
821 | * Page table page is not allocated. | |
822 | * Skip it, we don't want to force allocation | |
823 | * of unnecessary PTE pages just to set the protection. | |
824 | */ | |
825 | if (!pmap_pde_v(pmap_pde(pmap, va))) { | |
826 | /* XXX: avoid address wrap around */ | |
827 | if (va >= i386_trunc_pdr((vm_offset_t)-1)) | |
828 | break; | |
829 | va = i386_round_pdr(va + PAGE_SIZE) - PAGE_SIZE; | |
830 | continue; | |
831 | } | |
832 | ||
58c5bdc7 | 833 | pte = ptp + i386_btop(va); |
1b68e56d WJ |
834 | |
835 | /* | |
836 | * Page not valid. Again, skip it. | |
837 | * Should we do this? Or set protection anyway? | |
838 | */ | |
839 | if (!pmap_pte_v(pte)) | |
840 | continue; | |
841 | ||
842 | ix = 0; | |
843 | i386prot = pte_prot(pmap, prot); | |
844 | if(va < UPT_MAX_ADDRESS) | |
845 | i386prot |= 2 /*PG_u*/; | |
846 | do { | |
847 | /* clear VAC here if PG_RO? */ | |
848 | pmap_pte_set_prot(pte++, i386prot); | |
849 | /*TBIS(va + ix * I386_PAGE_SIZE);*/ | |
850 | } while (++ix != i386pagesperpage); | |
851 | } | |
852 | if (curproc && pmap == &curproc->p_vmspace->vm_pmap) | |
853 | pmap_activate(pmap, (struct pcb *)curproc->p_addr); | |
854 | } | |
855 | ||
856 | /* | |
857 | * Insert the given physical page (p) at | |
858 | * the specified virtual address (v) in the | |
859 | * target physical map with the protection requested. | |
860 | * | |
861 | * If specified, the page will be wired down, meaning | |
862 | * that the related pte can not be reclaimed. | |
863 | * | |
864 | * NB: This is the only routine which MAY NOT lazy-evaluate | |
865 | * or lose information. That is, this routine must actually | |
866 | * insert this page into the given map NOW. | |
867 | */ | |
868 | void | |
869 | pmap_enter(pmap, va, pa, prot, wired) | |
870 | register pmap_t pmap; | |
871 | vm_offset_t va; | |
872 | register vm_offset_t pa; | |
873 | vm_prot_t prot; | |
874 | boolean_t wired; | |
875 | { | |
876 | register pt_entry_t *pte; | |
877 | register int npte, ix; | |
878 | vm_offset_t opa; | |
879 | boolean_t cacheable = TRUE; | |
880 | boolean_t checkpv = TRUE; | |
881 | ||
882 | #ifdef DEBUG | |
883 | if (pmapdebug & (PDB_FOLLOW|PDB_ENTER)) | |
884 | printf("pmap_enter(%x, %x, %x, %x, %x)", | |
885 | pmap, va, pa, prot, wired); | |
886 | #endif | |
887 | if (pmap == NULL) | |
888 | return; | |
889 | ||
890 | if(va > VM_MAX_KERNEL_ADDRESS)panic("pmap_enter: toobig"); | |
891 | /* also, should not muck with PTD va! */ | |
892 | ||
893 | #ifdef DEBUG | |
894 | if (pmap == kernel_pmap) | |
895 | enter_stats.kernel++; | |
896 | else | |
897 | enter_stats.user++; | |
898 | #endif | |
899 | ||
900 | /* | |
901 | * Page Directory table entry not valid, we need a new PT page | |
902 | */ | |
903 | if (!pmap_pde_v(pmap_pde(pmap, va))) { | |
904 | pg("ptdi %x", pmap->pm_pdir[PTDPTDI]); | |
905 | } | |
906 | ||
907 | pte = pmap_pte(pmap, va); | |
908 | opa = pmap_pte_pa(pte); | |
909 | #ifdef DEBUG | |
910 | if (pmapdebug & PDB_ENTER) | |
911 | printf("enter: pte %x, *pte %x ", pte, *(int *)pte); | |
912 | #endif | |
913 | ||
914 | /* | |
915 | * Mapping has not changed, must be protection or wiring change. | |
916 | */ | |
917 | if (opa == pa) { | |
918 | #ifdef DEBUG | |
919 | enter_stats.pwchange++; | |
920 | #endif | |
921 | /* | |
922 | * Wiring change, just update stats. | |
923 | * We don't worry about wiring PT pages as they remain | |
924 | * resident as long as there are valid mappings in them. | |
925 | * Hence, if a user page is wired, the PT page will be also. | |
926 | */ | |
927 | if (wired && !pmap_pte_w(pte) || !wired && pmap_pte_w(pte)) { | |
928 | #ifdef DEBUG | |
929 | if (pmapdebug & PDB_ENTER) | |
930 | pg("enter: wiring change -> %x ", wired); | |
931 | #endif | |
932 | if (wired) | |
933 | pmap->pm_stats.wired_count++; | |
934 | else | |
935 | pmap->pm_stats.wired_count--; | |
936 | #ifdef DEBUG | |
937 | enter_stats.wchange++; | |
938 | #endif | |
939 | } | |
940 | goto validate; | |
941 | } | |
942 | ||
943 | /* | |
944 | * Mapping has changed, invalidate old range and fall through to | |
945 | * handle validating new mapping. | |
946 | */ | |
947 | if (opa) { | |
948 | #ifdef DEBUG | |
949 | if (pmapdebug & PDB_ENTER) | |
950 | printf("enter: removing old mapping %x pa %x ", va, opa); | |
951 | #endif | |
952 | pmap_remove(pmap, va, va + PAGE_SIZE); | |
953 | #ifdef DEBUG | |
954 | enter_stats.mchange++; | |
955 | #endif | |
956 | } | |
957 | ||
958 | /* | |
959 | * Enter on the PV list if part of our managed memory | |
960 | * Note that we raise IPL while manipulating pv_table | |
961 | * since pmap_enter can be called at interrupt time. | |
962 | */ | |
963 | if (pa >= vm_first_phys && pa < vm_last_phys) { | |
964 | register pv_entry_t pv, npv; | |
965 | int s; | |
966 | ||
967 | #ifdef DEBUG | |
968 | enter_stats.managed++; | |
969 | #endif | |
970 | pv = pa_to_pvh(pa); | |
971 | s = splimp(); | |
972 | #ifdef DEBUG | |
973 | if (pmapdebug & PDB_ENTER) | |
974 | printf("enter: pv at %x: %x/%x/%x ", | |
975 | pv, pv->pv_va, pv->pv_pmap, pv->pv_next); | |
976 | #endif | |
977 | /* | |
978 | * No entries yet, use header as the first entry | |
979 | */ | |
980 | if (pv->pv_pmap == NULL) { | |
981 | #ifdef DEBUG | |
982 | enter_stats.firstpv++; | |
983 | #endif | |
984 | pv->pv_va = va; | |
985 | pv->pv_pmap = pmap; | |
986 | pv->pv_next = NULL; | |
987 | pv->pv_flags = 0; | |
988 | } | |
989 | /* | |
990 | * There is at least one other VA mapping this page. | |
991 | * Place this entry after the header. | |
992 | */ | |
993 | else { | |
994 | /*printf("second time: ");*/ | |
995 | #ifdef DEBUG | |
996 | for (npv = pv; npv; npv = npv->pv_next) | |
997 | if (pmap == npv->pv_pmap && va == npv->pv_va) | |
998 | panic("pmap_enter: already in pv_tab"); | |
999 | #endif | |
1000 | npv = (pv_entry_t) | |
1001 | malloc(sizeof *npv, M_VMPVENT, M_NOWAIT); | |
1002 | npv->pv_va = va; | |
1003 | npv->pv_pmap = pmap; | |
1004 | npv->pv_next = pv->pv_next; | |
1005 | pv->pv_next = npv; | |
1006 | #ifdef DEBUG | |
1007 | if (!npv->pv_next) | |
1008 | enter_stats.secondpv++; | |
1009 | #endif | |
1010 | } | |
1011 | splx(s); | |
1012 | } | |
1013 | /* | |
1014 | * Assumption: if it is not part of our managed memory | |
1015 | * then it must be device memory which may be volitile. | |
1016 | */ | |
1017 | if (pmap_initialized) { | |
1018 | checkpv = cacheable = FALSE; | |
1019 | #ifdef DEBUG | |
1020 | enter_stats.unmanaged++; | |
1021 | #endif | |
1022 | } | |
1023 | ||
1024 | /* | |
1025 | * Increment counters | |
1026 | */ | |
1027 | pmap->pm_stats.resident_count++; | |
1028 | if (wired) | |
1029 | pmap->pm_stats.wired_count++; | |
1030 | ||
1031 | validate: | |
1032 | /* | |
1033 | * Now validate mapping with desired protection/wiring. | |
1034 | * Assume uniform modified and referenced status for all | |
1035 | * I386 pages in a MACH page. | |
1036 | */ | |
1037 | npte = (pa & PG_FRAME) | pte_prot(pmap, prot) | PG_V; | |
1038 | npte |= (*(int *)pte & (PG_M|PG_U)); | |
1039 | if (wired) | |
1040 | npte |= PG_W; | |
1041 | if(va < UPT_MIN_ADDRESS) | |
1042 | npte |= PG_u; | |
1043 | else if(va < UPT_MAX_ADDRESS) | |
1044 | npte |= PG_u | PG_RW; | |
1045 | #ifdef DEBUG | |
1046 | if (pmapdebug & PDB_ENTER) | |
1047 | printf("enter: new pte value %x ", npte); | |
1048 | #endif | |
1049 | ix = 0; | |
1050 | do { | |
1051 | *(int *)pte++ = npte; | |
1052 | /*TBIS(va);*/ | |
1053 | npte += I386_PAGE_SIZE; | |
1054 | va += I386_PAGE_SIZE; | |
1055 | } while (++ix != i386pagesperpage); | |
1056 | pte--; | |
1057 | #ifdef DEBUGx | |
1058 | cache, tlb flushes | |
1059 | #endif | |
1060 | /*pads(pmap);*/ | |
1061 | /*load_cr3(((struct pcb *)curproc->p_addr)->pcb_ptd);*/ | |
1062 | tlbflush(); | |
1063 | } | |
1064 | ||
1065 | /* | |
1066 | * pmap_page_protect: | |
1067 | * | |
1068 | * Lower the permission for all mappings to a given page. | |
1069 | */ | |
1070 | void | |
1071 | pmap_page_protect(phys, prot) | |
1072 | vm_offset_t phys; | |
1073 | vm_prot_t prot; | |
1074 | { | |
1075 | switch (prot) { | |
1076 | case VM_PROT_READ: | |
1077 | case VM_PROT_READ|VM_PROT_EXECUTE: | |
1078 | pmap_copy_on_write(phys); | |
1079 | break; | |
1080 | case VM_PROT_ALL: | |
1081 | break; | |
1082 | default: | |
1083 | pmap_remove_all(phys); | |
1084 | break; | |
1085 | } | |
1086 | } | |
1087 | ||
1088 | /* | |
1089 | * Routine: pmap_change_wiring | |
1090 | * Function: Change the wiring attribute for a map/virtual-address | |
1091 | * pair. | |
1092 | * In/out conditions: | |
1093 | * The mapping must already exist in the pmap. | |
1094 | */ | |
1095 | void | |
1096 | pmap_change_wiring(pmap, va, wired) | |
1097 | register pmap_t pmap; | |
1098 | vm_offset_t va; | |
1099 | boolean_t wired; | |
1100 | { | |
1101 | register pt_entry_t *pte; | |
1102 | register int ix; | |
1103 | ||
1104 | #ifdef DEBUG | |
1105 | if (pmapdebug & PDB_FOLLOW) | |
1106 | printf("pmap_change_wiring(%x, %x, %x)", pmap, va, wired); | |
1107 | #endif | |
1108 | if (pmap == NULL) | |
1109 | return; | |
1110 | ||
1111 | pte = pmap_pte(pmap, va); | |
1112 | #ifdef DEBUG | |
1113 | /* | |
1114 | * Page table page is not allocated. | |
1115 | * Should this ever happen? Ignore it for now, | |
1116 | * we don't want to force allocation of unnecessary PTE pages. | |
1117 | */ | |
1118 | if (!pmap_pde_v(pmap_pde(pmap, va))) { | |
1119 | if (pmapdebug & PDB_PARANOIA) | |
1120 | pg("pmap_change_wiring: invalid PDE for %x ", va); | |
1121 | return; | |
1122 | } | |
1123 | /* | |
1124 | * Page not valid. Should this ever happen? | |
1125 | * Just continue and change wiring anyway. | |
1126 | */ | |
1127 | if (!pmap_pte_v(pte)) { | |
1128 | if (pmapdebug & PDB_PARANOIA) | |
1129 | pg("pmap_change_wiring: invalid PTE for %x ", va); | |
1130 | } | |
1131 | #endif | |
1132 | if (wired && !pmap_pte_w(pte) || !wired && pmap_pte_w(pte)) { | |
1133 | if (wired) | |
1134 | pmap->pm_stats.wired_count++; | |
1135 | else | |
1136 | pmap->pm_stats.wired_count--; | |
1137 | } | |
1138 | /* | |
1139 | * Wiring is not a hardware characteristic so there is no need | |
1140 | * to invalidate TLB. | |
1141 | */ | |
1142 | ix = 0; | |
1143 | do { | |
1144 | pmap_pte_set_w(pte++, wired); | |
1145 | } while (++ix != i386pagesperpage); | |
1146 | } | |
1147 | ||
1148 | /* | |
1149 | * Routine: pmap_pte | |
1150 | * Function: | |
1151 | * Extract the page table entry associated | |
1152 | * with the given map/virtual_address pair. | |
1153 | * [ what about induced faults -wfj] | |
1154 | */ | |
1155 | ||
1156 | struct pte *pmap_pte(pmap, va) | |
1157 | register pmap_t pmap; | |
1158 | vm_offset_t va; | |
1159 | { | |
1160 | ||
1161 | #ifdef DEBUGx | |
1162 | if (pmapdebug & PDB_FOLLOW) | |
1163 | printf("pmap_pte(%x, %x) ->\n", pmap, va); | |
1164 | #endif | |
1165 | if (pmap && pmap_pde_v(pmap_pde(pmap, va))) { | |
1166 | ||
1167 | /* are we current address space or kernel? */ | |
1168 | if (pmap->pm_pdir[PTDPTDI].pd_pfnum == PTDpde.pd_pfnum | |
1169 | || pmap == kernel_pmap) | |
1170 | return ((struct pte *) vtopte(va)); | |
1171 | ||
1172 | /* otherwise, we are alternate address space */ | |
1173 | else { | |
1174 | if (pmap->pm_pdir[PTDPTDI].pd_pfnum | |
1175 | != APTDpde.pd_pfnum) { | |
1176 | APTDpde = pmap->pm_pdir[PTDPTDI]; | |
1177 | tlbflush(); | |
1178 | } | |
1179 | return((struct pte *) avtopte(va)); | |
1180 | } | |
1181 | } | |
1182 | return(0); | |
1183 | } | |
1184 | ||
1185 | /* | |
1186 | * Routine: pmap_extract | |
1187 | * Function: | |
1188 | * Extract the physical page address associated | |
1189 | * with the given map/virtual_address pair. | |
1190 | */ | |
1191 | ||
1192 | vm_offset_t | |
1193 | pmap_extract(pmap, va) | |
1194 | register pmap_t pmap; | |
1195 | vm_offset_t va; | |
1196 | { | |
1197 | register vm_offset_t pa; | |
1198 | ||
1199 | #ifdef DEBUGx | |
1200 | if (pmapdebug & PDB_FOLLOW) | |
1201 | pg("pmap_extract(%x, %x) -> ", pmap, va); | |
1202 | #endif | |
1203 | pa = 0; | |
1204 | if (pmap && pmap_pde_v(pmap_pde(pmap, va))) { | |
1205 | pa = *(int *) pmap_pte(pmap, va); | |
1206 | } | |
1207 | if (pa) | |
1208 | pa = (pa & PG_FRAME) | (va & ~PG_FRAME); | |
1209 | #ifdef DEBUGx | |
1210 | if (pmapdebug & PDB_FOLLOW) | |
1211 | printf("%x\n", pa); | |
1212 | #endif | |
1213 | return(pa); | |
1214 | } | |
1215 | ||
1216 | /* | |
1217 | * Copy the range specified by src_addr/len | |
1218 | * from the source map to the range dst_addr/len | |
1219 | * in the destination map. | |
1220 | * | |
1221 | * This routine is only advisory and need not do anything. | |
1222 | */ | |
1223 | void pmap_copy(dst_pmap, src_pmap, dst_addr, len, src_addr) | |
1224 | pmap_t dst_pmap; | |
1225 | pmap_t src_pmap; | |
1226 | vm_offset_t dst_addr; | |
1227 | vm_size_t len; | |
1228 | vm_offset_t src_addr; | |
1229 | { | |
1230 | #ifdef DEBUG | |
1231 | if (pmapdebug & PDB_FOLLOW) | |
1232 | printf("pmap_copy(%x, %x, %x, %x, %x)", | |
1233 | dst_pmap, src_pmap, dst_addr, len, src_addr); | |
1234 | #endif | |
1235 | } | |
1236 | ||
1237 | /* | |
1238 | * Require that all active physical maps contain no | |
1239 | * incorrect entries NOW. [This update includes | |
1240 | * forcing updates of any address map caching.] | |
1241 | * | |
1242 | * Generally used to insure that a thread about | |
1243 | * to run will see a semantically correct world. | |
1244 | */ | |
1245 | void pmap_update() | |
1246 | { | |
1247 | #ifdef DEBUG | |
1248 | if (pmapdebug & PDB_FOLLOW) | |
1249 | printf("pmap_update()"); | |
1250 | #endif | |
1251 | tlbflush(); | |
1252 | } | |
1253 | ||
1254 | /* | |
1255 | * Routine: pmap_collect | |
1256 | * Function: | |
1257 | * Garbage collects the physical map system for | |
1258 | * pages which are no longer used. | |
1259 | * Success need not be guaranteed -- that is, there | |
1260 | * may well be pages which are not referenced, but | |
1261 | * others may be collected. | |
1262 | * Usage: | |
1263 | * Called by the pageout daemon when pages are scarce. | |
1264 | * [ needs to be written -wfj ] | |
1265 | */ | |
1266 | void | |
1267 | pmap_collect(pmap) | |
1268 | pmap_t pmap; | |
1269 | { | |
1270 | register vm_offset_t pa; | |
1271 | register pv_entry_t pv; | |
1272 | register int *pte; | |
1273 | vm_offset_t kpa; | |
1274 | int s; | |
1275 | ||
1276 | #ifdef DEBUG | |
1277 | int *pde; | |
1278 | int opmapdebug; | |
1279 | printf("pmap_collect(%x) ", pmap); | |
1280 | #endif | |
1281 | if (pmap != kernel_pmap) | |
1282 | return; | |
1283 | ||
1284 | } | |
1285 | ||
1286 | /* [ macro again?, should I force kstack into user map here? -wfj ] */ | |
1287 | void | |
1288 | pmap_activate(pmap, pcbp) | |
1289 | register pmap_t pmap; | |
1290 | struct pcb *pcbp; | |
1291 | { | |
1292 | int x; | |
1293 | #ifdef DEBUG | |
1294 | if (pmapdebug & (PDB_FOLLOW|PDB_PDRTAB)) | |
1295 | pg("pmap_activate(%x, %x) ", pmap, pcbp); | |
1296 | #endif | |
1297 | PMAP_ACTIVATE(pmap, pcbp); | |
1298 | /*printf("pde "); | |
1299 | for(x=0x3f6; x < 0x3fA; x++) | |
1300 | printf("%x ", pmap->pm_pdir[x]);*/ | |
1301 | /*pads(pmap);*/ | |
1302 | /*pg(" pcb_cr3 %x", pcbp->pcb_cr3);*/ | |
1303 | } | |
1304 | ||
1305 | /* | |
1306 | * Routine: pmap_kernel | |
1307 | * Function: | |
1308 | * Returns the physical map handle for the kernel. | |
1309 | */ | |
1310 | pmap_t | |
1311 | pmap_kernel() | |
1312 | { | |
1313 | return (kernel_pmap); | |
1314 | } | |
1315 | ||
1316 | /* | |
1317 | * pmap_zero_page zeros the specified (machine independent) | |
1318 | * page by mapping the page into virtual memory and using | |
1319 | * bzero to clear its contents, one machine dependent page | |
1320 | * at a time. | |
1321 | */ | |
1322 | pmap_zero_page(phys) | |
1323 | register vm_offset_t phys; | |
1324 | { | |
1325 | register int ix; | |
1326 | ||
1327 | #ifdef DEBUG | |
1328 | if (pmapdebug & PDB_FOLLOW) | |
1329 | printf("pmap_zero_page(%x)", phys); | |
1330 | #endif | |
1331 | phys >>= PG_SHIFT; | |
1332 | ix = 0; | |
1333 | do { | |
1334 | clearseg(phys++); | |
1335 | } while (++ix != i386pagesperpage); | |
1336 | } | |
1337 | ||
1338 | /* | |
1339 | * pmap_copy_page copies the specified (machine independent) | |
1340 | * page by mapping the page into virtual memory and using | |
1341 | * bcopy to copy the page, one machine dependent page at a | |
1342 | * time. | |
1343 | */ | |
1344 | pmap_copy_page(src, dst) | |
1345 | register vm_offset_t src, dst; | |
1346 | { | |
1347 | register int ix; | |
1348 | ||
1349 | #ifdef DEBUG | |
1350 | if (pmapdebug & PDB_FOLLOW) | |
1351 | printf("pmap_copy_page(%x, %x)", src, dst); | |
1352 | #endif | |
1353 | src >>= PG_SHIFT; | |
1354 | dst >>= PG_SHIFT; | |
1355 | ix = 0; | |
1356 | do { | |
1357 | physcopyseg(src++, dst++); | |
1358 | } while (++ix != i386pagesperpage); | |
1359 | } | |
1360 | ||
1361 | ||
1362 | /* | |
1363 | * Routine: pmap_pageable | |
1364 | * Function: | |
1365 | * Make the specified pages (by pmap, offset) | |
1366 | * pageable (or not) as requested. | |
1367 | * | |
1368 | * A page which is not pageable may not take | |
1369 | * a fault; therefore, its page table entry | |
1370 | * must remain valid for the duration. | |
1371 | * | |
1372 | * This routine is merely advisory; pmap_enter | |
1373 | * will specify that these pages are to be wired | |
1374 | * down (or not) as appropriate. | |
1375 | */ | |
1376 | pmap_pageable(pmap, sva, eva, pageable) | |
1377 | pmap_t pmap; | |
1378 | vm_offset_t sva, eva; | |
1379 | boolean_t pageable; | |
1380 | { | |
1381 | #ifdef DEBUG | |
1382 | if (pmapdebug & PDB_FOLLOW) | |
1383 | printf("pmap_pageable(%x, %x, %x, %x)", | |
1384 | pmap, sva, eva, pageable); | |
1385 | #endif | |
1386 | /* | |
1387 | * If we are making a PT page pageable then all valid | |
1388 | * mappings must be gone from that page. Hence it should | |
1389 | * be all zeros and there is no need to clean it. | |
1390 | * Assumptions: | |
1391 | * - we are called with only one page at a time | |
1392 | * - PT pages have only one pv_table entry | |
1393 | */ | |
1394 | if (pmap == kernel_pmap && pageable && sva + PAGE_SIZE == eva) { | |
1395 | register pv_entry_t pv; | |
1396 | register vm_offset_t pa; | |
1397 | ||
1398 | #ifdef DEBUG | |
1399 | if ((pmapdebug & (PDB_FOLLOW|PDB_PTPAGE)) == PDB_PTPAGE) | |
1400 | printf("pmap_pageable(%x, %x, %x, %x)", | |
1401 | pmap, sva, eva, pageable); | |
1402 | #endif | |
1403 | /*if (!pmap_pde_v(pmap_pde(pmap, sva))) | |
1404 | return;*/ | |
1405 | if(pmap_pte(pmap, sva) == 0) | |
1406 | return; | |
1407 | pa = pmap_pte_pa(pmap_pte(pmap, sva)); | |
1408 | if (pa < vm_first_phys || pa >= vm_last_phys) | |
1409 | return; | |
1410 | pv = pa_to_pvh(pa); | |
1411 | /*if (!ispt(pv->pv_va)) | |
1412 | return;*/ | |
1413 | #ifdef DEBUG | |
1414 | if (pv->pv_va != sva || pv->pv_next) { | |
1415 | pg("pmap_pageable: bad PT page va %x next %x\n", | |
1416 | pv->pv_va, pv->pv_next); | |
1417 | return; | |
1418 | } | |
1419 | #endif | |
1420 | /* | |
1421 | * Mark it unmodified to avoid pageout | |
1422 | */ | |
1423 | pmap_clear_modify(pa); | |
1424 | #ifdef needsomethinglikethis | |
1425 | if (pmapdebug & PDB_PTPAGE) | |
1426 | pg("pmap_pageable: PT page %x(%x) unmodified\n", | |
1427 | sva, *(int *)pmap_pte(pmap, sva)); | |
1428 | if (pmapdebug & PDB_WIRING) | |
1429 | pmap_check_wiring("pageable", sva); | |
1430 | #endif | |
1431 | } | |
1432 | } | |
1433 | ||
1434 | /* | |
1435 | * Clear the modify bits on the specified physical page. | |
1436 | */ | |
1437 | ||
1438 | void | |
1439 | pmap_clear_modify(pa) | |
1440 | vm_offset_t pa; | |
1441 | { | |
1442 | #ifdef DEBUG | |
1443 | if (pmapdebug & PDB_FOLLOW) | |
1444 | printf("pmap_clear_modify(%x)", pa); | |
1445 | #endif | |
1446 | pmap_changebit(pa, PG_M, FALSE); | |
1447 | } | |
1448 | ||
1449 | /* | |
1450 | * pmap_clear_reference: | |
1451 | * | |
1452 | * Clear the reference bit on the specified physical page. | |
1453 | */ | |
1454 | ||
1455 | void pmap_clear_reference(pa) | |
1456 | vm_offset_t pa; | |
1457 | { | |
1458 | #ifdef DEBUG | |
1459 | if (pmapdebug & PDB_FOLLOW) | |
1460 | printf("pmap_clear_reference(%x)", pa); | |
1461 | #endif | |
1462 | pmap_changebit(pa, PG_U, FALSE); | |
1463 | } | |
1464 | ||
1465 | /* | |
1466 | * pmap_is_referenced: | |
1467 | * | |
1468 | * Return whether or not the specified physical page is referenced | |
1469 | * by any physical maps. | |
1470 | */ | |
1471 | ||
1472 | boolean_t | |
1473 | pmap_is_referenced(pa) | |
1474 | vm_offset_t pa; | |
1475 | { | |
1476 | #ifdef DEBUG | |
1477 | if (pmapdebug & PDB_FOLLOW) { | |
1478 | boolean_t rv = pmap_testbit(pa, PG_U); | |
1479 | printf("pmap_is_referenced(%x) -> %c", pa, "FT"[rv]); | |
1480 | return(rv); | |
1481 | } | |
1482 | #endif | |
1483 | return(pmap_testbit(pa, PG_U)); | |
1484 | } | |
1485 | ||
1486 | /* | |
1487 | * pmap_is_modified: | |
1488 | * | |
1489 | * Return whether or not the specified physical page is modified | |
1490 | * by any physical maps. | |
1491 | */ | |
1492 | ||
1493 | boolean_t | |
1494 | pmap_is_modified(pa) | |
1495 | vm_offset_t pa; | |
1496 | { | |
1497 | #ifdef DEBUG | |
1498 | if (pmapdebug & PDB_FOLLOW) { | |
1499 | boolean_t rv = pmap_testbit(pa, PG_M); | |
1500 | printf("pmap_is_modified(%x) -> %c", pa, "FT"[rv]); | |
1501 | return(rv); | |
1502 | } | |
1503 | #endif | |
1504 | return(pmap_testbit(pa, PG_M)); | |
1505 | } | |
1506 | ||
1507 | vm_offset_t | |
1508 | pmap_phys_address(ppn) | |
1509 | int ppn; | |
1510 | { | |
1511 | return(i386_ptob(ppn)); | |
1512 | } | |
1513 | ||
1514 | /* | |
1515 | * Miscellaneous support routines follow | |
1516 | */ | |
1517 | ||
1518 | i386_protection_init() | |
1519 | { | |
1520 | register int *kp, prot; | |
1521 | ||
1522 | kp = protection_codes; | |
1523 | for (prot = 0; prot < 8; prot++) { | |
1524 | switch (prot) { | |
1525 | case VM_PROT_NONE | VM_PROT_NONE | VM_PROT_NONE: | |
1526 | *kp++ = 0; | |
1527 | break; | |
1528 | case VM_PROT_READ | VM_PROT_NONE | VM_PROT_NONE: | |
1529 | case VM_PROT_READ | VM_PROT_NONE | VM_PROT_EXECUTE: | |
1530 | case VM_PROT_NONE | VM_PROT_NONE | VM_PROT_EXECUTE: | |
1531 | *kp++ = PG_RO; | |
1532 | break; | |
1533 | case VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_NONE: | |
1534 | case VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_EXECUTE: | |
1535 | case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_NONE: | |
1536 | case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE: | |
1537 | *kp++ = PG_RW; | |
1538 | break; | |
1539 | } | |
1540 | } | |
1541 | } | |
1542 | ||
1543 | boolean_t | |
1544 | pmap_testbit(pa, bit) | |
1545 | register vm_offset_t pa; | |
1546 | int bit; | |
1547 | { | |
1548 | register pv_entry_t pv; | |
1549 | register int *pte, ix; | |
1550 | int s; | |
1551 | ||
1552 | if (pa < vm_first_phys || pa >= vm_last_phys) | |
1553 | return(FALSE); | |
1554 | ||
1555 | pv = pa_to_pvh(pa); | |
1556 | s = splimp(); | |
1557 | /* | |
1558 | * Check saved info first | |
1559 | */ | |
1560 | if (pmap_attributes[pa_index(pa)] & bit) { | |
1561 | splx(s); | |
1562 | return(TRUE); | |
1563 | } | |
1564 | /* | |
1565 | * Not found, check current mappings returning | |
1566 | * immediately if found. | |
1567 | */ | |
1568 | if (pv->pv_pmap != NULL) { | |
1569 | for (; pv; pv = pv->pv_next) { | |
1570 | pte = (int *) pmap_pte(pv->pv_pmap, pv->pv_va); | |
1571 | ix = 0; | |
1572 | do { | |
1573 | if (*pte++ & bit) { | |
1574 | splx(s); | |
1575 | return(TRUE); | |
1576 | } | |
1577 | } while (++ix != i386pagesperpage); | |
1578 | } | |
1579 | } | |
1580 | splx(s); | |
1581 | return(FALSE); | |
1582 | } | |
1583 | ||
1584 | pmap_changebit(pa, bit, setem) | |
1585 | register vm_offset_t pa; | |
1586 | int bit; | |
1587 | boolean_t setem; | |
1588 | { | |
1589 | register pv_entry_t pv; | |
1590 | register int *pte, npte, ix; | |
1591 | vm_offset_t va; | |
1592 | int s; | |
1593 | boolean_t firstpage = TRUE; | |
1594 | ||
1595 | #ifdef DEBUG | |
1596 | if (pmapdebug & PDB_BITS) | |
1597 | printf("pmap_changebit(%x, %x, %s)", | |
1598 | pa, bit, setem ? "set" : "clear"); | |
1599 | #endif | |
1600 | if (pa < vm_first_phys || pa >= vm_last_phys) | |
1601 | return; | |
1602 | ||
1603 | pv = pa_to_pvh(pa); | |
1604 | s = splimp(); | |
1605 | /* | |
1606 | * Clear saved attributes (modify, reference) | |
1607 | */ | |
1608 | if (!setem) | |
1609 | pmap_attributes[pa_index(pa)] &= ~bit; | |
1610 | /* | |
1611 | * Loop over all current mappings setting/clearing as appropos | |
1612 | * If setting RO do we need to clear the VAC? | |
1613 | */ | |
1614 | if (pv->pv_pmap != NULL) { | |
1615 | #ifdef DEBUG | |
1616 | int toflush = 0; | |
1617 | #endif | |
1618 | for (; pv; pv = pv->pv_next) { | |
1619 | #ifdef DEBUG | |
1620 | toflush |= (pv->pv_pmap == kernel_pmap) ? 2 : 1; | |
1621 | #endif | |
1622 | va = pv->pv_va; | |
1623 | ||
1624 | /* | |
1625 | * XXX don't write protect pager mappings | |
1626 | */ | |
1627 | if (bit == PG_RO) { | |
1628 | extern vm_offset_t pager_sva, pager_eva; | |
1629 | ||
1630 | if (va >= pager_sva && va < pager_eva) | |
1631 | continue; | |
1632 | } | |
1633 | ||
1634 | pte = (int *) pmap_pte(pv->pv_pmap, va); | |
1635 | ix = 0; | |
1636 | do { | |
1637 | if (setem) | |
1638 | npte = *pte | bit; | |
1639 | else | |
1640 | npte = *pte & ~bit; | |
1641 | if (*pte != npte) { | |
1642 | *pte = npte; | |
1643 | /*TBIS(va);*/ | |
1644 | } | |
1645 | va += I386_PAGE_SIZE; | |
1646 | pte++; | |
1647 | } while (++ix != i386pagesperpage); | |
1648 | ||
1649 | if (curproc && pv->pv_pmap == &curproc->p_vmspace->vm_pmap) | |
1650 | pmap_activate(pv->pv_pmap, (struct pcb *)curproc->p_addr); | |
1651 | } | |
1652 | #ifdef somethinglikethis | |
1653 | if (setem && bit == PG_RO && (pmapvacflush & PVF_PROTECT)) { | |
1654 | if ((pmapvacflush & PVF_TOTAL) || toflush == 3) | |
1655 | DCIA(); | |
1656 | else if (toflush == 2) | |
1657 | DCIS(); | |
1658 | else | |
1659 | DCIU(); | |
1660 | } | |
1661 | #endif | |
1662 | } | |
1663 | splx(s); | |
1664 | } | |
1665 | ||
1666 | #ifdef DEBUG | |
1667 | pmap_pvdump(pa) | |
1668 | vm_offset_t pa; | |
1669 | { | |
1670 | register pv_entry_t pv; | |
1671 | ||
1672 | printf("pa %x", pa); | |
1673 | for (pv = pa_to_pvh(pa); pv; pv = pv->pv_next) { | |
1674 | printf(" -> pmap %x, va %x, flags %x", | |
1675 | pv->pv_pmap, pv->pv_va, pv->pv_flags); | |
1676 | pads(pv->pv_pmap); | |
1677 | } | |
1678 | printf(" "); | |
1679 | } | |
1680 | ||
1681 | #ifdef notyet | |
1682 | pmap_check_wiring(str, va) | |
1683 | char *str; | |
1684 | vm_offset_t va; | |
1685 | { | |
1686 | vm_map_entry_t entry; | |
1687 | register int count, *pte; | |
1688 | ||
1689 | va = trunc_page(va); | |
1690 | if (!pmap_pde_v(pmap_pde(kernel_pmap, va)) || | |
1691 | !pmap_pte_v(pmap_pte(kernel_pmap, va))) | |
1692 | return; | |
1693 | ||
1694 | if (!vm_map_lookup_entry(pt_map, va, &entry)) { | |
1695 | pg("wired_check: entry for %x not found\n", va); | |
1696 | return; | |
1697 | } | |
1698 | count = 0; | |
1699 | for (pte = (int *)va; pte < (int *)(va+PAGE_SIZE); pte++) | |
1700 | if (*pte) | |
1701 | count++; | |
1702 | if (entry->wired_count != count) | |
1703 | pg("*%s*: %x: w%d/a%d\n", | |
1704 | str, va, entry->wired_count, count); | |
1705 | } | |
1706 | #endif | |
1707 | ||
1708 | /* print address space of pmap*/ | |
1709 | pads(pm) pmap_t pm; { | |
1710 | unsigned va, i, j; | |
1711 | struct pte *ptep; | |
1712 | ||
1713 | if(pm == kernel_pmap) return; | |
1714 | for (i = 0; i < 1024; i++) | |
1715 | if(pm->pm_pdir[i].pd_v) | |
1716 | for (j = 0; j < 1024 ; j++) { | |
1717 | va = (i<<22)+(j<<12); | |
1718 | if (pm == kernel_pmap && va < 0xfe000000) | |
1719 | continue; | |
1720 | if (pm != kernel_pmap && va > UPT_MAX_ADDRESS) | |
1721 | continue; | |
1722 | ptep = pmap_pte(pm, va); | |
1723 | if(pmap_pte_v(ptep)) | |
1724 | printf("%x:%x ", va, *(int *)ptep); | |
1725 | } ; | |
1726 | ||
1727 | } | |
1728 | #endif |