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


#include"types.h"
#include"lib.h"
#include"except.h"
#include"sp.h"
#include"mp.h"
#include"mm.h"
#include"interrupt.h"
#include"console.h"
#include"proc.h"
#include"floppy.h"
#include"ata.h"
#include"device.h"
#include"serial.h"
#include"time.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ファミリー */


int cpuid();
void start_tasking();
static int enable_a20();
extern int count_cpu_clock();
extern int exist_mem(int*,int);


void main()
{
	uint *pagedir;


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

	/* Init tty */
	init_console();
	init_com();
	switch(TTY)
	{
		case 1:
			write_tty=write_console;
			break;
		case 2:
			write_tty=write_com1;
			break;
		case 3:
			write_tty=write_com2;
			break;
	}

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

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

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

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

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

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

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

	/* ページングの設定 */
	if((pagedir=init_idle_page())==NULL)
	{
		printk("Fail init_idle_page!\n");
		idle();
	}

	/* Idle prcessを生成する */
	if(set_idle_proc(0,pagedir)==-1)
	{
		printk("Fail set_idle_proc! cpu=%d.\n",0);
		idle();
	}

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

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

/**************************************************/
extern void test_hd();
extern void test_fd();
extern void test_com();
extern void test_time();
extern void test_device();
/***************************************************/
void init()
{
	SYSCALL0_FRAME frame={
		USER_DATA_DES,
		USER_DATA_DES,
		USER_BEG,
		USER_CODE_DES,
		USER_ESP_BEG,
		USER_DATA_DES
	};


	/* init processを起動する */
	switch(sys_fork())
    {
    	case -1:
    		printk("Faile sys_fork!\n");
    		idle();

    	case 0:
    		break;
    		/* user stackを割り当てる */
/*			load_user_page((void*)(USER_ESP_BEG-1),PAGE_RW,cputask[0].current_task);
    		if(sys_exec(NULL,NULL,frame)==-1)
    		{
    			printk("Faile sys_exec!\n");
    			idle();
    		}
    		else
    		{
    			asm volatile("
					movl	%0,%%esp
					popl	%%ds
					popl	%%es
					lret
    				"::"g"(&frame)
    			);
    		}
*/
    	default:
    		/* タスキング開始 */
			start_tasking();
	}
/************************************************************************/
	if(sys_fork()==0)idle();
	if(sys_fork()==0)idle();
	if(sys_fork()==0)idle();
	if(sys_fork()==0)idle();
/**************************************************************************/

	/* Init file system */
	init_fs();

	/* Init device system */
	if(init_device()==-1)
	{
		printk("Fail init_device!" );
		idle();
	}

	/* Init time */
	init_time();

	/* Set COM interrupt */
	set_com_intr();

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

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

/*************************************************/
/*	test_hd();*/
/*	test_fd();*/
/*	test_com();*/
	test_time();
/*	test_device();*/
/***************************************************/

	idle();
}


/*
 * アドレスライン20ビット目がBIOSで無効にされているので有効にする
 * return : 0,Error -1
 */
int enable_a20()
{
	enum{
		COMMAND_REG=0x64,	/* Command IO register */
		STAT_REG=0x64,		/* Status IO register */
		OUTPUT_REG=0x60		/* Data IO register */
	};

	volatile int *test1,*test2;
	int i;


	/* Enable A20 */
	while(inb(STAT_REG)&2);
	outb(COMMAND_REG,0xad);		/* Disable keyboard */
	while(inb(STAT_REG)&2);
	outb(COMMAND_REG,0xd1);		/* Write to output */
	while(inb(STAT_REG)&2);
	outb(OUTPUT_REG,0xdf);		/* A20enable */
	while(inb(STAT_REG)&2);
	outb(COMMAND_REG,0xae);		/* Enable keyboard */
	while(inb(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()
 * 返り値 : バージョン情報
 */
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)
	);
} 	
