/*
    MMHal.c, Copyright  (c) by Lukas Ruf, lr@lpr.ch,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public Licence.
 
    A full copy of the GNU GPL is provided in the file COPYING found in the 
    development root of Topsy.
*/
/*
	File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/Memory/ia32/MMHal.c,v $
 	Author(s):             Lukas Ruf, lr@lpr.ch
 	Affiliation:           ETH Zuerich, TIK
 	Version:               $Revision: 3035 $
 	Creation Date:         
 	Last Date of Change:   $Date: 2007-05-30 19:20:44 +0200 (Mit, 30 Mai 2007) $      by: $Author: sryffel $
*/

#include "MMHal.h"
#include "mmHalAsm.h"
#include "SupportAsm.h"
#include "Video.h"
#include "Support.h"

#include "cpu.h"
#include "MemoryLayout.h"

#define MEMPAGEUSEDADDR 0x11000
static volatile unsigned short MemSelector=0;

/* definition of most important selectors, used for Kernel Code and Data as
   for User Code and Data 
   Define all of them as unsigned long even if they are only unsigned short 
   in fact.
 */

unsigned long KCSEL;
unsigned long KDSEL;
unsigned long UCSEL;
unsigned long UDSEL;


unsigned long MaxPhysMem()
{
  return __maxPhysMem();
}

unsigned long MaxMemAvail()
{
  return __memPagesAvail(MemSelector)*4096;
}

void SetMemRangeUsed(unsigned long plow, unsigned long phigh)
{
  unsigned long i;
  plow  = plow >> 12;
  phigh = phigh >> 12;
  for (i = plow; (i < phigh); i++)
    __setPageUsed(MemSelector,i);
  return;
}

unsigned long MemUsed()
{
  return __memPagesUsed(MemSelector)*4096;
}

void MemoryInit()
{
  unsigned long kmem;
  volatile unsigned long lmem  = MaxPhysMem();
  volatile unsigned long ltext = __getTextSize();
  volatile unsigned long ldata = __getDataSize();
  ltext = ((ltext >> 12)+1) << 12;
  ldata = ((ldata >> 12)+1) << 12;
  kmem  = K0SEG_BASE + ltext + ldata;

  /* Resize the Kernel data space to 4GB */
  Set_GDTEntry(GC_OS_DATA_SEL, KDATASEG_BASE, KDATASEG_SIZE >> 12, MM_DATA_B5, MM_DATA_B6);

  MemSelector = Get_New_GDTE();
  Set_GDTEntry(MemSelector, MEMPAGEUSEDADDR, 8, MM_DATA_B5, MM_DATA_B6);
  __emptyPUT(MemSelector);
  printf("Memory Management {Topsy i386, pre3.0}\n");
  printf("Physical memory        : 0x00000000-0x%8x = %7iKB (%3iMB)\n",(unsigned int)lmem,(unsigned int)lmem>>10, (unsigned int)lmem>>20);
  printf("Memory management      : 0x00000000-0x%8x = %7iKB (%3iMB)\n",(unsigned int)MaxMemAvail(),(unsigned int)MaxMemAvail()>>10, (unsigned int)MaxMemAvail()>>20);
  printf("Control structures     : 0x00000000-0x   20000\n");
  SetMemRangeUsed(0x000000, 0x020000);  // IDT, GDT etc.
  printf("Kernel space           : 0x%8x-0x%8x\n", K0SEG_BASE, (unsigned int)kmem);
  SetMemRangeUsed(K0SEG_BASE, kmem);
  printf("Hardware IO            : 0x   A0000-0x  100000\n");
  SetMemRangeUsed(0x0A0000, 0x100000);  // Mem Map Region
  printf("Not physically avail.  : 0x%8x-0x%8x\n",(unsigned int)lmem, MaxMemAvail());
  SetMemRangeUsed((unsigned int)lmem, MaxMemAvail());
  printf("Available memory       : %iKB (%iMB)\n",(unsigned int)MaxMemAvail()>>10, (unsigned int)MaxMemAvail()>>20);
  KDSEL = GC_KDSEL;  /* CONSTANT VALUE from selector.h */
  KCSEL = GC_KCSEL;
  printf("Kernel Selectors       : CODE 0x%2x   DATA 0x%2x\n",(unsigned int)KCSEL,(unsigned int)KDSEL);
  /* Create USER Segments :-) */
  /* SET CORRECT PROTECTION LEVEL !!!! */
  /* Code Segment, Protection Level 3  */
  UCSEL = Get_New_GDTE();
  /* user starts at 1MB and goes up to lmem (=Physically available memory) */
  Set_GDTEntry((unsigned short)UCSEL,KUSEG_BASE,(lmem-KUSEG_BASE) >> 12/*old was: KUSEG_SIZE >> 12*/,
               (MM_CODE_B5 | MM_B5_3), MM_CODE_B6);  /* Code Segment, Protection Level 3 */
  UDSEL = Get_New_GDTE();
  Set_GDTEntry((unsigned short)UDSEL,KUSEG_BASE,(lmem-KUSEG_BASE) >> 12/*old was: KUSEG_SIZE >> 12*/,
               (MM_DATA_B5 | MM_B5_3), MM_DATA_B6);  /* Data Segment, Protection Level 3 */
  printf("User   Selectors       : CODE 0x%2x   DATA 0x%2x\n",(unsigned int)UCSEL,(unsigned int)UDSEL);

  return;
}

/*GDT*************************************************************************/

static unsigned short gNr_Of_GDTE;

void DispGDTRState()
{
  unsigned short llimit;
  unsigned long  lbase;
  __get_GDTR(&llimit,&lbase);
  printf("GDT State: GDTR.limit: 0x%03x // GDTR.base: 0x%x\n",(int)llimit,(int)lbase);
  return;
}

void GDTInit()
{
  gNr_Of_GDTE = 6;
  DispGDTRState();
  return;
}

void Set_GDTEntry(unsigned short pSelector, unsigned long pBase,
                  unsigned long pSize, unsigned char pAccess, unsigned char pType)
{
  __set_GDTEntry(pSelector, pBase, pSize, pAccess, pType);
  return;
}

void Get_GDTE(unsigned short pSelector, GDTEPtr pGDTE)
{
  // printf("%s: calling __get_GDTE() with pSelector=%i, pGDTE=0x%x\n", __FILE__, pSelector, pGDTE);
  __get_GDTE(pSelector, pGDTE);
  return;
}

unsigned short Get_New_GDTE()
{
  // printf("%s: Get_New_GDTE()\n",__FILE__);
  unsigned short  llimit;
  unsigned long   lbase;
  GDTE  lGDTE;
  
  unsigned short  i = 8;                          // leave dummy selector untouched
  __get_GDTR(&llimit,&lbase);
  // printf("%s: called __get_GDTR(), llimit=%i, lbase=0x%x \n",__FILE__, llimit, lbase);
  Get_GDTE(i,&lGDTE);                     // get free GDTE
  // printf("%s: called Get_GDTE(), i=%i\n",__FILE__, i);
  // printf("%s: lGDTE=%02x%02x %02x%02x %02x%02x %02x%02x\n", __FILE__, lGDTE.Byte0, lGDTE.Byte1, lGDTE.Byte2, lGDTE.Byte3, 
  //        lGDTE.Byte4, lGDTE.Byte5, lGDTE.Byte6, lGDTE.Byte7); 
  
  while ((i < llimit) &&
         (lGDTE.Byte0+lGDTE.Byte1+lGDTE.Byte2+lGDTE.Byte3+
          lGDTE.Byte4+lGDTE.Byte5+lGDTE.Byte6+lGDTE.Byte7 != 0))
  {
    i+=8;
    Get_GDTE(i,&lGDTE);
    // printf("%s: called Get_GDTE(), i=%i\n",__FILE__, i);
  }
  
  if ((i >= llimit) &&                  // there is no free GDTE
      (llimit < (0xFFFF-0x1000)))
  {     // but there is still free room in the GDT Segment
    // printf("%s: no free GDTE\n", __FILE__);
    __get_GDTR(&llimit,&lbase);
    // printf("%s: called __get_GDTR(), llimit=%i, lbase=0x%x \n",__FILE__, llimit, lbase);
    llimit+=8;
    // printf("%s: calling __set_GDTR, llimit=%i, lbase=0x%x \n", __FILE__, llimit, lbase);
    __set_GDTR(llimit,lbase);
    gNr_Of_GDTE = llimit >> 3;
  }
  return i;
}


/*IDT*************************************************************************/

/* define IDT_BASE locally here to ommit including Constant.h */
/* WARNING!!! CHECK if this conflicts with PDT !!! */
#define IDT_BASE  0x00010000

static unsigned short gIDTSel;

void Set_IDT_Sel()
{
  gIDTSel = Get_New_GDTE();
  printf("IDT Alias Selector : 0x%x\n",(int)gIDTSel);
  Set_GDTEntry(gIDTSel,IDT_BASE,   1 /*1 Page*/   , MM_DATA_B5, MM_DATA_B6);
  // __reset_TLB();           // make new GDTE acitve, already done in __set_GDTEntry
  return;
}

void Set_Def_IDTEntry(unsigned char pEntry, unsigned long pOffs, unsigned short pType)
{
  __set_IDTEntry(gIDTSel, pEntry << 3, GC_OS_CODE_SEL, pOffs, pType);
  return;
}


void ResetIDTR()
{
  __reset_IDTR();
  return;
}

void DispIDTRState()
{
  unsigned short llimit;
  unsigned long  lbase;
  __get_IDTR(&llimit,&lbase);
  printf("IDT State: IDTR.limit: 0x%x // IDTR.base: 0x%x\n",(int)llimit,(int)lbase);
  return;
}

void IDTInit()
{
  Set_IDT_Sel();
  DispIDTRState();
  return;
}

/*TSS*************************************************************************/

unsigned short    BSP_TSS_Selector; /* TSS Selector of the Boot Strap Porcessor */
unsigned short    BSP_TSS_Alias;

#if 0
unsigned short    U_TSS_Selector;
unsigned short    U_TSS_Alias;
#endif 

/* BSP TSS is required for restoring the stack segment of the
   appropriate privilege level.
 */
void TSSInit()
{
  BSP_TSS_Selector  = Get_New_GDTE(); // Get New Selector for BSP TSS       Selector
  Set_GDTEntry(BSP_TSS_Selector , BSP_TSS_BASE, K_TSS_SIZE, K_TSS_B5  , K_TSS_B6);
  BSP_TSS_Alias     = Get_New_GDTE(); // Get New Selector for BSP TSS Alias Selector
  Set_GDTEntry(BSP_TSS_Alias    , BSP_TSS_BASE, K_TSS_SIZE, MM_DATA_B5, MM_DATA_B6_BYTE);
  __createTSS(BSP_TSS_Alias);
  __activateTSS(BSP_TSS_Selector);
  printf("BSP TSS: 0x%x // TSS Alias: 0x%x\n",(int)BSP_TSS_Selector,(int)BSP_TSS_Alias);
#if 0
  U_TSS_Selector  = Get_New_GDTE(); // Get New Selector for User TSS Selector
  Set_GDTEntry(U_TSS_Selector , U_TSS_BASE, U_TSS_SIZE, U_TSS_B5  , U_TSS_B6);
  U_TSS_Alias     = Get_New_GDTE(); // Get New Selector for User TSS Alias Selector
  Set_GDTEntry(U_TSS_Alias    , U_TSS_BASE, U_TSS_SIZE, MM_DATA_B5, MM_DATA_B6_BYTE);
  __createTSS(U_TSS_Alias);
  printf("User   TSS: 0x%x // TSS Alias: 0x%x\n",(int)U_TSS_Selector,(int)U_TSS_Alias);
#endif
  return;
}



