]> git.saurik.com Git - apple/xnu.git/blob - osfmk/i386/commpage/commpage.c
dea334e43de2274d95de19bc3a848831f5b08f13
[apple/xnu.git] / osfmk / i386 / commpage / commpage.c
1 /*
2 * Copyright (c) 2003-2008 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
29 /*
30 * Here's what to do if you want to add a new routine to the comm page:
31 *
32 * 1. Add a definition for it's address in osfmk/i386/cpu_capabilities.h,
33 * being careful to reserve room for future expansion.
34 *
35 * 2. Write one or more versions of the routine, each with it's own
36 * commpage_descriptor. The tricky part is getting the "special",
37 * "musthave", and "canthave" fields right, so that exactly one
38 * version of the routine is selected for every machine.
39 * The source files should be in osfmk/i386/commpage/.
40 *
41 * 3. Add a ptr to your new commpage_descriptor(s) in the "routines"
42 * array in osfmk/i386/commpage/commpage_asm.s. There are two
43 * arrays, one for the 32-bit and one for the 64-bit commpage.
44 *
45 * 4. Write the code in Libc to use the new routine.
46 */
47
48 #include <mach/mach_types.h>
49 #include <mach/machine.h>
50 #include <mach/vm_map.h>
51 #include <mach/mach_vm.h>
52 #include <i386/tsc.h>
53 #include <i386/rtclock.h>
54 #include <i386/cpu_data.h>
55 #include <i386/machine_routines.h>
56 #include <i386/misc_protos.h>
57 #include <machine/cpu_capabilities.h>
58 #include <machine/commpage.h>
59 #include <machine/pmap.h>
60 #include <vm/vm_kern.h>
61 #include <vm/vm_map.h>
62
63 #include <ipc/ipc_port.h>
64
65 #include <kern/page_decrypt.h>
66
67 /* the lists of commpage routines are in commpage_asm.s */
68 extern commpage_descriptor* commpage_32_routines[];
69 extern commpage_descriptor* commpage_64_routines[];
70
71 /* translated commpage descriptors from commpage_sigs.c */
72 extern commpage_descriptor sigdata_descriptor;
73 extern commpage_descriptor *ba_descriptors[];
74
75 extern vm_map_t commpage32_map; // the shared submap, set up in vm init
76 extern vm_map_t commpage64_map; // the shared submap, set up in vm init
77
78 char *commPagePtr32 = NULL; // virtual addr in kernel map of 32-bit commpage
79 char *commPagePtr64 = NULL; // ...and of 64-bit commpage
80 int _cpu_capabilities = 0; // define the capability vector
81
82 int noVMX = 0; /* if true, do not set kHasAltivec in ppc _cpu_capabilities */
83
84 typedef uint32_t commpage_address_t;
85
86 static commpage_address_t next; // next available address in comm page
87 static commpage_address_t cur_routine; // comm page address of "current" routine
88 static boolean_t matched; // true if we've found a match for "current" routine
89
90 static char *commPagePtr; // virtual addr in kernel map of commpage we are working on
91 static commpage_address_t commPageBaseOffset; // subtract from 32-bit runtime address to get offset in virtual commpage in kernel map
92
93 static commpage_time_data *time_data32 = NULL;
94 static commpage_time_data *time_data64 = NULL;
95
96 /* Allocate the commpage and add to the shared submap created by vm:
97 * 1. allocate a page in the kernel map (RW)
98 * 2. wire it down
99 * 3. make a memory entry out of it
100 * 4. map that entry into the shared comm region map (R-only)
101 */
102
103 static void*
104 commpage_allocate(
105 vm_map_t submap, // commpage32_map or commpage_map64
106 size_t area_used ) // _COMM_PAGE32_AREA_USED or _COMM_PAGE64_AREA_USED
107 {
108 vm_offset_t kernel_addr = 0; // address of commpage in kernel map
109 vm_offset_t zero = 0;
110 vm_size_t size = area_used; // size actually populated
111 vm_map_entry_t entry;
112 ipc_port_t handle;
113
114 if (submap == NULL)
115 panic("commpage submap is null");
116
117 if (vm_map(kernel_map,&kernel_addr,area_used,0,VM_FLAGS_ANYWHERE,NULL,0,FALSE,VM_PROT_ALL,VM_PROT_ALL,VM_INHERIT_NONE))
118 panic("cannot allocate commpage");
119
120 if (vm_map_wire(kernel_map,kernel_addr,kernel_addr+area_used,VM_PROT_DEFAULT,FALSE))
121 panic("cannot wire commpage");
122
123 /*
124 * Now that the object is created and wired into the kernel map, mark it so that no delay
125 * copy-on-write will ever be performed on it as a result of mapping it into user-space.
126 * If such a delayed copy ever occurred, we could remove the kernel's wired mapping - and
127 * that would be a real disaster.
128 *
129 * JMM - What we really need is a way to create it like this in the first place.
130 */
131 if (!vm_map_lookup_entry( kernel_map, vm_map_trunc_page(kernel_addr), &entry) || entry->is_sub_map)
132 panic("cannot find commpage entry");
133 entry->object.vm_object->copy_strategy = MEMORY_OBJECT_COPY_NONE;
134
135 if (mach_make_memory_entry( kernel_map, // target map
136 &size, // size
137 kernel_addr, // offset (address in kernel map)
138 VM_PROT_ALL, // map it RWX
139 &handle, // this is the object handle we get
140 NULL )) // parent_entry (what is this?)
141 panic("cannot make entry for commpage");
142
143 if (vm_map_64( submap, // target map (shared submap)
144 &zero, // address (map into 1st page in submap)
145 area_used, // size
146 0, // mask
147 VM_FLAGS_FIXED, // flags (it must be 1st page in submap)
148 handle, // port is the memory entry we just made
149 0, // offset (map 1st page in memory entry)
150 FALSE, // copy
151 VM_PROT_READ|VM_PROT_EXECUTE, // cur_protection (R-only in user map)
152 VM_PROT_READ|VM_PROT_EXECUTE, // max_protection
153 VM_INHERIT_SHARE )) // inheritance
154 panic("cannot map commpage");
155
156 ipc_port_release(handle);
157
158 return (void*)(intptr_t)kernel_addr; // return address in kernel map
159 }
160
161 /* Get address (in kernel map) of a commpage field. */
162
163 static void*
164 commpage_addr_of(
165 commpage_address_t addr_at_runtime )
166 {
167 return (void*) ((uintptr_t)commPagePtr + (addr_at_runtime - commPageBaseOffset));
168 }
169
170 /* Determine number of CPUs on this system. We cannot rely on
171 * machine_info.max_cpus this early in the boot.
172 */
173 static int
174 commpage_cpus( void )
175 {
176 int cpus;
177
178 cpus = ml_get_max_cpus(); // NB: this call can block
179
180 if (cpus == 0)
181 panic("commpage cpus==0");
182 if (cpus > 0xFF)
183 cpus = 0xFF;
184
185 return cpus;
186 }
187
188 /* Initialize kernel version of _cpu_capabilities vector (used by KEXTs.) */
189
190 static void
191 commpage_init_cpu_capabilities( void )
192 {
193 int bits;
194 int cpus;
195 ml_cpu_info_t cpu_info;
196
197 bits = 0;
198 ml_cpu_get_info(&cpu_info);
199
200 switch (cpu_info.vector_unit) {
201 case 8:
202 bits |= kHasSSE4_2;
203 /* fall thru */
204 case 7:
205 bits |= kHasSSE4_1;
206 /* fall thru */
207 case 6:
208 bits |= kHasSupplementalSSE3;
209 /* fall thru */
210 case 5:
211 bits |= kHasSSE3;
212 /* fall thru */
213 case 4:
214 bits |= kHasSSE2;
215 /* fall thru */
216 case 3:
217 bits |= kHasSSE;
218 /* fall thru */
219 case 2:
220 bits |= kHasMMX;
221 default:
222 break;
223 }
224 switch (cpu_info.cache_line_size) {
225 case 128:
226 bits |= kCache128;
227 break;
228 case 64:
229 bits |= kCache64;
230 break;
231 case 32:
232 bits |= kCache32;
233 break;
234 default:
235 break;
236 }
237 cpus = commpage_cpus(); // how many CPUs do we have
238
239 if (cpus == 1)
240 bits |= kUP;
241
242 bits |= (cpus << kNumCPUsShift);
243
244 bits |= kFastThreadLocalStorage; // we use %gs for TLS
245
246 if (cpu_mode_is64bit()) // k64Bit means processor is 64-bit capable
247 bits |= k64Bit;
248
249 if (tscFreq <= SLOW_TSC_THRESHOLD) /* is TSC too slow for _commpage_nanotime? */
250 bits |= kSlow;
251
252 _cpu_capabilities = bits; // set kernel version for use by drivers etc
253 }
254
255 int
256 _get_cpu_capabilities(void)
257 {
258 return _cpu_capabilities;
259 }
260
261 /* Copy data into commpage. */
262
263 static void
264 commpage_stuff(
265 commpage_address_t address,
266 const void *source,
267 int length )
268 {
269 void *dest = commpage_addr_of(address);
270
271 if (address < next)
272 panic("commpage overlap at address 0x%p, 0x%x < 0x%x", dest, address, next);
273
274 bcopy(source,dest,length);
275
276 next = address + length;
277 }
278
279 static void
280 commpage_stuff_swap(
281 commpage_address_t address,
282 void *source,
283 int length,
284 int legacy )
285 {
286 if ( legacy ) {
287 void *dest = commpage_addr_of(address);
288 dest = (void *)((uintptr_t) dest + _COMM_PAGE_SIGS_OFFSET);
289 switch (length) {
290 case 2:
291 OSWriteSwapInt16(dest, 0, *(uint16_t *)source);
292 break;
293 case 4:
294 OSWriteSwapInt32(dest, 0, *(uint32_t *)source);
295 break;
296 case 8:
297 OSWriteSwapInt64(dest, 0, *(uint64_t *)source);
298 break;
299 }
300 }
301 }
302
303 static void
304 commpage_stuff2(
305 commpage_address_t address,
306 void *source,
307 int length,
308 int legacy )
309 {
310 commpage_stuff_swap(address, source, length, legacy);
311 commpage_stuff(address, source, length);
312 }
313
314 /* Copy a routine into comm page if it matches running machine.
315 */
316 static void
317 commpage_stuff_routine(
318 commpage_descriptor *rd )
319 {
320 uint32_t must,cant;
321
322 if (rd->commpage_address != cur_routine) {
323 if ((cur_routine!=0) && (matched==0))
324 panic("commpage no match for last, next address %08x", rd->commpage_address);
325 cur_routine = rd->commpage_address;
326 matched = 0;
327 }
328
329 must = _cpu_capabilities & rd->musthave;
330 cant = _cpu_capabilities & rd->canthave;
331
332 if ((must == rd->musthave) && (cant == 0)) {
333 if (matched)
334 panic("commpage multiple matches for address %08x", rd->commpage_address);
335 matched = 1;
336
337 commpage_stuff(rd->commpage_address,rd->code_address,rd->code_length);
338 }
339 }
340
341 /* Fill in the 32- or 64-bit commpage. Called once for each.
342 * The 32-bit ("legacy") commpage has a bunch of stuff added to it
343 * for translated processes, some of which is byte-swapped.
344 */
345
346 static void
347 commpage_populate_one(
348 vm_map_t submap, // commpage32_map or compage64_map
349 char ** kernAddressPtr, // &commPagePtr32 or &commPagePtr64
350 size_t area_used, // _COMM_PAGE32_AREA_USED or _COMM_PAGE64_AREA_USED
351 commpage_address_t base_offset, // will become commPageBaseOffset
352 commpage_descriptor** commpage_routines, // list of routine ptrs for this commpage
353 boolean_t legacy, // true if 32-bit commpage
354 commpage_time_data** time_data, // &time_data32 or &time_data64
355 const char* signature ) // "commpage 32-bit" or "commpage 64-bit"
356 {
357 short c2;
358 int c4;
359 static double two52 = 1048576.0 * 1048576.0 * 4096.0; // 2**52
360 static double ten6 = 1000000.0; // 10**6
361 commpage_descriptor **rd;
362 short version = _COMM_PAGE_THIS_VERSION;
363 int swapcaps;
364
365 next = 0;
366 cur_routine = 0;
367 commPagePtr = (char *)commpage_allocate( submap, (vm_size_t) area_used );
368 *kernAddressPtr = commPagePtr; // save address either in commPagePtr32 or 64
369 commPageBaseOffset = base_offset;
370
371 *time_data = commpage_addr_of( _COMM_PAGE_TIME_DATA_START );
372
373 /* Stuff in the constants. We move things into the comm page in strictly
374 * ascending order, so we can check for overlap and panic if so.
375 */
376 commpage_stuff(_COMM_PAGE_SIGNATURE,signature,(int)strlen(signature));
377 commpage_stuff2(_COMM_PAGE_VERSION,&version,sizeof(short),legacy);
378 commpage_stuff(_COMM_PAGE_CPU_CAPABILITIES,&_cpu_capabilities,sizeof(int));
379
380 /* excuse our magic constants, we cannot include ppc/cpu_capabilities.h */
381 /* always set kCache32 and kDcbaAvailable */
382 swapcaps = 0x44;
383 if ( _cpu_capabilities & kUP )
384 swapcaps |= (kUP + (1 << kNumCPUsShift));
385 else
386 swapcaps |= 2 << kNumCPUsShift; /* limit #cpus to 2 */
387 if ( ! noVMX ) /* if rosetta will be emulating altivec... */
388 swapcaps |= 0x101; /* ...then set kHasAltivec and kDataStreamsAvailable too */
389 commpage_stuff_swap(_COMM_PAGE_CPU_CAPABILITIES, &swapcaps, sizeof(int), legacy);
390 c2 = 32;
391 commpage_stuff_swap(_COMM_PAGE_CACHE_LINESIZE,&c2,2,legacy);
392
393 if (_cpu_capabilities & kCache32)
394 c2 = 32;
395 else if (_cpu_capabilities & kCache64)
396 c2 = 64;
397 else if (_cpu_capabilities & kCache128)
398 c2 = 128;
399 commpage_stuff(_COMM_PAGE_CACHE_LINESIZE,&c2,2);
400
401 c4 = MP_SPIN_TRIES;
402 commpage_stuff(_COMM_PAGE_SPIN_COUNT,&c4,4);
403
404 if ( legacy ) {
405 commpage_stuff2(_COMM_PAGE_2_TO_52,&two52,8,legacy);
406 commpage_stuff2(_COMM_PAGE_10_TO_6,&ten6,8,legacy);
407 }
408
409 for( rd = commpage_routines; *rd != NULL ; rd++ )
410 commpage_stuff_routine(*rd);
411
412 if (!matched)
413 panic("commpage no match on last routine");
414
415 if (next > _COMM_PAGE_END)
416 panic("commpage overflow: next = 0x%08x, commPagePtr = 0x%p", next, commPagePtr);
417
418 if ( legacy ) {
419 next = 0;
420 for( rd = ba_descriptors; *rd != NULL ; rd++ )
421 commpage_stuff_routine(*rd);
422
423 next = 0;
424 commpage_stuff_routine(&sigdata_descriptor);
425 }
426 }
427
428
429 /* Fill in commpages: called once, during kernel initialization, from the
430 * startup thread before user-mode code is running.
431 *
432 * See the top of this file for a list of what you have to do to add
433 * a new routine to the commpage.
434 */
435
436 void
437 commpage_populate( void )
438 {
439 commpage_init_cpu_capabilities();
440
441 commpage_populate_one( commpage32_map,
442 &commPagePtr32,
443 _COMM_PAGE32_AREA_USED,
444 _COMM_PAGE32_BASE_ADDRESS,
445 commpage_32_routines,
446 TRUE, /* legacy (32-bit) commpage */
447 &time_data32,
448 "commpage 32-bit");
449 #ifndef __LP64__
450 pmap_commpage32_init((vm_offset_t) commPagePtr32, _COMM_PAGE32_BASE_ADDRESS,
451 _COMM_PAGE32_AREA_USED/INTEL_PGBYTES);
452 #endif
453 time_data64 = time_data32; /* if no 64-bit commpage, point to 32-bit */
454
455 if (_cpu_capabilities & k64Bit) {
456 commpage_populate_one( commpage64_map,
457 &commPagePtr64,
458 _COMM_PAGE64_AREA_USED,
459 _COMM_PAGE32_START_ADDRESS, /* commpage address are relative to 32-bit commpage placement */
460 commpage_64_routines,
461 FALSE, /* not a legacy commpage */
462 &time_data64,
463 "commpage 64-bit");
464 #ifndef __LP64__
465 pmap_commpage64_init((vm_offset_t) commPagePtr64, _COMM_PAGE64_BASE_ADDRESS,
466 _COMM_PAGE64_AREA_USED/INTEL_PGBYTES);
467 #endif
468 }
469
470 rtc_nanotime_init_commpage();
471 }
472
473
474 /* Update commpage nanotime information. Note that we interleave
475 * setting the 32- and 64-bit commpages, in order to keep nanotime more
476 * nearly in sync between the two environments.
477 *
478 * This routine must be serialized by some external means, ie a lock.
479 */
480
481 void
482 commpage_set_nanotime(
483 uint64_t tsc_base,
484 uint64_t ns_base,
485 uint32_t scale,
486 uint32_t shift )
487 {
488 commpage_time_data *p32 = time_data32;
489 commpage_time_data *p64 = time_data64;
490 static uint32_t generation = 0;
491 uint32_t next_gen;
492
493 if (p32 == NULL) /* have commpages been allocated yet? */
494 return;
495
496 if ( generation != p32->nt_generation )
497 panic("nanotime trouble 1"); /* possibly not serialized */
498 if ( ns_base < p32->nt_ns_base )
499 panic("nanotime trouble 2");
500 if ((shift != 32) && ((_cpu_capabilities & kSlow)==0) )
501 panic("nanotime trouble 3");
502
503 next_gen = ++generation;
504 if (next_gen == 0)
505 next_gen = ++generation;
506
507 p32->nt_generation = 0; /* mark invalid, so commpage won't try to use it */
508 p64->nt_generation = 0;
509
510 p32->nt_tsc_base = tsc_base;
511 p64->nt_tsc_base = tsc_base;
512
513 p32->nt_ns_base = ns_base;
514 p64->nt_ns_base = ns_base;
515
516 p32->nt_scale = scale;
517 p64->nt_scale = scale;
518
519 p32->nt_shift = shift;
520 p64->nt_shift = shift;
521
522 p32->nt_generation = next_gen; /* mark data as valid */
523 p64->nt_generation = next_gen;
524 }
525
526
527 /* Disable commpage gettimeofday(), forcing commpage to call through to the kernel. */
528
529 void
530 commpage_disable_timestamp( void )
531 {
532 time_data32->gtod_generation = 0;
533 time_data64->gtod_generation = 0;
534 }
535
536
537 /* Update commpage gettimeofday() information. As with nanotime(), we interleave
538 * updates to the 32- and 64-bit commpage, in order to keep time more nearly in sync
539 * between the two environments.
540 *
541 * This routine must be serializeed by some external means, ie a lock.
542 */
543
544 void
545 commpage_set_timestamp(
546 uint64_t abstime,
547 uint64_t secs )
548 {
549 commpage_time_data *p32 = time_data32;
550 commpage_time_data *p64 = time_data64;
551 static uint32_t generation = 0;
552 uint32_t next_gen;
553
554 next_gen = ++generation;
555 if (next_gen == 0)
556 next_gen = ++generation;
557
558 p32->gtod_generation = 0; /* mark invalid, so commpage won't try to use it */
559 p64->gtod_generation = 0;
560
561 p32->gtod_ns_base = abstime;
562 p64->gtod_ns_base = abstime;
563
564 p32->gtod_sec_base = secs;
565 p64->gtod_sec_base = secs;
566
567 p32->gtod_generation = next_gen; /* mark data as valid */
568 p64->gtod_generation = next_gen;
569 }
570
571
572 /* Update _COMM_PAGE_MEMORY_PRESSURE. Called periodically from vm's compute_memory_pressure() */
573
574 void
575 commpage_set_memory_pressure(
576 unsigned int pressure )
577 {
578 char *cp;
579 uint32_t *ip;
580
581 cp = commPagePtr32;
582 if ( cp ) {
583 cp += (_COMM_PAGE_MEMORY_PRESSURE - _COMM_PAGE32_BASE_ADDRESS);
584 ip = (uint32_t*) cp;
585 *ip = (uint32_t) pressure;
586 }
587
588 cp = commPagePtr64;
589 if ( cp ) {
590 cp += (_COMM_PAGE_MEMORY_PRESSURE - _COMM_PAGE32_START_ADDRESS);
591 ip = (uint32_t*) cp;
592 *ip = (uint32_t) pressure;
593 }
594
595 }
596
597
598 /* Update _COMM_PAGE_SPIN_COUNT. We might want to reduce when running on a battery, etc. */
599
600 void
601 commpage_set_spin_count(
602 unsigned int count )
603 {
604 char *cp;
605 uint32_t *ip;
606
607 if (count == 0) /* we test for 0 after decrement, not before */
608 count = 1;
609
610 cp = commPagePtr32;
611 if ( cp ) {
612 cp += (_COMM_PAGE_SPIN_COUNT - _COMM_PAGE32_BASE_ADDRESS);
613 ip = (uint32_t*) cp;
614 *ip = (uint32_t) count;
615 }
616
617 cp = commPagePtr64;
618 if ( cp ) {
619 cp += (_COMM_PAGE_SPIN_COUNT - _COMM_PAGE32_START_ADDRESS);
620 ip = (uint32_t*) cp;
621 *ip = (uint32_t) count;
622 }
623
624 }
625
626
627 /* Check to see if a given address is in the Preemption Free Zone (PFZ) */
628
629 uint32_t
630 commpage_is_in_pfz32(uint32_t addr32)
631 {
632 if ( (addr32 >= _COMM_PAGE_PFZ_START) && (addr32 < _COMM_PAGE_PFZ_END)) {
633 return 1;
634 }
635 else
636 return 0;
637 }
638
639 uint32_t
640 commpage_is_in_pfz64(addr64_t addr64)
641 {
642 if ( (addr64 >= _COMM_PAGE_32_TO_64(_COMM_PAGE_PFZ_START))
643 && (addr64 < _COMM_PAGE_32_TO_64(_COMM_PAGE_PFZ_END))) {
644 return 1;
645 }
646 else
647 return 0;
648 }
649