/* ----------------------------------------------------------------------- *
 *   
 *   Copyright 2003 Murali Krishnan Ganapathy
 *
 *   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, Inc., 53 Temple Place Ste 330,
 *   Bostom MA 02111-1307, USA; either version 2 of the License, or
 *   (at your option) any later version; incorporated herein by reference.
 *
 * ----------------------------------------------------------------------- */

/*
 * COM32 image
 *
 * Redirecter: Given command line C ask ISOLINUX to boot
 * into "P C" where P is some prefix to be attached before C.
 *
 */

#include <com32.h>
#define NULL ((void *) 0)
#define BUFSIZE 513

unsigned char *msg; /* Pointer to message */
unsigned char kernel[BUFSIZE]; /* Upper bound for size of redirected image+ arguments */

// Prefix to add
unsigned char PREFIX[] ="remote image=";
// Header to print
unsigned char header[] = "Redirecting to..."; 
// Exit to command prompt
unsigned char quitmsg[] = "No kernel specified. Quitting...";
// Error message
unsigned char error[] = "You should never see this..";
// End of line
unsigned char eoln[] = "\r\n";

// Register sets for Interrupt calls
com32sys_t inreg, outreg;
// Temporary vars
unsigned int ctr,len;

static inline void memset(void *buf, int ch, unsigned int len)
{
  asm volatile("cld; rep; stosb"
	       : "+D" (buf), "+c" (len) : "a" (ch) : "memory");
}

static inline void memcpy(void *dst, const void *src, unsigned int len)
{
  asm volatile("cld; rep; movsb"
	       : "+D" (dst), "+S" (src), "+c" (len) : : "memory");
}

int quit(void)
{
  // Quit back to prompt
  __com32.cs_syscall(0x20,&inreg,NULL);
}

void print(unsigned char* str,unsigned int len)
     //Print Null-terminated string of length <= len
{
  memset(&inreg, 0, sizeof inreg);
  memcpy(__com32.cs_bounce,str,len);
  *((unsigned char *)(__com32.cs_bounce+len+1))=0; //Just in case
  inreg.eax.w[0] = 0x0002;	/* Write string */
  inreg.ebx.w[0] = OFFS(__com32.cs_bounce);
  inreg.es       = SEG(__com32.cs_bounce);
  __com32.cs_syscall(0x22, &inreg, NULL);
}

void execkernel(void)
     //Execute specified kernel or quit if kernel=NULL
{
  if ((kernel == NULL) || (kernel[0] == 0x00)) {
    print(quitmsg,sizeof quitmsg);
    quit();
  }
  print(kernel,BUFSIZE);
  print(eoln,sizeof eoln);
  // Ask ISOLINUX to execute the kernel
  memset(&inreg, 0, sizeof inreg);
  memcpy(__com32.cs_bounce,kernel,BUFSIZE);
  inreg.eax.w[0] = 0x0003;	/* Run Command*/
  inreg.ebx.w[0] = OFFS(__com32.cs_bounce);
  inreg.es       = SEG(__com32.cs_bounce);
  __com32.cs_syscall(0x22, &inreg, NULL);
  // What follows should never get executed.
  print(error,sizeof error);
  quit();
}

int __start(void)
{
  unsigned char *cptr; // real start of command line (skipping spaces)

  print(header,sizeof header);
  ctr = sizeof PREFIX; // Sizeof PREFIX string
  memcpy(kernel,PREFIX,ctr); // Copy PREFIX to buffer
  len = BUFSIZE - ctr; // Upper bound on size of the command line
  cptr = __com32.cs_cmdline;
  while (*cptr == ' ') cptr++; // Advance if we see a space
  memcpy(kernel+ctr-1,__com32.cs_cmdline+1,len); // Attach the command line
  // The +1 since we dont want the initial space in the cmdline
  execkernel(); // Execute the kernel
  return 0;
}

