/*
   Init.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/Startup/ia32/Init.c,v $
	Author(s):             Lukas Ruf, lr@lpr.ch
	Affiliation:           ETH Zuerich, TIK
	Version:               $Revision: 3051 $
	Creation Date:         
	Last Date of Change:   $Date: 2007-08-06 17:28:05 +0200 (Mon, 06 Aug 2007) $      by: $Author: sryffel $
*/

#include "Video.h"

#include "TMClock.h"
#include "Keyboard.h"
#include "Tools.h"
#include "asm.h"
#include "SupportAsm.h"
#include "Support.h"
#include "Exception.h"

#include "MMHal.h"

// #include "Memory.h"
// #include "MMMapping.h"
// #include "MemoryLayout.h"
#include "vm_bootup.h"

#include "CoreLoad.h"
// #include "BinaryFile.h"

#include "Startup.h"

#ifdef APIC
#include "TMHalApic.h"
#endif

/* cpu.h is included in fact only for USER_VMA_BASE and USERBIN_ADDR. */ 
#include "cpu.h"

/*
	 Concatenate: Concatenate chunks of bytes in Protected Mode.
	 Linear Data Segment is needed.
	 Please note, no controls on segment boundaries is performed... 
	 I lack time. :-)
 */
/*
   This concatenate routine implicates that the loaded chunks of byte all start
   with a leading binary file header: 
   user.bin and kernel.bin start with the new platform independent topsyheader, defined above
 */


#define ONETRACK 18*512                            /* 18 sectors by 512 Bytes per Track */
#define TRACKMAX 7                                 /* numbers of tracks per 64kB Segment */
#define BLOCKSIZE 64*1024                          /* 64 KB Segment */
#define BLOCKUSED TRACKMAX * ONETRACK              /* data per segment */
#define BLOCKREMAIN BLOCKSIZE - TRACKMAX*ONETRACK  /* Gap per segment */

#include "headerLayout.h"
/* ia32 topsyheader */
typedef struct topsyheader_s
{
  char          name[16];
  uint64        textstart;
  uint64        textsize;
  uint64        datastart;
  uint64        datasize;
  uint64        bssstart;
  uint64        bsssize;
  uint64        optsegment;
  uint64        optsegmentsize;
  uint64        nop1;         // empty         
  uint64        nop2;         // empty
  uint64        tracks;       // platform extension
} topsyheader_t;

static unsigned long TextSize = 0;           // Size of Code Segment
static unsigned long DataSize = 0;           // Size of Data Segment

extern char* __MARKER, __MARKER_fail;        // defined in start.S 
                                             //  in order to make sure that they are inside the first 64KB 
void __printstr(unsigned long pXYOffs, unsigned char pAttr, unsigned char *pstr); // defined in video.S 

//-LR asm(".section .bincat");

/*
   A Starting address range is leaded by the struct topsyheader defined above
   Every following Address range starts at previous+0x10000. In one 64KB-Block
   there are only TRACKMAX of Bytes stored within.
   The Address range is concatenated to the starting block, i.e. the starting 
   block remains at the location specified.
	 ptarget : location where the chunks are concatenated to.
	 psource : starting location, normaly equals to ptarget, except user chunk
 */
char Concatenate(void *ptarget, void *psource)
{
  long i,j,k, tr;
  char *pt, *ps;
  topsyheader_t* topsyheader; /* new header structure */

  pt = (char*)ptarget;
  ps = (char*)psource; /* avoid getting stupid by castings */
  topsyheader = (topsyheader_t*)(unsigned long)(psource + ORG_MARKER);
  if (str_ncmp((char*)TH_NAME,(char*)topsyheader->name,(int)str_len(TH_NAME)))
  {
    PANIC("Marker not found!");
  }
  
  printf("\n"); /* I am esthete, even in program coding */
  printf(" - Tracks:  %#.8x \n",(unsigned int)topsyheader->tracks);
  printf(" - Code:    %#.8x \n",(unsigned int)topsyheader->textsize);
  printf(" - Data:    %#.8x \n",(unsigned int)topsyheader->datasize);
  tr = topsyheader->tracks;
  j = 0;

#ifndef NETBOOT

  while (tr > 0)
  {
    for (k = 0; (k < TRACKMAX) && (tr > 0); k++)
    {
      for (i = 0; i < ONETRACK; i++)
        pt[j++] = *(ps+i);
      ps += ONETRACK;
      tr--;
    }
    ps+= BLOCKREMAIN;
  }
#else
  k = topsyheader->codesize + topsyheader->datasize;
  for (i=0; i < k ; i++)
  {
    pt[i] = ps[i];
  }
#endif
  return 0;
}

/* At this stage, no "global" memory access is allowed: this function will
 * overwrite all data in the way.  So, it is important that this function is
 * called as the very first function in high-level C.
 * The location of this function must be right after the sasm-section to 
 * guarantee that this code is available.
 */
char KernelConcatenate()
{

  char *pt, *ps;
  long   t,   s, k,i,n;
  topsyheader_t* topsyheader; /* new header structure */
  
  topsyheader = (topsyheader_t*)(unsigned long)(KERNEL_VMA_BASE + ORG_MARKER);
  /* Unfortunately TH_NAME is not in the first 64k anymore.. 
   * if (str_ncmp((char*)TH_NAME,(char*)topsyheader->name,(int)str_len(TH_NAME)))
   * {
   *  PANIC("Marker not found!");
   * }
   */
  
  //-LR   printf("\n"); /* I am esthete, even in program coding */
  //
  //-LR   printf("Kernel:\n");
  //-LR   printf(" - Tracks:  %#.8x \n",(unsigned int)BFD->tracksize);
  //-LR   printf(" - Code:    %#.8x \n",(unsigned int)BFD->codesize);
  //-LR   printf(" - Data:    %#.8x \n",(unsigned int)BFD->datasize);

  //-LR   asm("hlt");

#ifndef NETBOOT /* not needed when booting form the net. Image is in one block */
  /* we skip the first block: this one is already concatenated */
  pt = KERNEL_VMA_BASE; /* Target */
  t = BLOCKUSED;        /* where to start "attaching" further segments */

  ps = KERNEL_VMA_BASE; /* Source */
  s = 0x00;             /* from where to attach: start in 2nd segment */

  n = topsyheader->tracks;   /* No. of tracks in total */
  n = n - 7;                 /* One chunk is already concatenated */

  while (n  > 0)
  {
    ps += BLOCKSIZE;     /* change to next 64Kb Segment */
    s  = 0x00;          /* start at beginning of new segment */
    for (k = 0; (k < TRACKMAX) && (n > 0); k++)
    {
      for (i = 0; (i < ONETRACK); i++)
      {
        pt[t++] = ps[s++];
      }
      n--;
    }
  }
#endif
  return 0;
}


/* 
 * concatenates the User Code
 */
static void UserConcatenate() 
{
  kprintf("Concatenate USER");

  if (!Concatenate((void*)USER_VMA_BASE       ,(void*)USERBIN_ADDR))
    printf(" - found at %#.8x and concatenated to %#.8x\n",
           (unsigned int)USERBIN_ADDR,(unsigned int)USER_VMA_BASE);
  else
    printf(" - not found at %#.8x\n",
           (unsigned int)USERBIN_ADDR);
}

/*
 * concatenate the Driver Code
 */
static void DriverConcatenate()
{
  /* Drivers right after Kernel Code and Data, defined in Tospy/i386/cpu.h */
  /*
     printf("Concatenate DRIVER");
     if (!Concatenate((void*)TextSize+DataSize,(void*)DLOAD_ADDR))
     printf(" - found at %#.8x and concatenated to %#.8x\n",
     (unsigned int)DLOAD_ADDR,(unsigned int)(TextSize+DataSize));
     else
     printf(" - not found at %#.8x\n",
     (unsigned int)DLOAD_ADDR);
   */
}

/* Generate the vm_segmap as defined in VMemory/vm_bootup.h
 */
static void BuildSegMap()
{
#if 0
  Boolean userFound = FALSE;

  SegMapPtr SMD = &SegMapDesc_i386;       /* just for typing ... */

  /* Move User and Driver Code and Data to their appropriate places */

  /* USER */
  /* User Code and Data to the User Space, defined in Tospy/i386/cpu.h
     Concatenate User space in the User Load Address Range, as a funny
     bytecopy() call will move the User chunk to its appropriate location
     specified by UADDR_BASE.
   */ /* SORRY Lukas but this text is definitifly wrong, please rewrite
   --cjeker 20010315 (UADDR_BASE is allready the correct location) */
  kprintf("Concatenate USER");

  if (!Concatenate((void*)UADDR_BASE       ,(void*)ULOAD_ADDR))
  {
    printf(" - found at %#.8x and concatenated to %#.8x\n",
           (unsigned int)ULOAD_ADDR,(unsigned int)UADDR_BASE);
    userFound = TRUE;
  }
  else
    printf(" - not found at %#.8x\n",
           (unsigned int)ULOAD_ADDR);


  /* DRIVER */
  /* Drivers right after Kernel Code and Data, defined in Tospy/i386/cpu.h */
  /*
     printf("Concatenate DRIVER");
     if (!Concatenate((void*)TextSize+DataSize,(void*)DLOAD_ADDR))
     printf(" - found at %#.8x and concatenated to %#.8x\n",
     (unsigned int)DLOAD_ADDR,(unsigned int)(TextSize+DataSize));
     else
     printf(" - not found at %#.8x\n",
     (unsigned int)DLOAD_ADDR);
   */

  /* Create the Segment Descriptor Table dynamically */

  /* KERNEL */
  /* Kernel Code starts at location 64(dec) within the Kernel Code Segment */
  SMD->kernelCodeStart      = (Address)0x00;
  /* Note: TextSize is rounded to page boundary during assignments... :-) */
  SMD->kernelCodeSize       = TextSize;
  /* exactly above the Kernel Text Segment the Kernel Data start. */
  SMD->kernelDataStart      = (Address)TextSize;
  /* Note: DataSize is rounded to page boundary during assignments... :-) */
  //-CJ  SMD->kernelDataSize       = DataSize;
  /* If you modify thes values don't forget to have a look at DisplayContext() in
     IO/ia32/Debug.c and restoreContext() in Threads/ia32/TMHalAsm.S because they
     check the stack boundaries CJ 20010226 */

  SMD->kernelDataSize       = 0x00100000 - K1SEG_SIZE - TextSize;
  /* 1MB - 128kB - TextSize so that
  kernelDataSize + kernelCodeSize = 1MB - 128kB
  */

  /* USER */
  SMD->userCodeStart        = (Address)UADDR_BASE;
  SMD->userCodeSize         = PAGEBOUNDARY(((BinFileDescPtr)ULOAD_ADDR)->codesize);
  SMD->userDataStart        = (Address)((long)SMD->userCodeStart + (long)SMD->userCodeSize);
  SMD->userDataSize         = PAGEBOUNDARY(((BinFileDescPtr)ULOAD_ADDR)->datasize);
  /* User Code starts in the appropriate segment right at location 40. */
  SMD->userJumpAddress      = NULL;
  if (userFound)
    SMD->userJumpAddress    = (Address)UADDR_START;
  SMD->userLoadedInKernelAt = (Address)ULOAD_ADDR;

  /* LR 000604 */
  //-LR    asm("hlt");
#endif
}


static void ShowTopsyia32()
{
  char *lTopsy ="Topsy ia32 pre3.0, <topsy386@topsy.net>";
  Printstr(24,(40-str_len(lTopsy)/2),0x1F,lTopsy);
  return;
}

//-LR extern void* DummyBuffer;

int main()
{
  KernelConcatenate();
  TextSize = PAGEBOUNDARY(__getTextSize());
  DataSize = PAGEBOUNDARY(__getDataSize());
  VideoInit();
  ClearScreen();
  ShowTopsyia32();			         /* I would like to show my name :-) */
  /* GDTInit() must come first    */
  GDTInit();                     /* Global Descriptor Table Initialization */
  // printf("%s: GDTInit finished\n", __FILE__);
  /* IDTInit() must come second   */
  IDTInit();                     /* Interrupt Descriptor Table Initialization */
  TSSInit();                     /* Boot Strap Processor Task State Selector */
  printf("TextSize = %i\n", (int)TextSize);
  printf("DataSize = %i\n", (int)DataSize);
  //-LR   printf("@Dummy   = %#.8x\n", (int)DummyBuffer);
  ExceptionInit();               /* Processor Exception Handling */
  //MemoryInit();                  /* Low Level Memory Management */
  InterruptInit();               /* Peripheral Interrupt Handling */
  //-LR 20000612 -- probably set Timer Handling to early...
#ifdef APIC
  ApicInit();
  IOApicInit();
#endif
  TimerInit();                   /* Enable Working Interrupts 0x00 Timer 0 */
 
  UserConcatenate();             /* Concatenate User Code */
  DriverConcatenate();           /* Concatenate Driver Code */
  // BuildSegMap();              /* Create this SegMap Table, now part of vm_init() */

  ClearScreen();
  topsyMain();
  return 0;
}

