2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
32 * Copyright (c) 1990,1991,1992 The University of Utah and
33 * the Center for Software Science (CSS). All rights reserved.
35 * Permission to use, copy, modify and distribute this software is hereby
36 * granted provided that (1) source code retains these copyright, permission,
37 * and disclaimer notices, and (2) redistributions including binaries
38 * reproduce the notices in supporting documentation, and (3) all advertising
39 * materials mentioning features or use of this software display the following
40 * acknowledgement: ``This product includes software developed by the Center
41 * for Software Science at the University of Utah.''
43 * THE UNIVERSITY OF UTAH AND CSS ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
44 * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSS DISCLAIM ANY LIABILITY OF
45 * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
47 * CSS requests users of this software to return to css-dist@cs.utah.edu any
48 * improvements that they make and grant CSS redistribution rights.
50 * Utah $Hdr: pcb.c 1.23 92/06/27$
57 #include <mach/mach_types.h>
58 #include <mach/thread_status.h>
60 #include <kern/kern_types.h>
61 #include <kern/task.h>
62 #include <kern/thread.h>
63 #include <kern/misc_protos.h>
64 #include <kern/mach_param.h>
67 #include <vm/vm_map.h>
68 #include <vm/vm_kern.h>
70 #include <ppc/misc_protos.h>
71 #include <ppc/cpu_internal.h>
72 #include <ppc/exception.h>
73 #include <ppc/proc_reg.h>
76 #include <ppc/mappings.h>
77 #include <ppc/savearea.h>
78 #include <ppc/Firmware.h>
80 #include <ppc/thread.h>
81 #include <ppc/vmachmon.h>
82 #include <ppc/low_trace.h>
83 #include <ppc/lowglobals.h>
85 #include <sys/kdebug.h>
87 void machine_act_terminate(thread_t
);
90 * These constants are dumb. They should not be in asm.h!
93 #define KF_SIZE (FM_SIZE+ARG_SIZE+FM_REDZONE)
96 int fpu_trap_count
= 0;
97 int fpu_switch_count
= 0;
98 int vec_trap_count
= 0;
99 int vec_switch_count
= 0;
103 * consider_machine_collect: try to collect machine-dependent pages
106 consider_machine_collect()
109 * none currently available
115 consider_machine_adjust()
117 consider_mapping_adjust();
121 * switch_context: Switch from one thread to another, needed for
126 machine_switch_context(
128 thread_continue_t continuation
,
131 register thread_t retval
;
133 facility_context
*fowner
;
134 struct per_proc_info
*ppinfo
;
137 panic("machine_switch_context");
139 ppinfo
= getPerProc(); /* Get our processor block */
141 ppinfo
->old_thread
= (unsigned int)old
;
142 ppinfo
->cpu_flags
&= ~traceBE
; /* disable branch tracing if on */
144 /* Our context might wake up on another processor, so we must
145 * not keep hot state in our FPU, it must go back to the pcb
146 * so that it can be found by the other if needed
148 if(real_ncpus
> 1) { /* This is potentially slow, so only do when actually SMP */
149 fowner
= ppinfo
->FPU_owner
; /* Cache this because it may change */
150 if(fowner
) { /* Is there any live context? */
151 if(fowner
->facAct
== old
) { /* Is it for us? */
152 fpu_save(fowner
); /* Yes, save it */
155 fowner
= ppinfo
->VMX_owner
; /* Cache this because it may change */
156 if(fowner
) { /* Is there any live context? */
157 if(fowner
->facAct
== old
) { /* Is it for us? */
158 vec_save(fowner
); /* Yes, save it */
164 * If old thread is running VM, save per proc userProtKey and FamVMmode spcFlags bits in the thread spcFlags
165 * This bits can be modified in the per proc without updating the thread spcFlags
167 if(old
->machine
.specFlags
& runningVM
) {
168 old
->machine
.specFlags
&= ~(userProtKey
|FamVMmode
);
169 old
->machine
.specFlags
|= (ppinfo
->spcFlags
) & (userProtKey
|FamVMmode
);
171 old
->machine
.specFlags
&= ~OnProc
;
172 new->machine
.specFlags
|= OnProc
;
175 * We do not have to worry about the PMAP module, so switch.
177 * We must not use thread->map since this may not be the actual
178 * task map, but the map being used for a klcopyin/out.
181 if(new->machine
.specFlags
& runningVM
) { /* Is the new guy running a VM? */
182 pmap_switch(new->machine
.vmmCEntry
->vmmPmap
); /* Switch to the VM's pmap */
183 ppinfo
->VMMareaPhys
= new->machine
.vmmCEntry
->vmmContextPhys
;
184 ppinfo
->VMMXAFlgs
= new->machine
.vmmCEntry
->vmmXAFlgs
;
185 ppinfo
->FAMintercept
= new->machine
.vmmCEntry
->vmmFAMintercept
;
187 else { /* otherwise, we use the task's pmap */
188 new_pmap
= new->task
->map
->pmap
;
189 if ((old
->task
->map
->pmap
!= new_pmap
) || (old
->machine
.specFlags
& runningVM
)) {
190 pmap_switch(new_pmap
); /* Switch if there is a change */
194 if(old
->machine
.umwSpace
!= invalSpace
) { /* Does our old guy have an active window? */
195 old
->machine
.umwSpace
|= umwSwitchAway
; /* Show we switched away from this guy */
196 hw_blow_seg(lowGlo
.lgUMWvaddr
); /* Blow off the first segment */
197 hw_blow_seg(lowGlo
.lgUMWvaddr
+ 0x10000000ULL
); /* Blow off the second segment */
200 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SCHED
,MACH_SCHED
) | DBG_FUNC_NONE
,
201 old
->reason
, (int)new, old
->sched_pri
, new->sched_pri
, 0);
203 retval
= Switch_context(old
, continuation
, new);
204 assert(retval
!= NULL
);
206 if (branch_tracing_enabled()) {
207 ppinfo
= getPerProc(); /* Get our processor block */
208 ppinfo
->cpu_flags
|= traceBE
; /* restore branch tracing */
211 /* We've returned from having switched context, so we should be
212 * back in the original context.
219 * Initialize the machine-dependent state for a new thread.
222 machine_thread_create(
226 savearea
*sv
; /* Pointer to newly allocated savearea */
227 unsigned int *CIsTooLimited
, i
;
229 hw_atomic_add((uint32_t *)&saveanchor
.savetarget
, 4); /* Account for the number of saveareas we think we "need"
230 for this activation */
231 assert(thread
->machine
.pcb
== (savearea
*)0); /* Make sure there was no previous savearea */
233 sv
= save_alloc(); /* Go get us a savearea */
235 bzero((char *)((unsigned int)sv
+ sizeof(savearea_comm
)), (sizeof(savearea
) - sizeof(savearea_comm
))); /* Clear it */
237 sv
->save_hdr
.save_prev
= 0; /* Clear the back pointer */
238 sv
->save_hdr
.save_flags
= (sv
->save_hdr
.save_flags
& ~SAVtype
) | (SAVgeneral
<< SAVtypeshft
); /* Mark as in use */
239 sv
->save_hdr
.save_act
= thread
; /* Set who owns it */
240 thread
->machine
.pcb
= sv
; /* Point to the save area */
241 thread
->machine
.curctx
= &thread
->machine
.facctx
; /* Initialize facility context */
242 thread
->machine
.facctx
.facAct
= thread
; /* Initialize facility context pointer to activation */
243 thread
->machine
.umwSpace
= invalSpace
; /* Initialize user memory window space to invalid */
244 thread
->machine
.preemption_count
= 0; /* Initialize preemption counter */
247 * User threads will pull their context from the pcb when first
248 * returning to user mode, so fill in all the necessary values.
249 * Kernel threads are initialized from the save state structure
250 * at the base of the kernel stack (see stack_attach()).
253 thread
->machine
.upcb
= sv
; /* Set user pcb */
254 sv
->save_srr1
= (uint64_t)MSR_EXPORT_MASK_SET
; /* Set the default user MSR */
255 if(task_has_64BitAddr(task
)) sv
->save_srr1
|= (uint64_t)MASK32(MSR_SF
) << 32; /* If 64-bit task, force 64-bit mode */
256 sv
->save_fpscr
= 0; /* Clear all floating point exceptions */
257 sv
->save_vrsave
= 0; /* Set the vector save state */
258 sv
->save_vscr
[0] = 0x00000000;
259 sv
->save_vscr
[1] = 0x00000000;
260 sv
->save_vscr
[2] = 0x00000000;
261 sv
->save_vscr
[3] = 0x00010000; /* Disable java mode and clear saturated */
263 return(KERN_SUCCESS
);
267 * Machine-dependent cleanup prior to destroying a thread
270 machine_thread_destroy(
273 register savearea
*pcb
, *ppsv
;
274 register savearea_vec
*vsv
, *vpsv
;
275 register savearea_fpu
*fsv
, *fpsv
;
276 register savearea
*svp
;
281 * This function will release all context.
284 machine_act_terminate(thread
); /* Make sure all virtual machines are dead first */
288 * Walk through and release all floating point and vector contexts. Also kill live context.
292 intr
= ml_set_interrupts_enabled(FALSE
); /* Disable for interruptions */
294 toss_live_vec(thread
->machine
.curctx
); /* Dump live vectors */
296 vsv
= thread
->machine
.curctx
->VMXsave
; /* Get the top vector savearea */
298 while(vsv
) { /* Any VMX saved state? */
299 vpsv
= vsv
; /* Remember so we can toss this */
300 vsv
= CAST_DOWN(savearea_vec
*, vsv
->save_hdr
.save_prev
); /* Get one underneath our's */
301 save_release((savearea
*)vpsv
); /* Release it */
304 thread
->machine
.curctx
->VMXsave
= 0; /* Kill chain */
306 toss_live_fpu(thread
->machine
.curctx
); /* Dump live float */
308 fsv
= thread
->machine
.curctx
->FPUsave
; /* Get the top float savearea */
310 while(fsv
) { /* Any float saved state? */
311 fpsv
= fsv
; /* Remember so we can toss this */
312 fsv
= CAST_DOWN(savearea_fpu
*, fsv
->save_hdr
.save_prev
); /* Get one underneath our's */
313 save_release((savearea
*)fpsv
); /* Release it */
316 thread
->machine
.curctx
->FPUsave
= 0; /* Kill chain */
319 * free all regular saveareas.
322 pcb
= thread
->machine
.pcb
; /* Get the general savearea */
324 while(pcb
) { /* Any float saved state? */
325 ppsv
= pcb
; /* Remember so we can toss this */
326 pcb
= CAST_DOWN(savearea
*, pcb
->save_hdr
.save_prev
); /* Get one underneath our's */
327 save_release(ppsv
); /* Release it */
330 hw_atomic_sub((uint32_t *)&saveanchor
.savetarget
, 4); /* Unaccount for the number of saveareas we think we "need" */
332 (void) ml_set_interrupts_enabled(intr
); /* Restore interrupts if enabled */
337 * act_machine_sv_free
338 * release saveareas associated with an act. if flag is true, release
339 * user level savearea(s) too, else don't
341 * This code must run with interruptions disabled because an interrupt handler could use
342 * floating point and/or vectors. If this happens and the thread we are blowing off owns
343 * the facility, we can deadlock.
346 act_machine_sv_free(thread_t act
)
348 register savearea
*pcb
, *userpcb
;
349 register savearea_vec
*vsv
, *vpst
, *vsvt
;
350 register savearea_fpu
*fsv
, *fpst
, *fsvt
;
351 register savearea
*svp
;
356 * This function will release all non-user state context.
361 * Walk through and release all floating point and vector contexts that are not
362 * user state. We will also blow away live context if it belongs to non-user state.
363 * Note that the level can not change while we are in this code. Nor can another
364 * context be pushed on the stack.
366 * We do nothing here if the current level is user. Otherwise,
367 * the live context is cleared. Then we find the user saved context.
368 * Next, we take the sync lock (to keep us from munging things in *_switch).
369 * The level is set to 0 and all stacked context other than user is dequeued.
370 * Then we unlock. Next, all of the old kernel contexts are released.
374 intr
= ml_set_interrupts_enabled(FALSE
); /* Disable for interruptions */
376 if(act
->machine
.curctx
->VMXlevel
) { /* Is the current level user state? */
378 toss_live_vec(act
->machine
.curctx
); /* Dump live vectors if is not user */
380 if(!hw_lock_to((hw_lock_t
)&act
->machine
.curctx
->VMXsync
, LockTimeOut
)) { /* Get the sync lock */
381 panic("act_machine_sv_free - timeout getting VMX sync lock\n"); /* Tell all and die */
384 vsv
= act
->machine
.curctx
->VMXsave
; /* Get the top vector savearea */
385 while(vsv
&& vsv
->save_hdr
.save_level
) vsv
= (savearea_vec
*)vsv
->save_hdr
.save_prev
; /* Find user context if any */
387 vsvt
= act
->machine
.curctx
->VMXsave
; /* Get the top of the chain */
388 act
->machine
.curctx
->VMXsave
= vsv
; /* Point to the user context */
389 act
->machine
.curctx
->VMXlevel
= 0; /* Set the level to user */
390 hw_lock_unlock((hw_lock_t
)&act
->machine
.curctx
->VMXsync
); /* Unlock */
392 while(vsvt
) { /* Clear any VMX saved state */
393 if (vsvt
== vsv
) break; /* Done when hit user if any */
394 vpst
= vsvt
; /* Remember so we can toss this */
395 vsvt
= (savearea_vec
*)vsvt
->save_hdr
.save_prev
; /* Get one underneath our's */
396 save_ret((savearea
*)vpst
); /* Release it */
401 if(act
->machine
.curctx
->FPUlevel
) { /* Is the current level user state? */
403 toss_live_fpu(act
->machine
.curctx
); /* Dump live floats if is not user */
405 if(!hw_lock_to((hw_lock_t
)&act
->machine
.curctx
->FPUsync
, LockTimeOut
)) { /* Get the sync lock */
406 panic("act_machine_sv_free - timeout getting FPU sync lock\n"); /* Tell all and die */
409 fsv
= act
->machine
.curctx
->FPUsave
; /* Get the top floats savearea */
410 while(fsv
&& fsv
->save_hdr
.save_level
) fsv
= (savearea_fpu
*)fsv
->save_hdr
.save_prev
; /* Find user context if any */
412 fsvt
= act
->machine
.curctx
->FPUsave
; /* Get the top of the chain */
413 act
->machine
.curctx
->FPUsave
= fsv
; /* Point to the user context */
414 act
->machine
.curctx
->FPUlevel
= 0; /* Set the level to user */
415 hw_lock_unlock((hw_lock_t
)&act
->machine
.curctx
->FPUsync
); /* Unlock */
417 while(fsvt
) { /* Clear any VMX saved state */
418 if (fsvt
== fsv
) break; /* Done when hit user if any */
419 fpst
= fsvt
; /* Remember so we can toss this */
420 fsvt
= (savearea_fpu
*)fsvt
->save_hdr
.save_prev
; /* Get one underneath our's */
421 save_ret((savearea
*)fpst
); /* Release it */
427 * free all regular saveareas except a user savearea, if any
430 pcb
= act
->machine
.pcb
; /* Get the general savearea */
431 userpcb
= 0; /* Assume no user context for now */
433 while(pcb
) { /* Any float saved state? */
434 if (pcb
->save_srr1
& MASK(MSR_PR
)) { /* Is this a user savearea? */
435 userpcb
= pcb
; /* Remember so we can toss this */
438 svp
= pcb
; /* Remember this */
439 pcb
= CAST_DOWN(savearea
*, pcb
->save_hdr
.save_prev
); /* Get one underneath our's */
440 save_ret(svp
); /* Release it */
443 act
->machine
.pcb
= userpcb
; /* Chain in the user if there is one, or 0 if not */
444 (void) ml_set_interrupts_enabled(intr
); /* Restore interrupts if enabled */
449 machine_act_terminate(
452 if(act
->machine
.bbDescAddr
) { /* Check if the Blue box assist is active */
453 disable_bluebox_internal(act
); /* Kill off bluebox */
456 if(act
->machine
.vmmControl
) { /* Check if VMM is active */
457 vmm_tear_down_all(act
); /* Kill off all VMM contexts */
462 machine_thread_terminate_self(void)
464 machine_act_terminate(current_thread());
468 machine_thread_init(void)
471 #if KERNEL_STACK_SIZE > PPC_PGBYTES
472 panic("KERNEL_STACK_SIZE can't be greater than PPC_PGBYTES\n");
480 dump_thread(thread_t th
)
482 printf(" thread @ 0x%x:\n", th
);
486 dump_act(thread_t thr_act
)
491 printf("thread(0x%x)(%d): task=%x(%d)\n",
492 thr_act
, thr_act
->ref_count
,
493 thr_act
->task
, thr_act
->task
? thr_act
->task
->ref_count
: 0);
495 printf("\tsusp=%x active=%x\n",
496 thr_act
->suspend_count
, thr_act
->active
);
498 return((int)thr_act
);
506 return(current_thread()->machine
.upcb
->save_srr0
);
510 * detach and return a kernel stack from a thread
514 machine_stack_detach(
519 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_SCHED
,MACH_STACK_DETACH
),
520 thread
, thread
->priority
,
521 thread
->sched_pri
, 0, 0);
523 act_machine_sv_free(thread
);
525 stack
= thread
->kernel_stack
;
526 thread
->kernel_stack
= 0;
531 * attach a kernel stack to a thread and initialize it
533 * attaches a stack to a thread. if there is no save
534 * area we allocate one. the top save area is then
535 * loaded with the pc (continuation address), the initial
536 * stack pointer, and a std kernel MSR. if the top
537 * save area is the user save area bad things will
543 machine_stack_attach(
550 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_SCHED
,MACH_STACK_ATTACH
),
551 thread
, thread
->priority
,
552 thread
->sched_pri
, 0, 0);
555 kss
= (unsigned int *)STACK_IKS(stack
);
556 thread
->kernel_stack
= stack
;
558 /* during initialization we sometimes do not have an
559 activation. in that case do not do anything */
560 sv
= save_get(); /* cannot block */
561 sv
->save_hdr
.save_flags
= (sv
->save_hdr
.save_flags
& ~SAVtype
) | (SAVgeneral
<< SAVtypeshft
); /* Mark as in use */
562 sv
->save_hdr
.save_act
= thread
;
563 sv
->save_hdr
.save_prev
= (addr64_t
)((uintptr_t)thread
->machine
.pcb
);
564 thread
->machine
.pcb
= sv
;
566 sv
->save_srr0
= (unsigned int)thread_continue
;
567 /* sv->save_r3 = ARG ? */
568 sv
->save_r1
= (vm_offset_t
)((int)kss
- KF_SIZE
);
569 sv
->save_srr1
= MSR_SUPERVISOR_INT_OFF
;
570 sv
->save_fpscr
= 0; /* Clear all floating point exceptions */
571 sv
->save_vrsave
= 0; /* Set the vector save state */
572 sv
->save_vscr
[3] = 0x00010000; /* Supress java mode */
573 *(CAST_DOWN(int *, sv
->save_r1
)) = 0;
575 thread
->machine
.ksp
= 0;
579 * move a stack from old to new thread
583 machine_stack_handoff(
590 facility_context
*fowner
;
592 struct per_proc_info
*ppinfo
;
598 panic("machine_stack_handoff");
600 stack
= machine_stack_detach(old
);
601 new->kernel_stack
= stack
;
602 if (stack
== old
->reserved_stack
) {
603 assert(new->reserved_stack
);
604 old
->reserved_stack
= new->reserved_stack
;
605 new->reserved_stack
= stack
;
608 ppinfo
= getPerProc(); /* Get our processor block */
610 ppinfo
->cpu_flags
&= ~traceBE
; /* Turn off special branch trace */
612 if(real_ncpus
> 1) { /* This is potentially slow, so only do when actually SMP */
613 fowner
= ppinfo
->FPU_owner
; /* Cache this because it may change */
614 if(fowner
) { /* Is there any live context? */
615 if(fowner
->facAct
== old
) { /* Is it for us? */
616 fpu_save(fowner
); /* Yes, save it */
619 fowner
= ppinfo
->VMX_owner
; /* Cache this because it may change */
620 if(fowner
) { /* Is there any live context? */
621 if(fowner
->facAct
== old
) { /* Is it for us? */
622 vec_save(fowner
); /* Yes, save it */
628 * If old thread is running VM, save per proc userProtKey and FamVMmode spcFlags bits in the thread spcFlags
629 * This bits can be modified in the per proc without updating the thread spcFlags
631 if(old
->machine
.specFlags
& runningVM
) { /* Is the current thread running a VM? */
632 old
->machine
.specFlags
&= ~(userProtKey
|FamVMmode
);
633 old
->machine
.specFlags
|= (ppinfo
->spcFlags
) & (userProtKey
|FamVMmode
);
635 old
->machine
.specFlags
&= ~OnProc
;
636 new->machine
.specFlags
|= OnProc
;
638 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SCHED
,MACH_STACK_HANDOFF
) | DBG_FUNC_NONE
,
639 old
->reason
, (int)new, old
->sched_pri
, new->sched_pri
, 0);
642 if(new->machine
.specFlags
& runningVM
) { /* Is the new guy running a VM? */
643 pmap_switch(new->machine
.vmmCEntry
->vmmPmap
); /* Switch to the VM's pmap */
644 ppinfo
->VMMareaPhys
= new->machine
.vmmCEntry
->vmmContextPhys
;
645 ppinfo
->VMMXAFlgs
= new->machine
.vmmCEntry
->vmmXAFlgs
;
646 ppinfo
->FAMintercept
= new->machine
.vmmCEntry
->vmmFAMintercept
;
648 else { /* otherwise, we use the task's pmap */
649 new_pmap
= new->task
->map
->pmap
;
650 if ((old
->task
->map
->pmap
!= new_pmap
) || (old
->machine
.specFlags
& runningVM
)) {
651 pmap_switch(new_pmap
);
655 machine_set_current_thread(new);
656 ppinfo
->Uassist
= new->machine
.cthread_self
;
658 ppinfo
->ppbbTaskEnv
= new->machine
.bbTaskEnv
;
659 ppinfo
->spcFlags
= new->machine
.specFlags
;
661 old
->machine
.umwSpace
|= umwSwitchAway
; /* Show we switched away from this guy */
662 mp
= (mapping_t
*)&ppinfo
->ppUMWmp
;
663 mp
->mpSpace
= invalSpace
; /* Since we can't handoff in the middle of copy in/out, just invalidate */
665 if (branch_tracing_enabled())
666 ppinfo
->cpu_flags
|= traceBE
;
668 if(trcWork
.traceMask
) dbgTrace(0x9903, (unsigned int)old
, (unsigned int)new, 0, 0); /* Cut trace entry if tracing */
674 * clean and initialize the current kernel stack and go to
675 * the given continuation routine
680 thread_continue_t continuation
,
682 wait_result_t wresult
)
684 thread_t self
= current_thread();
688 assert(self
->kernel_stack
);
689 kss
= (unsigned int *)STACK_IKS(self
->kernel_stack
);
690 assert(continuation
);
692 tsp
= (vm_offset_t
)((int)kss
- KF_SIZE
);
696 Call_continuation(continuation
, parameter
, wresult
, tsp
);