/*
 * interrupt1.S
 *
 * Copyright 2006, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 *
 * 割り込みアセンブラルーチン
 */


#define ASM_FILE


#include <config.h>
#include <interrupt.h>
#include <segment.h>
#include <mm.h>


/****************************************************************************
 *
 * マクロ
 *
 ****************************************************************************/

// コンテキストセーブの変更は、proc.cのset_context()に影響する。
.macro SAVE
	pushal
	pushl	%es
	pushl	%ds
/*	pushl	%fs
	pushl	%gs*/
	movl	$KERNEL_DATA_DES,%eax
	movl	%eax,%es
	movl	%eax,%ds
	cld
.endm

.macro SAVE_TRAP
	pushl	$0
	SAVE
.endm

.macro RESTORE
/*	popl	%gs
	popl	%fs*/
	popl	%ds
	popl	%es
	popal
	addl	$4,%esp
	iret
.endm

#define SYSCALL_PARAM 20	// システムコールパラメータ位置

// 《注意》変更したら"SYSCALL_PARAM"と"SYSCALL*_FRAME"の変更も必要
.macro SAVE_SYSCALL
	pushl	%ds
	pushl	%es
	movl	$KERNEL_DATA_DES,%edx
	movl	%edx,%ds
	movl	%edx,%es
	cld
/*****************************/
	pushl	%eax
	call	incrementNest
	call	calcCpuTimeInSys
	popl	%eax
/*****************************/
#ifdef DEBUG
	pushl	%eax
	pushl	syscall_table(,%eax,4)
	call	saveSyscallEntry
	popl	%eax
	popl	%eax
#endif
.endm

.macro RESTORE_SYSCALL
/******************************/
	pushl	%eax
	call	calcCpuTimeOutSyscall
	call	decrementNest
	call    doSignal
	popl	%eax
/******************************/
	popl	%es
	popl	%ds
.endm

.macro SWITCH
	pushl	$0				// ネスト割り込み時の前の割り込み時のesp値を保存するところなのでとりあえず０を入れる。
	pushl	%esp			// "switch_task"から戻ると次のタスクのesp値が入ったアドレスが設定される。
	cli						// アドレス空間が変わるまで割り込みを禁止する。
	call	switch_task
	popl	%edx			// 次のタスクのesp値が入ったアドレス。
	movl	(%edx),%esp
	movl	%eax,%cr3		// 次のタスクの仮想メモリー空間にに移行する。
	sti
	popl	%eax
	movl	%eax,(%edx)		// ネスト割り込み時の前の割り込みesp値を入れ代わったタスクのesp値保存領域に保存する。
.endm

.macro EXCEPT handler
	SAVE
/*****************************/
	call	incrementNest
	call	calcCpuTimeInSys
/*****************************/
	call	\handler
/******************************/
	call	decrementNest
/*******************************/
	call	doSignal
	call	calcCpuTimeOutIntr
	RESTORE
.endm

.macro TRAP irq, handler
	SAVE_TRAP
/*****************************/
	call	incrementNest
	call	calcCpuTimeInSys
/*****************************/
	call	*irq_entry+\handler*4
	pushl	%eax
/******************************/
	call	decrementNest
/*******************************/
	pushl	$\irq
	call	*irq_eoi
	addl	$4,%esp
	call	doSignal
	popl	%eax
	orl		%eax,%eax
	jz		1f
	SWITCH
1:
	call	calcCpuTimeOutIntr
	RESTORE
.endm


.text

/****************************************************************************
 *
 * 例外割り込みエントリー
 *
 ****************************************************************************/

.globl except0
except0:
	pushl	$0
	EXCEPT excDivideError

.globl except1
except1:
	pushl	$0
	EXCEPT debug

.globl except2
except2:
	pushl	$0
	EXCEPT excNonmaskableInterrupt

.globl except3
except3:
	pushl	$0
	EXCEPT excBreakpoint

.globl except4
except4:
	pushl	$0
	EXCEPT excOverflow

.globl except5
except5:
	pushl	$0
	EXCEPT excBoundsCheck

.globl except6
except6:
	pushl	$0
	EXCEPT excInvalidOpcode

.globl except7
except7:
	pushl	$0
	EXCEPT excCoprocessorNotAvailable

.globl except8
except8:
	EXCEPT excDoubleFault

.globl except9
except9:
	pushl	$0
	EXCEPT excCopressorOverrun

.globl except10
except10:
	EXCEPT excInvalidTss

.globl except11
except11:
	EXCEPT excSegmentNotPresent

.globl except12
except12:
	EXCEPT excStackException

.globl except13
except13:
	EXCEPT excGeneralProtection

.globl except14
except14:
	EXCEPT pageFault

.globl except15
except15:
	pushl	$0
	EXCEPT excReserve

.globl except16
except16:
	pushl	$0
	EXCEPT excFpuError

.globl except17
except17:
	EXCEPT excAlignmentCheck

.globl except18
except18:
	pushl	$0
	EXCEPT excMachineCheck

.globl except19
except19:
	pushl	$0
	EXCEPT excSimdFpuError

/****************************************************************************
 *
 * デバイス割り込みエントリー
 *
 ****************************************************************************/

.globl apictimer
apictimer:
	TRAP 0,APIC_TIMER

.globl irq0
irq0:
	TRAP 0,IRQ0

.globl irq1
irq1:
	TRAP 1,IRQ1

.globl irq3
irq3:
	TRAP 3,IRQ3

.globl irq4
irq4:
	TRAP 4,IRQ4

.globl irq5
irq5:
	TRAP 5,IRQ5

.globl irq6
irq6:
	TRAP 6,IRQ6

.globl irq7
irq7:
	TRAP 7,IRQ7

.globl irq8
irq8:
	TRAP 8,IRQ8

.globl irq9
irq9:
	TRAP 9,IRQ9

.globl irq10
irq10:
	TRAP 10,IRQ10

.globl irq11
irq11:
	TRAP 11,IRQ11

.globl irq12
irq12:
	TRAP 12,IRQ12

.globl irq13
irq13:
	TRAP 13,IRQ13

.globl irq14
irq14:
	TRAP 14,IRQ14

.globl irq15
irq15:
	TRAP 15,IRQ15

.globl flash_pagedir
flash_pagedir:
	TRAP 0,FLASH_PAGEDIR


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

.globl syscall0
syscall0:
	SAVE_SYSCALL
	call	*syscall_table(,%eax,4)
	RESTORE_SYSCALL
	lret	$4*1

.globl syscall1
syscall1:
	SAVE_SYSCALL
	movl	SYSCALL_PARAM(%esp),%edx
	pushl	%edx
	call	*syscall_table(,%eax,4)
	addl	$4*1,%esp
	RESTORE_SYSCALL
	lret	$4*2

.globl syscall2
syscall2:
	SAVE_SYSCALL
	movl	SYSCALL_PARAM + 4(%esp),%edx
	pushl	%edx
	movl	SYSCALL_PARAM + 4(%esp),%ecx
	pushl	%ecx
	call	*syscall_table(,%eax,4)
	addl	$4*2,%esp
	RESTORE_SYSCALL
	lret	$4*3

.globl syscall3
syscall3:
	SAVE_SYSCALL
	movl	SYSCALL_PARAM + 8(%esp),%edx
	pushl	%edx
	movl	SYSCALL_PARAM + 8(%esp),%ecx
	pushl	%ecx
	movl	SYSCALL_PARAM + 8(%esp),%edx
	pushl	%edx
	call	*syscall_table(,%eax,4)
	addl	$4*3,%esp
	RESTORE_SYSCALL
	lret	$4*4

.globl syscall4
syscall4:
	SAVE_SYSCALL
	movl	SYSCALL_PARAM + 12(%esp),%edx
	pushl	%edx
	movl	SYSCALL_PARAM + 12(%esp),%ecx
	pushl	%ecx
	movl	SYSCALL_PARAM + 12(%esp),%edx
	pushl	%edx
	movl	SYSCALL_PARAM + 12(%esp),%ecx
	pushl	%ecx
	call	*syscall_table(,%eax,4)
	addl	$4*4,%esp
	RESTORE_SYSCALL
	lret	$4*5

.globl syscall5
syscall5:
	SAVE_SYSCALL
	movl	SYSCALL_PARAM + 16(%esp),%edx
	pushl	%edx
	movl	SYSCALL_PARAM + 16(%esp),%ecx
	pushl	%ecx
	movl	SYSCALL_PARAM + 16(%esp),%edx
	pushl	%edx
	movl	SYSCALL_PARAM + 16(%esp),%ecx
	pushl	%ecx
	movl	SYSCALL_PARAM + 16(%esp),%edx
	pushl	%edx
	call	*syscall_table(,%eax,4)
	addl	$4*5,%esp
	RESTORE_SYSCALL
	lret	$4*6

.globl syscall6
syscall6:
	SAVE_SYSCALL
	movl	SYSCALL_PARAM + 20(%esp),%edx
	pushl	%edx
	movl	SYSCALL_PARAM + 20(%esp),%ecx
	pushl	%ecx
	movl	SYSCALL_PARAM + 20(%esp),%edx
	pushl	%edx
	movl	SYSCALL_PARAM + 20(%esp),%ecx
	pushl	%ecx
	movl	SYSCALL_PARAM + 20(%esp),%edx
	pushl	%edx
	movl	SYSCALL_PARAM + 20(%esp),%ecx
	pushl	%ecx
	call	*syscall_table(,%eax,4)
	addl	$4*6,%esp
	RESTORE_SYSCALL
	lret	$4*7


/****************************************************************************
 *
 * プロセス操作関数
 *
 ****************************************************************************/

/*
 * void wait_task()
 */
.globl wait_task
wait_task:
	popl	%eax			/* 呼び出元eip */
	pushfl
	pushl	$KERNEL_CODE_DES
	pushl	%eax
	SAVE_TRAP
	SWITCH
	RESTORE


/*
 * sys_exitからの戻り関数。
 * 次のタスクにスイッチし、カーネルページとページディレクトリを開放する。
 * void returnExit(uint *)
 * parameters : ページディレクトリ
 */
.globl returnExit
returnExit:
	movl	4(%esp),%ebp	/* memory struct address. */
	// ページを開放する前にタスクスイッチしておく
	pushl	$0				/* 前の割り込み時のesp値を保存 */
	pushl	%esp			/* esp値、戻り値は次のタスクのesp値保存アドレス */
	call	switch_task
	popl	%edx			/* コンテキストがセーブされたスタックのトップesp値 */
	movl	(%edx),%esp
	movl	%eax,%cr3		/* 次のタスクのページディレクトリ */
	popl	%eax			/* 前の割り込み時のesp値 */
	movl	%eax,(%edx)

	pushl	%ebp
	call	releasePageDir
//	cli
	addl	$4,%esp
	call	calcCpuTimeOutIntr
	RESTORE


/*
 * uint setContext(uint);
 * fork時の子プロセスのコンテキストを構築する。
 * 子プロセスはこの関数の戻りから開始する。
 * コンテキストは、interrupt.Sのコンテキスト保存コードに依存する。
 * parameters : 子プロセスのスタックの物理アドレス
 * return : 子プロセスのスタック開始値,子プロセスなら0
 */
.globl setContext
setContext:
	/* 親スタックから子スタックへコピー。 */
	movl	%esp,%edx
	addl	$4,%edx
	movl	%edx,%ecx
	movl	$KERNEL_ESP_BEG,%eax
	subl	%edx,%eax
	pushl	%eax				/* memcpy()のサイズ。 */
	pushl	%ecx				/* memcpy()のソースバッファ。 */
	movl	(%ecx),%edx
	addl	$0x1000,%edx
	subl	%eax,%edx
	pushl	%edx				/* memcpy()のディスティネーションバッファ。 */
	call	memcpy				/* memcpy()。 */
	addl	$12,%esp

	/* espを子プロセスのスタックに設定。 */
	movl	%esp,%edx
	addl	$4,%esp
	andl	$0xfff,%esp
	orl		4(%edx),%esp

	/* コンテキストの設定。 */
	pushfl
	pushl	%cs
	pushl	(%edx)
	xorl	%eax,%eax			/* 子プロセス帰り値=0。 */
	SAVE_TRAP
	pushl	$0					/* before_esp */

	/* 子プロセス開始スタック値の設定。 */
	movl	%edx,%eax
	andl	$~0xfff,%eax
	andl	$0xfff,%esp
	orl		%esp,%eax
	movl	%edx,%esp

	ret


/****************************************************************************
 *
 * ユーザーモードシグナルハンドラ実行
 *
 ****************************************************************************/


// void callUserHandler(
//		uint pageEnt,		// 20
//		uint *pageTbl,		// 24
//		SIG_ACTION userEip,	// 28
//		uint userEsp,		// 32
//		uint fpuSave,		// 36
//		uint *o_backEsp		// 40
// )
.globl callUserHandler
callUserHandler:
	pushl	%ebp		// 12
	pushl	%esi		// 8
	pushl	%edi		// 4
	pushl	%ebx		// 0

	movl	40(%esp),%eax
	movl	%esp,(%eax)		// 元のESP
	movl	36(%esp),%edx	// fpuSave
	pushl	%edx
	cli
	call	setFpuSaveAddr
	addl	$4,%esp
	// カーネルスタックページを入れ替える。
	call	getPageDir		// eax = page directory
	movl	20(%esp),%esi	// pageEnt
	movl	24(%esp),%edi	// pageTbl
	movl	28(%esp),%ebx	// userEip
	movl	32(%esp),%ebp	// userEsp
	movl	%esi,(%edi)		//カーネルスタックページ入れ替え
	movl	%eax,%cr3		//ページディレクトリの更新。
	sti
	call	setTs

	// スタックにコンテキストを設定してユーザーハンドラに移行
	pushl	$USER_DATA_DES		// user ss
	pushl	%ebp				// user esp
	pushl	$USER_CODE_DES		// user cs
	pushl	%ebx				// userEip
	movl	$USER_DATA_DES,%edx
	movl	%edx,%ds
	movl	%edx,%es
	lret


// void returnCallUserHandler(
//		uint kernelEsp,		// 4
//		uint pageEnt,		// 8
//		uint *pageTbl,		// 12
// );
.globl returnCallUserHandler
returnCallUserHandler:
	cli
	call	resetFpuSaveAddr
	call	getPageDir		// eax = page directory
	movl	4(%esp),%ebp	// kernelEsp
	movl	8(%esp),%esi	// pageEnt
	movl	12(%esp),%edi	// pageTbl
	movl	%esi,(%edi)		//カーネルスタックページ入れ替え
	movl	%eax,%cr3		// ページディレクトリの更新
	movl	%ebp,%esp		// ESPを元に戻す
	sti
	call	setTs

	// "callUserHandler"呼び出し元に戻る。
	popl	%ebx
	popl	%edi
	popl	%esi
	popl	%ebp
	ret
