]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ppc/PseudoKernel.c
xnu-123.5.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 void DumpTheSave(struct savearea *save); /* (TEST/DEBUG) */
46
47 /*
48 ** Function: NotifyInterruption
49 **
50 ** Inputs:
51 ** ppcInterrupHandler - interrupt handler to execute
52 ** interruptStatePtr - current interrupt state
53 **
54 ** Outputs:
55 **
56 ** Notes:
57 **
58 */
59 kern_return_t syscall_notify_interrupt ( void ) {
60
61 UInt32 interruptState;
62 task_t task;
63 spl_t s;
64 thread_act_t act, fact;
65 thread_t thread;
66 bbRupt *bbr;
67 BTTD_t *bttd;
68 int i;
69
70 task = current_task(); /* Figure out who our task is */
71
72 task_lock(task); /* Lock our task */
73
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 */
76
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... */
83 }
84 }
85 fact = (thread_act_t)fact->thr_acts.next; /* Go to the next one */
86 }
87
88 if(!act) { /* Couldn't find a bluebox */
89 task_unlock(task); /* Release task lock */
90 return KERN_FAILURE; /* No tickie, no shirtee... */
91 }
92
93 act_lock_thread(act); /* Make sure this stays 'round */
94 task_unlock(task); /* Safe to release now */
95
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.
99 */
100
101 if ( (unsigned int)act == (unsigned int)current_act() ) {
102 bttd->InterruptControlWord = bttd->InterruptControlWord |
103 ((bttd->postIntMask >> kCR2ToBackupShift) & kBackupCR2Mask);
104
105 act_unlock_thread(act); /* Unlock the activation */
106 return KERN_SUCCESS;
107 }
108
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 */
112 }
113
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... */
117 }
118
119 (void)hw_atomic_add(&act->mact.bbPendRupt, 1); /* Count this 'rupt */
120 bbr->rh.handler = bbSetRupt; /* Set interruption routine */
121
122 bbr->rh.next = act->handlers; /* Put our interrupt at the start of the list */
123 act->handlers = &bbr->rh;
124
125 s = splsched(); /* No talking in class */
126 act_set_apc(act); /* Set an APC AST */
127 splx(s); /* Ok, you can talk now */
128
129 act_unlock_thread(act); /* Unlock the activation */
130 return KERN_SUCCESS; /* We're done... */
131 }
132
133 /*
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
137 */
138
139 void bbSetRupt(ReturnHandler *rh, thread_act_t act) {
140
141 savearea *sv;
142 BTTD_t *bttd;
143 bbRupt *bbr;
144 UInt32 interruptState;
145
146 bbr = (bbRupt *)rh; /* Make our area convenient */
147
148 if(!(act->mact.bbDescAddr)) { /* Is BlueBox still enabled? */
149 kfree((vm_offset_t)bbr, sizeof(bbRupt)); /* No, release the control block */
150 return;
151 }
152
153 (void)hw_atomic_sub(&act->mact.bbPendRupt, 1); /* Uncount this 'rupt */
154
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 */
157 return;
158 }
159
160 bttd = (BTTD_t *)(act->mact.bbDescAddr & -PAGE_SIZE);
161
162 interruptState = (bttd->InterruptControlWord & kInterruptStateMask) >> kInterruptStateShift;
163
164 switch (interruptState) {
165
166 case kInSystemContext:
167 sv->save_cr |= bttd->postIntMask; /* post int in CR2 */
168 break;
169
170 case kInAlternateContext:
171 bttd->InterruptControlWord = (bttd->InterruptControlWord & ~kInterruptStateMask) |
172 (kInPseudoKernel << kInterruptStateShift);
173
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 */
182
183 case kInExceptionHandler:
184 case kInPseudoKernel:
185 case kOutsideBlue:
186 bttd->InterruptControlWord = bttd->InterruptControlWord |
187 ((bttd->postIntMask >> kCR2ToBackupShift) & kBackupCR2Mask);
188 break;
189
190 default:
191 break;
192 }
193
194 kfree((vm_offset_t)bbr, sizeof(bbRupt)); /* Release the control block */
195 return;
196
197 }
198
199 /*
200 * This function is used to enable the firmware assist code for bluebox traps, system calls
201 * and interrupts.
202 *
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.
205 *
206 */
207
208 kern_return_t enable_bluebox(
209 host_t host,
210 void *taskID, /* opaque task ID */
211 void *TWI_TableStart, /* Start of TWI table */
212 char *Desc_TableStart /* Start of descriptor table */
213 ) {
214
215 thread_t th;
216 vm_offset_t kerndescaddr, physdescaddr, origdescoffset;
217 kern_return_t ret;
218
219 th = current_thread(); /* Get our thread */
220
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 */
226
227 /* Get the page offset of the descriptor */
228 origdescoffset = (vm_offset_t)Desc_TableStart & (PAGE_SIZE - 1);
229
230 /* Align the descriptor to a page */
231 Desc_TableStart = (char *)((vm_offset_t)Desc_TableStart & -PAGE_SIZE);
232
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,
237 FALSE);
238
239 if(ret != KERN_SUCCESS) { /* Couldn't wire it, spit on 'em... */
240 return KERN_FAILURE;
241 }
242
243 physdescaddr = /* Get the physical address of the page */
244 pmap_extract(th->top_act->map->pmap, (vm_offset_t) Desc_TableStart);
245
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,
251 TRUE);
252 return KERN_FAILURE; /* Split... */
253 }
254
255 (void) pmap_enter(kernel_pmap, /* Map this into the kernel */
256 kerndescaddr, physdescaddr, VM_PROT_READ|VM_PROT_WRITE,
257 TRUE);
258
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 */
266
267 {
268 /* mark the proc to indicate that this is a TBE proc */
269 extern void tbeproc(void *proc);
270
271 tbeproc(th->top_act->task->bsd_info);
272 }
273
274 return KERN_SUCCESS;
275 }
276
277 kern_return_t disable_bluebox( host_t host ) { /* User call to terminate bluebox */
278
279 thread_act_t act;
280
281 act = current_act(); /* Get our thread */
282
283 if (host == HOST_NULL) return KERN_INVALID_HOST;
284
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... */
287
288 disable_bluebox_internal(act); /* Clean it all up */
289 return KERN_SUCCESS; /* Leave */
290 }
291
292 void disable_bluebox_internal(thread_act_t act) { /* Terminate bluebox */
293
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,
297 FALSE);
298
299 kmem_free(kernel_map, (vm_offset_t)act->mact.bbDescAddr & -PAGE_SIZE, PAGE_SIZE); /* Release the page */
300
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 */
308 return;
309 }
310
311 /*
312 * Use the new PPCcall method to enable blue box threads
313 *
314 * save->r3 = taskID
315 * save->r4 = TWI_TableStart
316 * save->r5 = Desc_TableStart
317 *
318 */
319 int bb_enable_bluebox( struct savearea *save )
320 {
321 kern_return_t rc;
322
323 rc = enable_bluebox( (host_t)0xFFFFFFFF, (void *)save->save_r3, (void *)save->save_r4, (char *)save->save_r5 );
324 save->save_r3 = rc;
325 return 1; /* Return with normal AST checking */
326 }
327
328 /*
329 * Use the new PPCcall method to disable blue box threads
330 *
331 */
332 int bb_disable_bluebox( struct savearea *save )
333 {
334 kern_return_t rc;
335
336 rc = disable_bluebox( (host_t)0xFFFFFFFF );
337 save->save_r3 = rc;
338 return 1; /* Return with normal AST checking */
339 }
340
341 /*
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
344 * in MacOS 9.
345 *
346 * save->r3 = taskID
347 * save->r4 = taskEnv
348 */
349
350 int bb_settaskenv( struct savearea *save )
351 {
352 int i;
353 task_t task;
354 thread_act_t act, fact;
355
356
357 task = current_task(); /* Figure out who our task is */
358
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 */
362
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... */
368 }
369 }
370 fact = (thread_act_t)fact->thr_acts.next; /* Go to the next one */
371 }
372
373 if ( !act || !act->active) {
374 task_unlock(task); /* Release task lock */
375 goto failure;
376 }
377
378 act_lock_thread(act); /* Make sure this stays 'round */
379 task_unlock(task); /* Safe to release now */
380
381 act->mact.bbTaskEnv = save->save_r4;
382
383 act_unlock_thread(act); /* Unlock the activation */
384 save->save_r3 = 0;
385 return KERN_SUCCESS;
386
387 failure:
388 save->save_r3 = -1; /* we failed to find the taskID */
389 return KERN_FAILURE;
390 }