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