/* Copyright (C) 1992, the Florida State University
   Distributed by the Florida State University under the terms of the
   GNU Library General Public License.

This file is part of Pthreads.

Pthreads is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation (version 2).

Pthreads is distributed "AS IS" in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with Pthreads; see the file COPYING.  If not, write
to the Free Software Foundation, 675 Mass Ave, Cambridge,
MA 02139, USA.

Report problems and direct all questions to:

  pthreads-bugs@ada.cs.fsu.edu

  @(#)pthread_init.c	1.16 3/5/93

*/

#include "pthread_internals.h"
#include <sun4/asm_linkage.h>
#include <sun4/frame.h>

extern pthread_body();
#ifdef STACK_CHECK
extern int page_size;
#endif

/*
 * Stack layout:
 * high +-----------+ + +
 *      |           | | | WINDOWSIZE (prev. window for callee save area)
 *      +-----------+ | +
 *      |           | PTHREAD_BODY_OFFSET (space for pthread_body())
 *      |           | |
 *      +-----------+ + start of user stack space
 *      |           | |
 *      |           | |
 *      |           | |
 *      |           | |
 *      |           | attr.stacksize (space for user functions)
 *      |           | |
 *      |           | |
 *      |           | |
 *      |           | |
 *      +-----------+ + end of user stack space
 *      | --------- | | +
 *      |  locked   | | page_size: locked page, bus error on overflow
 *      | --------- | | + PA(stack_base): next smallest page aligned address
 *      |           | 2*page_size
 *      |           | |
 * low  +-----------+ + stack_base
 *
 * ifdef STACK_CHECK:
 * Lock the closest page to the end of the stack to cause a bus error
 * (or and illegal instruction if no signal handler cannot be pushed
 * because the stack limit is exceeded, causes bad signal stack message)
 * on stack overflow. We allocate space conservatively (two pages) to
 * ensure that the locked page does not cross into the allocated stack
 * stack due to page alignment. Notice that only whole pages can be
 * locked, i.e. smaller amounts will be extended to the next page
 * boundary.
 */

/*------------------------------------------------------------*/
/*
 * pthread_initialize - associate stack space with thread
 */
void pthread_initialize(t)
pthread_t t;
{
  struct frame *sp;

#ifdef STACK_CHECK
  sp = (struct frame *)
    (t->stack_base + page_size * 2 +
     SA(t->attr.stacksize + PTHREAD_BODY_OFFSET - WINDOWSIZE));
  sp->fr_savfp = (struct frame *)
    (t->stack_base + page_size * 2 +
     SA(t->attr.stacksize + PTHREAD_BODY_OFFSET));
#else  
  sp = (struct frame *)
    (t->stack_base + SA(t->attr.stacksize + PTHREAD_BODY_OFFSET - WINDOWSIZE));
  sp->fr_savfp = (struct frame *)
    (t->stack_base + SA(t->attr.stacksize + PTHREAD_BODY_OFFSET));
#endif
  t->context[0] = (int) sp;
  t->context[1] = (int) pthread_body - RETURN_OFFSET;
}

/*------------------------------------------------------------*/
/*
 * push_fake_call - push a user handler for a signal on some thread's stack
 * The user handler will be the first code executed when control
 * returns to the thread.
 */
void push_fake_call(p, handler_add, sig, scp, temp_mask)
     pthread_t p;
     void (*handler_add)();
     int sig;
     struct context_t *scp;
     sigset_t temp_mask;
{
  void fake_call_wrapper();
  struct frame *framep;
  int new_context = scp == (struct context_t *) DIRECTED_AT_THREAD;

  /*
   * create a new frame for the wrapper, and bind sp of the new 
   * frame to the frame structure.
   */
  if (new_context) {                /* init context structure if neccessary */
                                    /* allocate space on stack */
    scp = (struct context_t *) (p->context[SP_OFFSET] -
				SA(sizeof(struct context_t)));
    /*
     * need space for previous window in case callee stores parameters here
     */
    framep = (struct frame *) ((int) scp - 2 * WINDOWSIZE);
    scp->sc_mask = p->mask;
    scp->sc_sp = p->context[SP_OFFSET];
    /*
     * The offset will be substracted in the wrapper to hide this issue
     * from the user (in case he changes the return address)
     */
    scp->sc_pc = p->context[PC_OFFSET] + RETURN_OFFSET;
  }
  else
    framep = (struct frame *) (p->context[SP_OFFSET] - WINDOWSIZE);

  framep->fr_savfp = (struct frame *) p->context[SP_OFFSET];
                                    /* put fp in saved area of i6. */
  framep->fr_savpc = p->context[PC_OFFSET];
                                    /* put pc in saved area of i7. */
  framep->fr_arg[0] = (int) handler_add;
                                    /* save handler's address as i0. */
  framep->fr_arg[1] = p->mask;      /* save signal mask as i1. */
  framep->fr_arg[2] = sig;          /* arg0 to user handler in i2. */
  framep->fr_arg[3] = (int) &p->sig_info[sig == -1 ? 0 : sig];
				    /* arg1 to user handler in i3. */
  framep->fr_arg[4] = (int) scp;    /* arg2 to user handler in i4. */
  framep->fr_arg[5] = new_context;  /* used by wrapper to restore context */
  framep->fr_local[6] = (int) &p->cond; /* addr. of inter. cond. wait in l6 */
  framep->fr_local[7] = (int) p->scp;   /* old context pointer in l7 */
  p->context[SP_OFFSET] = (int) framep;      /* store new sp */
  p->context[PC_OFFSET] = 
    (int) fake_call_wrapper - RETURN_OFFSET; /* store new pc */
  p->mask |= temp_mask;             /* update the per-thread mask. */
}
/*------------------------------------------------------------*/
