1 | /* |
2 | * Copyright (c) 2009-2010 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 | * file: pal_routines.c |
31 | * Platform Abstraction Layer routines for bare-metal i386 and x86_64 |
32 | */ |
33 | |
34 | |
35 | #include <kern/kern_types.h> |
36 | #include <mach/mach_types.h> |
37 | #include <kern/thread.h> |
38 | #include <kern/simple_lock.h> |
39 | |
40 | #include <sys/kdebug.h> |
41 | #include <machine/pal_routines.h> |
42 | #include <i386/serial_io.h> |
43 | #include <i386/lapic.h> |
44 | #include <i386/proc_reg.h> |
45 | #include <i386/misc_protos.h> |
46 | #include <i386/machine_routines.h> |
47 | #include <i386/pmap.h> |
48 | |
49 | //#define PAL_DEBUG 1 |
50 | #ifdef PAL_DEBUG |
51 | #define DBG(x...) kprintf("PAL_DBG: " x) |
52 | #else |
53 | #define DBG(x...) |
54 | #endif /* PAL_DEBUG */ |
55 | |
56 | extern void *gPEEFIRuntimeServices; |
57 | extern void *gPEEFISystemTable; |
58 | |
59 | /* nanotime conversion information */ |
60 | pal_rtc_nanotime_t pal_rtc_nanotime_info = {0,0,0,0,1,0}; |
61 | |
62 | /* APIC kext may use this to access xnu internal state */ |
63 | struct pal_apic_table *apic_table = NULL; |
64 | |
65 | decl_simple_lock_data(static , pal_efi_lock); |
66 | #ifdef __x86_64__ |
67 | static pml4_entry_t IDPML4[PTE_PER_PAGE] __attribute__ ((aligned (4096))); |
68 | uint64_t pal_efi_saved_cr0; |
69 | uint64_t pal_efi_saved_cr3; |
70 | #endif |
71 | |
72 | |
73 | /* Serial routines */ |
74 | int |
75 | pal_serial_init(void) |
76 | { |
77 | return serial_init(); |
78 | } |
79 | |
80 | void |
81 | pal_serial_putc_nocr(char c) |
82 | { |
83 | serial_putc(c); |
84 | } |
85 | |
86 | void |
87 | pal_serial_putc(char c) |
88 | { |
89 | serial_putc(c); |
90 | if (c == '\n') |
91 | serial_putc('\r'); |
92 | } |
93 | |
94 | int |
95 | pal_serial_getc(void) |
96 | { |
97 | return serial_getc(); |
98 | } |
99 | |
100 | |
101 | /* Generic routines */ |
102 | void |
103 | pal_i386_init(void) |
104 | { |
105 | simple_lock_init(&pal_efi_lock, 0); |
106 | } |
107 | |
108 | void |
109 | pal_get_control_registers( pal_cr_t *cr0, pal_cr_t *cr2, |
110 | pal_cr_t *cr3, pal_cr_t *cr4 ) |
111 | { |
112 | *cr0 = get_cr0(); |
113 | *cr2 = get_cr2(); |
114 | *cr3 = get_cr3_raw(); |
115 | *cr4 = get_cr4(); |
116 | } |
117 | |
118 | |
119 | /* |
120 | * define functions below here to ensure we have symbols for these, |
121 | * even though they're not used on this platform. |
122 | */ |
123 | #undef pal_dbg_page_fault |
124 | void |
125 | pal_dbg_page_fault( thread_t thread __unused, |
126 | user_addr_t vaddr __unused, |
127 | kern_return_t kr __unused ) |
128 | { |
129 | } |
130 | |
131 | #undef pal_dbg_set_task_name |
132 | void |
133 | pal_dbg_set_task_name( task_t task __unused ) |
134 | { |
135 | } |
136 | |
137 | #undef pal_set_signal_delivery |
138 | void |
139 | pal_set_signal_delivery(thread_t thread __unused) |
140 | { |
141 | } |
142 | |
143 | /* EFI thunks */ |
144 | extern void |
145 | _pal_efi_call_in_64bit_mode_asm(uint64_t func, |
146 | struct pal_efi_registers *efi_reg, |
147 | void *stack_contents, |
148 | size_t stack_contents_size); |
149 | |
150 | kern_return_t |
151 | pal_efi_call_in_64bit_mode(uint64_t func, |
152 | struct pal_efi_registers *efi_reg, |
153 | void *stack_contents, |
154 | size_t stack_contents_size, /* 16-byte multiple */ |
155 | uint64_t *efi_status) |
156 | { |
157 | DBG("pal_efi_call_in_64bit_mode(0x%016llx, %p, %p, %lu, %p)\n" , |
158 | func, efi_reg, stack_contents, stack_contents_size, efi_status); |
159 | |
160 | if (func == 0) { |
161 | return KERN_INVALID_ADDRESS; |
162 | } |
163 | |
164 | if ((efi_reg == NULL) |
165 | || (stack_contents == NULL) |
166 | || (stack_contents_size % 16 != 0)) { |
167 | return KERN_INVALID_ARGUMENT; |
168 | } |
169 | |
170 | if (!gPEEFISystemTable || !gPEEFIRuntimeServices) { |
171 | return KERN_NOT_SUPPORTED; |
172 | } |
173 | |
174 | if (func < VM_MIN_KERNEL_ADDRESS) { |
175 | /* |
176 | * EFI Runtime Services must be mapped in our address |
177 | * space at an appropriate location. |
178 | */ |
179 | return KERN_INVALID_ADDRESS; |
180 | } |
181 | |
182 | _pal_efi_call_in_64bit_mode_asm(func, |
183 | efi_reg, |
184 | stack_contents, |
185 | stack_contents_size); |
186 | |
187 | *efi_status = efi_reg->rax; |
188 | |
189 | return KERN_SUCCESS; |
190 | } |
191 | |
192 | extern void |
193 | _pal_efi_call_in_32bit_mode_asm(uint32_t func, |
194 | struct pal_efi_registers *efi_reg, |
195 | void *stack_contents, |
196 | size_t stack_contents_size); |
197 | |
198 | kern_return_t |
199 | pal_efi_call_in_32bit_mode(uint32_t func, |
200 | struct pal_efi_registers *efi_reg, |
201 | void *stack_contents, |
202 | size_t stack_contents_size, /* 16-byte multiple */ |
203 | uint32_t *efi_status) |
204 | { |
205 | DBG("pal_efi_call_in_32bit_mode(0x%08x, %p, %p, %lu, %p)\n" , |
206 | func, efi_reg, stack_contents, stack_contents_size, efi_status); |
207 | |
208 | if (func == 0) { |
209 | return KERN_INVALID_ADDRESS; |
210 | } |
211 | |
212 | if ((efi_reg == NULL) |
213 | || (stack_contents == NULL) |
214 | || (stack_contents_size % 16 != 0)) { |
215 | return KERN_INVALID_ARGUMENT; |
216 | } |
217 | |
218 | if (!gPEEFISystemTable || !gPEEFIRuntimeServices) { |
219 | return KERN_NOT_SUPPORTED; |
220 | } |
221 | |
222 | DBG("pal_efi_call_in_32bit_mode() efi_reg:\n" ); |
223 | DBG(" rcx: 0x%016llx\n" , efi_reg->rcx); |
224 | DBG(" rdx: 0x%016llx\n" , efi_reg->rdx); |
225 | DBG(" r8: 0x%016llx\n" , efi_reg->r8); |
226 | DBG(" r9: 0x%016llx\n" , efi_reg->r9); |
227 | DBG(" rax: 0x%016llx\n" , efi_reg->rax); |
228 | |
229 | DBG("pal_efi_call_in_32bit_mode() stack:\n" ); |
230 | #if PAL_DEBUG |
231 | size_t i; |
232 | for (i = 0; i < stack_contents_size; i += sizeof(uint32_t)) { |
233 | uint32_t *p = (uint32_t *) ((uintptr_t)stack_contents + i); |
234 | DBG(" %p: 0x%08x\n" , p, *p); |
235 | } |
236 | #endif |
237 | |
238 | #ifdef __x86_64__ |
239 | /* |
240 | * Ensure no interruptions. |
241 | * Taking a spinlock for serialization is technically unnecessary |
242 | * because the EFIRuntime kext should serialize. |
243 | */ |
244 | boolean_t istate = ml_set_interrupts_enabled(FALSE); |
245 | simple_lock(&pal_efi_lock); |
246 | |
247 | /* |
248 | * Switch to special page tables with the entire high kernel space |
249 | * double-mapped into the bottom 4GB. |
250 | * |
251 | * NB: We assume that all data passed exchanged with RuntimeServices is |
252 | * located in the 4GB of KVA based at VM_MIN_ADDRESS. In particular, kexts |
253 | * loaded the basement (below VM_MIN_ADDRESS) cannot pass static data. |
254 | * Kernel stack and heap space is OK. |
255 | */ |
256 | MARK_CPU_IDLE(cpu_number()); |
257 | pal_efi_saved_cr3 = get_cr3_raw(); |
258 | pal_efi_saved_cr0 = get_cr0(); |
259 | IDPML4[KERNEL_PML4_INDEX] = IdlePML4[KERNEL_PML4_INDEX]; |
260 | IDPML4[0] = IdlePML4[KERNEL_PML4_INDEX]; |
261 | clear_ts(); |
262 | set_cr3_raw((uint64_t) ID_MAP_VTOP(IDPML4)); |
263 | |
264 | swapgs(); /* Save kernel's GS base */ |
265 | |
266 | /* Set segment state ready for compatibility mode */ |
267 | set_gs(NULL_SEG); |
268 | set_fs(NULL_SEG); |
269 | set_es(KERNEL_DS); |
270 | set_ds(KERNEL_DS); |
271 | set_ss(KERNEL_DS); |
272 | |
273 | _pal_efi_call_in_32bit_mode_asm(func, |
274 | efi_reg, |
275 | stack_contents, |
276 | stack_contents_size); |
277 | |
278 | /* Restore NULL segment state */ |
279 | set_ss(NULL_SEG); |
280 | set_es(NULL_SEG); |
281 | set_ds(NULL_SEG); |
282 | |
283 | swapgs(); /* Restore kernel's GS base */ |
284 | |
285 | /* Restore the 64-bit user GS base we just destroyed */ |
286 | wrmsr64(MSR_IA32_KERNEL_GS_BASE, |
287 | current_cpu_datap()->cpu_uber.cu_user_gs_base); |
288 | |
289 | /* End of mapping games */ |
290 | set_cr3_raw(pal_efi_saved_cr3); |
291 | set_cr0(pal_efi_saved_cr0); |
292 | MARK_CPU_ACTIVE(cpu_number()); |
293 | |
294 | simple_unlock(&pal_efi_lock); |
295 | ml_set_interrupts_enabled(istate); |
296 | #else |
297 | _pal_efi_call_in_32bit_mode_asm(func, |
298 | efi_reg, |
299 | stack_contents, |
300 | stack_contents_size); |
301 | #endif |
302 | |
303 | *efi_status = (uint32_t)efi_reg->rax; |
304 | DBG("pal_efi_call_in_32bit_mode() efi_status: 0x%x\n" , *efi_status); |
305 | |
306 | return KERN_SUCCESS; |
307 | } |
308 | |
309 | /* wind-back a syscall instruction */ |
310 | void |
311 | pal_syscall_restart(thread_t thread __unused, x86_saved_state_t *state) |
312 | { |
313 | /* work out which flavour thread it is */ |
314 | if( is_saved_state32(state) ) |
315 | { |
316 | x86_saved_state32_t *regs32; |
317 | regs32 = saved_state32(state); |
318 | |
319 | if (regs32->cs == SYSENTER_CS || regs32->cs == SYSENTER_TF_CS) |
320 | regs32->eip -= 5; |
321 | else |
322 | regs32->eip -= 2; |
323 | } |
324 | else |
325 | { |
326 | x86_saved_state64_t *regs64; |
327 | |
328 | assert( is_saved_state64(state) ); |
329 | regs64 = saved_state64(state); |
330 | |
331 | /* Only one instruction for 64-bit threads */ |
332 | regs64->isf.rip -= 2; |
333 | } |
334 | |
335 | } |
336 | |
337 | /* Helper function to put the machine to sleep (or shutdown) */ |
338 | |
339 | boolean_t |
340 | pal_machine_sleep(uint8_t type_a __unused, uint8_t type_b __unused, uint32_t bit_position __unused, |
341 | uint32_t disable_mask __unused, uint32_t enable_mask __unused) |
342 | { |
343 | return 0; |
344 | } |
345 | |
346 | |
347 | /* shouldn't be used on native */ |
348 | void |
349 | pal_get_kern_regs( x86_saved_state_t *state ) |
350 | { |
351 | panic( "pal_get_kern_regs called. state %p\n" , state ); |
352 | } |
353 | |
354 | void |
355 | pal_preemption_assert(void) |
356 | { |
357 | } |
358 | |