]> git.saurik.com Git - apple/xnu.git/blob - osfmk/i386/i386_vm_init.c
1ed0d11516ac0fb2a7619824db1a6fe7e2c10304
[apple/xnu.git] / osfmk / i386 / i386_vm_init.c
1 /*
2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30 /*
31 * @OSF_COPYRIGHT@
32 */
33 /*
34 * Mach Operating System
35 * Copyright (c) 1991,1990,1989, 1988 Carnegie Mellon University
36 * All Rights Reserved.
37 *
38 * Permission to use, copy, modify and distribute this software and its
39 * documentation is hereby granted, provided that both the copyright
40 * notice and this permission notice appear in all copies of the
41 * software, derivative works or modified versions, and any portions
42 * thereof, and that both notices appear in supporting documentation.
43 *
44 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
45 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
46 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
47 *
48 * Carnegie Mellon requests users of this software to return to
49 *
50 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
51 * School of Computer Science
52 * Carnegie Mellon University
53 * Pittsburgh PA 15213-3890
54 *
55 * any improvements or extensions that they make and grant Carnegie Mellon
56 * the rights to redistribute these changes.
57 */
58
59 #include <platforms.h>
60 #include <mach_kdb.h>
61 #include <himem.h>
62
63 #include <mach/i386/vm_param.h>
64
65 #include <string.h>
66 #include <mach/vm_param.h>
67 #include <mach/vm_prot.h>
68 #include <mach/machine.h>
69 #include <mach/time_value.h>
70 #include <kern/spl.h>
71 #include <kern/assert.h>
72 #include <kern/debug.h>
73 #include <kern/misc_protos.h>
74 #include <kern/cpu_data.h>
75 #include <kern/processor.h>
76 #include <vm/vm_page.h>
77 #include <vm/pmap.h>
78 #include <vm/vm_kern.h>
79 #include <i386/pmap.h>
80 #include <i386/ipl.h>
81 #include <i386/pio.h>
82 #include <i386/misc_protos.h>
83 #include <i386/mp_slave_boot.h>
84 #include <i386/cpuid.h>
85 #ifdef __MACHO__
86 #include <mach/thread_status.h>
87 #endif
88
89 vm_size_t mem_size = 0;
90 vm_offset_t first_avail = 0;/* first after page tables */
91 vm_offset_t last_addr;
92
93 uint64_t max_mem;
94 uint64_t sane_size = 0; /* we are going to use the booter memory
95 table info to construct this */
96
97 pmap_paddr_t avail_start, avail_end;
98 vm_offset_t virtual_avail, virtual_end;
99 pmap_paddr_t avail_remaining;
100 vm_offset_t static_memory_end = 0;
101
102 #ifndef __MACHO__
103 extern char edata, end;
104 #endif
105
106 #ifdef __MACHO__
107 #include <mach-o/loader.h>
108 vm_offset_t edata, etext, end;
109
110 /*
111 * _mh_execute_header is the mach_header for the currently executing
112 * 32 bit kernel
113 */
114 extern struct mach_header _mh_execute_header;
115 void *sectTEXTB; int sectSizeTEXT;
116 void *sectDATAB; int sectSizeDATA;
117 void *sectOBJCB; int sectSizeOBJC;
118 void *sectLINKB; int sectSizeLINK;
119 void *sectPRELINKB; int sectSizePRELINK;
120 void *sectHIBB; int sectSizeHIB;
121
122 extern void *getsegdatafromheader(struct mach_header *, const char *, int *);
123 #endif
124
125 /*
126 * Basic VM initialization.
127 */
128 void
129 i386_vm_init(unsigned int maxmem, KernelBootArgs_t *args)
130 {
131 pmap_memory_region_t *pmptr;
132 MemoryRange *mptr;
133 ppnum_t fap;
134 unsigned int i;
135 ppnum_t maxpg = (maxmem >> I386_PGSHIFT);
136
137 #ifdef __MACHO__
138 /* Now retrieve addresses for end, edata, and etext
139 * from MACH-O headers.
140 */
141
142 sectTEXTB = (void *) getsegdatafromheader(
143 &_mh_execute_header, "__TEXT", &sectSizeTEXT);
144 sectDATAB = (void *) getsegdatafromheader(
145 &_mh_execute_header, "__DATA", &sectSizeDATA);
146 sectOBJCB = (void *) getsegdatafromheader(
147 &_mh_execute_header, "__OBJC", &sectSizeOBJC);
148 sectLINKB = (void *) getsegdatafromheader(
149 &_mh_execute_header, "__LINKEDIT", &sectSizeLINK);
150 sectHIBB = (void *)getsegdatafromheader(
151 &_mh_execute_header, "__HIB", &sectSizeHIB);
152 sectPRELINKB = (void *) getsegdatafromheader(
153 &_mh_execute_header, "__PRELINK", &sectSizePRELINK);
154
155 etext = (vm_offset_t) sectTEXTB + sectSizeTEXT;
156 edata = (vm_offset_t) sectDATAB + sectSizeDATA;
157 #endif
158 #ifndef __MACHO__
159 /*
160 * Zero the BSS.
161 */
162
163 bzero((char *)&edata,(unsigned)(&end - &edata));
164 #endif
165
166 /*
167 * Initialize the pic prior to any possible call to an spl.
168 */
169
170 set_cpu_model();
171 vm_set_page_size();
172
173 /*
174 * Compute the memory size.
175 */
176
177 avail_remaining = 0;
178 avail_end = 0;
179 pmptr = pmap_memory_regions;
180 pmap_memory_region_count = pmap_memory_region_current = 0;
181 fap = (ppnum_t) i386_btop(first_avail);
182 mptr = args->memoryMap;
183
184 #ifdef PAE
185 #define FOURGIG 0x0000000100000000ULL
186 for (i=0; i < args->memoryMapCount; i++,mptr++) {
187 ppnum_t base, top;
188
189 base = (ppnum_t) (mptr->base >> I386_PGSHIFT);
190 top = (ppnum_t) ((mptr->base + mptr->length) >> I386_PGSHIFT) - 1;
191
192 if (maxmem) {
193 if (base >= maxpg) break;
194 top = (top > maxpg)? maxpg : top;
195 }
196
197 if (kMemoryRangeUsable != mptr->type) continue;
198 sane_size += (uint64_t)(mptr->length);
199 #ifdef DEVICES_HANDLE_64BIT_IO /* XXX enable else clause when I/O to high memory works */
200 if (top < fap) {
201 /* entire range below first_avail */
202 continue;
203 } else if (mptr->base >= FOURGIG) {
204 /* entire range above 4GB (pre PAE) */
205 continue;
206 } else if ( (base < fap) &&
207 (top > fap)) {
208 /* spans first_avail */
209 /* put mem below first avail in table but
210 mark already allocated */
211 pmptr->base = base;
212 pmptr->alloc = pmptr->end = (fap - 1);
213 pmptr->type = mptr->type;
214 /* we bump these here inline so the accounting below works
215 correctly */
216 pmptr++;
217 pmap_memory_region_count++;
218 pmptr->alloc = pmptr->base = fap;
219 pmptr->type = mptr->type;
220 pmptr->end = top;
221 } else if ( (mptr->base < FOURGIG) &&
222 ((mptr->base+mptr->length) > FOURGIG) ) {
223 /* spans across 4GB (pre PAE) */
224 pmptr->alloc = pmptr->base = base;
225 pmptr->type = mptr->type;
226 pmptr->end = (FOURGIG >> I386_PGSHIFT) - 1;
227 } else {
228 /* entire range useable */
229 pmptr->alloc = pmptr->base = base;
230 pmptr->type = mptr->type;
231 pmptr->end = top;
232 }
233 #else
234 if (top < fap) {
235 /* entire range below first_avail */
236 continue;
237 } else if ( (base < fap) &&
238 (top > fap)) {
239 /* spans first_avail */
240 pmptr->alloc = pmptr->base = fap;
241 pmptr->type = mptr->type;
242 pmptr->end = top;
243 } else {
244 /* entire range useable */
245 pmptr->alloc = pmptr->base = base;
246 pmptr->type = mptr->type;
247 pmptr->end = top;
248 }
249 #endif
250 if (i386_ptob(pmptr->end) > avail_end ) {
251 avail_end = i386_ptob(pmptr->end);
252 }
253 avail_remaining += (pmptr->end - pmptr->base);
254 pmap_memory_region_count++;
255 pmptr++;
256 }
257 #else /* non PAE follows */
258 #define FOURGIG 0x0000000100000000ULL
259 for (i=0; i < args->memoryMapCount; i++,mptr++) {
260 ppnum_t base, top;
261
262 base = (ppnum_t) (mptr->base >> I386_PGSHIFT);
263 top = (ppnum_t) ((mptr->base + mptr->length) >> I386_PGSHIFT) - 1;
264
265 if (maxmem) {
266 if (base >= maxpg) break;
267 top = (top > maxpg)? maxpg : top;
268 }
269
270 if (kMemoryRangeUsable != mptr->type) continue;
271
272 // save other regions
273 if (kMemoryRangeNVS == mptr->type) {
274 // Mark this as a memory range (for hibernation),
275 // but don't count as usable memory
276 pmptr->base = base;
277 pmptr->end = ((mptr->base + mptr->length + I386_PGBYTES - 1) >> I386_PGSHIFT) - 1;
278 pmptr->alloc = pmptr->end;
279 pmptr->type = mptr->type;
280 kprintf("NVS region: 0x%x ->0x%x\n", pmptr->base, pmptr->end);
281 } else if (kMemoryRangeUsable != mptr->type) {
282 continue;
283 } else {
284 // Usable memory region
285 sane_size += (uint64_t)(mptr->length);
286 if (top < fap) {
287 /* entire range below first_avail */
288 /* salvage some low memory pages */
289 /* we use some very low memory at startup */
290 /* mark as already allocated here */
291 pmptr->base = 0x18; /* PAE and HIB use below this */
292 pmptr->alloc = pmptr->end = top; /* mark as already mapped */
293 pmptr->type = mptr->type;
294 } else if (mptr->base >= FOURGIG) {
295 /* entire range above 4GB (pre PAE) */
296 continue;
297 } else if ( (base < fap) &&
298 (top > fap)) {
299 /* spans first_avail */
300 /* put mem below first avail in table but
301 mark already allocated */
302 pmptr->base = base;
303 pmptr->alloc = pmptr->end = (fap - 1);
304 pmptr->type = mptr->type;
305 /* we bump these here inline so the accounting below works
306 correctly */
307 pmptr++;
308 pmap_memory_region_count++;
309 pmptr->alloc = pmptr->base = fap;
310 pmptr->type = mptr->type;
311 pmptr->end = top;
312 } else if ( (mptr->base < FOURGIG) &&
313 ((mptr->base+mptr->length) > FOURGIG) ) {
314 /* spans across 4GB (pre PAE) */
315 pmptr->alloc = pmptr->base = base;
316 pmptr->type = mptr->type;
317 pmptr->end = (FOURGIG >> I386_PGSHIFT) - 1;
318 } else {
319 /* entire range useable */
320 pmptr->alloc = pmptr->base = base;
321 pmptr->type = mptr->type;
322 pmptr->end = top;
323 }
324
325 if (i386_ptob(pmptr->end) > avail_end ) {
326 avail_end = i386_ptob(pmptr->end);
327 }
328
329 avail_remaining += (pmptr->end - pmptr->base);
330 pmap_memory_region_count++;
331 pmptr++;
332 }
333 }
334 #endif
335
336 #ifdef PRINT_PMAP_MEMORY_TABLE
337 {
338 unsigned int j;
339 pmap_memory_region_t *p = pmap_memory_regions;
340 for (j=0;j<pmap_memory_region_count;j++, p++) {
341 kprintf("%d base 0x%x alloc 0x%x top 0x%x\n",j,
342 p->base, p->alloc, p->end);
343 }
344 }
345 #endif
346
347 avail_start = first_avail;
348
349 if (maxmem) { /* if user set maxmem try to use it */
350 uint64_t tmp = (uint64_t)maxmem;
351 /* can't set below first_avail or above actual memory */
352 if ( (maxmem > first_avail) && (tmp < sane_size) ) {
353 sane_size = tmp;
354 avail_end = maxmem;
355 }
356 }
357 // round up to a megabyte - mostly accounting for the
358 // low mem madness
359 sane_size += ( 0x100000ULL - 1);
360 sane_size &= ~0xFFFFFULL;
361
362 #ifndef PAE
363 if (sane_size < FOURGIG)
364 mem_size = (unsigned long) sane_size;
365 else
366 mem_size = (unsigned long) (FOURGIG >> 1);
367 #else
368 mem_size = (unsigned long) sane_size;
369 #endif
370
371 max_mem = sane_size;
372
373 /* now make sane size sane */
374 #define MIN(a,b) (((a)<(b))?(a):(b))
375 #define MEG (1024*1024)
376 sane_size = MIN(sane_size, 256*MEG);
377
378 kprintf("Physical memory %d MB\n",
379 mem_size/MEG);
380
381 /*
382 * Initialize kernel physical map.
383 * Kernel virtual address starts at VM_KERNEL_MIN_ADDRESS.
384 */
385 pmap_bootstrap(0);
386
387
388 }
389
390 unsigned int
391 pmap_free_pages(void)
392 {
393 return avail_remaining;
394 }
395
396 boolean_t
397 pmap_next_page(
398 ppnum_t *pn)
399 {
400
401 while (pmap_memory_region_current < pmap_memory_region_count) {
402 if (pmap_memory_regions[pmap_memory_region_current].alloc ==
403 pmap_memory_regions[pmap_memory_region_current].end) {
404 pmap_memory_region_current++;
405 continue;
406 }
407 *pn = pmap_memory_regions[pmap_memory_region_current].alloc++;
408 avail_remaining--;
409
410 return TRUE;
411 }
412 return FALSE;
413 }
414
415 boolean_t
416 pmap_valid_page(
417 ppnum_t pn)
418 {
419 unsigned int i;
420 pmap_memory_region_t *pmptr = pmap_memory_regions;
421
422 assert(pn);
423 for (i=0; i<pmap_memory_region_count; i++, pmptr++) {
424 if ( (pn >= pmptr->base) && (pn <= pmptr->end) ) {
425 if (pmptr->type == kMemoryRangeUsable)
426 return TRUE;
427 else
428 return FALSE;
429 }
430 }
431 return FALSE;
432 }