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