home *** CD-ROM | disk | FTP | other *** search
- /*
- * Mach Operating System
- * Copyright (c) 1992 Carnegie Mellon University
- * All Rights Reserved.
- *
- * Permission to use, copy, modify and distribute this software and its
- * documentation is hereby granted, provided that both the copyright
- * notice and this permission notice appear in all copies of the
- * software, derivative works or modified versions, and any portions
- * thereof, and that both notices appear in supporting documentation.
- *
- * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
- * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
- * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
- *
- * Carnegie Mellon requests users of this software to return to
- *
- * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
- * School of Computer Science
- * Carnegie Mellon University
- * Pittsburgh PA 15213-3890
- *
- * any improvements or extensions that they make and grant Carnegie Mellon
- * the rights to redistribute these changes.
- */
- /*
- * HISTORY
- * $Log: pcb.c,v $
- * Revision 2.5 92/01/03 20:31:35 dbg
- * Add user_stack_low and set_user_regs for passing control to
- * bootstrap in user space.
- * [91/08/12 dbg]
- *
- * Revision 2.4 91/07/31 18:13:55 dbg
- * Add stack_switching support.
- * [91/03/25 dbg]
- *
- * Revision 2.3 91/03/16 14:58:56 rpd
- * Added continuation argument to stack_attach.
- * [91/03/14 rpd]
- *
- * Revision 2.2 91/01/08 15:52:39 rpd
- * Added KEEP_STACKS support.
- * [91/01/06 rpd]
- *
- * Added empty frame on thread start for tracing.
- * [89/05/25 rwd]
- *
- * Converted for MACH kernel.
- * [88/11/03 dbg]
- *
- * Revision 2.1 89/08/03 16:51:35 rwd
- * Created.
- *
- * Revision 2.5 88/08/25 18:20:11 mwyoung
- * Corrected include file references.
- * [88/08/22 mwyoung]
- *
- * Correct typo.
- * [88/08/12 13:30:43 mwyoung]
- *
- * Implement THREAD_STATE_FLAVOR_LIST flavor in thread_getstatus.
- * [88/08/11 18:49:15 mwyoung]
- *
- * 17-Mar-88 David Golub (dbg) at Carnegie-Mellon University
- * Allow thread_setstatus to set the trace bits.
- *
- * 12-Feb-88 Jonathan J. Chew (jjc) at Carnegie-Mellon University
- * Added call to fpa_save() in pcb_synch().
- * Changed to test FPA_USED bit in pcb_fpa_flags to see if
- * FPA is being used.
- *
- * 25-Jan-88 Robert Baron (rvb) at Carnegie-Mellon University
- * Rather than have "u" expand to current_thread()->u_address, have
- * it expand to the constant U_ADDRESS which is updated by load_context
- * when the thread changes.
- * Define load_context_data() to set U_ADDRESS.utask and U_ADDRESS.uthread.
- * And be sure to have initial_context call it.
- *
- * 21-Jan-88 David Golub (dbg) at Carnegie-Mellon University
- * Changed for new thread_status interface.
- *
- * 07-Jan-88 Jonathan J. Chew (jjc) at Carnegie-Mellon University
- * Added call to fpa_fork_context() in thread_dup() to copy
- * FPA context from parent to child.
- * Created pcb_terminate().
- *
- * 17-Nov-87 Jonathan J. Chew (jjc) at Carnegie-Mellon University
- * Changed FPIS_NULLSZ to FPIS_VERSNULL.
- *
- * 15-Dec-87 Richard Sanzi (sanzi) at Carnegie-Mellon University
- * Changed pcb parameter to pcb_init() to be the thread
- * (so that all functions in this module take consistent
- * parameters).
- *
- * 17-Aug-87 Jonathan J. Chew (jjc) at Carnegie-Mellon University
- * Changed thread_{set,get}status to return something of
- * type kern_return_t.
- *
- * 10-Jul-87 David Black (dlb) at Carnegie-Mellon University
- * Added pcb_synch() stub pending real implementation.
- *
- * 20-Apr-87 David Golub (dbg) at Carnegie-Mellon University
- * Added support for MACH.
- *
- * 23-Oct-86 David Golub (dbg) at Carnegie-Mellon University
- * Added thread_start for MACH option.
- *
- * 9-Aug-86 Jonathan J. Chew (jjc) at Carnegie-Mellon University
- * Created.
- *
- */
-
- #include <mach/kern_return.h>
- #include <mach/thread_status.h>
- #include <mach/vm_param.h>
-
- #include <kern/counters.h>
- #include <kern/mach_param.h>
- #include <kern/thread.h>
- #include <kern/zalloc.h>
-
- #include <machine/thread.h>
- #include <machine/psl.h>
- #include <machine/frame.h>
- #include <fpa.h>
- #if NFPA >0
- #include <sundev/fpareg.h>
- #endif NFPA >0
-
- extern thread_t Switch_context();
- extern void Stack_handoff();
- extern void Thread_continue();
-
- zone_t pcb_zone;
-
- void stack_attach(thread, stack, continuation)
- register thread_t thread;
- vm_offset_t stack;
- void (*continuation)();
- {
- counter(if (++c_stacks_current > c_stacks_max)
- c_stacks_max = c_stacks_current);
-
- thread->kernel_stack = stack;
-
- /*
- * We want to run continuation, giving it as an argument
- * the return value from Load_context/Switch_context.
- * Thread_continue takes care of the mismatch between
- * the argument-passing/return-value conventions.
- * This function will not return normally,
- * so we don't have to worry about a return address.
- */
- STACK_MKS(stack)->pc = (int) (void (*)()) Thread_continue;
- STACK_MKS(stack)->a2 = (int) continuation;
- STACK_MKS(stack)->sp = (int) STACK_MKS(stack);
- }
-
- vm_offset_t stack_detach(thread)
- register thread_t thread;
- {
- register vm_offset_t stack;
-
- counter(if (--c_stacks_current < c_stacks_min)
- c_stacks_min = c_stacks_current);
-
- stack = thread->kernel_stack;
- thread->kernel_stack = 0;
- return stack;
- }
-
- /*
- * stack_handoff:
- *
- * Move the current thread's kernel stack to the new thread.
- */
-
- void stack_handoff(old, new)
- register thread_t old;
- register thread_t new;
- {
-
- fp_save(old);
-
- {
- task_t old_task, new_task;
-
- if ((old_task = old->task) != (new_task = new->task)) {
- PMAP_DEACTIVATE_USER(vm_map_pmap(old_task->map), old, 0);
- PMAP_ACTIVATE_USER(vm_map_pmap(new_task->map), new, 0);
- }
- }
-
- active_threads[0] = new;
- set_msp(new->pcb->user_regs);
-
- fp_restore(new);
- }
-
- thread_t switch_context(old, continuation, new)
- register thread_t old;
- void (*continuation)();
- register thread_t new;
- {
-
- fp_save(old);
-
- {
- task_t old_task, new_task;
-
- if ((old_task = old->task) != (new_task = new->task)) {
- PMAP_DEACTIVATE_USER(vm_map_pmap(old_task->map), old, 0);
- PMAP_ACTIVATE_USER(vm_map_pmap(new_task->map), new, 0);
- }
- }
-
- return Switch_context(old, continuation, new);
- }
-
- void pcb_module_init()
- {
- pcb_zone = zinit(sizeof(struct pcb),
- THREAD_MAX * sizeof(struct pcb),
- THREAD_CHUNK * sizeof(struct pcb),
- FALSE, "mc68020 pcb state");
- }
-
-
- extern thread_t fp_threads[];
- extern short fppstate;
-
- void pcb_init(thread)
- register thread_t thread;
- {
- register pcb_t pcb;
- register struct mc68020_saved_state *regs;
-
- pcb = (pcb_t) zalloc(pcb_zone);
- thread->pcb = pcb;
-
- bzero((char *)pcb, sizeof(struct pcb));
-
- /*
- * Set up the initial user register block.
- * Put it at the very end of the user_state area,
- * so that a full-size exception frame will still
- * fit.
- */
- regs = ((struct mc68020_saved_state *)
- &pcb->user_state[USER_STATE_SIZE]) - 1;
- pcb->user_regs = regs;
-
- /*
- * Set up the initial user state.
- */
- regs->sr = SR_MASTER; /* force to master stack on trap to kernel */
- regs->stkfmt = 0; /* short format stack frame */
- regs->vector = 0;
- }
-
- void pcb_terminate(thread)
- register thread_t thread;
- {
- register pcb_t pcb;
-
- pcb = thread->pcb;
-
- #if NFPA > 0
- if (pcb->fpas)
- fpaclose_thread(thread);
- #endif
- zfree(pcb_zone, (vm_offset_t) pcb);
- thread->pcb = 0;
- }
-
- void pcb_collect(thread)
- thread_t thread;
- {
- }
-
- /*
- * thread_setstatus:
- *
- * Set the status of the specified thread.
- */
-
- kern_return_t thread_setstatus(thread, flavor, tstate, count)
- thread_t thread;
- int flavor;
- thread_state_t tstate;
- unsigned int count;
- {
- register struct sun_thread_state *state;
- register pcb_t pcb;
- register struct mc68020_saved_state *regs;
-
- if (flavor == SUN_THREAD_STATE_REGS) {
- if (count < SUN_THREAD_STATE_REGS_COUNT) {
- return(KERN_INVALID_ARGUMENT);
- }
- state = (struct sun_thread_state *) tstate;
-
- pcb = thread->pcb;
- regs = pcb->user_regs;
-
- regs->d0 = state->d0;
- regs->d1 = state->d1;
- regs->d2 = state->d2;
- regs->d3 = state->d3;
- regs->d4 = state->d4;
- regs->d5 = state->d5;
- regs->d6 = state->d6;
- regs->d7 = state->d7;
-
- regs->a0 = state->a0;
- regs->a1 = state->a1;
- regs->a2 = state->a2;
- regs->a3 = state->a3;
- regs->a4 = state->a4;
- regs->a5 = state->a5;
- regs->a6 = state->a6;
- regs->sp = state->sp;
-
- if (regs->pc != state->pc)
- pcb->discard_frame = TRUE;
- regs->pc = state->pc;
-
- /*
- * Enforce user mode status register:
- * must have user mode, user stack, interrupt priority 0.
- * Force to master stack on trap to kernel mode.
- * User may set trace bits, but both trace bits cannot be
- * on (undefined).
- */
- regs->sr = state->sr & ~(SR_SMODE|SR_INTPRI) | SR_MASTER;
- if ((regs->sr & (SR_T1|SR_T0)) == (SR_T1|SR_T0))
- regs->sr &= ~SR_T0;
-
- if (fppstate > 0) {
- /*
- * FP registers are in the pcb
- */
- if (state->fp.fps_flags == 0) {
- /*
- * no FP state
- */
- bzero((char *)&pcb->mc68881s,
- sizeof(struct mc68881_state));
- }
- else {
- /*
- * FP state
- */
- if (pcb->mc68881s.fp_status.fps_flags == FPS_UNUSED) {
- /* how do we make load_context load new FP
- register values? */
- }
- pcb->mc68881s.fp_status = state->fp;
- }
- }
- return(KERN_SUCCESS);
- }
- else if (flavor == SUN_THREAD_STATE_FPA) {
- if (count < SUN_THREAD_STATE_FPA_COUNT)
- return(KERN_INVALID_ARGUMENT);
-
- #if NFPA > 0
- /*
- * Set this thread's FPA registers XXX
- */
- #else NFPA > 0
- return(KERN_FAILURE); /* no FPA */
- #endif NFPA > 0
- }
- else {
- return(KERN_INVALID_ARGUMENT);
- }
- }
-
- /*
- * thread_getstatus:
- *
- * Get the status of the specified thread.
- */
-
- kern_return_t thread_getstatus(thread, flavor, tstate, count)
- register thread_t thread;
- int flavor;
- thread_state_t tstate; /* pointer to OUT array */
- unsigned int *count; /* IN/OUT */
- {
- register struct sun_thread_state *state;
- register pcb_t pcb;
- register struct mc68020_saved_state *regs;
-
- switch (flavor) {
- case THREAD_STATE_FLAVOR_LIST:
- if (*count < 2)
- return(KERN_INVALID_ARGUMENT);
- tstate[0] = SUN_THREAD_STATE_REGS;
- tstate[1] = SUN_THREAD_STATE_FPA;
- *count = 2;
- break;
-
- case SUN_THREAD_STATE_REGS:
- if (*count < SUN_THREAD_STATE_REGS_COUNT) {
- return(KERN_INVALID_ARGUMENT);
- }
-
- state = (struct sun_thread_state *) tstate;
-
- pcb = thread->pcb;
- regs = pcb->user_regs;
-
- state->d0 = regs->d0;
- state->d1 = regs->d1;
- state->d2 = regs->d2;
- state->d3 = regs->d3;
- state->d4 = regs->d4;
- state->d5 = regs->d5;
- state->d6 = regs->d6;
- state->d7 = regs->d7;
- state->a0 = regs->a0;
- state->a1 = regs->a1;
- state->a2 = regs->a2;
- state->a3 = regs->a3;
- state->a4 = regs->a4;
- state->a5 = regs->a5;
- state->a6 = regs->a6;
- state->sp = regs->sp;
-
- state->pc = regs->pc;
- state->sr = regs->sr;
-
- if (fppstate > 0) {
- /*
- * Fp registers are in the PCB
- */
- pcb->mc68881s.fp_status.fps_flags =
- EXT_FPS_FLAGS(&pcb->mc68881s.fp_istate);
- state->fp = pcb->mc68881s.fp_status;
- }
- else {
- /*
- * FP not in use
- */
- bzero((char *)&state->fp, sizeof(state->fp));
- }
- *count = SUN_THREAD_STATE_REGS_COUNT;
- break;
-
- case SUN_THREAD_STATE_FPA:
- if (*count < SUN_THREAD_STATE_FPA_COUNT)
- return(KERN_INVALID_ARGUMENT);
-
- #if NFPA > 0
- /* XXX There probably should also be a runtime check
- * for FPA presence.
- */
- /*
- * Get this thread's FPA registers XXX
- */
- *count = SUN_THREAD_STATE_FPA_COUNT;
- #else NFPA > 0
- return(KERN_FAILURE); /* no FPA */
- #endif NFPA > 0
- break;
-
- default:
- return(KERN_INVALID_ARGUMENT);
- }
- return(KERN_SUCCESS);
- }
-
- /*
- * Alter the thread`s state so that a following thread_exception_return
- * will make the thread return 'retval' from a syscall.
- */
- void
- thread_set_syscall_return(thread, retval)
- thread_t thread;
- kern_return_t retval;
- {
- thread->pcb->user_regs->d0 = retval;
- }
-
-
- /*
- * Return prefered address of user stack.
- * Always returns low address. If stack grows up,
- * the stack grows away from this address;
- * if stack grows down, the stack grows towards this
- * address.
- */
- vm_offset_t
- user_stack_low(stack_size)
- vm_size_t stack_size;
- {
- return (VM_MAX_ADDRESS - stack_size);
- }
-
- /*
- * Allocate argument area and set registers for first user thread.
- */
- vm_offset_t
- set_user_regs(stack_base, stack_size, entry, arg_size)
- vm_offset_t stack_base; /* low address */
- vm_offset_t stack_size;
- int *entry;
- vm_size_t arg_size;
- {
- vm_offset_t arg_addr;
- register struct mc68020_saved_state *saved_state;
-
- arg_size = (arg_size + sizeof(int) - 1) & ~(sizeof(int)-1);
- arg_addr = stack_base + stack_size - arg_size;
-
- saved_state = current_thread()->pcb->user_regs;
- saved_state->sp = (int)arg_addr;
- saved_state->pc = entry[0];
-
- return (arg_addr);
- }
-