]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
55e303ae | 2 | * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved. |
1c79356b A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
43866e37 | 6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. |
1c79356b | 7 | * |
43866e37 A |
8 | * This file contains Original Code and/or Modifications of Original Code |
9 | * as defined in and that are subject to the Apple Public Source License | |
10 | * Version 2.0 (the 'License'). You may not use this file except in | |
11 | * compliance with the License. Please obtain a copy of the License at | |
12 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
13 | * file. | |
14 | * | |
15 | * The Original Code and all software distributed under the License are | |
16 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
1c79356b A |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
43866e37 A |
19 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
20 | * Please see the License for the specific language governing rights and | |
21 | * limitations under the License. | |
1c79356b A |
22 | * |
23 | * @APPLE_LICENSE_HEADER_END@ | |
24 | */ | |
25 | /* | |
26 | * This file is used to maintain the virtual to real mappings for a PowerPC machine. | |
27 | * The code herein is primarily used to bridge between the pmap layer and the hardware layer. | |
28 | * Currently, some of the function of this module is contained within pmap.c. We may want to move | |
29 | * all of this into it (or most anyway) for the sake of performance. We shall see as we write it. | |
30 | * | |
31 | * We also depend upon the structure of the phys_entry control block. We do put some processor | |
32 | * specific stuff in there. | |
33 | * | |
34 | */ | |
35 | ||
36 | #include <cpus.h> | |
37 | #include <debug.h> | |
38 | #include <mach_kgdb.h> | |
39 | #include <mach_vm_debug.h> | |
40 | #include <db_machine_commands.h> | |
41 | ||
42 | #include <kern/thread.h> | |
43 | #include <kern/thread_act.h> | |
44 | #include <mach/vm_attributes.h> | |
45 | #include <mach/vm_param.h> | |
55e303ae | 46 | #include <vm/vm_fault.h> |
1c79356b A |
47 | #include <vm/vm_kern.h> |
48 | #include <vm/vm_map.h> | |
49 | #include <vm/vm_page.h> | |
50 | #include <kern/spl.h> | |
51 | ||
52 | #include <kern/misc_protos.h> | |
55e303ae | 53 | #include <ppc/exception.h> |
1c79356b A |
54 | #include <ppc/misc_protos.h> |
55 | #include <ppc/proc_reg.h> | |
56 | ||
57 | #include <vm/pmap.h> | |
58 | #include <ppc/pmap.h> | |
1c79356b A |
59 | #include <ppc/mem.h> |
60 | ||
61 | #include <ppc/new_screen.h> | |
62 | #include <ppc/Firmware.h> | |
63 | #include <ppc/mappings.h> | |
64 | #include <ddb/db_output.h> | |
65 | ||
55e303ae | 66 | #include <console/video_console.h> /* (TEST/DEBUG) */ |
1c79356b A |
67 | |
68 | #define PERFTIMES 0 | |
69 | ||
1c79356b A |
70 | vm_map_t mapping_map = VM_MAP_NULL; |
71 | ||
55e303ae | 72 | unsigned int incrVSID = 0; /* VSID increment value */ |
1c79356b | 73 | unsigned int mappingdeb0 = 0; |
55e303ae A |
74 | unsigned int mappingdeb1 = 0; |
75 | int ppc_max_adrsp; /* Maximum address spaces */ | |
76 | ||
77 | addr64_t *mapdebug; /* (BRINGUP) */ | |
78 | extern unsigned int DebugWork; /* (BRINGUP) */ | |
79 | ||
1c79356b | 80 | extern unsigned int hash_table_size; |
55e303ae A |
81 | |
82 | void mapping_verify(void); | |
83 | void mapping_phys_unused(ppnum_t pa); | |
84 | ||
1c79356b A |
85 | /* |
86 | * ppc_prot translates from the mach representation of protections to the PPC version. | |
0b4e3aa0 A |
87 | * We also allow for a direct setting of the protection bits. This extends the mach |
88 | * concepts to allow the greater control we need for Virtual Machines (VMM). | |
1c79356b A |
89 | * Calculation of it like this saves a memory reference - and maybe a couple of microseconds. |
90 | * It eliminates the used of this table. | |
0b4e3aa0 | 91 | * unsigned char ppc_prot[16] = { 0, 3, 2, 2, 3, 3, 2, 2, 0, 1, 2, 3, 0, 1, 2, 3 }; |
1c79356b A |
92 | */ |
93 | ||
0b4e3aa0 | 94 | #define ppc_prot(p) ((0xE4E4AFAC >> (p << 1)) & 3) |
1c79356b A |
95 | |
96 | /* | |
97 | * About PPC VSID generation: | |
98 | * | |
99 | * This function is called to generate an address space ID. This space ID must be unique within | |
100 | * the system. For the PowerPC, it is used to build the VSID. We build a VSID in the following | |
101 | * way: space ID << 4 | segment. Since a VSID is 24 bits, and out of that, we reserve the last | |
102 | * 4, so, we can have 2^20 (2M) unique IDs. Each pmap has a unique space ID, so we should be able | |
103 | * to have 2M pmaps at a time, which we couldn't, we'd run out of memory way before then. The | |
104 | * problem is that only a certain number of pmaps are kept in a free list and if that is full, | |
105 | * they are release. This causes us to lose track of what space IDs are free to be reused. | |
106 | * We can do 4 things: 1) not worry about it, 2) keep all free pmaps, 3) rebuild all mappings | |
107 | * when the space ID wraps, or 4) scan the list of pmaps and find a free one. | |
108 | * | |
109 | * Yet another consideration is the hardware use of the VSID. It is used as part of the hash | |
110 | * calculation for virtual address lookup. An improperly chosen value could potentially cause | |
111 | * too many hashes to hit the same bucket, causing PTEG overflows. The actual hash function | |
112 | * is (page index XOR vsid) mod number of ptegs. For a 32MB machine, using the suggested | |
113 | * hash table size, there are 2^12 (8192) PTEGs. Remember, though, that the bottom 4 bits | |
114 | * are reserved for the segment number, which means that we really have 2^(12-4) 512 space IDs | |
115 | * before we start hashing to the same buckets with the same vaddrs. Also, within a space ID, | |
116 | * every 8192 pages (32MB) within a segment will hash to the same bucket. That's 8 collisions | |
117 | * per segment. So, a scan of every page for 256MB would fill 32 PTEGs completely, but | |
118 | * with no overflow. I don't think that this is a problem. | |
119 | * | |
120 | * There may be a problem with the space ID, though. A new space ID is generate (mainly) | |
121 | * whenever there is a fork. There shouldn't really be any problem because (for a 32MB | |
122 | * machine) we can have 512 pmaps and still not have hash collisions for the same address. | |
123 | * The potential problem, though, is if we get long-term pmaps that have space IDs that are | |
124 | * the same modulo 512. We can reduce this problem by having the segment number be bits | |
125 | * 0-3 of the space ID rather than 20-23. Doing this means that, in effect, corresponding | |
126 | * vaddrs in different segments hash to the same PTEG. While this is somewhat of a problem, | |
127 | * I don't think that it is as signifigant as the other, so, I'll make the space ID | |
128 | * with segment first. | |
129 | * | |
130 | * The final, and biggest problem is the wrap, which will happen every 2^20 space IDs. | |
131 | * While this is a problem that should only happen in periods counted in weeks, it can and | |
132 | * will happen. This is assuming a monotonically increasing space ID. If we were to search | |
133 | * for an inactive space ID, there could not be a wrap until there was 2^20 concurrent space IDs. | |
134 | * That's pretty unlikely to happen. There couldn't be enough storage to support a million tasks. | |
135 | * | |
136 | * So, what we do is to keep all active pmaps in a chain (anchored from kernel_pmap and | |
137 | * locked by free_pmap_lock) that is sorted in VSID sequence order. | |
138 | * | |
139 | * Whenever we need a VSID, we walk the list looking for the next in the sequence from | |
140 | * the last that was freed. The we allocate that. | |
141 | * | |
142 | * NOTE: We must be called with interruptions off and free_pmap_lock held. | |
143 | * | |
144 | */ | |
145 | ||
146 | /* | |
147 | * mapping_init(); | |
148 | * Do anything that needs to be done before the mapping system can be used. | |
149 | * Hash table must be initialized before we call this. | |
150 | * | |
151 | * Calculate the SID increment. Currently we use size^(1/2) + size^(1/4) + 1; | |
152 | */ | |
153 | ||
154 | void mapping_init(void) { | |
155 | ||
55e303ae | 156 | unsigned int tmp, maxeff, rwidth; |
d7e50217 | 157 | |
55e303ae | 158 | ppc_max_adrsp = maxAdrSp; /* Set maximum address spaces */ |
1c79356b | 159 | |
55e303ae A |
160 | maxeff = 32; /* Assume 32-bit */ |
161 | if(per_proc_info[0].pf.Available & pf64Bit) maxeff = 64; /* Is this a 64-bit machine? */ | |
d7e50217 | 162 | |
55e303ae A |
163 | rwidth = per_proc_info[0].pf.pfMaxVAddr - maxAdrSpb; /* Reduce address width by width of address space ID */ |
164 | if(rwidth > maxeff) rwidth = maxeff; /* If we still have more virtual than effective, clamp at effective */ | |
de355530 | 165 | |
55e303ae A |
166 | vm_max_address = 0xFFFFFFFFFFFFFFFFULL >> (64 - rwidth); /* Get maximum effective address supported */ |
167 | vm_max_physical = 0xFFFFFFFFFFFFFFFFULL >> (64 - per_proc_info[0].pf.pfMaxPAddr); /* Get maximum physical address supported */ | |
de355530 | 168 | |
55e303ae A |
169 | if(per_proc_info[0].pf.Available & pf64Bit) { /* Are we 64 bit? */ |
170 | tmp = 12; /* Size of hash space */ | |
171 | } | |
172 | else { | |
173 | __asm__ volatile("cntlzw %0, %1" : "=r" (tmp) : "r" (hash_table_size)); /* Get number of leading 0s */ | |
174 | tmp = 32 - tmp; /* Size of hash space */ | |
175 | } | |
de355530 | 176 | |
55e303ae A |
177 | incrVSID = 1 << ((tmp + 1) >> 1); /* Get ceiling of sqrt of table size */ |
178 | incrVSID |= 1 << ((tmp + 1) >> 2); /* Get ceiling of quadroot of table size */ | |
179 | incrVSID |= 1; /* Set bit and add 1 */ | |
de355530 | 180 | |
55e303ae | 181 | return; |
1c79356b | 182 | |
de355530 | 183 | } |
1c79356b | 184 | |
55e303ae | 185 | |
1c79356b | 186 | /* |
55e303ae A |
187 | * mapping_remove(pmap_t pmap, addr64_t va); |
188 | * Given a pmap and virtual address, this routine finds the mapping and unmaps it. | |
189 | * The mapping block will be added to | |
190 | * the free list. If the free list threshold is reached, garbage collection will happen. | |
0b4e3aa0 | 191 | * |
55e303ae A |
192 | * We also pass back the next higher mapped address. This is done so that the higher level |
193 | * pmap_remove function can release a range of addresses simply by calling mapping_remove | |
194 | * in a loop until it finishes the range or is returned a vaddr of 0. | |
0b4e3aa0 | 195 | * |
55e303ae | 196 | * Note that if the mapping is not found, we return the next VA ORed with 1 |
0b4e3aa0 A |
197 | * |
198 | */ | |
0b4e3aa0 | 199 | |
55e303ae A |
200 | addr64_t mapping_remove(pmap_t pmap, addr64_t va) { /* Remove a single mapping for this VADDR |
201 | Returns TRUE if a mapping was found to remove */ | |
0b4e3aa0 | 202 | |
55e303ae A |
203 | mapping *mp; |
204 | addr64_t nextva; | |
de355530 | 205 | |
55e303ae | 206 | disable_preemption(); /* Don't change threads */ |
1c79356b | 207 | |
55e303ae A |
208 | while(1) { /* Keep trying until we truely fail */ |
209 | mp = hw_rem_map(pmap, va, &nextva); /* Remove a mapping from this pmap */ | |
210 | if(((unsigned int)mp & mapRetCode) != mapRtRemove) break; /* If it is gone, we are done */ | |
1c79356b | 211 | } |
1c79356b | 212 | |
55e303ae | 213 | enable_preemption(); /* Thread change ok */ |
de355530 | 214 | |
55e303ae | 215 | if(!mp) return (nextva | 1); /* Nothing found to unmap */ |
de355530 | 216 | |
55e303ae | 217 | if((unsigned int)mp & mapRetCode) { /* Was there a failure? */ |
de355530 | 218 | |
55e303ae A |
219 | panic("mapping_remove: hw_rem_map failed - pmap = %08X, va = %016llX, code = %08X\n", |
220 | pmap, va, mp); | |
de355530 | 221 | } |
de355530 | 222 | |
55e303ae | 223 | mapping_free(mp); /* Add mapping to the free list */ |
1c79356b | 224 | |
55e303ae A |
225 | return nextva; /* Tell them we did it */ |
226 | } | |
de355530 | 227 | |
1c79356b | 228 | /* |
55e303ae | 229 | * mapping_make(pmap, va, pa, flags, size, prot) - map a virtual address to a real one |
1c79356b A |
230 | * |
231 | * This routine takes the given parameters, builds a mapping block, and queues it into the | |
232 | * correct lists. | |
233 | * | |
55e303ae A |
234 | * pmap (virtual address) is the pmap to map into |
235 | * va (virtual address) is the 64-bit virtual address that is being mapped | |
236 | * pa (physical page number) is the physical page number (i.e., physcial address >> 12). This is | |
237 | * a 32-bit quantity. | |
238 | * Flags: | |
239 | * block if 1, mapping is a block, size parameter is used. Note: we do not keep | |
240 | * reference and change information or allow protection changes of blocks. | |
241 | * any changes must first unmap and then remap the area. | |
242 | * use attribute Use specified attributes for map, not defaults for physical page | |
243 | * perm Mapping is permanent | |
244 | * cache inhibited Cache inhibited (used if use attribute or block set ) | |
245 | * guarded Guarded access (used if use attribute or block set ) | |
246 | * size size of block (not used if not block) | |
247 | * prot VM protection bits | |
248 | * attr Cachability/Guardedness | |
249 | * | |
250 | * Returns 0 if mapping was successful. Returns vaddr that overlaps/collides. | |
251 | * Returns 1 for any other failure. | |
252 | * | |
253 | * Note that we make an assumption that all memory in the range 0f 0x0000000080000000 to 0x00000000FFFFFFFF is reserved | |
254 | * for I/O and default the cache attrubutes appropriately. The caller is free to set whatever they want however. | |
255 | * | |
256 | * If there is any physical page that is not found in the physent table, the mapping is forced to be a | |
257 | * block mapping of length 1. This keeps us from trying to update a physent during later mapping use, | |
258 | * e.g., fault handling. | |
259 | * | |
1c79356b | 260 | * |
1c79356b A |
261 | */ |
262 | ||
55e303ae | 263 | addr64_t mapping_make(pmap_t pmap, addr64_t va, ppnum_t pa, unsigned int flags, unsigned int size, vm_prot_t prot) { /* Make an address mapping */ |
1c79356b | 264 | |
55e303ae A |
265 | register mapping *mp; |
266 | addr64_t colladdr; | |
267 | unsigned int pindex, mflags, pattr, wimg; | |
268 | phys_entry *physent; | |
269 | int i, nlists; | |
1c79356b | 270 | |
55e303ae | 271 | disable_preemption(); /* Don't change threads */ |
de355530 | 272 | |
55e303ae A |
273 | pindex = 0; |
274 | ||
275 | mflags = 0x01000000; /* Start building mpFlags field (busy count = 1) */ | |
1c79356b | 276 | |
55e303ae | 277 | if(!(flags & mmFlgBlock)) { /* Is this a block map? */ |
1c79356b | 278 | |
55e303ae A |
279 | size = 1; /* Set size to 1 page if not block */ |
280 | ||
281 | physent = mapping_phys_lookup(pa, &pindex); /* Get physical entry */ | |
282 | if(!physent) { /* Did we find the physical page? */ | |
283 | mflags |= mpBlock; /* Force this to a block if no physent */ | |
284 | size = 1; /* Force size to 1 page */ | |
285 | pattr = 0; /* Assume normal, non-I/O memory */ | |
286 | if((pa & 0xFFF80000) == 0x00080000) pattr = mmFlgCInhib | mmFlgGuarded; /* If this page is in I/O range, set I/O attributes */ | |
1c79356b | 287 | } |
55e303ae | 288 | else pattr = ((physent->ppLink & (ppI | ppG)) >> 4); /* Get the default attributes from physent */ |
de355530 | 289 | |
55e303ae | 290 | if(flags & mmFlgUseAttr) pattr = flags & (mmFlgCInhib | mmFlgGuarded); /* Use requested attributes */ |
1c79356b | 291 | } |
55e303ae A |
292 | else { /* This is a block */ |
293 | ||
294 | pattr = flags & (mmFlgCInhib | mmFlgGuarded); /* Use requested attributes */ | |
295 | mflags |= mpBlock; /* Show that this is a block */ | |
de355530 | 296 | } |
1c79356b | 297 | |
55e303ae A |
298 | wimg = 0x2; /* Set basic PPC wimg to 0b0010 - Coherent */ |
299 | if(pattr & mmFlgCInhib) wimg |= 0x4; /* Add cache inhibited if we need to */ | |
300 | if(pattr & mmFlgGuarded) wimg |= 0x1; /* Add guarded if we need to */ | |
1c79356b | 301 | |
55e303ae | 302 | mflags = mflags | (pindex << 16); /* Stick in the physical entry table index */ |
1c79356b | 303 | |
55e303ae | 304 | if(flags & mmFlgPerm) mflags |= mpPerm; /* Set permanent mapping */ |
1c79356b | 305 | |
55e303ae A |
306 | size = size - 1; /* Change size to offset */ |
307 | if(size > 0xFFFF) return 1; /* Leave if size is too big */ | |
1c79356b | 308 | |
55e303ae | 309 | nlists = mapSetLists(pmap); /* Set number of lists this will be on */ |
de355530 | 310 | |
55e303ae A |
311 | mp = mapping_alloc(nlists); /* Get a spare mapping block with this many lists */ |
312 | ||
313 | /* the mapping is zero except that the mpLists field is set */ | |
314 | mp->mpFlags |= mflags; /* Add in the rest of the flags to mpLists */ | |
315 | mp->mpSpace = pmap->space; /* Set the address space/pmap lookup ID */ | |
316 | mp->mpBSize = size; /* Set the size */ | |
317 | mp->mpPte = 0; /* Set the PTE invalid */ | |
318 | mp->mpPAddr = pa; /* Set the physical page number */ | |
319 | mp->mpVAddr = (va & ~mpHWFlags) | (wimg << 3) | ppc_prot(prot); /* Add the protection and attributes to the field */ | |
de355530 | 320 | |
55e303ae A |
321 | while(1) { /* Keep trying... */ |
322 | colladdr = hw_add_map(pmap, mp); /* Go add the mapping to the pmap */ | |
323 | if(!colladdr) { /* All is ok... */ | |
324 | enable_preemption(); /* Ok to switch around here */ | |
325 | return 0; /* Return... */ | |
326 | } | |
d7e50217 | 327 | |
55e303ae A |
328 | if((colladdr & mapRetCode) == mapRtRemove) { /* Is our target being removed? */ |
329 | (void)mapping_remove(pmap, colladdr); /* Yes, go help out */ | |
330 | continue; /* Try to add it now */ | |
331 | } | |
332 | ||
333 | if((colladdr & mapRetCode) == mapRtMapDup) { /* Is our target already mapped (collision mapping must be identical)? */ | |
334 | mapping_free(mp); /* Return mapping to the free list */ | |
335 | enable_preemption(); /* Ok to switch around here */ | |
336 | return 0; /* Normal return */ | |
1c79356b | 337 | } |
1c79356b | 338 | |
55e303ae A |
339 | if(colladdr != mapRtBadLk) { /* Did it collide? */ |
340 | mapping_free(mp); /* Yeah, toss the pending mapping */ | |
341 | enable_preemption(); /* Ok to switch around here */ | |
342 | return colladdr; /* Pass back the overlapping address */ | |
343 | } | |
344 | ||
345 | panic("mapping_make: hw_add_map failed - code = %08X, pmap = %08X, va = %016llX, mapping = %08X\n", | |
346 | colladdr, pmap, va, mp); /* Die dead */ | |
1c79356b A |
347 | } |
348 | ||
55e303ae | 349 | return 1; /* Leave... */ |
1c79356b A |
350 | } |
351 | ||
352 | ||
353 | /* | |
55e303ae | 354 | * mapping *mapping_find(pmap, va, *nextva, full) - Finds a mapping |
1c79356b | 355 | * |
55e303ae A |
356 | * Looks up the vaddr and returns the mapping and the next mapped va |
357 | * If full is true, it will descend through all nested pmaps to find actual mapping | |
1c79356b | 358 | * |
55e303ae | 359 | * Must be called with interruptions disabled or we can hang trying to remove found mapping. |
1c79356b | 360 | * |
55e303ae A |
361 | * Returns 0 if not found and the virtual address of the mapping if it is |
362 | * Note that the mappings busy count is bumped. It is the responsibility of the caller | |
363 | * to drop the count. If this is not done, any attempt to remove the mapping will hang. | |
1c79356b | 364 | * |
55e303ae | 365 | * NOTE: The nextva field is not valid when full is TRUE. |
1c79356b | 366 | * |
1c79356b A |
367 | * |
368 | */ | |
369 | ||
55e303ae | 370 | mapping *mapping_find(pmap_t pmap, addr64_t va, addr64_t *nextva, int full) { /* Make an address mapping */ |
de355530 | 371 | |
55e303ae A |
372 | register mapping *mp; |
373 | addr64_t curva; | |
374 | pmap_t curpmap; | |
375 | int nestdepth; | |
de355530 | 376 | |
55e303ae A |
377 | curpmap = pmap; /* Remember entry */ |
378 | nestdepth = 0; /* Set nest depth */ | |
379 | curva = (addr64_t)va; /* Set current va */ | |
de355530 | 380 | |
55e303ae | 381 | while(1) { |
1c79356b | 382 | |
55e303ae A |
383 | mp = hw_find_map(curpmap, curva, nextva); /* Find the mapping for this address */ |
384 | if((unsigned int)mp == mapRtBadLk) { /* Did we lock up ok? */ | |
385 | panic("mapping_find: pmap lock failure - rc = %08X, pmap = %08X\n", mp, curpmap); /* Die... */ | |
1c79356b | 386 | } |
55e303ae A |
387 | |
388 | if(!mp || !(mp->mpFlags & mpNest) || !full) break; /* Are we a nest or are we only going one deep? */ | |
1c79356b | 389 | |
55e303ae A |
390 | if(mp->mpFlags & mpSpecial) { /* Don't chain through a special mapping */ |
391 | mp = 0; /* Set not found */ | |
392 | break; | |
1c79356b | 393 | } |
1c79356b | 394 | |
55e303ae A |
395 | if(nestdepth++ > 64) { /* Have we nested too far down? */ |
396 | panic("mapping_find: too many nested pmaps - va = %016llX, curva = %016llX, pmap = %08X, curpmap = %08X\n", | |
397 | va, curva, pmap, curpmap); | |
1c79356b | 398 | } |
55e303ae A |
399 | |
400 | curva = curva + mp->mpNestReloc; /* Relocate va to new pmap */ | |
401 | curpmap = (pmap_t) pmapTrans[mp->mpSpace].pmapVAddr; /* Get the address of the nested pmap */ | |
402 | mapping_drop_busy(mp); /* We have everything we need from the mapping */ | |
403 | ||
1c79356b A |
404 | } |
405 | ||
55e303ae | 406 | return mp; /* Return the mapping if we found one */ |
1c79356b A |
407 | } |
408 | ||
1c79356b | 409 | /* |
55e303ae | 410 | * kern_return_t mapping_protect(pmap_t pmap, addt_t va, vm_prot_t prot, addr64_t *nextva) - change the protection of a virtual page |
1c79356b | 411 | * |
55e303ae A |
412 | * This routine takes a pmap and virtual address and changes |
413 | * the protection. If there are PTEs associated with the mappings, they will be invalidated before | |
414 | * the protection is changed. | |
1c79356b | 415 | * |
55e303ae A |
416 | * We return success if we change the protection or if there is no page mapped at va. We return failure if |
417 | * the va corresponds to a block mapped area or the mapping is permanant. | |
de355530 | 418 | * |
1c79356b A |
419 | * |
420 | */ | |
1c79356b | 421 | |
55e303ae | 422 | int mapping_protect(pmap_t pmap, addr64_t va, vm_prot_t prot, addr64_t *nextva) { /* Change protection of a virtual page */ |
1c79356b | 423 | |
55e303ae | 424 | int ret; |
de355530 | 425 | |
55e303ae | 426 | ret = hw_protect(pmap, va, ppc_prot(prot), nextva); /* Try to change the protect here */ |
1c79356b | 427 | |
55e303ae A |
428 | switch (ret) { /* Decode return code */ |
429 | ||
430 | case mapRtOK: /* Changed */ | |
431 | case mapRtNotFnd: /* Didn't find it */ | |
432 | return mapRtOK; /* Ok, return... */ | |
433 | break; | |
de355530 | 434 | |
55e303ae A |
435 | case mapRtBlock: /* Block map, just ignore request */ |
436 | case mapRtNest: /* Nested pmap, just ignore request */ | |
437 | return ret; /* Pass back return code */ | |
438 | break; | |
439 | ||
440 | default: | |
441 | panic("mapping_protect: hw_protect failed - rc = %d, pmap = %08X, va = %016llX\n", ret, pmap, va); | |
442 | ||
1c79356b A |
443 | } |
444 | ||
1c79356b | 445 | } |
1c79356b A |
446 | |
447 | /* | |
55e303ae | 448 | * void mapping_protect_phys(ppnum_t pa, vm_prot_t prot) - change the protection of a physical page |
1c79356b A |
449 | * |
450 | * This routine takes a physical entry and runs through all mappings attached to it and changes | |
451 | * the protection. If there are PTEs associated with the mappings, they will be invalidated before | |
55e303ae | 452 | * the protection is changed. There is no limitation on changes, e.g., |
1c79356b A |
453 | * higher to lower, lower to higher. |
454 | * | |
55e303ae A |
455 | * Any mapping that is marked permanent is not changed |
456 | * | |
1c79356b A |
457 | * Phys_entry is unlocked. |
458 | */ | |
459 | ||
55e303ae | 460 | void mapping_protect_phys(ppnum_t pa, vm_prot_t prot) { /* Change protection of all mappings to page */ |
1c79356b | 461 | |
55e303ae A |
462 | unsigned int pindex; |
463 | phys_entry *physent; | |
de355530 | 464 | |
55e303ae A |
465 | physent = mapping_phys_lookup(pa, &pindex); /* Get physical entry */ |
466 | if(!physent) { /* Did we find the physical page? */ | |
467 | panic("mapping_protect_phys: invalid physical page %08X\n", pa); | |
de355530 | 468 | } |
1c79356b | 469 | |
55e303ae | 470 | hw_walk_phys(physent, hwpSPrtPhy, hwpSPrtMap, hwpNoop, ppc_prot(prot)); /* Set the new protection for page and mappings */ |
de355530 | 471 | |
1c79356b A |
472 | return; /* Leave... */ |
473 | } | |
474 | ||
475 | ||
476 | /* | |
55e303ae | 477 | * void mapping_clr_mod(ppnum_t pa) - clears the change bit of a physical page |
1c79356b A |
478 | * |
479 | * This routine takes a physical entry and runs through all mappings attached to it and turns | |
55e303ae | 480 | * off the change bit. |
1c79356b A |
481 | */ |
482 | ||
55e303ae A |
483 | void mapping_clr_mod(ppnum_t pa) { /* Clears the change bit of a physical page */ |
484 | ||
485 | unsigned int pindex; | |
486 | phys_entry *physent; | |
487 | ||
488 | physent = mapping_phys_lookup(pa, &pindex); /* Get physical entry */ | |
489 | if(!physent) { /* Did we find the physical page? */ | |
490 | panic("mapping_clr_mod: invalid physical page %08X\n", pa); | |
491 | } | |
1c79356b | 492 | |
55e303ae | 493 | hw_walk_phys(physent, hwpNoop, hwpCCngMap, hwpCCngPhy, 0); /* Clear change for page and mappings */ |
1c79356b A |
494 | return; /* Leave... */ |
495 | } | |
496 | ||
497 | ||
498 | /* | |
55e303ae | 499 | * void mapping_set_mod(ppnum_t pa) - set the change bit of a physical page |
1c79356b A |
500 | * |
501 | * This routine takes a physical entry and runs through all mappings attached to it and turns | |
55e303ae | 502 | * on the change bit. |
1c79356b A |
503 | */ |
504 | ||
55e303ae A |
505 | void mapping_set_mod(ppnum_t pa) { /* Sets the change bit of a physical page */ |
506 | ||
507 | unsigned int pindex; | |
508 | phys_entry *physent; | |
509 | ||
510 | physent = mapping_phys_lookup(pa, &pindex); /* Get physical entry */ | |
511 | if(!physent) { /* Did we find the physical page? */ | |
512 | panic("mapping_set_mod: invalid physical page %08X\n", pa); | |
513 | } | |
d7e50217 | 514 | |
55e303ae | 515 | hw_walk_phys(physent, hwpNoop, hwpSCngMap, hwpSCngPhy, 0); /* Set change for page and mappings */ |
1c79356b A |
516 | return; /* Leave... */ |
517 | } | |
518 | ||
519 | ||
520 | /* | |
55e303ae | 521 | * void mapping_clr_ref(ppnum_t pa) - clears the reference bit of a physical page |
1c79356b | 522 | * |
de355530 | 523 | * This routine takes a physical entry and runs through all mappings attached to it and turns |
55e303ae | 524 | * off the reference bit. |
1c79356b A |
525 | */ |
526 | ||
55e303ae | 527 | void mapping_clr_ref(ppnum_t pa) { /* Clears the reference bit of a physical page */ |
de355530 | 528 | |
55e303ae A |
529 | unsigned int pindex; |
530 | phys_entry *physent; | |
531 | ||
532 | physent = mapping_phys_lookup(pa, &pindex); /* Get physical entry */ | |
533 | if(!physent) { /* Did we find the physical page? */ | |
534 | panic("mapping_clr_ref: invalid physical page %08X\n", pa); | |
d7e50217 | 535 | } |
55e303ae A |
536 | |
537 | hw_walk_phys(physent, hwpNoop, hwpCRefMap, hwpCRefPhy, 0); /* Clear reference for page and mappings */ | |
de355530 A |
538 | return; /* Leave... */ |
539 | } | |
540 | ||
541 | ||
542 | /* | |
55e303ae | 543 | * void mapping_set_ref(ppnum_t pa) - set the reference bit of a physical page |
de355530 A |
544 | * |
545 | * This routine takes a physical entry and runs through all mappings attached to it and turns | |
55e303ae | 546 | * on the reference bit. |
de355530 A |
547 | */ |
548 | ||
55e303ae A |
549 | void mapping_set_ref(ppnum_t pa) { /* Sets the reference bit of a physical page */ |
550 | ||
551 | unsigned int pindex; | |
552 | phys_entry *physent; | |
553 | ||
554 | physent = mapping_phys_lookup(pa, &pindex); /* Get physical entry */ | |
555 | if(!physent) { /* Did we find the physical page? */ | |
556 | panic("mapping_set_ref: invalid physical page %08X\n", pa); | |
557 | } | |
d7e50217 | 558 | |
55e303ae | 559 | hw_walk_phys(physent, hwpNoop, hwpSRefMap, hwpSRefPhy, 0); /* Set reference for page and mappings */ |
de355530 | 560 | return; /* Leave... */ |
1c79356b A |
561 | } |
562 | ||
563 | ||
564 | /* | |
55e303ae | 565 | * void mapping_tst_mod(ppnum_t pa) - test the change bit of a physical page |
1c79356b A |
566 | * |
567 | * This routine takes a physical entry and runs through all mappings attached to it and tests | |
55e303ae | 568 | * the changed bit. |
1c79356b A |
569 | */ |
570 | ||
55e303ae A |
571 | boolean_t mapping_tst_mod(ppnum_t pa) { /* Tests the change bit of a physical page */ |
572 | ||
573 | unsigned int pindex, rc; | |
574 | phys_entry *physent; | |
575 | ||
576 | physent = mapping_phys_lookup(pa, &pindex); /* Get physical entry */ | |
577 | if(!physent) { /* Did we find the physical page? */ | |
578 | panic("mapping_tst_mod: invalid physical page %08X\n", pa); | |
579 | } | |
d7e50217 | 580 | |
55e303ae A |
581 | rc = hw_walk_phys(physent, hwpTCngPhy, hwpTCngMap, hwpNoop, 0); /* Set change for page and mappings */ |
582 | return ((rc & (unsigned long)ppC) != 0); /* Leave with change bit */ | |
1c79356b A |
583 | } |
584 | ||
585 | ||
586 | /* | |
55e303ae | 587 | * void mapping_tst_ref(ppnum_t pa) - tests the reference bit of a physical page |
de355530 A |
588 | * |
589 | * This routine takes a physical entry and runs through all mappings attached to it and tests | |
55e303ae | 590 | * the reference bit. |
1c79356b A |
591 | */ |
592 | ||
55e303ae A |
593 | boolean_t mapping_tst_ref(ppnum_t pa) { /* Tests the reference bit of a physical page */ |
594 | ||
595 | unsigned int pindex, rc; | |
596 | phys_entry *physent; | |
597 | ||
598 | physent = mapping_phys_lookup(pa, &pindex); /* Get physical entry */ | |
599 | if(!physent) { /* Did we find the physical page? */ | |
600 | panic("mapping_tst_ref: invalid physical page %08X\n", pa); | |
601 | } | |
1c79356b | 602 | |
55e303ae A |
603 | rc = hw_walk_phys(physent, hwpTRefPhy, hwpTRefMap, hwpNoop, 0); /* Test reference for page and mappings */ |
604 | return ((rc & (unsigned long)ppR) != 0); /* Leave with reference bit */ | |
1c79356b A |
605 | } |
606 | ||
607 | ||
de355530 | 608 | /* |
55e303ae | 609 | * phys_ent *mapping_phys_lookup(ppnum_t pp, unsigned int *pindex) - tests the reference bit of a physical page |
de355530 | 610 | * |
55e303ae A |
611 | * This routine takes a physical page number and returns the phys_entry associated with it. It also |
612 | * calculates the bank address associated with the entry | |
613 | * the reference bit. | |
de355530 A |
614 | */ |
615 | ||
55e303ae | 616 | phys_entry *mapping_phys_lookup(ppnum_t pp, unsigned int *pindex) { /* Finds the physical entry for the page */ |
de355530 | 617 | |
55e303ae A |
618 | phys_entry *physent; |
619 | int i; | |
620 | ||
621 | for(i = 0; i < pmap_mem_regions_count; i++) { /* Walk through the list */ | |
622 | if(!(unsigned int)pmap_mem_regions[i].mrPhysTab) continue; /* Skip any empty lists */ | |
623 | if((pp < pmap_mem_regions[i].mrStart) || (pp > pmap_mem_regions[i].mrEnd)) continue; /* This isn't ours */ | |
624 | ||
625 | *pindex = (i * sizeof(mem_region_t)) / 4; /* Make the word index to this list */ | |
626 | ||
627 | return &pmap_mem_regions[i].mrPhysTab[pp - pmap_mem_regions[i].mrStart]; /* Return the physent pointer */ | |
628 | } | |
629 | ||
630 | return (phys_entry *)0; /* Shucks, can't find it... */ | |
631 | ||
de355530 | 632 | } |
d7e50217 A |
633 | |
634 | ||
55e303ae A |
635 | |
636 | ||
1c79356b A |
637 | /* |
638 | * mapping_adjust(void) - Releases free mapping blocks and/or allocates new ones | |
639 | * | |
640 | * This routine frees any mapping blocks queued to mapCtl.mapcrel. It also checks | |
641 | * the number of free mappings remaining, and if below a threshold, replenishes them. | |
642 | * The list will be replenshed from mapCtl.mapcrel if there are enough. Otherwise, | |
643 | * a new one is allocated. | |
644 | * | |
55e303ae A |
645 | * This routine allocates and/or frees memory and must be called from a safe place. |
646 | * Currently, vm_pageout_scan is the safest place. | |
1c79356b A |
647 | */ |
648 | ||
649 | thread_call_t mapping_adjust_call; | |
650 | static thread_call_data_t mapping_adjust_call_data; | |
651 | ||
652 | void mapping_adjust(void) { /* Adjust free mappings */ | |
653 | ||
55e303ae | 654 | kern_return_t retr = KERN_SUCCESS; |
1c79356b A |
655 | mappingblok *mb, *mbn; |
656 | spl_t s; | |
657 | int allocsize, i; | |
658 | extern int vm_page_free_count; | |
659 | ||
660 | if(mapCtl.mapcmin <= MAPPERBLOK) { | |
55e303ae | 661 | mapCtl.mapcmin = (sane_size / PAGE_SIZE) / 16; |
1c79356b A |
662 | |
663 | #if DEBUG | |
664 | kprintf("mapping_adjust: minimum entries rqrd = %08X\n", mapCtl.mapcmin); | |
665 | kprintf("mapping_adjust: free = %08X; in use = %08X; release = %08X\n", | |
666 | mapCtl.mapcfree, mapCtl.mapcinuse, mapCtl.mapcreln); | |
667 | #endif | |
668 | } | |
669 | ||
670 | s = splhigh(); /* Don't bother from now on */ | |
671 | if(!hw_lock_to((hw_lock_t)&mapCtl.mapclock, LockTimeOut)) { /* Lock the control header */ | |
672 | panic("mapping_adjust - timeout getting control lock (1)\n"); /* Tell all and die */ | |
673 | } | |
674 | ||
675 | if (mapping_adjust_call == NULL) { | |
9bccf70c A |
676 | thread_call_setup(&mapping_adjust_call_data, |
677 | (thread_call_func_t)mapping_adjust, | |
678 | (thread_call_param_t)NULL); | |
1c79356b A |
679 | mapping_adjust_call = &mapping_adjust_call_data; |
680 | } | |
681 | ||
682 | while(1) { /* Keep going until we've got enough */ | |
683 | ||
684 | allocsize = mapCtl.mapcmin - mapCtl.mapcfree; /* Figure out how much we need */ | |
685 | if(allocsize < 1) break; /* Leave if we have all we need */ | |
686 | ||
687 | if((unsigned int)(mbn = mapCtl.mapcrel)) { /* Can we rescue a free one? */ | |
688 | mapCtl.mapcrel = mbn->nextblok; /* Dequeue it */ | |
689 | mapCtl.mapcreln--; /* Back off the count */ | |
690 | allocsize = MAPPERBLOK; /* Show we allocated one block */ | |
691 | } | |
55e303ae | 692 | else { /* No free ones, try to get it */ |
1c79356b A |
693 | |
694 | allocsize = (allocsize + MAPPERBLOK - 1) / MAPPERBLOK; /* Get the number of pages we need */ | |
55e303ae | 695 | |
1c79356b A |
696 | hw_lock_unlock((hw_lock_t)&mapCtl.mapclock); /* Unlock our stuff */ |
697 | splx(s); /* Restore 'rupts */ | |
698 | ||
699 | for(; allocsize > 0; allocsize >>= 1) { /* Try allocating in descending halves */ | |
700 | retr = kmem_alloc_wired(mapping_map, (vm_offset_t *)&mbn, PAGE_SIZE * allocsize); /* Find a virtual address to use */ | |
701 | if((retr != KERN_SUCCESS) && (allocsize == 1)) { /* Did we find any memory at all? */ | |
9bccf70c | 702 | break; |
1c79356b A |
703 | } |
704 | if(retr == KERN_SUCCESS) break; /* We got some memory, bail out... */ | |
705 | } | |
55e303ae | 706 | |
1c79356b A |
707 | allocsize = allocsize * MAPPERBLOK; /* Convert pages to number of maps allocated */ |
708 | s = splhigh(); /* Don't bother from now on */ | |
709 | if(!hw_lock_to((hw_lock_t)&mapCtl.mapclock, LockTimeOut)) { /* Lock the control header */ | |
710 | panic("mapping_adjust - timeout getting control lock (2)\n"); /* Tell all and die */ | |
711 | } | |
712 | } | |
55e303ae | 713 | |
9bccf70c A |
714 | if (retr != KERN_SUCCESS) |
715 | break; /* Fail to alocate, bail out... */ | |
1c79356b A |
716 | for(; allocsize > 0; allocsize -= MAPPERBLOK) { /* Release one block at a time */ |
717 | mapping_free_init((vm_offset_t)mbn, 0, 1); /* Initialize a non-permanent block */ | |
718 | mbn = (mappingblok *)((unsigned int)mbn + PAGE_SIZE); /* Point to the next slot */ | |
719 | } | |
55e303ae | 720 | |
1c79356b A |
721 | if ((mapCtl.mapcinuse + mapCtl.mapcfree + (mapCtl.mapcreln * (MAPPERBLOK + 1))) > mapCtl.mapcmaxalloc) |
722 | mapCtl.mapcmaxalloc = mapCtl.mapcinuse + mapCtl.mapcfree + (mapCtl.mapcreln * (MAPPERBLOK + 1)); | |
723 | } | |
724 | ||
725 | if(mapCtl.mapcholdoff) { /* Should we hold off this release? */ | |
726 | mapCtl.mapcrecurse = 0; /* We are done now */ | |
727 | hw_lock_unlock((hw_lock_t)&mapCtl.mapclock); /* Unlock our stuff */ | |
728 | splx(s); /* Restore 'rupts */ | |
729 | return; /* Return... */ | |
730 | } | |
731 | ||
732 | mbn = mapCtl.mapcrel; /* Get first pending release block */ | |
733 | mapCtl.mapcrel = 0; /* Dequeue them */ | |
734 | mapCtl.mapcreln = 0; /* Set count to 0 */ | |
735 | ||
736 | hw_lock_unlock((hw_lock_t)&mapCtl.mapclock); /* Unlock our stuff */ | |
737 | splx(s); /* Restore 'rupts */ | |
738 | ||
739 | while((unsigned int)mbn) { /* Toss 'em all */ | |
740 | mb = mbn->nextblok; /* Get the next */ | |
55e303ae | 741 | |
1c79356b | 742 | kmem_free(mapping_map, (vm_offset_t) mbn, PAGE_SIZE); /* Release this mapping block */ |
55e303ae | 743 | |
1c79356b A |
744 | mbn = mb; /* Chain to the next */ |
745 | } | |
746 | ||
55e303ae | 747 | __asm__ volatile("eieio"); /* Make sure all is well */ |
1c79356b A |
748 | mapCtl.mapcrecurse = 0; /* We are done now */ |
749 | return; | |
750 | } | |
751 | ||
752 | /* | |
753 | * mapping_free(mapping *mp) - release a mapping to the free list | |
754 | * | |
755 | * This routine takes a mapping and adds it to the free list. | |
756 | * If this mapping make the block non-empty, we queue it to the free block list. | |
757 | * NOTE: we might want to queue it to the end to keep quelch the pathalogical | |
758 | * case when we get a mapping and free it repeatedly causing the block to chain and unchain. | |
759 | * If this release fills a block and we are above the threshold, we release the block | |
760 | */ | |
761 | ||
762 | void mapping_free(struct mapping *mp) { /* Release a mapping */ | |
763 | ||
764 | mappingblok *mb, *mbn; | |
765 | spl_t s; | |
55e303ae | 766 | unsigned int full, mindx, lists; |
1c79356b | 767 | |
55e303ae | 768 | mindx = ((unsigned int)mp & (PAGE_SIZE - 1)) >> 6; /* Get index to mapping */ |
1c79356b | 769 | mb = (mappingblok *)((unsigned int)mp & -PAGE_SIZE); /* Point to the mapping block */ |
55e303ae A |
770 | lists = (mp->mpFlags & mpLists); /* get #lists */ |
771 | if ((lists == 0) || (lists > kSkipListMaxLists)) /* panic if out of range */ | |
772 | panic("mapping_free: mpLists invalid\n"); | |
773 | ||
774 | #if 0 | |
775 | mp->mpFlags = 0x99999999; /* (BRINGUP) */ | |
776 | mp->mpSpace = 0x9999; /* (BRINGUP) */ | |
777 | mp->mpBSize = 0x9999; /* (BRINGUP) */ | |
778 | mp->mpPte = 0x99999998; /* (BRINGUP) */ | |
779 | mp->mpPAddr = 0x99999999; /* (BRINGUP) */ | |
780 | mp->mpVAddr = 0x9999999999999999ULL; /* (BRINGUP) */ | |
781 | mp->mpAlias = 0x9999999999999999ULL; /* (BRINGUP) */ | |
782 | mp->mpList0 = 0x9999999999999999ULL; /* (BRINGUP) */ | |
783 | mp->mpList[0] = 0x9999999999999999ULL; /* (BRINGUP) */ | |
784 | mp->mpList[1] = 0x9999999999999999ULL; /* (BRINGUP) */ | |
785 | mp->mpList[2] = 0x9999999999999999ULL; /* (BRINGUP) */ | |
786 | ||
787 | if(lists > mpBasicLists) { /* (BRINGUP) */ | |
788 | mp->mpList[3] = 0x9999999999999999ULL; /* (BRINGUP) */ | |
789 | mp->mpList[4] = 0x9999999999999999ULL; /* (BRINGUP) */ | |
790 | mp->mpList[5] = 0x9999999999999999ULL; /* (BRINGUP) */ | |
791 | mp->mpList[6] = 0x9999999999999999ULL; /* (BRINGUP) */ | |
792 | mp->mpList[7] = 0x9999999999999999ULL; /* (BRINGUP) */ | |
793 | mp->mpList[8] = 0x9999999999999999ULL; /* (BRINGUP) */ | |
794 | mp->mpList[9] = 0x9999999999999999ULL; /* (BRINGUP) */ | |
795 | mp->mpList[10] = 0x9999999999999999ULL; /* (BRINGUP) */ | |
796 | } | |
797 | #endif | |
798 | ||
1c79356b A |
799 | |
800 | s = splhigh(); /* Don't bother from now on */ | |
801 | if(!hw_lock_to((hw_lock_t)&mapCtl.mapclock, LockTimeOut)) { /* Lock the control header */ | |
802 | panic("mapping_free - timeout getting control lock\n"); /* Tell all and die */ | |
803 | } | |
804 | ||
55e303ae | 805 | full = !(mb->mapblokfree[0] | mb->mapblokfree[1]); /* See if full now */ |
1c79356b | 806 | mb->mapblokfree[mindx >> 5] |= (0x80000000 >> (mindx & 31)); /* Flip on the free bit */ |
55e303ae A |
807 | if ( lists > mpBasicLists ) { /* if big block, lite the 2nd bit too */ |
808 | mindx++; | |
809 | mb->mapblokfree[mindx >> 5] |= (0x80000000 >> (mindx & 31)); | |
810 | mapCtl.mapcfree++; | |
811 | mapCtl.mapcinuse--; | |
812 | } | |
1c79356b A |
813 | |
814 | if(full) { /* If it was full before this: */ | |
815 | mb->nextblok = mapCtl.mapcnext; /* Move head of list to us */ | |
816 | mapCtl.mapcnext = mb; /* Chain us to the head of the list */ | |
9bccf70c A |
817 | if(!((unsigned int)mapCtl.mapclast)) |
818 | mapCtl.mapclast = mb; | |
1c79356b A |
819 | } |
820 | ||
821 | mapCtl.mapcfree++; /* Bump free count */ | |
822 | mapCtl.mapcinuse--; /* Decriment in use count */ | |
823 | ||
824 | mapCtl.mapcfreec++; /* Count total calls */ | |
825 | ||
826 | if(mapCtl.mapcfree > mapCtl.mapcmin) { /* Should we consider releasing this? */ | |
55e303ae | 827 | if(((mb->mapblokfree[0] | 0x80000000) & mb->mapblokfree[1]) == 0xFFFFFFFF) { /* See if empty now */ |
1c79356b A |
828 | |
829 | if(mapCtl.mapcnext == mb) { /* Are we first on the list? */ | |
830 | mapCtl.mapcnext = mb->nextblok; /* Unchain us */ | |
831 | if(!((unsigned int)mapCtl.mapcnext)) mapCtl.mapclast = 0; /* If last, remove last */ | |
832 | } | |
833 | else { /* We're not first */ | |
834 | for(mbn = mapCtl.mapcnext; mbn != 0; mbn = mbn->nextblok) { /* Search for our block */ | |
835 | if(mbn->nextblok == mb) break; /* Is the next one our's? */ | |
836 | } | |
837 | if(!mbn) panic("mapping_free: attempt to release mapping block (%08X) not on list\n", mp); | |
838 | mbn->nextblok = mb->nextblok; /* Dequeue us */ | |
839 | if(mapCtl.mapclast == mb) mapCtl.mapclast = mbn; /* If last, make our predecessor last */ | |
840 | } | |
841 | ||
842 | if(mb->mapblokflags & mbPerm) { /* Is this permanently assigned? */ | |
843 | mb->nextblok = mapCtl.mapcnext; /* Move chain head to us */ | |
844 | mapCtl.mapcnext = mb; /* Chain us to the head */ | |
845 | if(!((unsigned int)mb->nextblok)) mapCtl.mapclast = mb; /* If last, make us so */ | |
846 | } | |
847 | else { | |
848 | mapCtl.mapcfree -= MAPPERBLOK; /* Remove the block from the free count */ | |
849 | mapCtl.mapcreln++; /* Count on release list */ | |
850 | mb->nextblok = mapCtl.mapcrel; /* Move pointer */ | |
851 | mapCtl.mapcrel = mb; /* Chain us in front */ | |
852 | } | |
853 | } | |
854 | } | |
855 | ||
856 | if(mapCtl.mapcreln > MAPFRTHRSH) { /* Do we have way too many releasable mappings? */ | |
857 | if(hw_compare_and_store(0, 1, &mapCtl.mapcrecurse)) { /* Make sure we aren't recursing */ | |
858 | thread_call_enter(mapping_adjust_call); /* Go toss some */ | |
859 | } | |
860 | } | |
861 | hw_lock_unlock((hw_lock_t)&mapCtl.mapclock); /* Unlock our stuff */ | |
862 | splx(s); /* Restore 'rupts */ | |
863 | ||
864 | return; /* Bye, dude... */ | |
865 | } | |
866 | ||
867 | ||
868 | /* | |
55e303ae | 869 | * mapping_alloc(lists) - obtain a mapping from the free list |
1c79356b | 870 | * |
55e303ae A |
871 | * This routine takes a mapping off of the free list and returns its address. |
872 | * The mapping is zeroed, and its mpLists count is set. The caller passes in | |
873 | * the number of skiplists it would prefer; if this number is greater than | |
874 | * mpBasicLists (ie, 4) then we need to allocate a 128-byte mapping, which is | |
875 | * just two consequtive free entries coallesced into one. If we cannot find | |
876 | * two consequtive free entries, we clamp the list count down to mpBasicLists | |
877 | * and return a basic 64-byte node. Our caller never knows the difference. | |
1c79356b | 878 | * |
55e303ae | 879 | * If this allocation empties a block, we remove it from the free list. |
1c79356b A |
880 | * If this allocation drops the total number of free entries below a threshold, |
881 | * we allocate a new block. | |
882 | * | |
883 | */ | |
884 | ||
55e303ae | 885 | mapping *mapping_alloc(int lists) { /* Obtain a mapping */ |
1c79356b A |
886 | |
887 | register mapping *mp; | |
888 | mappingblok *mb, *mbn; | |
889 | spl_t s; | |
890 | int mindx; | |
891 | kern_return_t retr; | |
55e303ae A |
892 | int big = (lists > mpBasicLists); /* set flag if big block req'd */ |
893 | pmap_t refpmap, ckpmap; | |
894 | unsigned int space, i; | |
895 | int ref_count; | |
896 | addr64_t va, nextva; | |
897 | extern pmap_t free_pmap_list; | |
898 | extern int free_pmap_count; | |
899 | decl_simple_lock_data(extern,free_pmap_lock) | |
900 | boolean_t found_mapping; | |
901 | boolean_t do_rescan; | |
902 | ||
1c79356b A |
903 | s = splhigh(); /* Don't bother from now on */ |
904 | if(!hw_lock_to((hw_lock_t)&mapCtl.mapclock, LockTimeOut)) { /* Lock the control header */ | |
905 | panic("mapping_alloc - timeout getting control lock\n"); /* Tell all and die */ | |
906 | } | |
907 | ||
55e303ae A |
908 | if(!((unsigned int)mapCtl.mapcnext)) { /* Are there any free mappings? */ |
909 | ||
910 | /* | |
911 | * No free mappings. First, there may be some mapping blocks on the "to be released" | |
912 | * list. If so, rescue one. Otherwise, try to steal a couple blocks worth. | |
913 | */ | |
914 | ||
915 | if(mbn = mapCtl.mapcrel) { /* Try to rescue a block from impending doom */ | |
916 | mapCtl.mapcrel = mbn->nextblok; /* Pop the queue */ | |
917 | mapCtl.mapcreln--; /* Back off the count */ | |
918 | mapping_free_init((vm_offset_t)mbn, 0, 1); /* Initialize a non-permanent block */ | |
919 | goto rescued; | |
920 | } | |
921 | ||
922 | hw_lock_unlock((hw_lock_t)&mapCtl.mapclock); | |
923 | ||
924 | simple_lock(&free_pmap_lock); | |
925 | ||
926 | if(!hw_lock_to((hw_lock_t)&mapCtl.mapclock, LockTimeOut)) { /* Lock the control header */ | |
927 | panic("mapping_alloc - timeout getting control lock\n"); /* Tell all and die */ | |
928 | } | |
929 | ||
930 | if (!((unsigned int)mapCtl.mapcnext)) { | |
931 | ||
932 | refpmap = (pmap_t)cursor_pmap->pmap_link.next; | |
933 | space = mapCtl.mapcflush.spacenum; | |
934 | while (refpmap != cursor_pmap) { | |
935 | if(((pmap_t)(refpmap->pmap_link.next))->spaceNum > space) break; | |
936 | refpmap = (pmap_t)refpmap->pmap_link.next; | |
de355530 | 937 | } |
55e303ae A |
938 | |
939 | ckpmap = refpmap; | |
940 | va = mapCtl.mapcflush.addr; | |
941 | found_mapping = FALSE; | |
942 | ||
943 | while (mapCtl.mapcfree <= (MAPPERBLOK*2)) { | |
944 | ||
945 | hw_lock_unlock((hw_lock_t)&mapCtl.mapclock); | |
946 | ||
947 | ckpmap = (pmap_t)ckpmap->pmap_link.next; | |
948 | ||
949 | if ((ckpmap->stats.resident_count != 0) && (ckpmap != kernel_pmap)) { | |
950 | do_rescan = TRUE; | |
951 | for (i=0;i<8;i++) { | |
952 | mp = hw_purge_map(ckpmap, va, &nextva); | |
953 | ||
954 | if((unsigned int)mp & mapRetCode) { | |
955 | panic("mapping_alloc: hw_purge_map failed - pmap = %08X, va = %16llX, code = %08X\n", ckpmap, va, mp); | |
956 | } | |
957 | ||
958 | if(!mp) { | |
959 | if (do_rescan) | |
960 | do_rescan = FALSE; | |
961 | else | |
962 | break; | |
963 | } else { | |
964 | mapping_free(mp); | |
965 | found_mapping = TRUE; | |
966 | } | |
967 | ||
968 | va = nextva; | |
969 | } | |
970 | } | |
971 | ||
972 | if (ckpmap == refpmap) { | |
973 | if (found_mapping == FALSE) | |
974 | panic("no valid pmap to purge mappings\n"); | |
975 | else | |
976 | found_mapping = FALSE; | |
977 | } | |
978 | ||
979 | if(!hw_lock_to((hw_lock_t)&mapCtl.mapclock, LockTimeOut)) { /* Lock the control header */ | |
980 | panic("mapping_alloc - timeout getting control lock\n"); /* Tell all and die */ | |
981 | } | |
982 | ||
9bccf70c | 983 | } |
55e303ae A |
984 | |
985 | mapCtl.mapcflush.spacenum = ckpmap->spaceNum; | |
986 | mapCtl.mapcflush.addr = nextva; | |
9bccf70c | 987 | } |
55e303ae A |
988 | |
989 | simple_unlock(&free_pmap_lock); | |
990 | } | |
991 | ||
992 | rescued: | |
993 | ||
994 | mb = mapCtl.mapcnext; | |
995 | ||
996 | if ( big ) { /* if we need a big (128-byte) mapping */ | |
997 | mapCtl.mapcbig++; /* count attempts to allocate a big mapping */ | |
998 | mbn = NULL; /* this will be prev ptr */ | |
999 | mindx = 0; | |
1000 | while( mb ) { /* loop over mapping blocks with free entries */ | |
1001 | mindx = mapalc2(mb); /* try for 2 consequtive free bits in this block */ | |
1002 | ||
1003 | if ( mindx ) break; /* exit loop if we found them */ | |
1004 | mbn = mb; /* remember previous block */ | |
1005 | mb = mb->nextblok; /* move on to next block */ | |
1006 | } | |
1007 | if ( mindx == 0 ) { /* if we couldn't find 2 consequtive bits... */ | |
1008 | mapCtl.mapcbigfails++; /* count failures */ | |
1009 | big = 0; /* forget that we needed a big mapping */ | |
1010 | lists = mpBasicLists; /* clamp list count down to the max in a 64-byte mapping */ | |
1011 | mb = mapCtl.mapcnext; /* back to the first block with a free entry */ | |
1012 | } | |
1013 | else { /* if we did find a big mapping */ | |
1014 | mapCtl.mapcfree--; /* Decrement free count twice */ | |
1015 | mapCtl.mapcinuse++; /* Bump in use count twice */ | |
1016 | if ( mindx < 0 ) { /* if we just used the last 2 free bits in this block */ | |
1017 | if (mbn) { /* if this wasn't the first block */ | |
1018 | mindx = -mindx; /* make positive */ | |
1019 | mbn->nextblok = mb->nextblok; /* unlink this one from the middle of block list */ | |
1020 | if (mb == mapCtl.mapclast) { /* if we emptied last block */ | |
1021 | mapCtl.mapclast = mbn; /* then prev block is now last */ | |
1022 | } | |
1023 | } | |
1024 | } | |
1025 | } | |
1026 | } | |
1027 | ||
1028 | if ( !big ) { /* if we need a small (64-byte) mapping */ | |
1029 | if(!(mindx = mapalc1(mb))) /* Allocate a 1-bit slot */ | |
1030 | panic("mapping_alloc - empty mapping block detected at %08X\n", mb); | |
1031 | } | |
1c79356b A |
1032 | |
1033 | if(mindx < 0) { /* Did we just take the last one */ | |
1034 | mindx = -mindx; /* Make positive */ | |
1035 | mapCtl.mapcnext = mb->nextblok; /* Remove us from the list */ | |
1036 | if(!((unsigned int)mapCtl.mapcnext)) mapCtl.mapclast = 0; /* Removed the last one */ | |
1037 | } | |
1038 | ||
1039 | mapCtl.mapcfree--; /* Decrement free count */ | |
1040 | mapCtl.mapcinuse++; /* Bump in use count */ | |
1041 | ||
1042 | mapCtl.mapcallocc++; /* Count total calls */ | |
1043 | ||
1044 | /* | |
1045 | * Note: in the following code, we will attempt to rescue blocks only one at a time. | |
1046 | * Eventually, after a few more mapping_alloc calls, we will catch up. If there are none | |
1047 | * rescueable, we will kick the misc scan who will allocate some for us. We only do this | |
1048 | * if we haven't already done it. | |
1049 | * For early boot, we are set up to only rescue one block at a time. This is because we prime | |
1050 | * the release list with as much as we need until threads start. | |
1051 | */ | |
55e303ae | 1052 | |
1c79356b A |
1053 | if(mapCtl.mapcfree < mapCtl.mapcmin) { /* See if we need to replenish */ |
1054 | if(mbn = mapCtl.mapcrel) { /* Try to rescue a block from impending doom */ | |
1055 | mapCtl.mapcrel = mbn->nextblok; /* Pop the queue */ | |
1056 | mapCtl.mapcreln--; /* Back off the count */ | |
1057 | mapping_free_init((vm_offset_t)mbn, 0, 1); /* Initialize a non-permanent block */ | |
1058 | } | |
1059 | else { /* We need to replenish */ | |
1060 | if (mapCtl.mapcfree < (mapCtl.mapcmin / 4)) { | |
1061 | if(hw_compare_and_store(0, 1, &mapCtl.mapcrecurse)) { /* Make sure we aren't recursing */ | |
1062 | thread_call_enter(mapping_adjust_call); /* Go allocate some more */ | |
1063 | } | |
1064 | } | |
1065 | } | |
1066 | } | |
1067 | ||
1068 | hw_lock_unlock((hw_lock_t)&mapCtl.mapclock); /* Unlock our stuff */ | |
1069 | splx(s); /* Restore 'rupts */ | |
1070 | ||
1071 | mp = &((mapping *)mb)[mindx]; /* Point to the allocated mapping */ | |
55e303ae A |
1072 | mp->mpFlags = lists; /* set the list count */ |
1073 | ||
1074 | ||
1c79356b A |
1075 | return mp; /* Send it back... */ |
1076 | } | |
1077 | ||
1078 | ||
1079 | void | |
1080 | consider_mapping_adjust() | |
1081 | { | |
1082 | spl_t s; | |
1083 | ||
1084 | s = splhigh(); /* Don't bother from now on */ | |
1085 | if(!hw_lock_to((hw_lock_t)&mapCtl.mapclock, LockTimeOut)) { /* Lock the control header */ | |
55e303ae | 1086 | panic("consider_mapping_adjust -- lock timeout\n"); |
1c79356b A |
1087 | } |
1088 | ||
1089 | if (mapCtl.mapcfree < (mapCtl.mapcmin / 4)) { | |
1090 | if(hw_compare_and_store(0, 1, &mapCtl.mapcrecurse)) { /* Make sure we aren't recursing */ | |
1091 | thread_call_enter(mapping_adjust_call); /* Go allocate some more */ | |
1092 | } | |
1093 | } | |
1094 | ||
1095 | hw_lock_unlock((hw_lock_t)&mapCtl.mapclock); /* Unlock our stuff */ | |
1096 | splx(s); /* Restore 'rupts */ | |
1097 | ||
1098 | } | |
1099 | ||
1100 | ||
1101 | ||
1102 | /* | |
1103 | * void mapping_free_init(mb, perm) - Adds a block of storage to the free mapping list | |
1104 | * | |
55e303ae A |
1105 | * The mapping block is a page size area on a page boundary. It contains 1 header and 63 |
1106 | * mappings. This call adds and initializes a block for use. Mappings come in two sizes, | |
1107 | * 64 and 128 bytes (the only difference is the number of skip-lists.) When we allocate a | |
1108 | * 128-byte mapping we just look for two consequtive free 64-byte mappings, so most of the | |
1109 | * code only deals with "basic" 64-byte mappings. This works for two reasons: | |
1110 | * - Only one in 256 mappings is big, so they are rare. | |
1111 | * - If we cannot find two consequtive free mappings, we just return a small one. | |
1112 | * There is no problem with doing this, except a minor performance degredation. | |
1113 | * Therefore, all counts etc in the mapping control structure are in units of small blocks. | |
1c79356b A |
1114 | * |
1115 | * The header contains a chain link, bit maps, a virtual to real translation mask, and | |
1116 | * some statistics. Bit maps map each slot on the page (bit 0 is not used because it | |
1117 | * corresponds to the header). The translation mask is the XOR of the virtual and real | |
1118 | * addresses (needless to say, the block must be wired). | |
1119 | * | |
1120 | * We handle these mappings the same way as saveareas: the block is only on the chain so | |
1121 | * long as there are free entries in it. | |
1122 | * | |
1123 | * Empty blocks are garbage collected when there are at least mapCtl.mapcmin pages worth of free | |
1124 | * mappings. Blocks marked PERM won't ever be released. | |
1125 | * | |
1126 | * If perm is negative, the mapping is initialized, but immediately queued to the mapCtl.mapcrel | |
1127 | * list. We do this only at start up time. This is done because we only allocate blocks | |
1128 | * in the pageout scan and it doesn't start up until after we run out of the initial mappings. | |
1129 | * Therefore, we need to preallocate a bunch, but we don't want them to be permanent. If we put | |
1130 | * them on the release queue, the allocate routine will rescue them. Then when the | |
1131 | * pageout scan starts, all extra ones will be released. | |
1132 | * | |
1133 | */ | |
1134 | ||
1135 | ||
1136 | void mapping_free_init(vm_offset_t mbl, int perm, boolean_t locked) { | |
1137 | /* Set's start and end of a block of mappings | |
1138 | perm indicates if the block can be released | |
1139 | or goes straight to the release queue . | |
1140 | locked indicates if the lock is held already */ | |
1141 | ||
1142 | mappingblok *mb; | |
1143 | spl_t s; | |
1144 | int i; | |
55e303ae A |
1145 | addr64_t raddr; |
1146 | ppnum_t pp; | |
1c79356b | 1147 | |
55e303ae | 1148 | mb = (mappingblok *)mbl; /* Start of area */ |
1c79356b A |
1149 | |
1150 | if(perm >= 0) { /* See if we need to initialize the block */ | |
1151 | if(perm) { | |
55e303ae | 1152 | raddr = (addr64_t)((unsigned int)mbl); /* Perm means V=R */ |
1c79356b | 1153 | mb->mapblokflags = mbPerm; /* Set perm */ |
55e303ae | 1154 | // mb->mapblokflags |= (unsigned int)mb; /* (BRINGUP) */ |
1c79356b A |
1155 | } |
1156 | else { | |
55e303ae A |
1157 | pp = pmap_find_phys(kernel_pmap, (addr64_t)mbl); /* Get the physical page */ |
1158 | if(!pp) { /* What gives? Where's the page? */ | |
1159 | panic("mapping_free_init: could not find translation for vaddr %016llX\n", (addr64_t)mbl); | |
1160 | } | |
1161 | ||
1162 | raddr = (addr64_t)pp << 12; /* Convert physical page to physical address */ | |
1c79356b | 1163 | mb->mapblokflags = 0; /* Set not perm */ |
55e303ae | 1164 | // mb->mapblokflags |= (unsigned int)mb; /* (BRINGUP) */ |
1c79356b A |
1165 | } |
1166 | ||
55e303ae | 1167 | mb->mapblokvrswap = raddr ^ (addr64_t)((unsigned int)mbl); /* Form translation mask */ |
1c79356b A |
1168 | |
1169 | mb->mapblokfree[0] = 0x7FFFFFFF; /* Set first 32 (minus 1) free */ | |
1170 | mb->mapblokfree[1] = 0xFFFFFFFF; /* Set next 32 free */ | |
1c79356b A |
1171 | } |
1172 | ||
1173 | s = splhigh(); /* Don't bother from now on */ | |
1174 | if(!locked) { /* Do we need the lock? */ | |
1175 | if(!hw_lock_to((hw_lock_t)&mapCtl.mapclock, LockTimeOut)) { /* Lock the control header */ | |
55e303ae | 1176 | panic("mapping_free_init: timeout getting control lock\n"); /* Tell all and die */ |
1c79356b A |
1177 | } |
1178 | } | |
1179 | ||
1180 | if(perm < 0) { /* Direct to release queue? */ | |
1181 | mb->nextblok = mapCtl.mapcrel; /* Move forward pointer */ | |
1182 | mapCtl.mapcrel = mb; /* Queue us on in */ | |
1183 | mapCtl.mapcreln++; /* Count the free block */ | |
1184 | } | |
1185 | else { /* Add to the free list */ | |
1186 | ||
1187 | mb->nextblok = 0; /* We always add to the end */ | |
1188 | mapCtl.mapcfree += MAPPERBLOK; /* Bump count */ | |
1189 | ||
1190 | if(!((unsigned int)mapCtl.mapcnext)) { /* First entry on list? */ | |
1191 | mapCtl.mapcnext = mapCtl.mapclast = mb; /* Chain to us */ | |
1192 | } | |
1193 | else { /* We are not the first */ | |
1194 | mapCtl.mapclast->nextblok = mb; /* Point the last to us */ | |
1195 | mapCtl.mapclast = mb; /* We are now last */ | |
1196 | } | |
1197 | } | |
1198 | ||
1199 | if(!locked) { /* Do we need to unlock? */ | |
1200 | hw_lock_unlock((hw_lock_t)&mapCtl.mapclock); /* Unlock our stuff */ | |
1201 | } | |
55e303ae A |
1202 | |
1203 | splx(s); /* Restore 'rupts */ | |
1c79356b A |
1204 | return; /* All done, leave... */ |
1205 | } | |
1206 | ||
1207 | ||
1208 | /* | |
1209 | * void mapping_prealloc(unsigned int) - Preallocates mapppings for large request | |
1210 | * | |
1211 | * No locks can be held, because we allocate memory here. | |
1212 | * This routine needs a corresponding mapping_relpre call to remove the | |
1213 | * hold off flag so that the adjust routine will free the extra mapping | |
1214 | * blocks on the release list. I don't like this, but I don't know | |
1215 | * how else to do this for now... | |
1216 | * | |
1217 | */ | |
1218 | ||
1219 | void mapping_prealloc(unsigned int size) { /* Preallocates mapppings for large request */ | |
1220 | ||
1221 | int nmapb, i; | |
1222 | kern_return_t retr; | |
1223 | mappingblok *mbn; | |
1224 | spl_t s; | |
1225 | ||
1226 | s = splhigh(); /* Don't bother from now on */ | |
1227 | if(!hw_lock_to((hw_lock_t)&mapCtl.mapclock, LockTimeOut)) { /* Lock the control header */ | |
1228 | panic("mapping_prealloc - timeout getting control lock\n"); /* Tell all and die */ | |
1229 | } | |
1230 | ||
1231 | nmapb = (size >> 12) + mapCtl.mapcmin; /* Get number of entries needed for this and the minimum */ | |
1232 | ||
1233 | mapCtl.mapcholdoff++; /* Bump the hold off count */ | |
1234 | ||
1235 | if((nmapb = (nmapb - mapCtl.mapcfree)) <= 0) { /* Do we already have enough? */ | |
1236 | hw_lock_unlock((hw_lock_t)&mapCtl.mapclock); /* Unlock our stuff */ | |
1237 | splx(s); /* Restore 'rupts */ | |
1238 | return; | |
1239 | } | |
55e303ae | 1240 | if (!hw_compare_and_store(0, 1, &mapCtl.mapcrecurse)) { /* Make sure we aren't recursing */ |
1c79356b | 1241 | hw_lock_unlock((hw_lock_t)&mapCtl.mapclock); /* Unlock our stuff */ |
55e303ae | 1242 | splx(s); /* Restore 'rupts */ |
1c79356b A |
1243 | return; |
1244 | } | |
1245 | nmapb = (nmapb + MAPPERBLOK - 1) / MAPPERBLOK; /* Get number of blocks to get */ | |
1246 | ||
1247 | hw_lock_unlock((hw_lock_t)&mapCtl.mapclock); /* Unlock our stuff */ | |
1248 | splx(s); /* Restore 'rupts */ | |
1249 | ||
1250 | for(i = 0; i < nmapb; i++) { /* Allocate 'em all */ | |
1251 | retr = kmem_alloc_wired(mapping_map, (vm_offset_t *)&mbn, PAGE_SIZE); /* Find a virtual address to use */ | |
55e303ae | 1252 | if(retr != KERN_SUCCESS) /* Did we get some memory? */ |
d7e50217 | 1253 | break; |
1c79356b A |
1254 | mapping_free_init((vm_offset_t)mbn, -1, 0); /* Initialize on to the release queue */ |
1255 | } | |
1256 | if ((mapCtl.mapcinuse + mapCtl.mapcfree + (mapCtl.mapcreln * (MAPPERBLOK + 1))) > mapCtl.mapcmaxalloc) | |
1257 | mapCtl.mapcmaxalloc = mapCtl.mapcinuse + mapCtl.mapcfree + (mapCtl.mapcreln * (MAPPERBLOK + 1)); | |
1258 | ||
1259 | mapCtl.mapcrecurse = 0; /* We are done now */ | |
1260 | } | |
1261 | ||
1262 | /* | |
1263 | * void mapping_relpre(void) - Releases preallocation release hold off | |
1264 | * | |
1265 | * This routine removes the | |
1266 | * hold off flag so that the adjust routine will free the extra mapping | |
1267 | * blocks on the release list. I don't like this, but I don't know | |
1268 | * how else to do this for now... | |
1269 | * | |
1270 | */ | |
1271 | ||
1272 | void mapping_relpre(void) { /* Releases release hold off */ | |
1273 | ||
1274 | spl_t s; | |
1275 | ||
1276 | s = splhigh(); /* Don't bother from now on */ | |
1277 | if(!hw_lock_to((hw_lock_t)&mapCtl.mapclock, LockTimeOut)) { /* Lock the control header */ | |
1278 | panic("mapping_relpre - timeout getting control lock\n"); /* Tell all and die */ | |
1279 | } | |
1280 | if(--mapCtl.mapcholdoff < 0) { /* Back down the hold off count */ | |
1281 | panic("mapping_relpre: hold-off count went negative\n"); | |
1282 | } | |
1283 | ||
1284 | hw_lock_unlock((hw_lock_t)&mapCtl.mapclock); /* Unlock our stuff */ | |
1285 | splx(s); /* Restore 'rupts */ | |
1286 | } | |
1287 | ||
1288 | /* | |
1289 | * void mapping_free_prime(void) - Primes the mapping block release list | |
1290 | * | |
1291 | * See mapping_free_init. | |
1292 | * No locks can be held, because we allocate memory here. | |
1293 | * One processor running only. | |
1294 | * | |
1295 | */ | |
1296 | ||
1297 | void mapping_free_prime(void) { /* Primes the mapping block release list */ | |
1298 | ||
1299 | int nmapb, i; | |
1300 | kern_return_t retr; | |
1301 | mappingblok *mbn; | |
1302 | vm_offset_t mapping_min; | |
1303 | ||
55e303ae | 1304 | retr = kmem_suballoc(kernel_map, &mapping_min, sane_size / 16, |
1c79356b A |
1305 | FALSE, TRUE, &mapping_map); |
1306 | ||
1307 | if (retr != KERN_SUCCESS) | |
1308 | panic("mapping_free_prime: kmem_suballoc failed"); | |
1309 | ||
1310 | ||
1311 | nmapb = (mapCtl.mapcfree + mapCtl.mapcinuse + MAPPERBLOK - 1) / MAPPERBLOK; /* Get permanent allocation */ | |
1312 | nmapb = nmapb * 4; /* Get 4 times our initial allocation */ | |
1313 | ||
1314 | #if DEBUG | |
1315 | kprintf("mapping_free_prime: free = %08X; in use = %08X; priming = %08X\n", | |
1316 | mapCtl.mapcfree, mapCtl.mapcinuse, nmapb); | |
1317 | #endif | |
1318 | ||
1319 | for(i = 0; i < nmapb; i++) { /* Allocate 'em all */ | |
1320 | retr = kmem_alloc_wired(mapping_map, (vm_offset_t *)&mbn, PAGE_SIZE); /* Find a virtual address to use */ | |
1321 | if(retr != KERN_SUCCESS) { /* Did we get some memory? */ | |
1322 | panic("Whoops... Not a bit of wired memory left for anyone\n"); | |
1323 | } | |
1324 | mapping_free_init((vm_offset_t)mbn, -1, 0); /* Initialize onto release queue */ | |
1325 | } | |
1326 | if ((mapCtl.mapcinuse + mapCtl.mapcfree + (mapCtl.mapcreln * (MAPPERBLOK + 1))) > mapCtl.mapcmaxalloc) | |
1327 | mapCtl.mapcmaxalloc = mapCtl.mapcinuse + mapCtl.mapcfree + (mapCtl.mapcreln * (MAPPERBLOK + 1)); | |
1328 | } | |
1329 | ||
1330 | ||
1331 | ||
1332 | mapping_fake_zone_info(int *count, vm_size_t *cur_size, vm_size_t *max_size, vm_size_t *elem_size, | |
1333 | vm_size_t *alloc_size, int *collectable, int *exhaustable) | |
1334 | { | |
1335 | *count = mapCtl.mapcinuse; | |
1336 | *cur_size = ((PAGE_SIZE / (MAPPERBLOK + 1)) * (mapCtl.mapcinuse + mapCtl.mapcfree)) + (PAGE_SIZE * mapCtl.mapcreln); | |
1337 | *max_size = (PAGE_SIZE / (MAPPERBLOK + 1)) * mapCtl.mapcmaxalloc; | |
1338 | *elem_size = (PAGE_SIZE / (MAPPERBLOK + 1)); | |
1339 | *alloc_size = PAGE_SIZE; | |
1340 | ||
1341 | *collectable = 1; | |
1342 | *exhaustable = 0; | |
1343 | } | |
1344 | ||
1345 | ||
1346 | /* | |
55e303ae | 1347 | * addr64_t mapping_p2v(pmap_t pmap, ppnum_t pa) - Finds first virtual mapping of a physical page in a space |
1c79356b | 1348 | * |
55e303ae A |
1349 | * First looks up the physical entry associated witht the physical page. Then searches the alias |
1350 | * list for a matching pmap. It grabs the virtual address from the mapping, drops busy, and returns | |
1351 | * that. | |
1c79356b | 1352 | * |
1c79356b A |
1353 | */ |
1354 | ||
55e303ae | 1355 | addr64_t mapping_p2v(pmap_t pmap, ppnum_t pa) { /* Finds first virtual mapping of a physical page in a space */ |
1c79356b | 1356 | |
55e303ae A |
1357 | spl_t s; |
1358 | mapping *mp; | |
1359 | unsigned int pindex; | |
1360 | phys_entry *physent; | |
1361 | addr64_t va; | |
de355530 | 1362 | |
55e303ae A |
1363 | physent = mapping_phys_lookup(pa, &pindex); /* Get physical entry */ |
1364 | if(!physent) { /* Did we find the physical page? */ | |
1365 | panic("mapping_p2v: invalid physical page %08X\n", pa); | |
1c79356b | 1366 | } |
1c79356b | 1367 | |
55e303ae | 1368 | s = splhigh(); /* Make sure interruptions are disabled */ |
1c79356b | 1369 | |
55e303ae | 1370 | mp = (mapping *) hw_find_space(physent, pmap->space); /* Go find the first mapping to the page from the requested pmap */ |
1c79356b | 1371 | |
55e303ae A |
1372 | if(mp) { /* Did we find one? */ |
1373 | va = mp->mpVAddr & -4096; /* If so, get the cleaned up vaddr */ | |
1374 | mapping_drop_busy(mp); /* Go ahead and relase the mapping now */ | |
1c79356b | 1375 | } |
55e303ae | 1376 | else va = 0; /* Return failure */ |
1c79356b | 1377 | |
55e303ae | 1378 | splx(s); /* Restore 'rupts */ |
1c79356b | 1379 | |
55e303ae | 1380 | return va; /* Bye, bye... */ |
1c79356b | 1381 | |
1c79356b A |
1382 | } |
1383 | ||
1384 | /* | |
1385 | * phystokv(addr) | |
1386 | * | |
1387 | * Convert a physical address to a kernel virtual address if | |
1388 | * there is a mapping, otherwise return NULL | |
1389 | */ | |
1390 | ||
1391 | vm_offset_t phystokv(vm_offset_t pa) { | |
1392 | ||
55e303ae A |
1393 | addr64_t va; |
1394 | ppnum_t pp; | |
1c79356b | 1395 | |
55e303ae A |
1396 | pp = pa >> 12; /* Convert to a page number */ |
1397 | ||
1398 | if(!(va = mapping_p2v(kernel_pmap, pp))) { | |
1c79356b A |
1399 | return 0; /* Can't find it, return 0... */ |
1400 | } | |
55e303ae A |
1401 | |
1402 | return (va | (pa & (PAGE_SIZE - 1))); /* Build and return VADDR... */ | |
1403 | ||
1404 | } | |
1405 | ||
1406 | /* | |
1407 | * kvtophys(addr) | |
1408 | * | |
1409 | * Convert a kernel virtual address to a physical address | |
1410 | */ | |
1411 | vm_offset_t kvtophys(vm_offset_t va) { | |
1412 | ||
1413 | return pmap_extract(kernel_pmap, va); /* Find mapping and lock the physical entry for this mapping */ | |
1c79356b A |
1414 | |
1415 | } | |
1416 | ||
1417 | /* | |
1418 | * void ignore_zero_fault(boolean_t) - Sets up to ignore or honor any fault on | |
1419 | * page 0 access for the current thread. | |
1420 | * | |
1421 | * If parameter is TRUE, faults are ignored | |
1422 | * If parameter is FALSE, faults are honored | |
1423 | * | |
1424 | */ | |
1425 | ||
1426 | void ignore_zero_fault(boolean_t type) { /* Sets up to ignore or honor any fault on page 0 access for the current thread */ | |
1427 | ||
1428 | if(type) current_act()->mact.specFlags |= ignoreZeroFault; /* Ignore faults on page 0 */ | |
1429 | else current_act()->mact.specFlags &= ~ignoreZeroFault; /* Honor faults on page 0 */ | |
1430 | ||
1431 | return; /* Return the result or 0... */ | |
1432 | } | |
1433 | ||
1434 | ||
55e303ae A |
1435 | /* |
1436 | * Copies data between a physical page and a virtual page, or 2 physical. This is used to | |
1437 | * move data from the kernel to user state. Note that the "which" parm | |
1438 | * says which of the parameters is physical and if we need to flush sink/source. | |
1439 | * Note that both addresses may be physicical but only one may be virtual | |
1c79356b | 1440 | * |
55e303ae A |
1441 | * The rules are that the size can be anything. Either address can be on any boundary |
1442 | * and span pages. The physical data must be congiguous as must the virtual. | |
1c79356b | 1443 | * |
55e303ae A |
1444 | * We can block when we try to resolve the virtual address at each page boundary. |
1445 | * We don't check protection on the physical page. | |
1c79356b | 1446 | * |
55e303ae A |
1447 | * Note that we will not check the entire range and if a page translation fails, |
1448 | * we will stop with partial contents copied. | |
1c79356b A |
1449 | * |
1450 | */ | |
1451 | ||
55e303ae | 1452 | kern_return_t copypv(addr64_t source, addr64_t sink, unsigned int size, int which) { |
1c79356b A |
1453 | |
1454 | vm_map_t map; | |
1455 | kern_return_t ret; | |
55e303ae A |
1456 | addr64_t pa, nextva, vaddr, paddr; |
1457 | register mapping *mp; | |
1c79356b | 1458 | spl_t s; |
55e303ae A |
1459 | unsigned int sz, left, lop, csize; |
1460 | int needtran, bothphys; | |
1461 | unsigned int pindex; | |
1462 | phys_entry *physent; | |
1463 | vm_prot_t prot; | |
1464 | int orig_which; | |
1c79356b | 1465 | |
55e303ae | 1466 | orig_which = which; |
1c79356b | 1467 | |
55e303ae | 1468 | map = (which & cppvKmap) ? kernel_map : current_map_fast(); |
1c79356b | 1469 | |
55e303ae A |
1470 | if((which & (cppvPsrc | cppvPsnk)) == 0 ) { /* Make sure that only one is virtual */ |
1471 | panic("copypv: no more than 1 parameter may be virtual\n"); /* Not allowed */ | |
1472 | } | |
1473 | ||
1474 | bothphys = 1; /* Assume both are physical */ | |
1475 | ||
1476 | if(!(which & cppvPsnk)) { /* Is there a virtual page here? */ | |
1477 | vaddr = sink; /* Sink side is virtual */ | |
1478 | bothphys = 0; /* Show both aren't physical */ | |
1479 | prot = VM_PROT_READ | VM_PROT_WRITE; /* Sink always must be read/write */ | |
1480 | } else if(!(which & cppvPsrc)) { /* Source side is virtual */ | |
1481 | vaddr = source; /* Source side is virtual */ | |
1482 | bothphys = 0; /* Show both aren't physical */ | |
1483 | prot = VM_PROT_READ; /* Virtual source is always read only */ | |
1484 | } | |
1c79356b | 1485 | |
55e303ae A |
1486 | needtran = 1; /* Show we need to map the virtual the first time */ |
1487 | s = splhigh(); /* Don't bother me */ | |
1c79356b | 1488 | |
55e303ae | 1489 | while(size) { |
de355530 | 1490 | |
55e303ae A |
1491 | if(!bothphys && (needtran || !(vaddr & 4095LL))) { /* If first time or we stepped onto a new page, we need to translate */ |
1492 | if(!needtran) { /* If this is not the first translation, we need to drop the old busy */ | |
1493 | mapping_drop_busy(mp); /* Release the old mapping now */ | |
1494 | } | |
1495 | needtran = 0; | |
1496 | ||
1497 | while(1) { | |
1498 | mp = mapping_find(map->pmap, vaddr, &nextva, 1); /* Find and busy the mapping */ | |
1499 | if(!mp) { /* Was it there? */ | |
1500 | if(per_proc_info[cpu_number()].istackptr == 0) | |
1501 | panic("copypv: No vaild mapping on memory %s %x", "RD", vaddr); | |
1502 | ||
1503 | splx(s); /* Restore the interrupt level */ | |
1504 | ret = vm_fault(map, trunc_page_32((vm_offset_t)vaddr), prot, FALSE, FALSE, NULL, 0); /* Didn't find it, try to fault it in... */ | |
1505 | ||
1506 | if(ret != KERN_SUCCESS)return KERN_FAILURE; /* Didn't find any, return no good... */ | |
1507 | ||
1508 | s = splhigh(); /* Don't bother me */ | |
1509 | continue; /* Go try for the map again... */ | |
1510 | ||
1511 | } | |
1512 | if (mp->mpVAddr & mpI) { /* cache inhibited, so force the appropriate page to be flushed before */ | |
1513 | if (which & cppvPsrc) /* and after the copy to avoid cache paradoxes */ | |
1514 | which |= cppvFsnk; | |
1515 | else | |
1516 | which |= cppvFsrc; | |
1517 | } else | |
1518 | which = orig_which; | |
1519 | ||
1520 | /* Note that we have to have the destination writable. So, if we already have it, or we are mapping the source, | |
1521 | we can just leave. | |
1522 | */ | |
1523 | if((which & cppvPsnk) || !(mp->mpVAddr & 1)) break; /* We got it mapped R/W or the source is not virtual, leave... */ | |
1524 | ||
1525 | mapping_drop_busy(mp); /* Go ahead and release the mapping for now */ | |
1526 | if(per_proc_info[cpu_number()].istackptr == 0) | |
1527 | panic("copypv: No vaild mapping on memory %s %x", "RDWR", vaddr); | |
1528 | splx(s); /* Restore the interrupt level */ | |
1529 | ||
1530 | ret = vm_fault(map, trunc_page_32((vm_offset_t)vaddr), VM_PROT_READ | VM_PROT_WRITE, FALSE, FALSE, NULL, 0); /* check for a COW area */ | |
1531 | if (ret != KERN_SUCCESS) return KERN_FAILURE; /* We couldn't get it R/W, leave in disgrace... */ | |
1532 | s = splhigh(); /* Don't bother me */ | |
1533 | } | |
1534 | paddr = ((addr64_t)mp->mpPAddr << 12) + (vaddr - (mp->mpVAddr & -4096LL)); /* construct the physical address... this calculation works */ | |
1535 | /* properly on both single page and block mappings */ | |
1536 | if(which & cppvPsrc) sink = paddr; /* If source is physical, then the sink is virtual */ | |
1537 | else source = paddr; /* Otherwise the source is */ | |
1c79356b | 1538 | } |
55e303ae A |
1539 | |
1540 | lop = (unsigned int)(4096LL - (sink & 4095LL)); /* Assume sink smallest */ | |
1541 | if(lop > (unsigned int)(4096LL - (source & 4095LL))) lop = (unsigned int)(4096LL - (source & 4095LL)); /* No, source is smaller */ | |
1542 | ||
1543 | csize = size; /* Assume we can copy it all */ | |
1544 | if(lop < size) csize = lop; /* Nope, we can't do it all */ | |
1545 | ||
1546 | if(which & cppvFsrc) flush_dcache64(source, csize, 1); /* If requested, flush source before move */ | |
1547 | if(which & cppvFsnk) flush_dcache64(sink, csize, 1); /* If requested, flush sink before move */ | |
1c79356b | 1548 | |
55e303ae A |
1549 | bcopy_physvir(source, sink, csize); /* Do a physical copy, virtually */ |
1550 | ||
1551 | if(which & cppvFsrc) flush_dcache64(source, csize, 1); /* If requested, flush source after move */ | |
1552 | if(which & cppvFsnk) flush_dcache64(sink, csize, 1); /* If requested, flush sink after move */ | |
1c79356b | 1553 | |
b4c24cb9 | 1554 | /* |
55e303ae A |
1555 | * Note that for certain ram disk flavors, we may be copying outside of known memory. |
1556 | * Therefore, before we try to mark it modifed, we check if it exists. | |
b4c24cb9 A |
1557 | */ |
1558 | ||
55e303ae A |
1559 | if( !(which & cppvNoModSnk)) { |
1560 | physent = mapping_phys_lookup(sink >> 12, &pindex); /* Get physical entry for sink */ | |
1561 | if(physent) mapping_set_mod((ppnum_t)(sink >> 12)); /* Make sure we know that it is modified */ | |
1562 | } | |
1563 | if( !(which & cppvNoRefSrc)) { | |
1564 | physent = mapping_phys_lookup(source >> 12, &pindex); /* Get physical entry for source */ | |
1565 | if(physent) mapping_set_ref((ppnum_t)(source >> 12)); /* Make sure we know that it is modified */ | |
1566 | } | |
1567 | size = size - csize; /* Calculate what is left */ | |
1568 | vaddr = vaddr + csize; /* Move to next sink address */ | |
1569 | source = source + csize; /* Bump source to next physical address */ | |
1570 | sink = sink + csize; /* Bump sink to next physical address */ | |
b4c24cb9 | 1571 | } |
55e303ae A |
1572 | |
1573 | if(!bothphys) mapping_drop_busy(mp); /* Go ahead and release the mapping of the virtual page if any */ | |
1574 | splx(s); /* Open up for interrupts */ | |
b4c24cb9 | 1575 | |
55e303ae | 1576 | return KERN_SUCCESS; |
b4c24cb9 A |
1577 | } |
1578 | ||
1579 | ||
1c79356b | 1580 | /* |
55e303ae | 1581 | * Debug code |
1c79356b | 1582 | */ |
1c79356b | 1583 | |
55e303ae | 1584 | void mapping_verify(void) { |
1c79356b | 1585 | |
55e303ae A |
1586 | spl_t s; |
1587 | mappingblok *mb, *mbn; | |
1588 | int relncnt; | |
1589 | unsigned int dumbodude; | |
de355530 | 1590 | |
55e303ae A |
1591 | dumbodude = 0; |
1592 | ||
1593 | s = splhigh(); /* Don't bother from now on */ | |
de355530 | 1594 | |
55e303ae A |
1595 | mbn = 0; /* Start with none */ |
1596 | for(mb = mapCtl.mapcnext; mb; mb = mb->nextblok) { /* Walk the free chain */ | |
1597 | if((mappingblok *)(mb->mapblokflags & 0x7FFFFFFF) != mb) { /* Is tag ok? */ | |
1598 | panic("mapping_verify: flags tag bad, free chain; mb = %08X, tag = %08X\n", mb, mb->mapblokflags); | |
1599 | } | |
1600 | mbn = mb; /* Remember the last one */ | |
1c79356b | 1601 | } |
55e303ae A |
1602 | |
1603 | if(mapCtl.mapcnext && (mapCtl.mapclast != mbn)) { /* Do we point to the last one? */ | |
1604 | panic("mapping_verify: last pointer bad; mb = %08X, mapclast = %08X\n", mb, mapCtl.mapclast); | |
1c79356b A |
1605 | } |
1606 | ||
55e303ae A |
1607 | relncnt = 0; /* Clear count */ |
1608 | for(mb = mapCtl.mapcrel; mb; mb = mb->nextblok) { /* Walk the release chain */ | |
1609 | dumbodude |= mb->mapblokflags; /* Just touch it to make sure it is mapped */ | |
1610 | relncnt++; /* Count this one */ | |
1611 | } | |
1c79356b | 1612 | |
55e303ae A |
1613 | if(mapCtl.mapcreln != relncnt) { /* Is the count on release queue ok? */ |
1614 | panic("mapping_verify: bad release queue count; mapcreln = %d, cnt = %d, ignore this = %08X\n", mapCtl.mapcreln, relncnt, dumbodude); | |
1615 | } | |
1c79356b | 1616 | |
55e303ae | 1617 | splx(s); /* Restore 'rupts */ |
1c79356b | 1618 | |
1c79356b A |
1619 | return; |
1620 | } | |
1621 | ||
55e303ae | 1622 | void mapping_phys_unused(ppnum_t pa) { |
1c79356b | 1623 | |
55e303ae A |
1624 | unsigned int pindex; |
1625 | phys_entry *physent; | |
1c79356b | 1626 | |
55e303ae A |
1627 | physent = mapping_phys_lookup(pa, &pindex); /* Get physical entry */ |
1628 | if(!physent) return; /* Did we find the physical page? */ | |
1c79356b | 1629 | |
55e303ae | 1630 | if(!(physent->ppLink & ~(ppLock | ppN | ppFlags))) return; /* No one else is here */ |
1c79356b | 1631 | |
55e303ae | 1632 | panic("mapping_phys_unused: physical page (%08X) in use, physent = %08X\n", pa, physent); |
1c79356b | 1633 | |
de355530 | 1634 | } |
d7e50217 | 1635 | |
55e303ae A |
1636 | |
1637 | ||
1638 | ||
1639 | ||
1640 | ||
1641 | ||
1642 | ||
1643 | ||
1644 |