/*
** boot_atari.c -- This program loads the Atari Linux kernel and launches it.
**
** Copyright 1994 by Bj”rn Brauel
**
** This file is subject to the terms and conditions of the GNU General Public
** License.  See the file README.legal in the main directory of this archive
** for more details.
**
*/

#include <osbind.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <file.h>
#include <types.h>
#include <unistd.h>

#define m68k
#define PORTAR

/* Atari bootstrap include file */
#include "bootstra.h"

/* required Atari linux include files */
#include "a.out.h"
#include "bootinfo.h"

/* temporary stack size */
#define TEMP_STACKSIZE	1024


struct bootinfo bi, *kbi;

long  START_MEM;
long  MEM_SIZE;     

void usage(void)
{
	fprintf (stderr, "Usage:\n"
		 "\tbootstrap [-d] [-k kernel_executable] [-r ramdisk_file]"
		 " [option...]\n");
	exit (EXIT_FAILURE);
}

int main(int argc, char *argv[])
{
    long i,memreq;
    long   old_super_stack;
    int kfd,rfd;
    int debugflag=0;
    char *memptr;
    long offs;
    struct exec kexec;
	char *kernel_name = "vmlinux";
	char *ramdisk_name = NULL;
    void *stack;

  rfd=-1;
	/* print the greet message */
	puts("Atari Linux Bootstrap version 0.1");
	puts("Copyright 1994 by Bj”rn Brauel\n");

	/* machine is Atari */
	bi.machtype = MACH_ATARI;
    old_super_stack = Super( 0L );
    START_MEM = 0x380000L;                              /* For F030 ! -4 MB */
	MEM_SIZE = START_MEM;
    Super((void *) old_super_stack );


    bi.cputype = CPU_68030;
	bi.cputype |= FPU_68881;
    bi.num_memory = 1;
    bi.memory[0].addr=0x80000L;
    bi.memory[0].size=0x2ffffcL;    /* F030 -> 3 MB of user memory */
    
  strcpy(bi.command_line,argv[1]);

	putchar ('\n');
	putchar ('\n');

	/*
	 * Copy command line options into the kernel command line.
	 */
	printf ("Command line is '%s'\n", bi.command_line);

	/* tell us where the kernel will go */
	printf("\nThe kernel will be located at %08lx\n", START_MEM);


	/* open kernel executable and read exec header */
	if ((kfd = open (kernel_name, O_RDONLY)) == -1) {
		fprintf (stderr, "Unable to open kernel file %s\n", kernel_name);
		exit (EXIT_FAILURE);
	}

	if (read (kfd, (void *)&kexec, sizeof(kexec)) != sizeof(kexec)) {
		fprintf (stderr, "Unable to read exec header from %s\n",
			 kernel_name);
		exit (EXIT_FAILURE);
	}

	if (ramdisk_name) {
		if ((rfd = open (ramdisk_name, O_RDONLY)) == -1) {
			fprintf (stderr, "Unable to open ramdisk file %s\n",
				 ramdisk_name);
			exit (EXIT_FAILURE);
		}
		/* record ramdisk size */
		bi.ramdisk_size = (lseek (rfd, 0, SEEK_END)) >> 10;
	} else
		bi.ramdisk_size = 0;
	/* find offset to boot_info structure */
    offs = 	get_nlist (kernel_name, "_boot_info");

	if (offs==0) {
		perror ("get_nlist");
		exit (EXIT_FAILURE);
	} else {
		kbi = (struct bootinfo *)(START_MEM + offs - kexec.a_entry);
	}

	memreq = kexec.a_text + kexec.a_data + (bi.ramdisk_size << 10);
    memptr = (char *)Malloc (memreq);
	if (!memptr) {
		fprintf (stderr, "Unable to allocate memory\n");
		exit (EXIT_FAILURE);
	}

	if (lseek (kfd, N_TXTOFF(kexec), SEEK_SET) == -1) {
		fprintf (stderr, "Failed to seek to text\n");
		Mfree ((void *)memptr);
		exit (EXIT_FAILURE);
	}
	if (read (kfd, memptr, kexec.a_text) != kexec.a_text) {
		fprintf (stderr, "Failed to read text\n");
		Mfree ((void *)memptr);
		exit (EXIT_FAILURE);
	}
	if (lseek (kfd, N_DATOFF(kexec), SEEK_SET) == -1) {
		fprintf (stderr, "Failed to seek to data\n");
		Mfree ((void *)memptr);
		exit (EXIT_FAILURE);
	}
	if (read (kfd, memptr + kexec.a_text, kexec.a_data) != kexec.a_data) {
		fprintf (stderr, "Failed to read data\n");
		Mfree ((void *)memptr);
		exit (EXIT_FAILURE);
	}
	close (kfd);

	if (rfd != -1) {
		if (lseek (rfd, 0, SEEK_SET) == -1) {
			fprintf (stderr, "Failed to seek to beginning of ramdisk file\n");
			Mfree ((void *)memptr);
			exit (EXIT_FAILURE);
		}
		if (read (rfd, memptr + kexec.a_text + kexec.a_data,
			  bi.ramdisk_size << 10) != (bi.ramdisk_size << 10)) {
			fprintf (stderr, "Failed to read ramdisk file\n");
			Mfree ((void *)memptr);
			exit (EXIT_FAILURE);
		}
		close (rfd);
	}

	/* allocate temporary stack */
	stack = (void *)Malloc((long)TEMP_STACKSIZE);
	if (!stack) {
		fprintf (stderr, "Unable to allocate memory for stack\n");
		Mfree ((void *)memptr);
		exit (EXIT_FAILURE);
	}

	if (debugflag) {
		if (bi.ramdisk_size)
			printf ("RAM disk at %#lx, size is %ldK\n",
				(unsigned long)memptr + kexec.a_text + kexec.a_data,
				bi.ramdisk_size);

		printf ("\nKernel text at %#lx, code size %d\n",
			START_MEM + N_TXTADDR(kexec), kexec.a_text);
		printf ("Kernel data at %#lx, data size %d\n",
			START_MEM + N_DATADDR(kexec), kexec.a_data );
		printf ("Kernel bss  at %#lx, bss  size %d\n",
			START_MEM + N_BSSADDR(kexec), kexec.a_bss );

		printf ("\nKernel boot_info is at %#lx physical, %#lx virtual\n",
			(unsigned long)kbi, (unsigned long)kbi - START_MEM + kexec.a_entry);

		printf ("\nKernel entry is %#x\n", kexec.a_entry );

		printf ("ramdisk dest top is %#lx\n", START_MEM + MEM_SIZE);
		printf ("ramdisk lower limit is %#lx\n",
			(unsigned long)(memptr + kexec.a_text + kexec.a_data));
		printf ("ramdisk src top is %#lx\n",
			(unsigned long)(memptr + kexec.a_text + kexec.a_data)
			+ (bi.ramdisk_size << 10));

		printf ("Type a key to continue the Linux boot...");
		fflush (stdout);
		getchar();
	}

	/* wait for things to settle down */
	sleep(2);

    /* Disable Interrupts */
   for(i=0;i<=15;i++)
      Jdisint((int)i);   
 
	/* Go into supervisor state and change stack*/
	Super(0L);
  change_stack(stack); 
	/* turn off caches */
	cache_off();

	/* turn off any mmu translation */
	disable_mmu ();  
	{
		/*
		 * there may be problems here if the compiler optimizer
		 * doesn't put these variables in registers, and the
		 * stack space was allocated at the beginning of the function
		 * and we've changed the stack.
		 */
		unsigned char *csrc, *cdest, *climit;
		unsigned long *src, *dest, *limit;

		/*
		 * copy the kernel text and data to their final resting place.
		 * The text is padded out (in the a.out file) to a multiple of
		 * the page size.
		 */

		src = (unsigned long *)memptr;
		dest = (unsigned long*)START_MEM;
		limit = (unsigned long *)(memptr + kexec.a_text + kexec.a_data);
		while (src < limit)
			*dest++ = *src++;

		/* clear kernel bss */
		dest = (unsigned long *)(START_MEM + kexec.a_text + kexec.a_data);
		limit = dest + kexec.a_bss / sizeof(unsigned long);
		while (dest < limit)
			*dest++ = 0;

		/* copy the ramdisk to the top of memory (from back to front) */
		dest = (unsigned long *)(START_MEM + MEM_SIZE);
		limit = (unsigned long *)(memptr + kexec.a_text + kexec.a_data);
		src = (unsigned long *)((unsigned long)limit + (bi.ramdisk_size << 10));
		while (src > limit)
			*--dest = *--src;
		bi.ramdisk_addr = (unsigned long)dest;

		/* copy the boot_info struct to the correct location */
		cdest = (unsigned char *)kbi;
		climit = cdest + sizeof (bi);
		csrc = (unsigned char *)&bi;
		while (cdest < climit)
			*cdest++ = *csrc++;
	}
	/* jump to start of kernel */
	jump_to (START_MEM);
    return(0);
	/* NOTREACHED */
}
