]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ppc/PseudoKernel.c
xnu-792.17.14.tar.gz
[apple/xnu.git] / osfmk / ppc / PseudoKernel.c
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, 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 File: PseudoKernel.c
30
31 Contains: BlueBox PseudoKernel calls
32 Written by: Mark Gorlinsky
33 Bill Angell
34
35 Copyright: 1997 by Apple Computer, Inc., all rights reserved
36
37 */
38
39 #include <mach/mach_types.h>
40 #include <mach/kern_return.h>
41
42 #include <kern/kalloc.h>
43 #include <kern/kern_types.h>
44 #include <kern/host.h>
45 #include <kern/task.h>
46 #include <kern/thread.h>
47 #include <ppc/PseudoKernel.h>
48 #include <ppc/exception.h>
49 #include <ppc/misc_protos.h>
50 #include <ppc/proc_reg.h>
51
52 #include <vm/pmap.h>
53 #include <vm/vm_map.h>
54 #include <vm/vm_kern.h>
55
56 void bbSetRupt(ReturnHandler *rh, thread_t ct);
57
58 /*
59 ** Function: NotifyInterruption
60 **
61 ** Inputs:
62 ** ppcInterrupHandler - interrupt handler to execute
63 ** interruptStatePtr - current interrupt state
64 **
65 ** Outputs:
66 **
67 ** Notes:
68 **
69 */
70 kern_return_t syscall_notify_interrupt ( void ) {
71
72 UInt32 interruptState;
73 task_t task;
74 thread_t act, fact;
75 bbRupt *bbr;
76 BTTD_t *bttd;
77 int i;
78
79 task = current_task(); /* Figure out who our task is */
80
81 task_lock(task); /* Lock our task */
82
83 fact = (thread_t)task->threads.next; /* Get the first activation on task */
84 act = 0; /* Pretend we didn't find it yet */
85
86 for(i = 0; i < task->thread_count; i++) { /* Scan the whole list */
87 if(fact->machine.bbDescAddr) { /* Is this a Blue thread? */
88 bttd = (BTTD_t *)(fact->machine.bbDescAddr & -PAGE_SIZE);
89 if(bttd->InterruptVector) { /* Is this the Blue interrupt thread? */
90 act = fact; /* Yeah... */
91 break; /* Found it, Bail the loop... */
92 }
93 }
94 fact = (thread_t)fact->task_threads.next; /* Go to the next one */
95 }
96
97 if(!act) { /* Couldn't find a bluebox */
98 task_unlock(task); /* Release task lock */
99 return KERN_FAILURE; /* No tickie, no shirtee... */
100 }
101
102 thread_reference(act);
103
104 task_unlock(task); /* Safe to release now */
105
106 thread_mtx_lock(act);
107
108 /* if the calling thread is the BlueBox thread that handles interrupts
109 * we know that we are in the PsuedoKernel and we can short circuit
110 * setting up the asynchronous task by setting a pending interrupt.
111 */
112
113 if ( (unsigned int)act == (unsigned int)current_thread() ) {
114 bttd->InterruptControlWord = bttd->InterruptControlWord |
115 ((bttd->postIntMask >> kCR2ToBackupShift) & kBackupCR2Mask);
116
117 thread_mtx_unlock(act); /* Unlock the activation */
118 thread_deallocate(act);
119 return KERN_SUCCESS;
120 }
121
122 if(act->machine.emPendRupts >= 16) { /* Have we hit the arbitrary maximum? */
123 thread_mtx_unlock(act); /* Unlock the activation */
124 thread_deallocate(act);
125 return KERN_RESOURCE_SHORTAGE; /* Too many pending right now */
126 }
127
128 if(!(bbr = (bbRupt *)kalloc(sizeof(bbRupt)))) { /* Get a return handler control block */
129 thread_mtx_unlock(act); /* Unlock the activation */
130 thread_deallocate(act);
131 return KERN_RESOURCE_SHORTAGE; /* No storage... */
132 }
133
134 (void)hw_atomic_add(&act->machine.emPendRupts, 1); /* Count this 'rupt */
135 bbr->rh.handler = bbSetRupt; /* Set interruption routine */
136
137 bbr->rh.next = act->handlers; /* Put our interrupt at the start of the list */
138 act->handlers = &bbr->rh;
139
140 act_set_apc(act); /* Set an APC AST */
141
142 thread_mtx_unlock(act); /* Unlock the activation */
143 thread_deallocate(act);
144 return KERN_SUCCESS; /* We're done... */
145 }
146
147 /*
148 * This guy is fired off asynchronously to actually do the 'rupt.
149 * We will find the user state savearea and modify it. If we can't,
150 * we just leave after releasing our work area
151 */
152
153 void bbSetRupt(ReturnHandler *rh, thread_t act) {
154
155 savearea *sv;
156 BTTD_t *bttd;
157 bbRupt *bbr;
158 UInt32 interruptState;
159
160 bbr = (bbRupt *)rh; /* Make our area convenient */
161
162 if(!(act->machine.bbDescAddr)) { /* Is BlueBox still enabled? */
163 kfree(bbr, sizeof(bbRupt)); /* No, release the control block */
164 return;
165 }
166
167 (void)hw_atomic_sub(&act->machine.emPendRupts, 1); /* Uncount this 'rupt */
168
169 if(!(sv = find_user_regs(act))) { /* Find the user state registers */
170 kfree(bbr, sizeof(bbRupt)); /* Couldn't find 'em, release the control block */
171 return;
172 }
173
174 bttd = (BTTD_t *)(act->machine.bbDescAddr & -PAGE_SIZE);
175
176 interruptState = (bttd->InterruptControlWord & kInterruptStateMask) >> kInterruptStateShift;
177
178 switch (interruptState) {
179
180 case kInSystemContext:
181 sv->save_cr |= bttd->postIntMask; /* post int in CR2 */
182 break;
183
184 case kInAlternateContext:
185 bttd->InterruptControlWord = (bttd->InterruptControlWord & ~kInterruptStateMask) |
186 (kInPseudoKernel << kInterruptStateShift);
187
188 bttd->exceptionInfo.srr0 = (unsigned int)sv->save_srr0; /* Save the current PC */
189 sv->save_srr0 = (uint64_t)act->machine.bbInterrupt; /* Set the new PC */
190 bttd->exceptionInfo.sprg1 = (unsigned int)sv->save_r1; /* Save the original R1 */
191 sv->save_r1 = (uint64_t)bttd->exceptionInfo.sprg0; /* Set the new R1 */
192 bttd->exceptionInfo.srr1 = (unsigned int)sv->save_srr1; /* Save the original MSR */
193 sv->save_srr1 &= ~(MASK(MSR_BE)|MASK(MSR_SE)); /* Clear SE|BE bits in MSR */
194 act->machine.specFlags &= ~bbNoMachSC; /* reactivate Mach SCs */
195 disable_preemption(); /* Don't move us around */
196 getPerProc()->spcFlags = act->machine.specFlags; /* Copy the flags */
197 enable_preemption(); /* Ok to move us around */
198 /* drop through to post int in backup CR2 in ICW */
199
200 case kInExceptionHandler:
201 case kInPseudoKernel:
202 case kOutsideBlue:
203 bttd->InterruptControlWord = bttd->InterruptControlWord |
204 ((bttd->postIntMask >> kCR2ToBackupShift) & kBackupCR2Mask);
205 break;
206
207 default:
208 break;
209 }
210
211 kfree(bbr, sizeof(bbRupt)); /* Release the control block */
212 return;
213
214 }
215
216 /*
217 * This function is used to enable the firmware assist code for bluebox traps, system calls
218 * and interrupts.
219 *
220 * The assist code can be called from two types of threads. The blue thread, which handles
221 * traps, system calls and interrupts and preemptive threads that only issue system calls.
222 *
223 */
224
225 kern_return_t enable_bluebox(
226 host_t host,
227 void *taskID, /* opaque task ID */
228 void *TWI_TableStart, /* Start of TWI table */
229 char *Desc_TableStart /* Start of descriptor table */
230 ) {
231
232 thread_t th;
233 vm_offset_t kerndescaddr, origdescoffset;
234 kern_return_t ret;
235 ppnum_t physdescpage;
236 BTTD_t *bttd;
237
238 th = current_thread(); /* Get our thread */
239
240 if ( host == HOST_NULL ) return KERN_INVALID_HOST;
241 if ( ! is_suser() ) return KERN_FAILURE; /* We will only do this for the superuser */
242 if ( th->machine.bbDescAddr ) return KERN_FAILURE; /* Bail if already authorized... */
243 if ( ! (unsigned int) Desc_TableStart ) return KERN_FAILURE; /* There has to be a descriptor page */
244 if ( ! TWI_TableStart ) return KERN_FAILURE; /* There has to be a TWI table */
245
246 /* Get the page offset of the descriptor */
247 origdescoffset = (vm_offset_t)Desc_TableStart & (PAGE_SIZE - 1);
248
249 /* Align the descriptor to a page */
250 Desc_TableStart = (char *)((vm_offset_t)Desc_TableStart & -PAGE_SIZE);
251
252 ret = vm_map_wire(th->map, /* Kernel wire the descriptor in the user's map */
253 (vm_offset_t)Desc_TableStart,
254 (vm_offset_t)Desc_TableStart + PAGE_SIZE,
255 VM_PROT_READ | VM_PROT_WRITE,
256 FALSE);
257
258 if(ret != KERN_SUCCESS) { /* Couldn't wire it, spit on 'em... */
259 return KERN_FAILURE;
260 }
261
262 physdescpage = /* Get the physical page number of the page */
263 pmap_find_phys(th->map->pmap, (addr64_t)Desc_TableStart);
264
265 ret = kmem_alloc_pageable(kernel_map, &kerndescaddr, PAGE_SIZE); /* Find a virtual address to use */
266 if(ret != KERN_SUCCESS) { /* Could we get an address? */
267 (void) vm_map_unwire(th->map, /* No, unwire the descriptor */
268 (vm_offset_t)Desc_TableStart,
269 (vm_offset_t)Desc_TableStart + PAGE_SIZE,
270 TRUE);
271 return KERN_FAILURE; /* Split... */
272 }
273
274 (void) pmap_enter(kernel_pmap, /* Map this into the kernel */
275 kerndescaddr, physdescpage, VM_PROT_READ|VM_PROT_WRITE,
276 VM_WIMG_USE_DEFAULT, TRUE);
277
278 bttd = (BTTD_t *)kerndescaddr; /* Get the address in a convienient spot */
279
280 th->machine.bbDescAddr = (unsigned int)kerndescaddr+origdescoffset; /* Set kernel address of the table */
281 th->machine.bbUserDA = (unsigned int)Desc_TableStart; /* Set user address of the table */
282 th->machine.bbTableStart = (unsigned int)TWI_TableStart; /* Set address of the trap table */
283 th->machine.bbTaskID = (unsigned int)taskID; /* Assign opaque task ID */
284 th->machine.bbTaskEnv = 0; /* Clean task environment data */
285 th->machine.emPendRupts = 0; /* Clean pending 'rupt count */
286 th->machine.bbTrap = bttd->TrapVector; /* Remember trap vector */
287 th->machine.bbSysCall = bttd->SysCallVector; /* Remember syscall vector */
288 th->machine.bbInterrupt = bttd->InterruptVector; /* Remember interrupt vector */
289 th->machine.bbPending = bttd->PendingIntVector; /* Remember pending vector */
290 th->machine.specFlags &= ~(bbNoMachSC | bbPreemptive); /* Make sure mach SCs are enabled and we are not marked preemptive */
291 th->machine.specFlags |= bbThread; /* Set that we are Classic thread */
292
293 if(!(bttd->InterruptVector)) { /* See if this is a preemptive (MP) BlueBox thread */
294 th->machine.specFlags |= bbPreemptive; /* Yes, remember it */
295 }
296
297 disable_preemption(); /* Don't move us around */
298 getPerProc()->spcFlags = th->machine.specFlags; /* Copy the flags */
299 enable_preemption(); /* Ok to move us around */
300
301 {
302 /* mark the proc to indicate that this is a TBE proc */
303 extern void tbeproc(void *proc);
304
305 tbeproc(th->task->bsd_info);
306 }
307
308 return KERN_SUCCESS;
309 }
310
311 kern_return_t disable_bluebox( host_t host ) { /* User call to terminate bluebox */
312
313 thread_t act;
314
315 act = current_thread(); /* Get our thread */
316
317 if (host == HOST_NULL) return KERN_INVALID_HOST;
318
319 if(!is_suser()) return KERN_FAILURE; /* We will only do this for the superuser */
320 if(!act->machine.bbDescAddr) return KERN_FAILURE; /* Bail if not authorized... */
321
322 disable_bluebox_internal(act); /* Clean it all up */
323 return KERN_SUCCESS; /* Leave */
324 }
325
326 void disable_bluebox_internal(thread_t act) { /* Terminate bluebox */
327
328 (void) vm_map_unwire(act->map, /* Unwire the descriptor in user's address space */
329 (vm_offset_t)act->machine.bbUserDA,
330 (vm_offset_t)act->machine.bbUserDA + PAGE_SIZE,
331 FALSE);
332
333 kmem_free(kernel_map, (vm_offset_t)act->machine.bbDescAddr & -PAGE_SIZE, PAGE_SIZE); /* Release the page */
334
335 act->machine.bbDescAddr = 0; /* Clear kernel pointer to it */
336 act->machine.bbUserDA = 0; /* Clear user pointer to it */
337 act->machine.bbTableStart = 0; /* Clear user pointer to TWI table */
338 act->machine.bbTaskID = 0; /* Clear opaque task ID */
339 act->machine.bbTaskEnv = 0; /* Clean task environment data */
340 act->machine.emPendRupts = 0; /* Clean pending 'rupt count */
341 act->machine.specFlags &= ~(bbNoMachSC | bbPreemptive | bbThread); /* Clean up Blue Box enables */
342 disable_preemption(); /* Don't move us around */
343 getPerProc()->spcFlags = act->machine.specFlags; /* Copy the flags */
344 enable_preemption(); /* Ok to move us around */
345 return;
346 }
347
348 /*
349 * Use the new PPCcall method to enable blue box threads
350 *
351 * save->r3 = taskID
352 * save->r4 = TWI_TableStart
353 * save->r5 = Desc_TableStart
354 *
355 */
356 int bb_enable_bluebox( struct savearea *save )
357 {
358 kern_return_t rc;
359
360 rc = enable_bluebox( (host_t)0xFFFFFFFF, (void *)save->save_r3, (void *)save->save_r4, (char *)save->save_r5 );
361 save->save_r3 = rc;
362 return 1; /* Return with normal AST checking */
363 }
364
365 /*
366 * Use the new PPCcall method to disable blue box threads
367 *
368 */
369 int bb_disable_bluebox( struct savearea *save )
370 {
371 kern_return_t rc;
372
373 rc = disable_bluebox( (host_t)0xFFFFFFFF );
374 save->save_r3 = rc;
375 return 1; /* Return with normal AST checking */
376 }
377
378 /*
379 * Search through the list of threads to find the matching taskIDs, then
380 * set the task environment pointer. A task in this case is a preemptive thread
381 * in MacOS 9.
382 *
383 * save->r3 = taskID
384 * save->r4 = taskEnv
385 */
386
387 int bb_settaskenv( struct savearea *save )
388 {
389 int i;
390 task_t task;
391 thread_t act, fact;
392
393
394 task = current_task(); /* Figure out who our task is */
395
396 task_lock(task); /* Lock our task */
397 fact = (thread_t)task->threads.next; /* Get the first activation on task */
398 act = 0; /* Pretend we didn't find it yet */
399
400 for(i = 0; i < task->thread_count; i++) { /* Scan the whole list */
401 if(fact->machine.bbDescAddr) { /* Is this a Blue thread? */
402 if ( fact->machine.bbTaskID == save->save_r3 ) { /* Is this the task we are looking for? */
403 act = fact; /* Yeah... */
404 break; /* Found it, Bail the loop... */
405 }
406 }
407 fact = (thread_t)fact->task_threads.next; /* Go to the next one */
408 }
409
410 if ( !act || !act->active) {
411 task_unlock(task); /* Release task lock */
412 save->save_r3 = -1; /* we failed to find the taskID */
413 return 1;
414 }
415
416 thread_reference(act);
417
418 task_unlock(task); /* Safe to release now */
419
420 thread_mtx_lock(act); /* Make sure this stays 'round */
421
422 act->machine.bbTaskEnv = save->save_r4;
423 if(act == current_thread()) { /* Are we setting our own? */
424 disable_preemption(); /* Don't move us around */
425 getPerProc()->ppbbTaskEnv = act->machine.bbTaskEnv; /* Remember the environment */
426 enable_preemption(); /* Ok to move us around */
427 }
428
429 thread_mtx_unlock(act); /* Unlock the activation */
430 thread_deallocate(act);
431 save->save_r3 = 0;
432 return 1;
433 }