]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. | |
7 | * | |
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 | |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
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. | |
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> | |
46 | #include <vm/vm_fault.h> | |
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> | |
53 | #include <ppc/exception.h> | |
54 | #include <ppc/misc_protos.h> | |
55 | #include <ppc/proc_reg.h> | |
56 | ||
57 | #include <vm/pmap.h> | |
58 | #include <ppc/pmap.h> | |
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 | ||
66 | #include <console/video_console.h> /* (TEST/DEBUG) */ | |
67 | ||
68 | #define PERFTIMES 0 | |
69 | ||
70 | vm_map_t mapping_map = VM_MAP_NULL; | |
71 | ||
72 | unsigned int incrVSID = 0; /* VSID increment value */ | |
73 | unsigned int mappingdeb0 = 0; | |
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 | ||
80 | extern unsigned int hash_table_size; | |
81 | ||
82 | void mapping_verify(void); | |
83 | void mapping_phys_unused(ppnum_t pa); | |
84 | ||
85 | /* | |
86 | * ppc_prot translates from the mach representation of protections to the PPC version. | |
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). | |
89 | * Calculation of it like this saves a memory reference - and maybe a couple of microseconds. | |
90 | * It eliminates the used of this table. | |
91 | * unsigned char ppc_prot[16] = { 0, 3, 2, 2, 3, 3, 2, 2, 0, 1, 2, 3, 0, 1, 2, 3 }; | |
92 | */ | |
93 | ||
94 | #define ppc_prot(p) ((0xE4E4AFAC >> (p << 1)) & 3) | |
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 | ||
156 | unsigned int tmp, maxeff, rwidth; | |
157 | ||
158 | ppc_max_adrsp = maxAdrSp; /* Set maximum address spaces */ | |
159 | ||
160 | maxeff = 32; /* Assume 32-bit */ | |
161 | if(per_proc_info[0].pf.Available & pf64Bit) maxeff = 64; /* Is this a 64-bit machine? */ | |
162 | ||
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 */ | |
165 | ||
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 */ | |
168 | ||
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 | } | |
176 | ||
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 */ | |
180 | ||
181 | return; | |
182 | ||
183 | } | |
184 | ||
185 | ||
186 | /* | |
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. | |
191 | * | |
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. | |
195 | * | |
196 | * Note that if the mapping is not found, we return the next VA ORed with 1 | |
197 | * | |
198 | */ | |
199 | ||
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 */ | |
202 | ||
203 | mapping *mp; | |
204 | addr64_t nextva; | |
205 | ||
206 | disable_preemption(); /* Don't change threads */ | |
207 | ||
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 */ | |
211 | } | |
212 | ||
213 | enable_preemption(); /* Thread change ok */ | |
214 | ||
215 | if(!mp) return (nextva | 1); /* Nothing found to unmap */ | |
216 | ||
217 | if((unsigned int)mp & mapRetCode) { /* Was there a failure? */ | |
218 | ||
219 | panic("mapping_remove: hw_rem_map failed - pmap = %08X, va = %016llX, code = %08X\n", | |
220 | pmap, va, mp); | |
221 | } | |
222 | ||
223 | mapping_free(mp); /* Add mapping to the free list */ | |
224 | ||
225 | return nextva; /* Tell them we did it */ | |
226 | } | |
227 | ||
228 | /* | |
229 | * mapping_make(pmap, va, pa, flags, size, prot) - map a virtual address to a real one | |
230 | * | |
231 | * This routine takes the given parameters, builds a mapping block, and queues it into the | |
232 | * correct lists. | |
233 | * | |
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 | * | |
260 | * | |
261 | */ | |
262 | ||
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 */ | |
264 | ||
265 | register mapping *mp; | |
266 | addr64_t colladdr; | |
267 | unsigned int pindex, mflags, pattr, wimg; | |
268 | phys_entry *physent; | |
269 | int i, nlists; | |
270 | ||
271 | disable_preemption(); /* Don't change threads */ | |
272 | ||
273 | pindex = 0; | |
274 | ||
275 | mflags = 0x01000000; /* Start building mpFlags field (busy count = 1) */ | |
276 | ||
277 | if(!(flags & mmFlgBlock)) { /* Is this a block map? */ | |
278 | ||
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 */ | |
287 | } | |
288 | else pattr = ((physent->ppLink & (ppI | ppG)) >> 4); /* Get the default attributes from physent */ | |
289 | ||
290 | if(flags & mmFlgUseAttr) pattr = flags & (mmFlgCInhib | mmFlgGuarded); /* Use requested attributes */ | |
291 | } | |
292 | else { /* This is a block */ | |
293 | ||
294 | pattr = flags & (mmFlgCInhib | mmFlgGuarded); /* Use requested attributes */ | |
295 | mflags |= mpBlock; /* Show that this is a block */ | |
296 | } | |
297 | ||
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 */ | |
301 | ||
302 | mflags = mflags | (pindex << 16); /* Stick in the physical entry table index */ | |
303 | ||
304 | if(flags & mmFlgPerm) mflags |= mpPerm; /* Set permanent mapping */ | |
305 | ||
306 | size = size - 1; /* Change size to offset */ | |
307 | if(size > 0xFFFF) return 1; /* Leave if size is too big */ | |
308 | ||
309 | nlists = mapSetLists(pmap); /* Set number of lists this will be on */ | |
310 | ||
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 */ | |
320 | ||
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 | } | |
327 | ||
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 */ | |
337 | } | |
338 | ||
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 */ | |
347 | } | |
348 | ||
349 | return 1; /* Leave... */ | |
350 | } | |
351 | ||
352 | ||
353 | /* | |
354 | * mapping *mapping_find(pmap, va, *nextva, full) - Finds a mapping | |
355 | * | |
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 | |
358 | * | |
359 | * Must be called with interruptions disabled or we can hang trying to remove found mapping. | |
360 | * | |
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. | |
364 | * | |
365 | * NOTE: The nextva field is not valid when full is TRUE. | |
366 | * | |
367 | * | |
368 | */ | |
369 | ||
370 | mapping *mapping_find(pmap_t pmap, addr64_t va, addr64_t *nextva, int full) { /* Make an address mapping */ | |
371 | ||
372 | register mapping *mp; | |
373 | addr64_t curva; | |
374 | pmap_t curpmap; | |
375 | int nestdepth; | |
376 | ||
377 | curpmap = pmap; /* Remember entry */ | |
378 | nestdepth = 0; /* Set nest depth */ | |
379 | curva = (addr64_t)va; /* Set current va */ | |
380 | ||
381 | while(1) { | |
382 | ||
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... */ | |
386 | } | |
387 | ||
388 | if(!mp || !(mp->mpFlags & mpNest) || !full) break; /* Are we a nest or are we only going one deep? */ | |
389 | ||
390 | if(mp->mpFlags & mpSpecial) { /* Don't chain through a special mapping */ | |
391 | mp = 0; /* Set not found */ | |
392 | break; | |
393 | } | |
394 | ||
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); | |
398 | } | |
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 | ||
404 | } | |
405 | ||
406 | return mp; /* Return the mapping if we found one */ | |
407 | } | |
408 | ||
409 | /* | |
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 | |
411 | * | |
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. | |
415 | * | |
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. | |
418 | * | |
419 | * | |
420 | */ | |
421 | ||
422 | int mapping_protect(pmap_t pmap, addr64_t va, vm_prot_t prot, addr64_t *nextva) { /* Change protection of a virtual page */ | |
423 | ||
424 | int ret; | |
425 | ||
426 | ret = hw_protect(pmap, va, ppc_prot(prot), nextva); /* Try to change the protect here */ | |
427 | ||
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; | |
434 | ||
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 | ||
443 | } | |
444 | ||
445 | } | |
446 | ||
447 | /* | |
448 | * void mapping_protect_phys(ppnum_t pa, vm_prot_t prot) - change the protection of a physical page | |
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 | |
452 | * the protection is changed. There is no limitation on changes, e.g., | |
453 | * higher to lower, lower to higher. | |
454 | * | |
455 | * Any mapping that is marked permanent is not changed | |
456 | * | |
457 | * Phys_entry is unlocked. | |
458 | */ | |
459 | ||
460 | void mapping_protect_phys(ppnum_t pa, vm_prot_t prot) { /* Change protection of all mappings to page */ | |
461 | ||
462 | unsigned int pindex; | |
463 | phys_entry *physent; | |
464 | ||
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); | |
468 | } | |
469 | ||
470 | hw_walk_phys(physent, hwpSPrtPhy, hwpSPrtMap, hwpNoop, ppc_prot(prot)); /* Set the new protection for page and mappings */ | |
471 | ||
472 | return; /* Leave... */ | |
473 | } | |
474 | ||
475 | ||
476 | /* | |
477 | * void mapping_clr_mod(ppnum_t pa) - clears the change bit of a physical page | |
478 | * | |
479 | * This routine takes a physical entry and runs through all mappings attached to it and turns | |
480 | * off the change bit. | |
481 | */ | |
482 | ||
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 | } | |
492 | ||
493 | hw_walk_phys(physent, hwpNoop, hwpCCngMap, hwpCCngPhy, 0); /* Clear change for page and mappings */ | |
494 | return; /* Leave... */ | |
495 | } | |
496 | ||
497 | ||
498 | /* | |
499 | * void mapping_set_mod(ppnum_t pa) - set the change bit of a physical page | |
500 | * | |
501 | * This routine takes a physical entry and runs through all mappings attached to it and turns | |
502 | * on the change bit. | |
503 | */ | |
504 | ||
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 | } | |
514 | ||
515 | hw_walk_phys(physent, hwpNoop, hwpSCngMap, hwpSCngPhy, 0); /* Set change for page and mappings */ | |
516 | return; /* Leave... */ | |
517 | } | |
518 | ||
519 | ||
520 | /* | |
521 | * void mapping_clr_ref(ppnum_t pa) - clears the reference bit of a physical page | |
522 | * | |
523 | * This routine takes a physical entry and runs through all mappings attached to it and turns | |
524 | * off the reference bit. | |
525 | */ | |
526 | ||
527 | void mapping_clr_ref(ppnum_t pa) { /* Clears the reference bit of a physical page */ | |
528 | ||
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); | |
535 | } | |
536 | ||
537 | hw_walk_phys(physent, hwpNoop, hwpCRefMap, hwpCRefPhy, 0); /* Clear reference for page and mappings */ | |
538 | return; /* Leave... */ | |
539 | } | |
540 | ||
541 | ||
542 | /* | |
543 | * void mapping_set_ref(ppnum_t pa) - set the reference bit of a physical page | |
544 | * | |
545 | * This routine takes a physical entry and runs through all mappings attached to it and turns | |
546 | * on the reference bit. | |
547 | */ | |
548 | ||
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 | } | |
558 | ||
559 | hw_walk_phys(physent, hwpNoop, hwpSRefMap, hwpSRefPhy, 0); /* Set reference for page and mappings */ | |
560 | return; /* Leave... */ | |
561 | } | |
562 | ||
563 | ||
564 | /* | |
565 | * void mapping_tst_mod(ppnum_t pa) - test the change bit of a physical page | |
566 | * | |
567 | * This routine takes a physical entry and runs through all mappings attached to it and tests | |
568 | * the changed bit. | |
569 | */ | |
570 | ||
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 | } | |
580 | ||
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 */ | |
583 | } | |
584 | ||
585 | ||
586 | /* | |
587 | * void mapping_tst_ref(ppnum_t pa) - tests the reference bit of a physical page | |
588 | * | |
589 | * This routine takes a physical entry and runs through all mappings attached to it and tests | |
590 | * the reference bit. | |
591 | */ | |
592 | ||
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 | } | |
602 | ||
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 */ | |
605 | } | |
606 | ||
607 | ||
608 | /* | |
609 | * phys_ent *mapping_phys_lookup(ppnum_t pp, unsigned int *pindex) - tests the reference bit of a physical page | |
610 | * | |
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. | |
614 | */ | |
615 | ||
616 | phys_entry *mapping_phys_lookup(ppnum_t pp, unsigned int *pindex) { /* Finds the physical entry for the page */ | |
617 | ||
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 | ||
632 | } | |
633 | ||
634 | ||
635 | ||
636 | ||
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 | * | |
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. | |
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 | ||
654 | kern_return_t retr = KERN_SUCCESS; | |
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) { | |
661 | mapCtl.mapcmin = (sane_size / PAGE_SIZE) / 16; | |
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) { | |
676 | thread_call_setup(&mapping_adjust_call_data, | |
677 | (thread_call_func_t)mapping_adjust, | |
678 | (thread_call_param_t)NULL); | |
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 | } | |
692 | else { /* No free ones, try to get it */ | |
693 | ||
694 | allocsize = (allocsize + MAPPERBLOK - 1) / MAPPERBLOK; /* Get the number of pages we need */ | |
695 | ||
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? */ | |
702 | break; | |
703 | } | |
704 | if(retr == KERN_SUCCESS) break; /* We got some memory, bail out... */ | |
705 | } | |
706 | ||
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 | } | |
713 | ||
714 | if (retr != KERN_SUCCESS) | |
715 | break; /* Fail to alocate, bail out... */ | |
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 | } | |
720 | ||
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 */ | |
741 | ||
742 | kmem_free(mapping_map, (vm_offset_t) mbn, PAGE_SIZE); /* Release this mapping block */ | |
743 | ||
744 | mbn = mb; /* Chain to the next */ | |
745 | } | |
746 | ||
747 | __asm__ volatile("eieio"); /* Make sure all is well */ | |
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; | |
766 | unsigned int full, mindx, lists; | |
767 | ||
768 | mindx = ((unsigned int)mp & (PAGE_SIZE - 1)) >> 6; /* Get index to mapping */ | |
769 | mb = (mappingblok *)((unsigned int)mp & -PAGE_SIZE); /* Point to the mapping block */ | |
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 | ||
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 | ||
805 | full = !(mb->mapblokfree[0] | mb->mapblokfree[1]); /* See if full now */ | |
806 | mb->mapblokfree[mindx >> 5] |= (0x80000000 >> (mindx & 31)); /* Flip on the free bit */ | |
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 | } | |
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 */ | |
817 | if(!((unsigned int)mapCtl.mapclast)) | |
818 | mapCtl.mapclast = mb; | |
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? */ | |
827 | if(((mb->mapblokfree[0] | 0x80000000) & mb->mapblokfree[1]) == 0xFFFFFFFF) { /* See if empty now */ | |
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 | /* | |
869 | * mapping_alloc(lists) - obtain a mapping from the free list | |
870 | * | |
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. | |
878 | * | |
879 | * If this allocation empties a block, we remove it from the free list. | |
880 | * If this allocation drops the total number of free entries below a threshold, | |
881 | * we allocate a new block. | |
882 | * | |
883 | */ | |
884 | ||
885 | mapping *mapping_alloc(int lists) { /* Obtain a mapping */ | |
886 | ||
887 | register mapping *mp; | |
888 | mappingblok *mb, *mbn; | |
889 | spl_t s; | |
890 | int mindx; | |
891 | kern_return_t retr; | |
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 | ||
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 | ||
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; | |
937 | } | |
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 | ||
983 | } | |
984 | ||
985 | mapCtl.mapcflush.spacenum = ckpmap->spaceNum; | |
986 | mapCtl.mapcflush.addr = nextva; | |
987 | } | |
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 | } | |
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 | */ | |
1052 | ||
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 */ | |
1072 | mp->mpFlags = lists; /* set the list count */ | |
1073 | ||
1074 | ||
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 */ | |
1086 | panic("consider_mapping_adjust -- lock timeout\n"); | |
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 | * | |
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. | |
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; | |
1145 | addr64_t raddr; | |
1146 | ppnum_t pp; | |
1147 | ||
1148 | mb = (mappingblok *)mbl; /* Start of area */ | |
1149 | ||
1150 | if(perm >= 0) { /* See if we need to initialize the block */ | |
1151 | if(perm) { | |
1152 | raddr = (addr64_t)((unsigned int)mbl); /* Perm means V=R */ | |
1153 | mb->mapblokflags = mbPerm; /* Set perm */ | |
1154 | // mb->mapblokflags |= (unsigned int)mb; /* (BRINGUP) */ | |
1155 | } | |
1156 | else { | |
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 */ | |
1163 | mb->mapblokflags = 0; /* Set not perm */ | |
1164 | // mb->mapblokflags |= (unsigned int)mb; /* (BRINGUP) */ | |
1165 | } | |
1166 | ||
1167 | mb->mapblokvrswap = raddr ^ (addr64_t)((unsigned int)mbl); /* Form translation mask */ | |
1168 | ||
1169 | mb->mapblokfree[0] = 0x7FFFFFFF; /* Set first 32 (minus 1) free */ | |
1170 | mb->mapblokfree[1] = 0xFFFFFFFF; /* Set next 32 free */ | |
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 */ | |
1176 | panic("mapping_free_init: timeout getting control lock\n"); /* Tell all and die */ | |
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 | } | |
1202 | ||
1203 | splx(s); /* Restore 'rupts */ | |
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 | } | |
1240 | if (!hw_compare_and_store(0, 1, &mapCtl.mapcrecurse)) { /* Make sure we aren't recursing */ | |
1241 | hw_lock_unlock((hw_lock_t)&mapCtl.mapclock); /* Unlock our stuff */ | |
1242 | splx(s); /* Restore 'rupts */ | |
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 */ | |
1252 | if(retr != KERN_SUCCESS) /* Did we get some memory? */ | |
1253 | break; | |
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 | ||
1304 | retr = kmem_suballoc(kernel_map, &mapping_min, sane_size / 16, | |
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 | /* | |
1347 | * addr64_t mapping_p2v(pmap_t pmap, ppnum_t pa) - Finds first virtual mapping of a physical page in a space | |
1348 | * | |
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. | |
1352 | * | |
1353 | */ | |
1354 | ||
1355 | addr64_t mapping_p2v(pmap_t pmap, ppnum_t pa) { /* Finds first virtual mapping of a physical page in a space */ | |
1356 | ||
1357 | spl_t s; | |
1358 | mapping *mp; | |
1359 | unsigned int pindex; | |
1360 | phys_entry *physent; | |
1361 | addr64_t va; | |
1362 | ||
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); | |
1366 | } | |
1367 | ||
1368 | s = splhigh(); /* Make sure interruptions are disabled */ | |
1369 | ||
1370 | mp = (mapping *) hw_find_space(physent, pmap->space); /* Go find the first mapping to the page from the requested pmap */ | |
1371 | ||
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 */ | |
1375 | } | |
1376 | else va = 0; /* Return failure */ | |
1377 | ||
1378 | splx(s); /* Restore 'rupts */ | |
1379 | ||
1380 | return va; /* Bye, bye... */ | |
1381 | ||
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 | ||
1393 | addr64_t va; | |
1394 | ppnum_t pp; | |
1395 | ||
1396 | pp = pa >> 12; /* Convert to a page number */ | |
1397 | ||
1398 | if(!(va = mapping_p2v(kernel_pmap, pp))) { | |
1399 | return 0; /* Can't find it, return 0... */ | |
1400 | } | |
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 */ | |
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 | ||
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 | |
1440 | * | |
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. | |
1443 | * | |
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. | |
1446 | * | |
1447 | * Note that we will not check the entire range and if a page translation fails, | |
1448 | * we will stop with partial contents copied. | |
1449 | * | |
1450 | */ | |
1451 | ||
1452 | kern_return_t copypv(addr64_t source, addr64_t sink, unsigned int size, int which) { | |
1453 | ||
1454 | vm_map_t map; | |
1455 | kern_return_t ret; | |
1456 | addr64_t pa, nextva, vaddr, paddr; | |
1457 | register mapping *mp; | |
1458 | spl_t s; | |
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; | |
1465 | ||
1466 | orig_which = which; | |
1467 | ||
1468 | map = (which & cppvKmap) ? kernel_map : current_map_fast(); | |
1469 | ||
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 | } | |
1485 | ||
1486 | needtran = 1; /* Show we need to map the virtual the first time */ | |
1487 | s = splhigh(); /* Don't bother me */ | |
1488 | ||
1489 | while(size) { | |
1490 | ||
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 */ | |
1538 | } | |
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 */ | |
1548 | ||
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 */ | |
1553 | ||
1554 | /* | |
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. | |
1557 | */ | |
1558 | ||
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 */ | |
1571 | } | |
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 */ | |
1575 | ||
1576 | return KERN_SUCCESS; | |
1577 | } | |
1578 | ||
1579 | ||
1580 | /* | |
1581 | * Debug code | |
1582 | */ | |
1583 | ||
1584 | void mapping_verify(void) { | |
1585 | ||
1586 | spl_t s; | |
1587 | mappingblok *mb, *mbn; | |
1588 | int relncnt; | |
1589 | unsigned int dumbodude; | |
1590 | ||
1591 | dumbodude = 0; | |
1592 | ||
1593 | s = splhigh(); /* Don't bother from now on */ | |
1594 | ||
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 */ | |
1601 | } | |
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); | |
1605 | } | |
1606 | ||
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 | } | |
1612 | ||
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 | } | |
1616 | ||
1617 | splx(s); /* Restore 'rupts */ | |
1618 | ||
1619 | return; | |
1620 | } | |
1621 | ||
1622 | void mapping_phys_unused(ppnum_t pa) { | |
1623 | ||
1624 | unsigned int pindex; | |
1625 | phys_entry *physent; | |
1626 | ||
1627 | physent = mapping_phys_lookup(pa, &pindex); /* Get physical entry */ | |
1628 | if(!physent) return; /* Did we find the physical page? */ | |
1629 | ||
1630 | if(!(physent->ppLink & ~(ppLock | ppN | ppFlags))) return; /* No one else is here */ | |
1631 | ||
1632 | panic("mapping_phys_unused: physical page (%08X) in use, physent = %08X\n", pa, physent); | |
1633 | ||
1634 | } | |
1635 | ||
1636 | ||
1637 | ||
1638 | ||
1639 | ||
1640 | ||
1641 | ||
1642 | ||
1643 | ||
1644 |