|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.1 (the "License"). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License.
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24:
25: /*
26: * Copyright (c) 1992 NeXT Computer, Inc.
27: *
28: * Intel386 Family: Private definitions for pmap.
29: *
30: * HISTORY
31: *
32: * 10 April 1992 ? at NeXT
33: * Created.
34: */
35:
36: /*
37: * For each managed physical page, there is a list of all currently
38: * valid virtual mappings of that page.
39: */
40: typedef struct pv_entry {
41: struct pv_entry * next; /* next pv_entry */
42: pmap_t pmap; /* pmap where mapping lies */
43: vm_offset_t va; /* virtual address for mapping */
44: } pv_head_t, *pv_entry_t;
45:
46: #define PV_ENTRY_NULL ((pv_entry_t) 0)
47:
48: zone_t pv_entry_zone; /* zone of pv_entry structures */
49:
50: /*
51: * First and last physical addresses that we maintain any information
52: * for. Initialized to zero so that pmap operations done before
53: * pmap_init won't touch any non-existent structures.
54: */
55: vm_offset_t vm_first_phys = (vm_offset_t) 0;
56: vm_offset_t vm_last_phys = (vm_offset_t) 0;
57: #define managed_page(pa) \
58: ((pa) >= vm_first_phys && (pa) < vm_last_phys)
59: boolean_t pmap_initialized = FALSE;/* Has pmap_init completed? */
60:
61: /*
62: * Page descriptor -
63: * Maintained for each managed page.
64: */
65: typedef struct pg_desc {
66: pv_head_t pv_list; /* head of pv list for page */
67: struct pg_exten * pg_exten; /* page extension for page */
68: unsigned int page_attrib :8, /* page attributes: */
69: #define PG_DIRTY 0x01 /* page has been modified */
70: #define PG_REFER 0x02 /* page has been referenced */
71: :0;
72: } pg_desc_t;
73:
74: #define PG_DESC_NULL ((pg_desc_t *) 0)
75:
76: pg_desc_t *pg_desc_tbl;
77: unsigned int pg_first_phys;
78:
79: #define pg_desc(pa) \
80: (&pg_desc_tbl[phys_to_pfn((unsigned int)(pa) - \
81: pg_first_phys) >> (ptes_per_vm_page - 1)])
82:
83: #define pg_desc_pvh(pd) (&(pd)->pv_list)
84:
85: /*
86: * Page extension -
87: * Contains information for pages used
88: * as page tables or page directories.
89: * NB: the 'links' field *must* be first.
90: */
91: typedef struct pg_exten {
92: queue_chain_t links; /* active/free queue links */
93: struct pg_desc * pg_desc; /* page descriptor for page */
94: unsigned int phys; /* physical address of page */
95: struct pmap * pmap; /* PT: which address map */
96: vm_offset_t offset; /* PT: what offset in map */
97: unsigned int alloc_count :16, /* PT: mappings, PD: pg dirs */
98: wired_count :16; /* PT: wired mappings */
99: unsigned int alloc_bmap :8, /* PD: alloc map */
100: unrefd_age :8, /* PT: # aging ticks */
101: :0;
102: } pg_exten_t;
103:
104: #define PG_EXTEN_NULL ((pg_exten_t *) 0)
105:
106: zone_t pg_exten_zone;
107:
108: /*
109: * A queue of free page tables is
110: * maintained which is emptied by
111: * pmap_update(). This strategy is
112: * used for two reasons. First, it is
113: * inconvenient to actually free the pages
114: * at the point in the code where it would
115: * be necessary. Secondly, keeping easily
116: * available spare page tables around makes
117: * pmap expansions quicker.
118: */
119: queue_head_t pt_active_queue, pt_free_queue;
120: int pt_active_count, pt_free_count;
121: int pt_alloc_count;
122:
123: static __inline__
124: void
125: pt_active_add(
126: pg_exten_t *pe
127: )
128: {
129: enqueue_tail(&pt_active_queue, (queue_entry_t)pe);
130: pt_active_count++;
131: }
132:
133: static __inline__
134: void
135: pt_active_remove(
136: pg_exten_t *pe
137: )
138: {
139: remqueue(&pt_active_queue, (queue_entry_t)pe);
140: pt_active_count--;
141: }
142:
143: static __inline__
144: pg_exten_t *
145: pt_free_obtain(
146: void
147: )
148: {
149: pg_exten_t *pe;
150:
151: if (((queue_entry_t)pe = dequeue_head(&pt_free_queue)))
152: pt_free_count--;
153:
154: return (pe);
155: }
156:
157: static __inline__
158: void
159: pt_free_add(
160: pg_exten_t *pe
161: )
162: {
163: enqueue_tail(&pt_free_queue, (queue_entry_t)pe);
164: pt_free_count++;
165: }
166:
167: /*
168: * A queue of pages that contain free
169: * page directories is maintained. This
170: * is also emptied by pmap_update() (if
171: * possible). This added complexity is
172: * needed to avoid wasting memory when
173: * running with a large MI page size.
174: */
175: queue_head_t pd_free_queue;
176: int pd_free_count;
177: int pd_alloc_count;
178:
179: static __inline__
180: pg_exten_t *
181: pd_free_obtain(
182: void
183: )
184: {
185: if (queue_empty(&pd_free_queue))
186: return (0);
187:
188: return ((pg_exten_t *) queue_first(&pd_free_queue));
189: }
190:
191: static __inline__
192: void
193: pd_free_add(
194: pg_exten_t *pe
195: )
196: {
197: enqueue_head(&pd_free_queue, (queue_entry_t)pe);
198: pd_free_count++;
199: }
200:
201: static __inline__
202: void
203: pd_free_remove(
204: pg_exten_t *pe
205: )
206: {
207: remqueue(&pd_free_queue, (queue_entry_t)pe);
208: pd_free_count--;
209: }
210:
211: /*
212: * A section is defined to be the amount
213: * of address space mapped by a single
214: * page table.
215: */
216:
217: /*
218: * Machine dependent sections.
219: */
220: #define I386_SECTBYTES ((I386_PGBYTES / sizeof (pt_entry_t)) * \
221: I386_PGBYTES)
222: #define i386_round_section(x) ((((unsigned int)(x)) + I386_SECTBYTES - 1) & \
223: ~(I386_SECTBYTES - 1))
224: #define i386_trunc_section(x) (((unsigned int)(x)) & ~(I386_SECTBYTES - 1))
225:
226: /*
227: * Machine independent sections.
228: */
229: #define SECTION_SIZE ((PAGE_SIZE / sizeof (pt_entry_t)) * \
230: I386_PGBYTES)
231: vm_offset_t section_size;
232: #define round_section(x) ((((unsigned int)(x)) + section_size - 1) & \
233: ~(section_size - 1))
234: #define trunc_section(x) (((unsigned int)(x)) & ~(section_size - 1))
235:
236: /*
237: * Locking and TLB invalidation
238: */
239:
240: /*
241: * Locking Protocols:
242: *
243: * There are two structures in the pmap module that need locking:
244: * the pmaps themselves, and the per-page pv_lists (which are locked
245: * by locking the pv_lock_table entry that corresponds to the pv_head
246: * for the list in question.) Most routines want to lock a pmap and
247: * then do operations in it that require pv_list locking -- however
248: * pmap_remove_all and pmap_copy_on_write operate on a physical page
249: * basis and want to do the locking in the reverse order, i.e. lock
250: * a pv_list and then go through all the pmaps referenced by that list.
251: * To protect against deadlock between these two cases, the pmap_lock
252: * is used. There are three different locking protocols as a result:
253: *
254: * 1. pmap operations only (pmap_extract, pmap_access, ...) Lock only
255: * the pmap.
256: *
257: * 2. pmap-based operations (pmap_enter, pmap_remove, ...) Get a read
258: * lock on the pmap_lock (shared read), then lock the pmap
259: * and finally the pv_lists as needed [i.e. pmap lock before
260: * pv_list lock.]
261: *
262: * 3. pv_list-based operations (pmap_remove_all, pmap_copy_on_write, ...)
263: * Get a write lock on the pmap_lock (exclusive write); this
264: * also guaranteees exclusive access to the pv_lists. Lock the
265: * pmaps as needed.
266: *
267: * At no time may any routine hold more than one pmap lock or more than
268: * one pv_list lock. Because interrupt level routines can allocate
269: * mbufs and cause pmap_enter's, the pmap_lock and the lock on the
270: * kernel_pmap can only be held at splvm.
271: *
272: * The dev_addr list is protected by using a write lock on the
273: * pmap_lock. Lookups are expected to be cheap.
274: */
275:
276: #define SPLVM(spl) { spl = splvm(); }
277: #define SPLX(spl) { splx(spl); }
278:
279: #define PMAP_READ_LOCK(pmap, spl) SPLVM(spl)
280: #define PMAP_WRITE_LOCK(spl) SPLVM(spl)
281: #define PMAP_READ_UNLOCK(pmap, spl) SPLX(spl)
282: #define PMAP_WRITE_UNLOCK(spl) SPLX(spl)
283: #define PMAP_WRITE_TO_READ_LOCK(pmap)
284:
285: #define LOCK_PVH(index)
286: #define UNLOCK_PVH(index)
287:
288: #define PMAP_UPDATE_TLBS(pmap, s, e) \
289: pmap_update_tlbs((pmap), (s), (e))
290:
291: struct {
292: int total;
293: int single;
294: } tlb_stat;
295:
296: /*
297: * Kernel pmap is statically
298: * allocated.
299: */
300: struct pmap kernel_pmap_store;
301: pmap_t kernel_pmap;
302:
303: /*
304: * Zone to allocate new
305: * pmaps from.
306: */
307: zone_t pmap_zone;
308:
309: int ptes_per_vm_page;
310:
311: static void pmap_expand(
312: pmap_t pmap,
313: vm_offset_t va);
314: static void pmap_allocate_mapping(
315: pmap_t pmap,
316: vm_offset_t va,
317: boolean_t wired);
318: static void pmap_deallocate_mappings(
319: pmap_t pmap,
320: vm_offset_t va,
321: int count,
322: int unwire_count,
323: boolean_t free_table);
324: static void pmap_wire_mapping(
325: pmap_t pmap,
326: vm_offset_t va);
327: static void pmap_unwire_mapping(
328: pmap_t pmap,
329: vm_offset_t va);
330: static void pmap_remove_range(
331: pmap_t pmap,
332: vm_offset_t start,
333: vm_offset_t end,
334: boolean_t free_table);
335: static void pmap_clear_page_attrib(
336: vm_offset_t pa,
337: int attrib);
338: static boolean_t pmap_check_page_attrib(
339: vm_offset_t pa,
340: int attrib);
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.