]>
git.saurik.com Git - apple/xnu.git/blob - osfmk/ppc/PseudoKernel.c
2 * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
31 Contains: BlueBox PseudoKernel calls
32 Written by: Mark Gorlinsky
35 Copyright: 1997 by Apple Computer, Inc., all rights reserved
39 #include <mach/mach_types.h>
40 #include <mach/mach_host.h>
41 #include <mach/kern_return.h>
43 #include <kern/kalloc.h>
44 #include <kern/kern_types.h>
45 #include <kern/host.h>
46 #include <kern/task.h>
47 #include <kern/thread.h>
48 #include <ppc/PseudoKernel.h>
49 #include <ppc/exception.h>
50 #include <ppc/misc_protos.h>
51 #include <ppc/proc_reg.h>
54 #include <vm/vm_map.h>
55 #include <vm/vm_kern.h>
57 extern int is_suser(void);
58 extern void tbeproc(void *proc
);
60 void bbSetRupt(ReturnHandler
*rh
, thread_t ct
);
63 ** Function: NotifyInterruption
66 ** ppcInterrupHandler - interrupt handler to execute
67 ** interruptStatePtr - current interrupt state
75 syscall_notify_interrupt(void)
83 task
= current_task(); /* Figure out who our task is */
85 task_lock(task
); /* Lock our task */
87 fact
= (thread_t
)task
->threads
.next
; /* Get the first activation on task */
88 act
= NULL
; /* Pretend we didn't find it yet */
90 for(i
= 0; i
< task
->thread_count
; i
++) { /* Scan the whole list */
91 if(fact
->machine
.bbDescAddr
) { /* Is this a Blue thread? */
92 bttd
= (BTTD_t
*)(fact
->machine
.bbDescAddr
& -PAGE_SIZE
);
93 if(bttd
->InterruptVector
) { /* Is this the Blue interrupt thread? */
94 act
= fact
; /* Yeah... */
95 break; /* Found it, Bail the loop... */
98 fact
= (thread_t
)fact
->task_threads
.next
; /* Go to the next one */
101 if(!act
) { /* Couldn't find a bluebox */
102 task_unlock(task
); /* Release task lock */
103 return KERN_FAILURE
; /* No tickie, no shirtee... */
106 thread_reference(act
);
108 task_unlock(task
); /* Safe to release now */
110 thread_mtx_lock(act
);
112 /* if the calling thread is the BlueBox thread that handles interrupts
113 * we know that we are in the PsuedoKernel and we can short circuit
114 * setting up the asynchronous task by setting a pending interrupt.
117 if (act
== current_thread()) {
118 bttd
->InterruptControlWord
= bttd
->InterruptControlWord
|
119 ((bttd
->postIntMask
>> kCR2ToBackupShift
) & kBackupCR2Mask
);
121 thread_mtx_unlock(act
); /* Unlock the activation */
122 thread_deallocate(act
);
126 if(act
->machine
.emPendRupts
>= 16) { /* Have we hit the arbitrary maximum? */
127 thread_mtx_unlock(act
); /* Unlock the activation */
128 thread_deallocate(act
);
129 return KERN_RESOURCE_SHORTAGE
; /* Too many pending right now */
132 if(!(bbr
= (bbRupt
*)kalloc(sizeof(bbRupt
)))) { /* Get a return handler control block */
133 thread_mtx_unlock(act
); /* Unlock the activation */
134 thread_deallocate(act
);
135 return KERN_RESOURCE_SHORTAGE
; /* No storage... */
138 (void)hw_atomic_add(&act
->machine
.emPendRupts
, 1); /* Count this 'rupt */
139 bbr
->rh
.handler
= bbSetRupt
; /* Set interruption routine */
141 bbr
->rh
.next
= act
->handlers
; /* Put our interrupt at the start of the list */
142 act
->handlers
= &bbr
->rh
;
144 act_set_apc(act
); /* Set an APC AST */
146 thread_mtx_unlock(act
); /* Unlock the activation */
147 thread_deallocate(act
);
148 return KERN_SUCCESS
; /* We're done... */
152 * This guy is fired off asynchronously to actually do the 'rupt.
153 * We will find the user state savearea and modify it. If we can't,
154 * we just leave after releasing our work area
157 void bbSetRupt(ReturnHandler
*rh
, thread_t act
) {
162 UInt32 interruptState
;
164 bbr
= (bbRupt
*)rh
; /* Make our area convenient */
166 if(!(act
->machine
.bbDescAddr
)) { /* Is BlueBox still enabled? */
167 kfree(bbr
, sizeof(bbRupt
)); /* No, release the control block */
171 (void)hw_atomic_sub(&act
->machine
.emPendRupts
, 1); /* Uncount this 'rupt */
173 if(!(sv
= find_user_regs(act
))) { /* Find the user state registers */
174 kfree(bbr
, sizeof(bbRupt
)); /* Couldn't find 'em, release the control block */
178 bttd
= (BTTD_t
*)(act
->machine
.bbDescAddr
& -PAGE_SIZE
);
180 interruptState
= (bttd
->InterruptControlWord
& kInterruptStateMask
) >> kInterruptStateShift
;
182 switch (interruptState
) {
184 case kInSystemContext
:
185 sv
->save_cr
|= bttd
->postIntMask
; /* post int in CR2 */
188 case kInAlternateContext
:
189 bttd
->InterruptControlWord
= (bttd
->InterruptControlWord
& ~kInterruptStateMask
) |
190 (kInPseudoKernel
<< kInterruptStateShift
);
192 bttd
->exceptionInfo
.srr0
= (unsigned int)sv
->save_srr0
; /* Save the current PC */
193 sv
->save_srr0
= (uint64_t)act
->machine
.bbInterrupt
; /* Set the new PC */
194 bttd
->exceptionInfo
.sprg1
= (unsigned int)sv
->save_r1
; /* Save the original R1 */
195 sv
->save_r1
= (uint64_t)bttd
->exceptionInfo
.sprg0
; /* Set the new R1 */
196 bttd
->exceptionInfo
.srr1
= (unsigned int)sv
->save_srr1
; /* Save the original MSR */
197 sv
->save_srr1
&= ~(MASK(MSR_BE
)|MASK(MSR_SE
)); /* Clear SE|BE bits in MSR */
198 act
->machine
.specFlags
&= ~bbNoMachSC
; /* reactivate Mach SCs */
199 disable_preemption(); /* Don't move us around */
200 getPerProc()->spcFlags
= act
->machine
.specFlags
; /* Copy the flags */
201 enable_preemption(); /* Ok to move us around */
202 /* drop through to post int in backup CR2 in ICW */
204 case kInExceptionHandler
:
205 case kInPseudoKernel
:
207 bttd
->InterruptControlWord
= bttd
->InterruptControlWord
|
208 ((bttd
->postIntMask
>> kCR2ToBackupShift
) & kBackupCR2Mask
);
215 kfree(bbr
, sizeof(bbRupt
)); /* Release the control block */
221 * This function is used to enable the firmware assist code for bluebox traps, system calls
224 * The assist code can be called from two types of threads. The blue thread, which handles
225 * traps, system calls and interrupts and preemptive threads that only issue system calls.
228 * _taskID opaque task ID
229 * _TWI_TableStart Start of TWI table
230 * _Desc_TableStart Start of descriptor table
234 enable_bluebox(host_t host
, unsigned _taskID
, unsigned _TWI_TableStart
,
235 unsigned _Desc_TableStart
)
237 /* XXX mig funness */
238 void *taskID
= (void *)_taskID
;
239 void *TWI_TableStart
= (void *)_TWI_TableStart
;
240 char *Desc_TableStart
= (char *)_Desc_TableStart
;
243 vm_offset_t kerndescaddr
, origdescoffset
;
245 ppnum_t physdescpage
;
248 th
= current_thread(); /* Get our thread */
250 if ( host
== HOST_NULL
) return KERN_INVALID_HOST
;
251 if ( ! is_suser() ) return KERN_FAILURE
; /* We will only do this for the superuser */
252 if ( th
->machine
.bbDescAddr
) return KERN_FAILURE
; /* Bail if already authorized... */
253 if ( ! (unsigned int) Desc_TableStart
) return KERN_FAILURE
; /* There has to be a descriptor page */
254 if ( ! TWI_TableStart
) return KERN_FAILURE
; /* There has to be a TWI table */
256 /* Get the page offset of the descriptor */
257 origdescoffset
= (vm_offset_t
)Desc_TableStart
& (PAGE_SIZE
- 1);
259 /* Align the descriptor to a page */
260 Desc_TableStart
= (char *)((vm_offset_t
)Desc_TableStart
& -PAGE_SIZE
);
262 ret
= vm_map_wire(th
->map
, /* Kernel wire the descriptor in the user's map */
263 (vm_offset_t
)Desc_TableStart
,
264 (vm_offset_t
)Desc_TableStart
+ PAGE_SIZE
,
265 VM_PROT_READ
| VM_PROT_WRITE
,
268 if(ret
!= KERN_SUCCESS
) { /* Couldn't wire it, spit on 'em... */
272 physdescpage
= /* Get the physical page number of the page */
273 pmap_find_phys(th
->map
->pmap
, CAST_USER_ADDR_T(Desc_TableStart
));
275 ret
= kmem_alloc_pageable(kernel_map
, &kerndescaddr
, PAGE_SIZE
); /* Find a virtual address to use */
276 if(ret
!= KERN_SUCCESS
) { /* Could we get an address? */
277 (void) vm_map_unwire(th
->map
, /* No, unwire the descriptor */
278 (vm_offset_t
)Desc_TableStart
,
279 (vm_offset_t
)Desc_TableStart
+ PAGE_SIZE
,
281 return KERN_FAILURE
; /* Split... */
284 (void) pmap_enter(kernel_pmap
, /* Map this into the kernel */
285 kerndescaddr
, physdescpage
, VM_PROT_READ
|VM_PROT_WRITE
,
286 VM_WIMG_USE_DEFAULT
, TRUE
);
288 bttd
= (BTTD_t
*)kerndescaddr
; /* Get the address in a convienient spot */
290 th
->machine
.bbDescAddr
= (unsigned int)kerndescaddr
+origdescoffset
; /* Set kernel address of the table */
291 th
->machine
.bbUserDA
= (unsigned int)Desc_TableStart
; /* Set user address of the table */
292 th
->machine
.bbTableStart
= (unsigned int)TWI_TableStart
; /* Set address of the trap table */
293 th
->machine
.bbTaskID
= (unsigned int)taskID
; /* Assign opaque task ID */
294 th
->machine
.bbTaskEnv
= 0; /* Clean task environment data */
295 th
->machine
.emPendRupts
= 0; /* Clean pending 'rupt count */
296 th
->machine
.bbTrap
= bttd
->TrapVector
; /* Remember trap vector */
297 th
->machine
.bbSysCall
= bttd
->SysCallVector
; /* Remember syscall vector */
298 th
->machine
.bbInterrupt
= bttd
->InterruptVector
; /* Remember interrupt vector */
299 th
->machine
.bbPending
= bttd
->PendingIntVector
; /* Remember pending vector */
300 th
->machine
.specFlags
&= ~(bbNoMachSC
| bbPreemptive
); /* Make sure mach SCs are enabled and we are not marked preemptive */
301 th
->machine
.specFlags
|= bbThread
; /* Set that we are Classic thread */
303 if(!(bttd
->InterruptVector
)) { /* See if this is a preemptive (MP) BlueBox thread */
304 th
->machine
.specFlags
|= bbPreemptive
; /* Yes, remember it */
307 disable_preemption(); /* Don't move us around */
308 getPerProc()->spcFlags
= th
->machine
.specFlags
; /* Copy the flags */
309 enable_preemption(); /* Ok to move us around */
312 /* mark the proc to indicate that this is a TBE proc */
314 tbeproc(th
->task
->bsd_info
);
320 kern_return_t
disable_bluebox( host_t host
) { /* User call to terminate bluebox */
324 act
= current_thread(); /* Get our thread */
326 if (host
== HOST_NULL
) return KERN_INVALID_HOST
;
328 if(!is_suser()) return KERN_FAILURE
; /* We will only do this for the superuser */
329 if(!act
->machine
.bbDescAddr
) return KERN_FAILURE
; /* Bail if not authorized... */
331 disable_bluebox_internal(act
); /* Clean it all up */
332 return KERN_SUCCESS
; /* Leave */
335 void disable_bluebox_internal(thread_t act
) { /* Terminate bluebox */
337 (void) vm_map_unwire(act
->map
, /* Unwire the descriptor in user's address space */
338 (vm_offset_t
)act
->machine
.bbUserDA
,
339 (vm_offset_t
)act
->machine
.bbUserDA
+ PAGE_SIZE
,
342 kmem_free(kernel_map
, (vm_offset_t
)act
->machine
.bbDescAddr
& -PAGE_SIZE
, PAGE_SIZE
); /* Release the page */
344 act
->machine
.bbDescAddr
= 0; /* Clear kernel pointer to it */
345 act
->machine
.bbUserDA
= 0; /* Clear user pointer to it */
346 act
->machine
.bbTableStart
= 0; /* Clear user pointer to TWI table */
347 act
->machine
.bbTaskID
= 0; /* Clear opaque task ID */
348 act
->machine
.bbTaskEnv
= 0; /* Clean task environment data */
349 act
->machine
.emPendRupts
= 0; /* Clean pending 'rupt count */
350 act
->machine
.specFlags
&= ~(bbNoMachSC
| bbPreemptive
| bbThread
); /* Clean up Blue Box enables */
351 disable_preemption(); /* Don't move us around */
352 getPerProc()->spcFlags
= act
->machine
.specFlags
; /* Copy the flags */
353 enable_preemption(); /* Ok to move us around */
358 * Use the new PPCcall method to enable blue box threads
361 * save->r4 = TWI_TableStart
362 * save->r5 = Desc_TableStart
365 int bb_enable_bluebox( struct savearea
*save
)
369 rc
= enable_bluebox((host_t
)0xFFFFFFFF,
370 CAST_DOWN(unsigned, save
->save_r3
),
371 CAST_DOWN(unsigned, save
->save_r4
),
372 CAST_DOWN(unsigned, save
->save_r5
));
374 return 1; /* Return with normal AST checking */
378 * Use the new PPCcall method to disable blue box threads
381 int bb_disable_bluebox( struct savearea
*save
)
385 rc
= disable_bluebox( (host_t
)0xFFFFFFFF );
387 return 1; /* Return with normal AST checking */
391 * Search through the list of threads to find the matching taskIDs, then
392 * set the task environment pointer. A task in this case is a preemptive thread
399 int bb_settaskenv( struct savearea
*save
)
406 task
= current_task(); /* Figure out who our task is */
408 task_lock(task
); /* Lock our task */
409 fact
= (thread_t
)task
->threads
.next
; /* Get the first activation on task */
410 act
= NULL
; /* Pretend we didn't find it yet */
412 for(i
= 0; i
< task
->thread_count
; i
++) { /* Scan the whole list */
413 if(fact
->machine
.bbDescAddr
) { /* Is this a Blue thread? */
414 if ( fact
->machine
.bbTaskID
== save
->save_r3
) { /* Is this the task we are looking for? */
415 act
= fact
; /* Yeah... */
416 break; /* Found it, Bail the loop... */
419 fact
= (thread_t
)fact
->task_threads
.next
; /* Go to the next one */
422 if ( !act
|| !act
->active
) {
423 task_unlock(task
); /* Release task lock */
424 save
->save_r3
= -1; /* we failed to find the taskID */
428 thread_reference(act
);
430 task_unlock(task
); /* Safe to release now */
432 thread_mtx_lock(act
); /* Make sure this stays 'round */
434 act
->machine
.bbTaskEnv
= save
->save_r4
;
435 if(act
== current_thread()) { /* Are we setting our own? */
436 disable_preemption(); /* Don't move us around */
437 getPerProc()->ppbbTaskEnv
= act
->machine
.bbTaskEnv
; /* Remember the environment */
438 enable_preemption(); /* Ok to move us around */
441 thread_mtx_unlock(act
); /* Unlock the activation */
442 thread_deallocate(act
);