]>
Commit | Line | Data |
---|---|---|
1c79356b A |
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 | } |