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