/*
 *  ReactOS kernel
 *  Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*
 * PROJECT:              ReactOS kernel
 * FILE:                 ntoskrnl/ke/i386/thread.c
 * PURPOSE:              Architecture multitasking functions
 * PROGRAMMER:           David Welch (welch@cwcom.net)
 * REVISION HISTORY:
 *             27/06/98: Created
 */

/* INCLUDES ****************************************************************/

#include <ddk/ntddk.h>
#include <internal/ntoskrnl.h>
#include <internal/ps.h>
#include <internal/i386/segment.h>
#include <internal/i386/mm.h>
#include <internal/ke.h>

#define NDEBUG
#include <internal/debug.h>

/* GLOBALS *******************************************************************/

#define FLAG_NT (1<<14)
#define FLAG_VM (1<<17)
#define FLAG_IF (1<<9)
#define FLAG_IOPL ((1<<12)+(1<<13))

/* FUNCTIONS *****************************************************************/

NTSTATUS 
Ki386ValidateUserContext(PCONTEXT Context)
/*
 * FUNCTION: Validates a processor context
 * ARGUMENTS:
 *        Context = Context to validate
 * RETURNS: Status
 * NOTE: This only validates the context as not violating system security, it
 * doesn't guararantee the thread won't crash at some point
 * NOTE2: This relies on there only being two selectors which can access 
 * system space
 */
{
   if (Context->Eip >= KERNEL_BASE)
     {
	return(STATUS_UNSUCCESSFUL);
     }
   if (Context->SegCs == KERNEL_CS)
     {
	return(STATUS_UNSUCCESSFUL);
     }
   if (Context->SegDs == KERNEL_DS)
     {
	return(STATUS_UNSUCCESSFUL);
     }
   if (Context->SegEs == KERNEL_DS)
     {
	return(STATUS_UNSUCCESSFUL);
     }
   if (Context->SegFs == KERNEL_DS)
     {
	return(STATUS_UNSUCCESSFUL);
     }
   if (Context->SegGs == KERNEL_DS)
     {
	return(STATUS_UNSUCCESSFUL);
     }
   if ((Context->EFlags & FLAG_IOPL) != 0 ||
       (Context->EFlags & FLAG_NT) ||
       (Context->EFlags & FLAG_VM) ||
       (!(Context->EFlags & FLAG_IF)))
     {
        return(STATUS_UNSUCCESSFUL);
     }
   return(STATUS_SUCCESS);
}

NTSTATUS
Ke386InitThreadWithContext(PKTHREAD Thread, PCONTEXT Context)
{
  PULONG KernelStack;
  ULONG InitSize;
  PKTRAP_FRAME TrapFrame;

  /*
   * Setup a stack frame for exit from the task switching routine
   */
  
  InitSize = 5 * sizeof(DWORD) + sizeof(DWORD) + 6 * sizeof(DWORD) + 
    sizeof(FLOATING_SAVE_AREA) + sizeof(KTRAP_FRAME);
  KernelStack = (PULONG)(Thread->KernelStack - InitSize);

  /* Set up the initial frame for the return from the dispatcher. */
  KernelStack[0] = 0;      /* EDI */
  KernelStack[1] = 0;      /* ESI */
  KernelStack[2] = 0;      /* EBX */
  KernelStack[3] = 0;      /* EBP */
  KernelStack[4] = (ULONG)PsBeginThreadWithContextInternal;   /* EIP */

  /* Save the context flags. */
  KernelStack[5] = Context->ContextFlags;

  /* Set up the initial values of the debugging registers. */
  KernelStack[6] = Context->Dr0;
  KernelStack[7] = Context->Dr1;
  KernelStack[8] = Context->Dr2;
  KernelStack[9] = Context->Dr3;
  KernelStack[10] = Context->Dr6;
  KernelStack[11] = Context->Dr7;

  /* Set up the initial floating point state. */
  memcpy((PVOID)&KernelStack[12], (PVOID)&Context->FloatSave,
	 sizeof(FLOATING_SAVE_AREA));

  /* Set up a trap frame from the context. */
  TrapFrame = (PKTRAP_FRAME)
    ((PVOID)KernelStack + 12 * sizeof(DWORD) + sizeof(FLOATING_SAVE_AREA));
  TrapFrame->DebugEbp = (PVOID)Context->Ebp;
  TrapFrame->DebugEip = (PVOID)Context->Eip;
  TrapFrame->DebugArgMark = 0;
  TrapFrame->DebugPointer = 0;
  TrapFrame->TempCs = 0;
  TrapFrame->TempEip = 0;
  TrapFrame->Gs = Context->SegGs;
  TrapFrame->Es = Context->SegEs;
  TrapFrame->Ds = Context->SegDs;
  TrapFrame->Edx = Context->Edx;
  TrapFrame->Ecx = Context->Ecx;
  TrapFrame->Eax = Context->Eax;
  TrapFrame->PreviousMode = UserMode;
  TrapFrame->ExceptionList = (PVOID)0xFFFFFFFF;
  TrapFrame->Fs = TEB_SELECTOR;
  TrapFrame->Edi = Context->Edi;
  TrapFrame->Esi = Context->Esi;
  TrapFrame->Ebx = Context->Ebx;
  TrapFrame->Ebp = Context->Ebp;
  TrapFrame->ErrorCode = 0;
  TrapFrame->Cs = Context->SegCs;
  TrapFrame->Eip = Context->Eip;
  TrapFrame->Eflags = Context->EFlags | FLAG_IF;
  TrapFrame->Eflags &= ~(FLAG_VM | FLAG_NT | FLAG_IOPL);
  TrapFrame->Esp = Context->Esp;
  TrapFrame->Ss = Context->SegSs;
  /* FIXME: Should check for a v86 mode context here. */

  /* Save back the new value of the kernel stack. */
  Thread->KernelStack = (PVOID)KernelStack;

  return(STATUS_SUCCESS);
}

NTSTATUS
Ke386InitThread(PKTHREAD Thread, 
		PKSTART_ROUTINE StartRoutine, 
		PVOID StartContext)
     /*
      * Initialize a thread
      */
{
  PULONG KernelStack;

  /*
   * Setup a stack frame for exit from the task switching routine
   */
  
  KernelStack = (PULONG)(Thread->KernelStack - (8*4));
  KernelStack[0] = 0;      /* EDI */
  KernelStack[1] = 0;      /* ESI */
  KernelStack[2] = 0;      /* EBX */
  KernelStack[3] = 0;      /* EBP */
  KernelStack[4] = (ULONG)PsBeginThread;   /* EIP */
  KernelStack[5] = 0;     /* Return EIP */
  KernelStack[6] = (ULONG)StartRoutine; /* First argument to PsBeginThread */
  KernelStack[7] = (ULONG)StartContext; /* Second argument to PsBeginThread */
  Thread->KernelStack = (VOID*)KernelStack;

  return(STATUS_SUCCESS);
}

/* EOF */
