]>
git.saurik.com Git - apple/xnu.git/blob - osfmk/ppc/PseudoKernel.c
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
25 Contains: BlueBox PseudoKernel calls
26 Written by: Mark Gorlinsky
29 Copyright: 1997 by Apple Computer, Inc., all rights reserved
33 #include <mach/mach_types.h>
34 #include <mach/kern_return.h>
35 #include <kern/host.h>
36 #include <kern/task.h>
37 #include <kern/thread.h>
38 #include <ppc/PseudoKernel.h>
39 #include <ppc/exception.h>
40 #include <ppc/misc_protos.h>
41 #include <ppc/proc_reg.h>
42 #include <vm/vm_kern.h>
44 void bbSetRupt(ReturnHandler
*rh
, thread_act_t ct
);
45 void DumpTheSave(struct savearea
*save
); /* (TEST/DEBUG) */
48 ** Function: NotifyInterruption
51 ** ppcInterrupHandler - interrupt handler to execute
52 ** interruptStatePtr - current interrupt state
59 kern_return_t
syscall_notify_interrupt ( void ) {
61 UInt32 interruptState
;
64 thread_act_t act
, fact
;
70 task
= current_task(); /* Figure out who our task is */
72 task_lock(task
); /* Lock our task */
74 fact
= (thread_act_t
)task
->thr_acts
.next
; /* Get the first activation on task */
75 act
= 0; /* Pretend we didn't find it yet */
77 for(i
= 0; i
< task
->thr_act_count
; i
++) { /* Scan the whole list */
78 if(fact
->mact
.bbDescAddr
) { /* Is this a Blue thread? */
79 bttd
= (BTTD_t
*)(fact
->mact
.bbDescAddr
& -PAGE_SIZE
);
80 if(bttd
->InterruptVector
) { /* Is this the Blue interrupt thread? */
81 act
= fact
; /* Yeah... */
82 break; /* Found it, Bail the loop... */
85 fact
= (thread_act_t
)fact
->thr_acts
.next
; /* Go to the next one */
88 if(!act
) { /* Couldn't find a bluebox */
89 task_unlock(task
); /* Release task lock */
90 return KERN_FAILURE
; /* No tickie, no shirtee... */
93 act_lock_thread(act
); /* Make sure this stays 'round */
94 task_unlock(task
); /* Safe to release now */
96 /* if the calling thread is the BlueBox thread that handles interrupts
97 * we know that we are in the PsuedoKernel and we can short circuit
98 * setting up the asynchronous task by setting a pending interrupt.
101 if ( (unsigned int)act
== (unsigned int)current_act() ) {
102 bttd
->InterruptControlWord
= bttd
->InterruptControlWord
|
103 ((bttd
->postIntMask
>> kCR2ToBackupShift
) & kBackupCR2Mask
);
105 act_unlock_thread(act
); /* Unlock the activation */
109 if(act
->mact
.bbPendRupt
>= 16) { /* Have we hit the arbitrary maximum? */
110 act_unlock_thread(act
); /* Unlock the activation */
111 return KERN_RESOURCE_SHORTAGE
; /* Too many pending right now */
114 if(!(bbr
= (bbRupt
*)kalloc(sizeof(bbRupt
)))) { /* Get a return handler control block */
115 act_unlock_thread(act
); /* Unlock the activation */
116 return KERN_RESOURCE_SHORTAGE
; /* No storage... */
119 (void)hw_atomic_add(&act
->mact
.bbPendRupt
, 1); /* Count this 'rupt */
120 bbr
->rh
.handler
= bbSetRupt
; /* Set interruption routine */
122 bbr
->rh
.next
= act
->handlers
; /* Put our interrupt at the start of the list */
123 act
->handlers
= &bbr
->rh
;
125 s
= splsched(); /* No talking in class */
126 act_set_apc(act
); /* Set an APC AST */
127 splx(s
); /* Ok, you can talk now */
129 act_unlock_thread(act
); /* Unlock the activation */
130 return KERN_SUCCESS
; /* We're done... */
134 * This guy is fired off asynchronously to actually do the 'rupt.
135 * We will find the user state savearea and modify it. If we can't,
136 * we just leave after releasing our work area
139 void bbSetRupt(ReturnHandler
*rh
, thread_act_t act
) {
144 UInt32 interruptState
;
146 bbr
= (bbRupt
*)rh
; /* Make our area convenient */
148 if(!(act
->mact
.bbDescAddr
)) { /* Is BlueBox still enabled? */
149 kfree((vm_offset_t
)bbr
, sizeof(bbRupt
)); /* No, release the control block */
153 (void)hw_atomic_sub(&act
->mact
.bbPendRupt
, 1); /* Uncount this 'rupt */
155 if(!(sv
= (savearea
*)find_user_regs(act
))) { /* Find the user state registers */
156 kfree((vm_offset_t
)bbr
, sizeof(bbRupt
)); /* Couldn't find 'em, release the control block */
160 bttd
= (BTTD_t
*)(act
->mact
.bbDescAddr
& -PAGE_SIZE
);
162 interruptState
= (bttd
->InterruptControlWord
& kInterruptStateMask
) >> kInterruptStateShift
;
164 switch (interruptState
) {
166 case kInSystemContext
:
167 sv
->save_cr
|= bttd
->postIntMask
; /* post int in CR2 */
170 case kInAlternateContext
:
171 bttd
->InterruptControlWord
= (bttd
->InterruptControlWord
& ~kInterruptStateMask
) |
172 (kInPseudoKernel
<< kInterruptStateShift
);
174 bttd
->exceptionInfo
.srr0
= sv
->save_srr0
; /* Save the current PC */
175 sv
->save_srr0
= bttd
->InterruptVector
; /* Set the new PC */
176 bttd
->exceptionInfo
.sprg1
= sv
->save_r1
; /* Save the original R1 */
177 sv
->save_r1
= bttd
->exceptionInfo
.sprg0
; /* Set the new R1 */
178 bttd
->exceptionInfo
.srr1
= sv
->save_srr1
; /* Save the original MSR */
179 sv
->save_srr1
&= ~(MASK(MSR_BE
)|MASK(MSR_SE
)); /* Clear SE|BE bits in MSR */
180 act
->mact
.specFlags
&= ~bbNoMachSC
; /* reactivate Mach SCs */
181 /* drop through to post int in backup CR2 in ICW */
183 case kInExceptionHandler
:
184 case kInPseudoKernel
:
186 bttd
->InterruptControlWord
= bttd
->InterruptControlWord
|
187 ((bttd
->postIntMask
>> kCR2ToBackupShift
) & kBackupCR2Mask
);
194 kfree((vm_offset_t
)bbr
, sizeof(bbRupt
)); /* Release the control block */
200 * This function is used to enable the firmware assist code for bluebox traps, system calls
203 * The assist code can be called from two types of threads. The blue thread, which handles
204 * traps, system calls and interrupts and preemptive threads that only issue system calls.
208 kern_return_t
enable_bluebox(
210 void *taskID
, /* opaque task ID */
211 void *TWI_TableStart
, /* Start of TWI table */
212 char *Desc_TableStart
/* Start of descriptor table */
216 vm_offset_t kerndescaddr
, physdescaddr
, origdescoffset
;
219 th
= current_thread(); /* Get our thread */
221 if ( host
== HOST_NULL
) return KERN_INVALID_HOST
;
222 if ( ! is_suser() ) return KERN_FAILURE
; /* We will only do this for the superuser */
223 if ( th
->top_act
->mact
.bbDescAddr
) return KERN_FAILURE
; /* Bail if already authorized... */
224 if ( ! (unsigned int) Desc_TableStart
) return KERN_FAILURE
; /* There has to be a descriptor page */
225 if ( ! TWI_TableStart
) return KERN_FAILURE
; /* There has to be a TWI table */
227 /* Get the page offset of the descriptor */
228 origdescoffset
= (vm_offset_t
)Desc_TableStart
& (PAGE_SIZE
- 1);
230 /* Align the descriptor to a page */
231 Desc_TableStart
= (char *)((vm_offset_t
)Desc_TableStart
& -PAGE_SIZE
);
233 ret
= vm_map_wire(th
->top_act
->map
, /* Kernel wire the descriptor in the user's map */
234 (vm_offset_t
)Desc_TableStart
,
235 (vm_offset_t
)Desc_TableStart
+ PAGE_SIZE
,
236 VM_PROT_READ
| VM_PROT_WRITE
,
239 if(ret
!= KERN_SUCCESS
) { /* Couldn't wire it, spit on 'em... */
243 physdescaddr
= /* Get the physical address of the page */
244 pmap_extract(th
->top_act
->map
->pmap
, (vm_offset_t
) Desc_TableStart
);
246 ret
= kmem_alloc_pageable(kernel_map
, &kerndescaddr
, PAGE_SIZE
); /* Find a virtual address to use */
247 if(ret
!= KERN_SUCCESS
) { /* Could we get an address? */
248 (void) vm_map_unwire(th
->top_act
->map
, /* No, unwire the descriptor */
249 (vm_offset_t
)Desc_TableStart
,
250 (vm_offset_t
)Desc_TableStart
+ PAGE_SIZE
,
252 return KERN_FAILURE
; /* Split... */
255 (void) pmap_enter(kernel_pmap
, /* Map this into the kernel */
256 kerndescaddr
, physdescaddr
, VM_PROT_READ
|VM_PROT_WRITE
,
259 th
->top_act
->mact
.bbDescAddr
= (unsigned int)kerndescaddr
+origdescoffset
; /* Set kernel address of the table */
260 th
->top_act
->mact
.bbUserDA
= (unsigned int)Desc_TableStart
; /* Set user address of the table */
261 th
->top_act
->mact
.bbTableStart
= (unsigned int)TWI_TableStart
; /* Set address of the trap table */
262 th
->top_act
->mact
.bbTaskID
= (unsigned int)taskID
; /* Assign opaque task ID */
263 th
->top_act
->mact
.bbTaskEnv
= 0; /* Clean task environment data */
264 th
->top_act
->mact
.bbPendRupt
= 0; /* Clean pending 'rupt count */
265 th
->top_act
->mact
.specFlags
&= ~bbNoMachSC
; /* Make sure mach SCs are enabled */
268 /* mark the proc to indicate that this is a TBE proc */
269 extern void tbeproc(void *proc
);
271 tbeproc(th
->top_act
->task
->bsd_info
);
277 kern_return_t
disable_bluebox( host_t host
) { /* User call to terminate bluebox */
281 act
= current_act(); /* Get our thread */
283 if (host
== HOST_NULL
) return KERN_INVALID_HOST
;
285 if(!is_suser()) return KERN_FAILURE
; /* We will only do this for the superuser */
286 if(!act
->mact
.bbDescAddr
) return KERN_FAILURE
; /* Bail if not authorized... */
288 disable_bluebox_internal(act
); /* Clean it all up */
289 return KERN_SUCCESS
; /* Leave */
292 void disable_bluebox_internal(thread_act_t act
) { /* Terminate bluebox */
294 (void) vm_map_unwire(act
->map
, /* Unwire the descriptor in user's address space */
295 (vm_offset_t
)act
->mact
.bbUserDA
,
296 (vm_offset_t
)act
->mact
.bbUserDA
+ PAGE_SIZE
,
299 kmem_free(kernel_map
, (vm_offset_t
)act
->mact
.bbDescAddr
& -PAGE_SIZE
, PAGE_SIZE
); /* Release the page */
301 act
->mact
.bbDescAddr
= 0; /* Clear kernel pointer to it */
302 act
->mact
.bbUserDA
= 0; /* Clear user pointer to it */
303 act
->mact
.bbTableStart
= 0; /* Clear user pointer to TWI table */
304 act
->mact
.bbTaskID
= 0; /* Clear opaque task ID */
305 act
->mact
.bbTaskEnv
= 0; /* Clean task environment data */
306 act
->mact
.bbPendRupt
= 0; /* Clean pending 'rupt count */
307 act
->mact
.specFlags
&= ~bbNoMachSC
; /* Clean up Blue Box enables */
312 * Use the new PPCcall method to enable blue box threads
315 * save->r4 = TWI_TableStart
316 * save->r5 = Desc_TableStart
319 int bb_enable_bluebox( struct savearea
*save
)
323 rc
= enable_bluebox( (host_t
)0xFFFFFFFF, (void *)save
->save_r3
, (void *)save
->save_r4
, (char *)save
->save_r5
);
325 return 1; /* Return with normal AST checking */
329 * Use the new PPCcall method to disable blue box threads
332 int bb_disable_bluebox( struct savearea
*save
)
336 rc
= disable_bluebox( (host_t
)0xFFFFFFFF );
338 return 1; /* Return with normal AST checking */
342 * Search through the list of threads to find the matching taskIDs, then
343 * set the task environment pointer. A task in this case is a preemptive thread
350 int bb_settaskenv( struct savearea
*save
)
354 thread_act_t act
, fact
;
357 task
= current_task(); /* Figure out who our task is */
359 task_lock(task
); /* Lock our task */
360 fact
= (thread_act_t
)task
->thr_acts
.next
; /* Get the first activation on task */
361 act
= 0; /* Pretend we didn't find it yet */
363 for(i
= 0; i
< task
->thr_act_count
; i
++) { /* Scan the whole list */
364 if(fact
->mact
.bbDescAddr
) { /* Is this a Blue thread? */
365 if ( fact
->mact
.bbTaskID
== save
->save_r3
) { /* Is this the task we are looking for? */
366 act
= fact
; /* Yeah... */
367 break; /* Found it, Bail the loop... */
370 fact
= (thread_act_t
)fact
->thr_acts
.next
; /* Go to the next one */
373 if ( !act
|| !act
->active
) {
374 task_unlock(task
); /* Release task lock */
378 act_lock_thread(act
); /* Make sure this stays 'round */
379 task_unlock(task
); /* Safe to release now */
381 act
->mact
.bbTaskEnv
= save
->save_r4
;
383 act_unlock_thread(act
); /* Unlock the activation */
388 save
->save_r3
= -1; /* we failed to find the taskID */