/* Machine or compiler-dependent portions of kernel
 * Turbo-C version for PC
 * Copyright 1991 Phil Karn, KA9Q
 */
#ifdef MSDOS
#include <dos.h>
#endif
#include "global.h"
#include "proc.h"
#ifndef UNIX
#include "pc.h"
#endif
#include "commands.h"
#include "socket.h"
  
#ifndef __BYTE_ORDER
#include <endian.h>
#endif

#ifdef MSDOS
char *Taskers[] = {
    "",
    "DoubleDos",
    "DesqView",
    "Windows",
    "DOS 5/6/7",
    "OS/2 DOS",
    "DPMI"
};
  
static oldNull;
  
/* Template for contents of jmp_buf in Turbo C */
struct env {
    unsigned        sp;
    unsigned        ss;
    unsigned        flag;
    unsigned        cs;
    unsigned        ip;
    unsigned        bp;
    unsigned        di;
    unsigned        es;
    unsigned        si;
    unsigned        ds;
};
#endif
  
#ifdef UNIX
/*
 * There are several different ways to implement jmp_buf's.  We use macros to
 * extract the fields.  Note that these must expand into lvalues, because we
 * load them during process initialization.  Current code only uses _SP and
 * _PC; this may change.
 *
 * I should fold the DOS code into this, but then I need to worry about
 * segments.  No thanks.
 */
  
#ifdef M_UNIX
/*
 * I am unsure of these; the i386 contents are not documented in 3.2.2.  I
 * guessed at them by comparing "info regs" with the contents of a jmp_buf
 * immediately after a setjmp().
 */
#define _PC(p) (p->env[5])
#define _SP(p) (p->env[4])
#define _BP(p) (p->env[3])
#endif
  
#ifdef linux
#if __GNU_LIBRARY__  >  1
/* GLIBC v6 */
#define _PC(p) (p->env->__jmpbuf[JB_PC])
#define _SP(p) (p->env->__jmpbuf[JB_SP])
#define _BP(p) (p->env->__jmpbuf[JB_BP])
#else
#define _PC(p) (p->env->__pc)
#define _SP(p) (p->env->__sp)
#define _BP(p) (p->env->__bp)
#endif
#endif

#ifdef sun
/*
 * In SVR4 we use the ucontext stuff, which is designed for this.  (Literally.
 * "man -s 5 ucontext" for the details.)  The jmp_buf stuff is impossible to
 * decipher on a SPARC, so we use what we're given.
 *
 * THESE FIELDS ARE READ-ONLY!  If you try to change them, havoc will ensue.
 */
#define _PC(p) (p->env.uc_mcontext.gregs[REG_PC])
#define _SP(p) (p->env.uc_mcontext.gregs[REG_SP])
#endif
 
#endif
  
#ifndef UNIX
static int chkintstk __ARGS((void));
#endif
  
void
kinit()
{
#ifndef UNIX
    int i;
  
    /* Initialize interrupt stack for high-water-mark checking */
    for(i=0;i<512;i++)
        Intstk[i] = STACKPAT;
  
    /* Remember location 0 pattern to detect null pointer derefs */
    oldNull = *(unsigned short *)NULL;
#endif
}
#if defined(PS_TRACEBACK) && defined(__TURBOC__)
/* display process' call history, giving absaddr/segno_for_lookup_in_nos.map */
void traceback(struct env *ep);
void traceback(ep)
struct env *ep;
{
    unsigned short far *context;
    void (*pc)();
    int maxdepth=25;

    context = MK_FP(ep->ss, ep->bp);  /* point to stackframe base of caller to pwait() */
    do {
        pc = MK_FP(context[2], context[1]);  /* large-model return address */
        tprintf("%8.8lx/%04x\n",ptol(pc),FP_SEG(pc)-_psp-0x10);
        context = MK_FP(ep->ss, context[0]);  /* point to previous stackframe base */
    } while (FP_OFF(context) && pc != MK_FP(0x55aa,0x55aa) && --maxdepth);  /* no more when at SP:0 */
}
#endif /* PS_TRACEBACK */

/* Print process table info
 * Since things can change while ps is running, the ready proceses are
 * displayed last. This is because an interrupt can make a process ready,
 * but a ready process won't spontaneously become unready. Therefore a
 * process that changes during ps may show up twice, but this is better
 * than not having it showing up at all.
 */
int
ps(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    register struct proc *pp;
#ifndef UNIX
    register struct env *ep;
#endif
    int i;
  
#ifdef UNIX
    tprintf("Uptime %s",tformat(secclock()));
#else
    tprintf("Uptime %s Stack %x max intstk %u psp %04x",tformat(secclock()),
    getss(),chkintstk(),_psp);
    if(Mtasker != 0){
        tprintf("\nRunning under %s",Taskers[Mtasker]);
    }
#endif
    tputs("\n");
  
#ifdef UNIX
    tputs("PID      SP       maxstk   stksize  event    fl  in  out  name\n");
  
    for (pp = Susptab; pp != NULLPROC; pp = pp->next)
    {
        if (tprintf("%8.8lx %8.8lx %-8x %-8x %8.8lx %c%c%c %3d %3d  %s\n",
            FP_SEG(pp), FP_SEG(_SP(pp)), pp->stksize, stkutil(pp), FP_SEG(pp->event),
            pp->i_state ? 'I' : ' ',
            (pp->state & WAITING) ? 'W' : ' ',
            (pp->state & SUSPEND) ? 'S' : ' ',
            pp->input, pp->output, pp->name) == EOF)
            return 0;
    }
    for (i = 0; i < PHASH; i++)
    {
        for (pp = Waittab[i]; pp != NULLPROC; pp = pp->next)
        {
            if (tprintf("%8.8lx %8.8lx %-8x %-8x %8.8lx %c%c%c %3d %3d  %s\n",
                FP_SEG(pp), FP_SEG(_SP(pp)), pp->stksize, stkutil(pp),
                FP_SEG(pp->event),
                pp->i_state ? 'I' : ' ',
                (pp->state & WAITING) ? 'W' : ' ',
                (pp->state & SUSPEND) ? 'S' : ' ',
                pp->input, pp->output, pp->name) == EOF)
                return 0;
        }
    }
    for (pp = Rdytab; pp != NULLPROC; pp = pp->next)
    {
        if (tprintf("%8.8lx %8.8lx %-8x %-8x %8.8lx %c%c%c %3d %3d  %s\n",
            FP_SEG(pp), FP_SEG(_SP(pp)), pp->stksize, stkutil(pp),
            FP_SEG(pp->event),
            pp->i_state ? 'I' : ' ',
            (pp->state & WAITING) ? 'W' : ' ',
            (pp->state & SUSPEND) ? 'S' : ' ',
            pp->input, pp->output, pp->name) == EOF)
            return 0;
    }
    if( Curproc != NULLPROC)
    {
        if (tprintf("%8.8lx %8.8lx %-8x %-8x %8.8lx %c%c%c %3d %3d  %s\n",
            FP_SEG(Curproc), FP_SEG(_SP(Curproc)), Curproc->stksize,
            stkutil(Curproc), FP_SEG(Curproc->event),
            Curproc->i_state ? 'I' : ' ',
            (Curproc->state & WAITING) ? 'W' : ' ',
            (Curproc->state & SUSPEND) ? 'S' : ' ',
            Curproc->input, Curproc->output,
            Curproc->name) == EOF)
            return 0;
    }
  
#else /* UNIX */
  
    tputs("PID  SP        maxstk    stksize   event     fl  in  out  name\n");
  
    for(pp = Susptab;pp != NULLPROC;pp = pp->next){
        ep = (struct env *)&pp->env;
        if(tprintf("%4.4x %-10lx%-10u%-10u%-10lx%c%c%c %3d %3d  %s\n",
            FP_SEG(pp),
            ptol(MK_FP(ep->ss,ep->sp)),
            pp->stksize,
            stkutil(pp),
            ptol(pp->event),
            pp->i_state ? 'I' : ' ',
            (pp->state & WAITING) ? 'W' : ' ',
            (pp->state & SUSPEND) ? 'S' : ' ',
            pp->input, pp->output,
            pp->name) == EOF)
            return 0;
#if defined(PS_TRACEBACK) && defined(__TURBOC__)
        if(argc > 1 && htoi(argv[1])==FP_SEG(pp))
            traceback(ep);
#endif
    }
    for(i=0;i<PHASH;i++){
        for(pp = Waittab[i];pp != NULLPROC;pp = pp->next){
            ep = (struct env *)&pp->env;
            if(tprintf("%4.4x %-10lx%-10u%-10u%-10lx%c%c%c %2d %2d  %s\n",
                FP_SEG(pp),ptol(MK_FP(ep->ss,ep->sp)),pp->stksize,stkutil(pp),
                ptol(pp->event),
                pp->i_state ? 'I' : ' ',
                (pp->state & WAITING) ? 'W' : ' ',
                (pp->state & SUSPEND) ? 'S' : ' ',
                pp->input,pp->output,
                pp->name) == EOF)
                return 0;
#if defined(PS_TRACEBACK) && defined(__TURBOC__)
            if(argc > 1 && htoi(argv[1])==FP_SEG(pp))
                traceback(ep);
#endif
        }
    }
    for(pp = Rdytab;pp != NULLPROC;pp = pp->next){
        ep = (struct env *)&pp->env;
        if(tprintf("%4.4x %-10lx%-10u%-10u          %c%c%c %2d %2d  %s\n",
            FP_SEG(pp),ptol(MK_FP(ep->ss,ep->sp)),pp->stksize,stkutil(pp),
            pp->i_state ? 'I' : ' ',
            (pp->state & WAITING) ? 'W' : ' ',
            (pp->state & SUSPEND) ? 'S' : ' ',
            pp->input,pp->output,
            pp->name) == EOF)
            return 0;
#if defined(PS_TRACEBACK) && defined(__TURBOC__)
        if(argc > 1 && htoi(argv[1])==FP_SEG(pp))
            traceback(ep);
#endif
    }
    if(Curproc != NULLPROC){
        ep = (struct env *)&Curproc->env;
        tprintf("%4.4x %-10lx%-10u%-10u          %c   %2d %2d  %s\n",
        FP_SEG(Curproc),ptol(MK_FP(ep->ss,ep->sp)),Curproc->stksize,
        stkutil(Curproc),
        Curproc->i_state ? 'I' : ' ',
        Curproc->input,Curproc->output,
        Curproc->name);
    }
  
#endif /* UNIX */
  
    return 0;
}
unsigned int
stkutil(pp)
struct proc *pp;
{
    unsigned i;
    register int16 *sp;
  
    i = pp->stksize;
#ifdef UNIX
    if (FP_SEG(pp->stack) == MAINSTKBASE)
        return i; /* can't check system stack, dynamic */
#endif /* UNIX */
    for(sp = pp->stack;*sp == STACKPAT && sp < pp->stack + pp->stksize;sp++)
        i--;
    return i;
}
/* Return number of used words in interrupt stack. Note hardwired value
 * for stack size; this is also found in the various .asm files
 */
#ifndef UNIX
static int
chkintstk()
{
    register int i;
    register int16 *cp;
  
    for(i=512,cp = Intstk; i != 0 && *cp == STACKPAT; cp++)
        i--;
    return i;
}
#endif
  
#ifdef CHKSTK
/* Verify that stack pointer for current process is within legal limits;
 * also check that no one has dereferenced a null pointer
 */
#ifdef UNIX
static void
__chkstk_internal(spp)
int16 spp;
{
    int16 *sp, *sbase, *stop;
  
    sp = &spp;      /* close enough for government work */
    sbase = Curproc->stack;
    if(sbase == NULL || sbase == MAINSTKBASE)
        return; /* Main task -- too hard to check */
    stop = sbase + Curproc->stksize;
    if(sp < sbase || sp >= stop){
        printf("Stack violation, process %s\n",Curproc->name);
        printf("SP = %lx, legal stack range [%lx,%lx)\n",
        ptol(sp),ptol(sbase),ptol(stop));
        fflush(stdout);
        rflush();
        killself();
    }
}
#endif
  
void
chkstk()
{
#ifdef UNIX
    __chkstk_internal(0);   /* must have an argument to take address of */
#else
    int16 *sbase;
    int16 *stop;
    int16 *sp;
#ifdef MULTITASK
    extern int Nokeys;              /* indicates we are shelled out [pc.c]*/
#endif
  
    sp = MK_FP(_SS,_SP);
    if(_SS == _DS){
        /* Probably in interrupt context */
        return;
    }
    sbase = Curproc->stack;
    if(sbase == NULL)
        return; /* Main task -- too hard to check */
  
    stop = sbase + Curproc->stksize;
    if(sp < sbase || sp >= stop){
        printf("Stack violation, process %s\n",Curproc->name);
        printf("SP = %lx, legal stack range [%lx,%lx)\n",
        ptol(sp),ptol(sbase),ptol(stop));
        fflush(stdout);
        killself();
    }
    if(*(unsigned short *)NULL != oldNull){
#ifdef MULTITASK
        if(!Nokeys)     /* don't complain if we are shelled out */
#endif
            printf("WARNING: Location 0 smashed, process %s\n",Curproc->name);
        *(unsigned short *)NULL = oldNull;
        fflush(stdout);
    }
#endif /* UNIX */
}
#endif /* CHKSTK */

#ifdef sun

/*
 * Run the specified function, then autodarwinate.
 * ucontext actually does the "right" thing without this, except that it
 * won't garbage-collect JNOS's process table....
 */

static void
_kicker(func, iarg, parg1, parg2)
void (*func) __ARGS((int, void *, void *));
int iarg;
void *parg1;
void *parg2;
{
    (*func)(iarg, parg1, parg2);
    killself();
}

#endif /* sun */

/* Machine-dependent initialization of a task */
void
psetup(pp,iarg,parg1,parg2,pc)
struct proc *pp;        /* Pointer to task structure */
int iarg;               /* Generic integer arg */
void *parg1;            /* Generic pointer arg #1 */
void *parg2;            /* Generic pointer arg #2 */
void (*pc) __ARGS((int,void *,void *));           /* Initial execution address */
{
#ifdef sun
    /*
     * The ucontext stuff does just about everything we need except call
     * killself().  Since we'd kind of like the JNOS process table to get
     * cleaned up, we simulate this by means of a wrapper function.
     */
    getcontext(&pp->env);
    makecontext(&pp->env, _kicker, 5, pc, iarg, parg1, parg2);
#else
    register int16 *stktop;
#ifndef UNIX
    register struct env *ep;
#endif
  
    /* Set up stack to make it appear as if the user's function was called
     * by killself() with the specified arguments. When the user returns,
     * killself() automatically cleans up.
     *
     * First, push args on stack in reverse order, simulating what C
     * does just before it calls a function.
     */
    stktop = (int16 *)(pp->stack + pp->stksize);

#ifdef UNIX
#if __BYTE_ORDER == __LITTLE_ENDIAN
    *--stktop = (((unsigned long) parg2 & 0xFFFF0000) >> 16) & 0x0000FFFF;
    *--stktop = ((unsigned long) parg2 & 0x0000FFFF);
    *--stktop = (((unsigned long) parg1 & 0xFFFF0000) >> 16) & 0x0000FFFF;
    *--stktop = ((unsigned long) parg1 & 0x0000FFFF);
    *--stktop = (((unsigned long) iarg & 0xFFFF0000) >> 16) & 0x0000FFFF;
    *--stktop = ((unsigned long) iarg & 0x0000FFFF);
    *--stktop = (((unsigned long) killself & 0xFFFF0000) >> 16) & 0x0000FFFF;
    *--stktop = ((unsigned long) killself & 0x0000FFFF);
#else
    *--stktop = ((unsigned long) parg2 & 0x0000FFFF);
    *--stktop = (((unsigned long) parg2 & 0xFFFF0000) >> 16) & 0x0000FFFF;
    *--stktop = ((unsigned long) parg1 & 0x0000FFFF);
    *--stktop = (((unsigned long) parg1 & 0xFFFF0000) >> 16) & 0x0000FFFF;
    *--stktop = ((unsigned long) iarg & 0x0000FFFF);
    *--stktop = (((unsigned long) iarg & 0xFFFF0000) >> 16) & 0x0000FFFF;
    *--stktop = ((unsigned long) killself & 0x0000FFFF);
    *--stktop = (((unsigned long) killself & 0xFFFF0000) >> 16) & 0x0000FFFF;
#endif
#else /* UNIX */
  
#ifdef  LARGEDATA
    *--stktop = FP_SEG(parg2);
#endif
    *--stktop = FP_OFF(parg2);
#ifdef  LARGEDATA
    *--stktop = FP_SEG(parg1);
#endif
    *--stktop = FP_OFF(parg1);
    *--stktop = iarg;
  
    /* Now push the entry address of killself(), simulating the call to
     * the user function.
     */
#ifdef  LARGECODE
    *--stktop = FP_SEG(killself);
#endif
    *--stktop = FP_OFF(killself);
#endif
  
    /* Set up task environment. Note that for Turbo-C, the setjmp
     * sets the interrupt enable flag in the environment so that
     * interrupts will be enabled when the task runs for the first time.
     * Note that this requires newproc() to be called with interrupts
     * enabled!
     */
    setjmp(pp->env);
#ifdef UNIX
    _SP(pp) = stktop;
    _BP(pp) = stktop;
    _PC(pp) = pc;
#else
    ep = (struct env *)&pp->env;
    ep->ss = FP_SEG(stktop);
    ep->sp = FP_OFF(stktop);
    ep->cs = FP_SEG(pc);    /* Doesn't hurt in small model */
    ep->ip = FP_OFF(pc);
#endif
#endif /* sun */
    /* Task initially runs with interrupts on */
    pp->i_state = 1;
}
unsigned
phash(event)
volatile void *event;
{
    register unsigned x;
  
    /* Fold the two halves of the pointer */
#ifdef UNIX
    x = (unsigned) event;
#else
    x = FP_SEG(event) ^ FP_OFF(event);
#endif
  
    /* If PHASH is a power of two, this will simply mask off the
     * higher order bits
     */
    return x % PHASH;
}
