]> git.saurik.com Git - apple/xnu.git/blame - osfmk/i386/pmap_x86_common.c
xnu-1456.1.26.tar.gz
[apple/xnu.git] / osfmk / i386 / pmap_x86_common.c
CommitLineData
b0d623f7
A
1/*
2 * Copyright (c) 2000-2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28#include <vm/pmap.h>
29#include <vm/vm_map.h>
30#include <i386/pmap_internal.h>
31/*
32 * The Intel platform can nest at the PDE level, so NBPDE (i.e. 2MB) at a time,
33 * on a NBPDE boundary.
34 */
35
36/* These symbols may be referenced directly by VM */
37uint64_t pmap_nesting_size_min = NBPDE;
38uint64_t pmap_nesting_size_max = 0 - (uint64_t)NBPDE;
39
40/*
41 * kern_return_t pmap_nest(grand, subord, va_start, size)
42 *
43 * grand = the pmap that we will nest subord into
44 * subord = the pmap that goes into the grand
45 * va_start = start of range in pmap to be inserted
46 * nstart = start of range in pmap nested pmap
47 * size = Size of nest area (up to 16TB)
48 *
49 * Inserts a pmap into another. This is used to implement shared segments.
50 *
51 * Note that we depend upon higher level VM locks to insure that things don't change while
52 * we are doing this. For example, VM should not be doing any pmap enters while it is nesting
53 * or do 2 nests at once.
54 */
55
56/*
57 * This routine can nest subtrees either at the PDPT level (1GiB) or at the
58 * PDE level (2MiB). We currently disallow disparate offsets for the "subord"
59 * container and the "grand" parent. A minor optimization to consider for the
60 * future: make the "subord" truly a container rather than a full-fledged
61 * pagetable hierarchy which can be unnecessarily sparse (DRK).
62 */
63
64kern_return_t pmap_nest(pmap_t grand, pmap_t subord, addr64_t va_start, addr64_t nstart, uint64_t size) {
65 vm_map_offset_t vaddr, nvaddr;
66 pd_entry_t *pde,*npde;
67 unsigned int i;
68 uint64_t num_pde;
69
70 if ((size & (pmap_nesting_size_min-1)) ||
71 (va_start & (pmap_nesting_size_min-1)) ||
72 (nstart & (pmap_nesting_size_min-1)) ||
73 ((size >> 28) > 65536)) /* Max size we can nest is 16TB */
74 return KERN_INVALID_VALUE;
75
76 if(size == 0) {
77 panic("pmap_nest: size is invalid - %016llX\n", size);
78 }
79
80 if (va_start != nstart)
81 panic("pmap_nest: va_start(0x%llx) != nstart(0x%llx)\n", va_start, nstart);
82
83 PMAP_TRACE(PMAP_CODE(PMAP__NEST) | DBG_FUNC_START,
84 (int) grand, (int) subord,
85 (int) (va_start>>32), (int) va_start, 0);
86
87 nvaddr = (vm_map_offset_t)nstart;
88 num_pde = size >> PDESHIFT;
89
90 PMAP_LOCK(subord);
91
92 subord->pm_shared = TRUE;
93
94 for (i = 0; i < num_pde;) {
95 if (((nvaddr & PDPTMASK) == 0) && (num_pde - i) >= NPDEPG && cpu_64bit) {
96
97 npde = pmap64_pdpt(subord, nvaddr);
98
99 while (0 == npde || ((*npde & INTEL_PTE_VALID) == 0)) {
100 PMAP_UNLOCK(subord);
101 pmap_expand_pdpt(subord, nvaddr);
102 PMAP_LOCK(subord);
103 npde = pmap64_pdpt(subord, nvaddr);
104 }
105 *npde |= INTEL_PDPTE_NESTED;
106 nvaddr += NBPDPT;
107 i += (uint32_t)NPDEPG;
108 }
109 else {
110 npde = pmap_pde(subord, nvaddr);
111
112 while (0 == npde || ((*npde & INTEL_PTE_VALID) == 0)) {
113 PMAP_UNLOCK(subord);
114 pmap_expand(subord, nvaddr);
115 PMAP_LOCK(subord);
116 npde = pmap_pde(subord, nvaddr);
117 }
118 nvaddr += NBPDE;
119 i++;
120 }
121 }
122
123 PMAP_UNLOCK(subord);
124
125 vaddr = (vm_map_offset_t)va_start;
126
127 PMAP_LOCK(grand);
128
129 for (i = 0;i < num_pde;) {
130 pd_entry_t tpde;
131
132 if (((vaddr & PDPTMASK) == 0) && ((num_pde - i) >= NPDEPG) && cpu_64bit) {
133 npde = pmap64_pdpt(subord, vaddr);
134 if (npde == 0)
135 panic("pmap_nest: no PDPT, subord %p nstart 0x%llx", subord, vaddr);
136 tpde = *npde;
137 pde = pmap64_pdpt(grand, vaddr);
138 if (0 == pde) {
139 PMAP_UNLOCK(grand);
140 pmap_expand_pml4(grand, vaddr);
141 PMAP_LOCK(grand);
142 pde = pmap64_pdpt(grand, vaddr);
143 }
144 if (pde == 0)
145 panic("pmap_nest: no PDPT, grand %p vaddr 0x%llx", grand, vaddr);
146 pmap_store_pte(pde, tpde);
147 vaddr += NBPDPT;
148 i += (uint32_t) NPDEPG;
149 }
150 else {
151 npde = pmap_pde(subord, nstart);
152 if (npde == 0)
153 panic("pmap_nest: no npde, subord %p nstart 0x%llx", subord, nstart);
154 tpde = *npde;
155 nstart += NBPDE;
156 pde = pmap_pde(grand, vaddr);
157 if ((0 == pde) && cpu_64bit) {
158 PMAP_UNLOCK(grand);
159 pmap_expand_pdpt(grand, vaddr);
160 PMAP_LOCK(grand);
161 pde = pmap_pde(grand, vaddr);
162 }
163
164 if (pde == 0)
165 panic("pmap_nest: no pde, grand %p vaddr 0x%llx", grand, vaddr);
166 vaddr += NBPDE;
167 pmap_store_pte(pde, tpde);
168 i++;
169 }
170 }
171
172 PMAP_UNLOCK(grand);
173
174 PMAP_TRACE(PMAP_CODE(PMAP__NEST) | DBG_FUNC_END, 0, 0, 0, 0, 0);
175
176 return KERN_SUCCESS;
177}
178
179/*
180 * kern_return_t pmap_unnest(grand, vaddr)
181 *
182 * grand = the pmap that we will un-nest subord from
183 * vaddr = start of range in pmap to be unnested
184 *
185 * Removes a pmap from another. This is used to implement shared segments.
186 */
187
188kern_return_t pmap_unnest(pmap_t grand, addr64_t vaddr, uint64_t size) {
189
190 pd_entry_t *pde;
191 unsigned int i;
192 uint64_t num_pde;
193 addr64_t va_start, va_end;
194 uint64_t npdpt = PMAP_INVALID_PDPTNUM;
195
196 PMAP_TRACE(PMAP_CODE(PMAP__UNNEST) | DBG_FUNC_START,
197 (int) grand,
198 (int) (vaddr>>32), (int) vaddr, 0, 0);
199
200 if ((size & (pmap_nesting_size_min-1)) ||
201 (vaddr & (pmap_nesting_size_min-1))) {
202 panic("pmap_unnest(%p,0x%llx,0x%llx): unaligned...\n",
203 grand, vaddr, size);
204 }
205
206 /* align everything to PDE boundaries */
207 va_start = vaddr & ~(NBPDE-1);
208 va_end = (vaddr + size + NBPDE - 1) & ~(NBPDE-1);
209 size = va_end - va_start;
210
211 PMAP_LOCK(grand);
212
213 num_pde = size >> PDESHIFT;
214 vaddr = va_start;
215
216 for (i = 0; i < num_pde; ) {
217 if ((pdptnum(grand, vaddr) != npdpt) && cpu_64bit) {
218 npdpt = pdptnum(grand, vaddr);
219 pde = pmap64_pdpt(grand, vaddr);
220 if (pde && (*pde & INTEL_PDPTE_NESTED)) {
221 pmap_store_pte(pde, (pd_entry_t)0);
222 i += (uint32_t) NPDEPG;
223 vaddr += NBPDPT;
224 continue;
225 }
226 }
227 pde = pmap_pde(grand, (vm_map_offset_t)vaddr);
228 if (pde == 0)
229 panic("pmap_unnest: no pde, grand %p vaddr 0x%llx\n", grand, vaddr);
230 pmap_store_pte(pde, (pd_entry_t)0);
231 i++;
232 vaddr += NBPDE;
233 }
234
235 PMAP_UPDATE_TLBS(grand, va_start, va_end);
236
237 PMAP_UNLOCK(grand);
238
239 PMAP_TRACE(PMAP_CODE(PMAP__UNNEST) | DBG_FUNC_END, 0, 0, 0, 0, 0);
240
241 return KERN_SUCCESS;
242}
243
244/* Invoked by the Mach VM to determine the platform specific unnest region */
245
246boolean_t pmap_adjust_unnest_parameters(pmap_t p, vm_map_offset_t *s, vm_map_offset_t *e) {
247 pd_entry_t *pdpte;
248 boolean_t rval = FALSE;
249
250 if (!cpu_64bit)
251 return rval;
252
253 PMAP_LOCK(p);
254
255 pdpte = pmap64_pdpt(p, *s);
256 if (pdpte && (*pdpte & INTEL_PDPTE_NESTED)) {
257 *s &= ~(NBPDPT -1);
258 rval = TRUE;
259 }
260
261 pdpte = pmap64_pdpt(p, *e);
262 if (pdpte && (*pdpte & INTEL_PDPTE_NESTED)) {
263 *e = ((*e + NBPDPT) & ~(NBPDPT -1));
264 rval = TRUE;
265 }
266
267 PMAP_UNLOCK(p);
268
269 return rval;
270}
271
272/*
273 * pmap_find_phys returns the (4K) physical page number containing a
274 * given virtual address in a given pmap.
275 * Note that pmap_pte may return a pde if this virtual address is
276 * mapped by a large page and this is taken into account in order
277 * to return the correct page number in this case.
278 */
279ppnum_t
280pmap_find_phys(pmap_t pmap, addr64_t va)
281{
282 pt_entry_t *ptp;
283 pd_entry_t *pdep;
284 ppnum_t ppn = 0;
285 pd_entry_t pde;
286 pt_entry_t pte;
287
288 mp_disable_preemption();
289
290 /* This refcount test is a band-aid--several infrastructural changes
291 * are necessary to eliminate invocation of this routine from arbitrary
292 * contexts.
293 */
294
295 if (!pmap->ref_count)
296 goto pfp_exit;
297
298 pdep = pmap_pde(pmap, va);
299
300 if ((pdep != PD_ENTRY_NULL) && ((pde = *pdep) & INTEL_PTE_VALID)) {
301 if (pde & INTEL_PTE_PS) {
302 ppn = (ppnum_t) i386_btop(pte_to_pa(pde));
303 ppn += (ppnum_t) ptenum(va);
304 }
305 else {
306 ptp = pmap_pte(pmap, va);
307 if ((PT_ENTRY_NULL != ptp) && (((pte = *ptp) & INTEL_PTE_VALID) != 0)) {
308 ppn = (ppnum_t) i386_btop(pte_to_pa(pte));
309 }
310 }
311 }
312pfp_exit:
313 mp_enable_preemption();
314
315 return ppn;
316}
317