/*
 * arch.c
 *
 * architecture-dependent process context code
 *
 */

#ifndef AIX32

#include "lwp.h"
#include "lwpint.h"

#if defined(BSD386)
void lwpInitContext(newp, sp)
	struct lwpProc *newp;
	void *sp;
{
	newp->context[2] = (int)sp;
	newp->context[0] = (int)lwpEntryPoint;
}

#elif defined(FBSD)

void lwpInitContext(newp, sp)
        struct lwpProc *newp;
        void *sp;
{
        setjmp (newp->context);
        newp->context->_jb[2] = (int)sp;
        newp->context->_jb[3] = (int)sp;
        newp->context->_jb[0] = (int)lwpEntryPoint;
}

#elif defined(__linux__)

void lwpInitContext(newp, sp)
	struct lwpProc *newp;
	void	*sp;
{
	newp->context->__sp = sp;
	newp->context->__bp = sp;
	newp->context->__pc = (void *)lwpEntryPoint;
}

#elif defined(SUN3)

void lwpInitContext(newp, sp)
	struct lwpProc *newp;
	void	*sp;
{
	newp->context[2] = (int)sp;
	newp->context[3] = (int)lwpEntryPoint;
}

#elif defined(SUN4)

void lwpInitContext(newp, sp)
	struct lwpProc *newp;
	void		*sp;
{
	static jmp_buf *cpp;
	extern struct lwpProc *LwpCurrent;

	bzero(newp->context, sizeof(newp->context));
	newp->context[0] = (int)sp;
	/* preserve cpp for new context */
	cpp = (jmp_buf *)&newp->context;
	if (!_setjmp(LwpCurrent->context)) {
		/* create new context */		
		/* flush registers */
		asm ("ta	0x03");
		/* %o0 <- newp */
		asm ("ld	[%fp+0x44], %o0");
		/* %o1 <- newp->context[0] */
		asm ("ld	[%o0], %o1");
		/* create min frame on new stack */
		asm ("save	%o1,-96, %sp");
		if (!_setjmp(*cpp))
			_longjmp(LwpCurrent->context, 1);
		lwpEntryPoint();
	}
}

#elif defined(__USLC__) && defined(i386)

/* USL/Unixware on an Intel 386/486/... processor.
 * Tested on Unixware v1.1.2, based on SYSV R4.2
 */

/* As per normal empire documentation, there is none.
 *
 * But, what we are attempting to do here is set up a longjump
 * context buffer so that the lwpEntryPoint is called when
 * the thread starts.
 *
 * I.E., what a setjmp/longjmp call set would do.
 *
 * How to figure this out?  Well, without the setjmp code, you
 * need to reverse engineer it by printing out the context buffer
 * and the processor registers, and mapping which ones need
 * to be set.
 *
 * Alternatively, you can single instruction step through the longjmp
 * function, and figure out the offsets that it uses.
 *
 * Using offsets in bytes,
 * context + 0x04 [1] -> esi  (general purpose reg)
 * context + 0x08 [2] -> edi  (general purpose reg)
 * context + 0x0C [3] -> ebp  (general purpose or parameter passing)
 * context + 0x10 [4] -> esp  (stack)
 * context + 0x14 [5] -> jump location for return
 */

void lwpInitContext(newp, sp)
	struct lwpProc *newp;
	void	*sp;
{
	newp->context[4] = (int)sp;
	newp->context[5] = (int)lwpEntryPoint;
}

#elif defined UCONTEXT

/*
 * Alternate aproach using setcontext en getcontext in stead of setjmp and
 * longjump. This should work on any SVr4 machine independant of
 * architecture. Unfortunaltely some changes are still nessesary in lwp.c.
 * Tested on IRIX 5.3
 */ 

void lwpInitContext(newp, spp)
      struct lwpProc *newp;
      stack_t *spp;
{
      getcontext (&(newp->context));
      newp->context.uc_stack.ss_sp = spp->ss_sp;
      newp->context.uc_stack.ss_size = spp->ss_size;
      makecontext (&(newp->context), lwpEntryPoint, 0);
}

#elif defined(ALPHA)

#include <c_asm.h>

void lwpInitContext(newp, sp)
	struct lwpProc *newp;
	void	*sp;
{
	extern long *_gp;

	/* register values obtained from setjmp.h */
	_setjmp(newp->context);
	newp->context[2] = (long)lwpEntryPoint;	/* program counter */
	newp->context[30] = (long)lwpEntryPoint; /* return address */
	newp->context[31] = (long)lwpEntryPoint; /* fake program value (!) */
	newp->context[34] = (long)sp;		/* stack pointer */
}

int lwpSave(jb)
	jmp_buf jb;
{
	return _setjmp(jb);
}

void lwpRestore(jb)
	jmp_buf jb;
{
	/* resume, but get the pv from the jmp_buf */
	asm("ldq	%pv, 248(%a0)");
	asm("stq	%a0, 16(%sp)");
	/* generates a warning, but functions just fine */
	asm("bsr	%ra, __longjump_resume");
}

#endif

#endif
