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