/*
 * setup2.c
 *
 * Copyright 2002, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 *
 * メイン初期化プログラム
 */


#include<types.h>
#include<sys/param.h>
#include<sys/module.h>
#include<lib.h>
#include<apm.h>
#include<except_i386.h>
#include<sp.h>
#include<mp.h>
#include<mm.h>
#include<interrupt.h>
#include<fs.h>
#include<console.h>
#include<proc.h>
#include<floppy.h>
#include<ata.h>
#include<device.h>
#include<serial.h>
#include<time.h>
#include<ext2_fs.h>
#include<miscdev.h>
#include<net/ether.h>
#include<net/net.h>
#include<errno.h>
#include<lock.h>
#include<share/utsname.h>
#include<term.h>
#include<test.h>


enum{
	CPU_FAMILY_OTHER=0,			/* その他のcpuファミリー */
	CPU_FAMILY_386=3,			/* 386互換 */
	CPU_FAMILY_486=4,			/* 486互換 */
	CPU_FAMILY_PENTIUM=5,		/* pentium互換 */
	CPU_FAMILY_PENTIUMPRO=6,	/* pentiumpro互換 */
	CPU_FAMILY_PENTIUM4=0xf		/* pentium4互換 */
};


int cpu_family;		/* cpuファミリー */


/*
 * アドレスライン20ビット目がBIOSで無効にされているので有効にする。
 * return : 0,Error -1
 */
static int enable_a20()
{
	volatile int *test1,*test2;
	int i;


	/* Enable A20. */
	while(inb(KEYBOARD_STAT_REG)&2);
	outb(KEYBOARD_COMMAND_REG,0xad);	/* Disable keyboard. */
	while(inb(KEYBOARD_STAT_REG)&2);
	outb(KEYBOARD_COMMAND_REG,0xd1);	/* Write to output. */
	while(inb(KEYBOARD_STAT_REG)&2);
	outb(KEYBOARD_OUTPUT_REG,0xdf);		/* A20 enable. */
	while(inb(KEYBOARD_STAT_REG)&2);
	outb(KEYBOARD_COMMAND_REG,0xae);	/* Enable keyboard. */
	while(inb(KEYBOARD_STAT_REG)&2);

	/* A20が有効になったかチェック。 */
	test1=(int*)0x00007c;
	test2=(int*)0x10007c;
	for(i=1;i<=2;++i)
	{
		*test1=i;
		if(*test2!=i)return 0;
	}

	return -1;
}


/*
 * int cpuid()
 * return : バージョン情報
 */
int cpuid();
asm(
"cpuid:"
	"movl	$1,%eax\n"
	"cpuid\n"
	"ret"
);


/*
 * タスキングを開始する。
 * espを調整してアイドルに入る。
 */
void start_tasking()
{
	asm volatile(
		"movl	%0,%%esp\n"
		"sti\n"
		"jmp	idle"
		::"i"(KERNEL_ESP_BEG)
	);
}


/*************************************************************************************************
 *
 * カーネルの初期化
 *
 *************************************************************************************************/


void main()
{
	/* 例外ハンドラを設定する。 */
	init_except();

	/* Init console. */
	initConsole();
	write_tty=writeConsole;

	/* TSSディスクリプタをロードする。 */
	set_tss(TSS_DES);

	/* Init cputask struct. */
	init_cputask(0);

	/* cpuファミリーのチェック。 */
	cpu_family=(cpuid()>>8)&0xf;
	if(cpu_family<CPU_FAMILY_PENTIUM)
	{
		PRINT_ERR("This cpu is not supported!");
		idle();
	}

	/* アドレスライン20ビット目を有効にする。 */
	if(enable_a20()==-1)
	{
		PRINT_ERR("A20 can't be enable. Boot stop!");
		idle();
	}

	/* Apmの初期化。 */
	InitApm();

	/* fpuの初期化。 */
	asm("fninit");

	/* メモリーシステムの初期化。 */
	initMm();

	/* cpuクロック値の測定。 */
	printk("cpu0 clock = %d HZ\n",count_cpu_clock());

	// シグナルの初期化
	initSignal();

	/* アイドルプロセスを生成する。 */
	if (set_idle_proc(0) < 0)
	{
		PRINT_ERR("set_idle_proc error!");
		idle();
	}

	/* 割り込みとSMPを初期化する。 */
	if (MFPS_addres)
	{
		if(init_mp()==-1)idle();
	}
	else init_sp();

	/* Init system call. */
	init_syscall();
}


/* 最初のユーザープロセス実行用定義。 */
extern int startExec(const char*,void*,void*);


void init()
{
	int rest;

	/* 割り込み開始。 */
	sti();

	/* 最初のプロセス process0 を生成する。 */
	rest = sys_fork();
	if (rest < 0){
		PRINT_ERR("Process0 fork() error!");
		idle();
	}
	else if (0 < rest){
		/* アイドルプロセスをスリープさせる。 */
		start_tasking();
	}

	/* Init time. */
	init_time();

	/* Init file system. */
	init_fs();

	/* デバイスファイルシステムの初期化 */
	initDeviceFs();

	/* init serial. */
	init_com();

	/* ブロックキャッシュの初期化 */
	initBlockCache();

	/* デバイスファイルシステムのマウント */
	if (mountDeviceFs() < 0)
	{
		PRINT_ERR("mountDeviceFs() error!");
		idle();
	}

	/* Init PCI. */
	initPci();

	/* Set COM interrupt. */
	set_com_intr();

	/* Init floppy drive. */
	printk("Init FLOPPY drive\n");
	init_fd();

	/* Init ATA drive. */
	printk("Init ATA drive\n");
	initAta();

	/* コンソールのデバイス登録。 */
	if((rest=registConsole())<0)
	{
		PRINT_ERR("registConsole error!");
		idle();
	}

	/* NULLデバイスの登録。 */
	if((rest=registNullDev())<0)
	{
		PRINT_ERR("registNullDev error!");
		idle();
	}

	/* EXT2ファイルシステムの初期化。 */
	if((rest=initExt2tFs())<0)
	{
		PRINT_ERR("initExt2tFs error!");
		idle();
	}

	/* ページング用記憶領域の初期化 */
	if ((rest = initBackupDev(PAGING_BACKUP_DEVICE)) < 0)
	{
		PRINT_ERR("initBackupDev error!");
		idle();
	}
	printk("Page backup device : %s\n",PAGING_BACKUP_DEVICE);

	/* mount root filesystem. */
	if((rest=mountRoot(ROOT_DEVICE,"ext2")) < 0)
	{
		PRINT_ERR("mountRoot error!");
		idle();
	}
	printk("Root device : %s\nRoot file system : ext2\n",ROOT_DEVICE);

	/* Init keyboard driver. */
	initKeyboard();

	/* ネットワークの初期化  */
	initNet();

	/* イーサネットドライバーの初期化 */
	if((rest=initEther())<0)
	{
		PRINT_ERR("initEther error!");
		idle();
	}

//************************************************************************************
	// テストドライバーの組み込み
	{
		extern int echoInitDevice(struct module *, int);
		rest = echoInitDevice(NULL, MOD_LOAD);
		if (rest < 0){
			printk("Failed initialize echo driver!\n");
		}
	}
//************************************************************************************
	
	/* Init process0. */
	if ((rest = initProc1())<0)
	{
		PRINT_ERR("initProc0 error!");
		idle();
	}

	/* 最初のユーザープロセスを起動する。 */
	rest = sys_fork();
   	if (rest < 0)
   	{
   		PRINT_ERR("Fork of first user process error!");
   		idle();
   	}
   	else if (rest == 0)
   	{
   		/* ユーザープログラムを実行する。 */
// 		rest = startExec("/shell",NULL,NULL);
   		rest = startExec("/sbin/init",NULL,NULL);
		if (rest < 0)
		{
			PRINT_ERR("startExec error!");
			idle();
		}
	}

	/* 子プロセスexit待ち。 */
	waitProc1();
}


/*************************************************************************************************
 *
 * システムコール
 *
 *************************************************************************************************/


static struct SYSUTS{
	char *sysname;
	char *nodename;
	char *release;
	char *version;
}sysuts={UTSNAME_SYSNAME,UTSNAME_NODENAME,UTSNAME_RELEASE,UTSNAME_VERSION};


int sys_uname(struct utsname *buf)
{
	if (checkMem(buf,sizeof(struct utsname)) == -1)
		return -EFAULT;

	memcpy(buf->sysname,sysuts.sysname,strlen(sysuts.sysname)+1);
	memcpy(buf->nodename,sysuts.nodename,strlen(sysuts.nodename)+1);
	memcpy(buf->release,sysuts.release,strlen(sysuts.release)+1);
	memcpy(buf->version,sysuts.version,strlen(sysuts.version)+1);
	memcpy(buf->machine,"UNDEFINE",strlen("UNDEFINE")+1);

	return 0;
}

/****************************************************************************************************/
void test_setup()
{
	printk("ok\n");
}
