/*
 * mm.c
 *
 * Copyright 2002, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 *
 * メモリー管理プログラム
 *
 * 競合データノート
 *		phis_linkの状態でページメモリーが他プロセスから横取りされるので、ページテーブルエントリーの変更と
 * 		phis_linkの特にPHYS_MEM_TABL要素のpage_tbl_addと同期させる必要がある。
 *
 * 課題
 *  ページの横取りで競合が起きないようにする。
 */


#include<types.h>
#include<sys/param.h>
#include<config.h>
#include<lib.h>
#include<lock.h>
#include<interrupt.h>
#include<proc.h>
#include<mp.h>
#include<segment.h>
#include<errno.h>
#include<fs.h>
#include<device.h>
#include<signal.h>
#include<mm.h>
#include<debug.h>
#include<test.h>




//#define DEBUG_MM 1

#ifdef DEBUG_MM
	#define STATIC
	#define INLINE
#else
	#define STATIC static
	#define INLINE	inline
#endif


static int backupPage(uint,PROC*,uint*);
static int restorePage(uint *pentry,void *buf);


/***********************************************************************************
 *
 *　物理メモリーページ管理
 *
 ***********************************************************************************/


/*
 * メモリーページテーブル
 */
typedef struct PHYS_MEM_TABL{
	short ref_count;			/* reference count */
	ushort heap_num;			/* Heap number */
	PROC *proc;					/* 割り当てているプロセス。 */
	uint *pentry;				/* 割り当てているページエントリーアドレス。 */
	struct PHYS_MEM_TABL *next;
	struct PHYS_MEM_TABL *prev;
}PHYS_MEM_TABL;


/*============================ プライベート ===================================*/


/*
 * メモリーページテーブルリンクヘッダ
 */
static PHYS_MEM_TABL *freeKrnMem;	// 一方向リスト　空きカーネル物理メモリーリンクトップ
static PHYS_MEM_TABL *freeUsrMem;	// 一方向リスト　空きユーザー物理メモリーリンクエンド
static PHYS_MEM_TABL usedKrnMem;	// 両方向リスト　使用中（横取可）カーネル物理メモリーリンクトップ
static PHYS_MEM_TABL usedUsrMem;	// 両方向リスト　使用中（横取可）ユーザー物理メモリーリンクトップ
static int physMemLock;				// PHYS_MEM_TABL用ロックゲート


/*
 * メモリーサイズ
 * setup.Sで代入
 */
static uint all_memory_size;


/*
 * メモリーアドレスからメモリーページテーブルアドレスを返す
 * parameters : Physical address
 * return : PHYS_MEM_TABL address
 */
STATIC INLINE PHYS_MEM_TABL *getPhysTableFromAddr(void *addr)
{
	return (PHYS_MEM_TABL*)((uint)addr / PAGE_SIZE * sizeof(PHYS_MEM_TABL) + PHYSICAL_PAGE_TABLE_ADD);
}


/*
 * メモリーページテーブルアドレスからメモリーアドレスを返す
 * parameters : PHYS_MEM_TABL address
 * return : Physical address
 */
STATIC INLINE void *getAddrFromPhysTable(PHYS_MEM_TABL *memtable)
{
	return (void*)(((uint)memtable-PHYSICAL_PAGE_TABLE_ADD)/sizeof(PHYS_MEM_TABL)*PAGE_SIZE);
}


/*
 * ！"physMemLock"でロックされた処理中から呼び出されること
 * メモリーテーブルフリーリンクから取り出す。
 * parameters : フリートップアドレスポインタ
 * return : メモリーテーブルアドレス
 */
STATIC INLINE PHYS_MEM_TABL *getFreeMem(PHYS_MEM_TABL **freeTop)
{
	PHYS_MEM_TABL *mtbl = *freeTop;

	mtbl->ref_count = 1;
	*freeTop = mtbl->next;
	mtbl->prev = mtbl->next = mtbl;

	return mtbl;
}


/*
 * ！"physMemLock"でロックされた処理中から呼び出されること
 * メモリーテーブルフリーリンクに加える。
 * parameters : 追加メモリーテーブル,フリーリンクトップ
 */
STATIC INLINE void addFreeMem(PHYS_MEM_TABL *mtbl,PHYS_MEM_TABL **freeTop)
{
	mtbl->ref_count = 0;
	mtbl->proc = NULL;
	mtbl->pentry = NULL;
	mtbl->next = *freeTop;
	*freeTop = mtbl;
}


/*
 * ！"physMemLock"でロックされた処理中から呼び出されること
 * ！必ず取り出すメモリーテーブルが存在するのを確認して呼び出されること
 * parameters : 使用中リンクエンド
 * return : メモリーテーブルアドレス
 */
STATIC INLINE PHYS_MEM_TABL *getUsedMem(PHYS_MEM_TABL *usedTop)
{
	PHYS_MEM_TABL *mtbl = usedTop->prev;

	mtbl->next->prev = mtbl->prev;
	mtbl->prev->next = mtbl->next;
	mtbl->next = mtbl->prev = mtbl;

	return mtbl;
}


/*
 * ！"physMemLock"でロックされた処理中から呼び出されること
 * parameters : 追加メモリーテーブル,使用中リンクトップ,プロセステーブル,割当てページエントリー
 */
STATIC INLINE void addUsedMem(PHYS_MEM_TABL *mtbl,PHYS_MEM_TABL *usedTop)
{
	mtbl->next = usedTop->next;
	mtbl->prev = usedTop;
	usedTop->next->prev = mtbl;
	usedTop->next = mtbl;
}


/*
 * ！"physMemLock"でロックされた処理中から呼び出されること
 * parameters : 追加メモリーテーブル,使用中リンクトップ,プロセステーブル,割当てページエントリー
 */
STATIC INLINE void rmoveUsedMem(PHYS_MEM_TABL *mtbl)
{
	mtbl->prev->next = mtbl->next;
	mtbl->next->prev = mtbl->prev;
	mtbl->next = mtbl->prev = mtbl;
}


/*
 * allocMultiKernelPage()用。
 * フリーカーネルメモリーリンクから削除して割り当てる。
 * parameters : 物理メモリーテーブルアドレス
 */
STATIC INLINE void delFreeKrnLink(PHYS_MEM_TABL *mtbl)
{
	PHYS_MEM_TABL *p;


	if (mtbl == freeKrnMem)
		freeKrnMem = mtbl->next;
	else
	{
		for (p = freeKrnMem; p->next != mtbl; p = p->next);
		p->next = mtbl->next;
	}

	mtbl->ref_count = 1;
	mtbl->next = mtbl;
}


/*============================ パブリック ===================================*/


/*
 * メモリーページの参照カウントを増やす
 * parameters : 物理アドレス
 */
STATIC void linkPhysTable(void *addr)
{
	PHYS_MEM_TABL *mtbl;


	mtbl = getPhysTableFromAddr(addr);

	enter_spinlock(&physMemLock);
	{
		++mtbl->ref_count;

		mtbl->proc = NULL;
		mtbl->pentry = NULL;

		// Used linkから削除する
		rmoveUsedMem(mtbl);
	}
	exit_spinlock(&physMemLock);
}


/*
 * メモリーページの参照カウントを減らす
 * parameters : 物理アドレス
 */
STATIC void unlinkPhysTable(void *addr)
{
	PHYS_MEM_TABL *mtbl;


	mtbl = getPhysTableFromAddr(addr);

#ifdef DEBUG
	if (mtbl->ref_count <= 0)
		printk("Warning! A No reference PhysTable is unlinked : addr=0x%x function=0x%x\n",addr,*((uint*)&addr-1));
#endif

	enter_spinlock(&physMemLock);
	{
		--mtbl->ref_count;
		if (mtbl->ref_count <= 0)
		{
			// Used linkから削除する
			rmoveUsedMem(mtbl);
			
			/* アロケートヘッダー番号をクリアー */
			mtbl->heap_num = 0;

			if ((uint)addr < KERNEL_END)
				addFreeMem(mtbl,&freeKrnMem);
			else
				addFreeMem(mtbl,&freeUsrMem);
		}
	}
	exit_spinlock(&physMemLock);
}


/*
 * メモリーページの参照カウントが２以上なら減らして元の参照カウントを返す
 * parameters : 物理アドレス
 */
STATIC int delAndGetPhysRefCount(void *addr)
{
	int rest;
	PHYS_MEM_TABL *mtbl = getPhysTableFromAddr(addr);


	enter_spinlock(&physMemLock);
	{
		if(mtbl->ref_count > 1)
		{
			rest = mtbl->ref_count;
			--mtbl->ref_count;
		}
		else
			rest = mtbl->ref_count;
	}
	exit_spinlock(&physMemLock);

	return rest;
}


/*
 * カーネル空間へメモリーページを割り当てる
 * 割り当てたページは横取り不可
 * return : Allocate addres or NULL
 */
STATIC void *allocKernelPage()
{
	PHYS_MEM_TABL *mtbl;


	enter_spinlock(&physMemLock);

	if (freeKrnMem != NULL){
		mtbl = getFreeMem(&freeKrnMem);
		exit_spinlock(&physMemLock);
	}
	else if (usedKrnMem.next != &usedKrnMem){
		mtbl = getUsedMem(&usedKrnMem);
		exit_spinlock(&physMemLock);

		/* メモリーの内容をバックアップする。 */
		if(backupPage((uint)getAddrFromPhysTable(mtbl),mtbl->proc,mtbl->pentry) == -1){
			return NULL;
		}
	}
	else{
		exit_spinlock(&physMemLock);
		return NULL;
	}

	return getAddrFromPhysTable(mtbl);
}


/*
 * ユーザー空間へメモリーページを割り当てる
 * 割り当てたページは横取り可
 * parameters : 割り当てるページエントリーアドレス
 * return : Allocate addres or error=NULL
 */
STATIC void *allocUserPage(uint *pentry)
{
	uint *old_pentry;
	PROC *old_proc;
	PHYS_MEM_TABL *mtbl;


	enter_spinlock(&physMemLock);

	if(freeUsrMem != NULL){
		mtbl = getFreeMem(&freeUsrMem);
		mtbl->pentry = pentry;
		mtbl->proc = get_current_task();
		addUsedMem(mtbl,&usedUsrMem);
		exit_spinlock(&physMemLock);
	}
	else if(freeKrnMem != NULL){
		mtbl = getFreeMem(&freeKrnMem);
		mtbl->pentry = pentry;
		mtbl->proc = get_current_task();
		addUsedMem(mtbl,&usedKrnMem);
		exit_spinlock(&physMemLock);
	}
	else if(usedUsrMem.next != &usedUsrMem){
		mtbl = getUsedMem(&usedUsrMem);
		old_proc = mtbl->proc;
		old_pentry = mtbl->pentry;
		mtbl->pentry = pentry;
		mtbl->proc = get_current_task();
		addUsedMem(mtbl,&usedUsrMem);
		exit_spinlock(&physMemLock);

		if(backupPage((uint)getAddrFromPhysTable(mtbl),old_proc,old_pentry) == -1){
			return NULL;
		}
	}
	else if(usedKrnMem.next != &usedKrnMem){
		mtbl = getUsedMem(&usedKrnMem);
		old_proc = mtbl->proc;
		old_pentry = mtbl->pentry;
		mtbl->pentry = pentry;
		mtbl->proc = get_current_task();
		addUsedMem(mtbl,&usedKrnMem);
		exit_spinlock(&physMemLock);

		if(backupPage((uint)getAddrFromPhysTable(mtbl),old_proc,old_pentry) == -1){
			return NULL;
		}
	}
	else{
		exit_spinlock(&physMemLock);
		return NULL;
	}

	return getAddrFromPhysTable(mtbl);
}


/*
 * カーネル空間へ連続したメモリーページを割り当てる
 * parameters : ページ数
 * return : 割当てアドレス or failed=NULL
 */
STATIC void *allocMultiKernelPage(int num)
{
	void *rest;
	int cnt;
	uint maxmem;
	PHYS_MEM_TABL *p;


	maxmem=(all_memory_size<KERNEL_DATA_END)?all_memory_size:KERNEL_DATA_END;
	p=(PHYS_MEM_TABL*)(PHYSICAL_PAGE_TABLE_ADD+maxmem/PAGE_SIZE*sizeof(PHYS_MEM_TABL))-1;
	rest=NULL;
	cnt=0;
	enter_spinlock(&physMemLock);
	{
		for(;p>=(PHYS_MEM_TABL*)PHYSICAL_PAGE_TABLE_ADD;--p)
		{
			if(p->ref_count==0)
			{
				if(++cnt==num)
				{
					while(--cnt>=0)delFreeKrnLink(p+cnt);
					rest=getAddrFromPhysTable(p);
					break;
				}
			}
			else cnt=0;
		}
	}
	exit_spinlock(&physMemLock);

	return rest;
}


/*
 * メモリーページテーブルを初期化する
 */
STATIC void initPhysMemTable()
{
	PHYS_MEM_TABL *p = (PHYS_MEM_TABL*)PHYSICAL_PAGE_TABLE_ADD;
	int c_mtbl, last;


	// 最初の1Mbyte分メモリーを割り当てる。
	for (c_mtbl = 0; c_mtbl < 0x100000 / PAGE_SIZE; ++c_mtbl){
		p[c_mtbl].ref_count = 1;
	}

	// 残りをフリーに設定する。
	freeKrnMem = &p[c_mtbl];
	for (last = all_memory_size / PAGE_SIZE; c_mtbl < last; ++c_mtbl){
		p[c_mtbl].ref_count = 0;
		p[c_mtbl].heap_num = 0;
		p[c_mtbl].proc = NULL;
		p[c_mtbl].pentry = NULL;
		p[c_mtbl].next = &p[c_mtbl + 1];
	}
	p[--c_mtbl].next = NULL;

	// ユーザー領域フリーの設定。
	if (getAddrFromPhysTable(&p[c_mtbl]) >= (void*)USER_BEG){
		freeUsrMem = getPhysTableFromAddr((void*)USER_BEG);
		(freeUsrMem-1)->next = NULL;
	}
	else{
		freeUsrMem = NULL;
	}

	/* メモリーテーブル分メモリーを割り当てる（横取不可）。 */
	last = ROUNDUP(PHYSICAL_PAGE_TABLE_ADD + all_memory_size / PAGE_SIZE * sizeof(PHYS_MEM_TABL), PAGE_SIZE) / PAGE_SIZE;
	for (c_mtbl = PHYSICAL_PAGE_TABLE_ADD / PAGE_SIZE; c_mtbl < last; ++c_mtbl){
		p[c_mtbl].ref_count = 1;
	}
	if (&p[PHYSICAL_PAGE_TABLE_ADD/PAGE_SIZE] == freeKrnMem){
		freeKrnMem=&p[last];
	}
	else{
		p[PHYSICAL_PAGE_TABLE_ADD/PAGE_SIZE-1].next = &p[last];
	}

	// Used linkを初期化する
	usedKrnMem.next = usedKrnMem.prev = &usedKrnMem;
	usedUsrMem.next = usedUsrMem.prev = &usedUsrMem;
}


/*
 * 物理メモリーサイズを測定する
 */
STATIC uint findAllMemsize()
{
	enum{EXIST_MEM_SIZE=0x800000};		/* メモリー測定単位 */

	uint volatile *point;


	/* メモリーを単位ごとに測定する*/
	for(point=(uint*)EXIST_MEM_SIZE-1;;point+=EXIST_MEM_SIZE/sizeof(uint))
	{
		*point=0x5a5a5a5a;
		if(*point!=0x5a5a5a5a)break;
		else
		{
			*point=0xf0f0f0f0;
			if(*point!=0xf0f0f0f0)break;
		}
	}

	return (all_memory_size=(uint)point-EXIST_MEM_SIZE+sizeof(uint));
}


/*
 *メモリーアロケートヘッダー番号を設定する
 * parameters : 物理メモリーアドレス,アロケートヘッダー番号
 */
STATIC void setAllocNum(void *addr,int num)
{
	getPhysTableFromAddr(addr)->heap_num = num;
}


/*
 *メモリーアロケートヘッダー番号を取得する
 * parameters : 物理メモリーアドレス
 * return : アロケートヘッダー番号
 */
STATIC int getAllocNum(void *addr)
{
	return getPhysTableFromAddr(addr)->heap_num;
}


/***********************************************************************************
 *
 * メモリーアロケート
 *
 ***********************************************************************************/


#define HEAP_NUM() (sizeof(heapHead) / sizeof(HEAP_HEAD))

/* 分割メモリーヘッダー */
typedef struct MEM_HEAD{
	struct MEM_HEAD *next;	/* 次の空きメモリー先頭 */
	int size;				/* 割り当てサイズ */
}MEM_HEAD;

/*
 * ヒープヘッダー
 * Note! : "memTop"の位置は必ず"MEM_HEAD"の"next"と同じにすること
 */
typedef struct{
	MEM_HEAD *memTop;		/* 空きメモリー先頭 */
	uint size;				/* 割り当てサイズ */
	int gate;				/* スピンロック用ゲート */
}HEAP_HEAD;


/*
 * PRIVATE
 * ヒープヘッダー
 */
static HEAP_HEAD heapHead[]={
	{NULL,0,     0},
	{NULL,0x10,  0},
	{NULL,0x20,  0},
	{NULL,0x40,  0},
	{NULL,0x80,  0},
	{NULL,0x100, 0},
	{NULL,0x200, 0},
	{NULL,0x400, 0},
	{NULL,0x800, 0},
	{NULL,0x1000,0},
};

/*
 * PRIVATE
 * バイト値からヒープ構造体ナンバーを求める
 */
static uchar heapNum1[]={
	0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
	2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
	3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
	3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
	4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
	4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
	4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
	4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
	5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
	5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
	5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
	5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
	5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
	5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
	5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
};
static uchar heapNum2[]={
	0,5,6,7,7,8,8,8,8,9,9,9,9,9,9,9,
	9,
};


/*
 * PRIVATE
 * ヒープサイズを得る。
 * parameters : 要求サイズ
 * return : ヒープ構造体ナンバー
 */
STATIC INLINE int getHeapSize(size_t size)
{
	if(size>=0x100)
		return heapNum2[ROUNDUP(size,0x100)>>8];
	return heapNum1[size];
}


/*============================ グローバルメソッド ======================================*/


/*
 * 要求サイズメモリーを割り当てる
 * parameters : Size by byte
 * return : allocate address or NULL
 */
void *kmalloc(size_t size)
{
	union{
		MEM_HEAD *head;
		uint ui;
	}mem,alloc;
	int i;


	if (size == 0)
		return NULL;

	if (size <= PAGE_SIZE)
	{
		i = getHeapSize(size);

		enter_spinlock(&heapHead[i].gate);
		{
			if (heapHead[i].memTop == NULL)
			{
				/* 1ページメモリーを確保。 */
				mem.head = allocKernelPage();
				if (mem.head == NULL)
				{
					exit_spinlock(&heapHead[i].gate);
					return NULL;
				}

				/*
				 * 開放するとき、addressから割当サイズがわかるようにする。
				 */
				setAllocNum(mem.head,i);

				mem.head->next = NULL;
				mem.head->size = PAGE_SIZE;
				heapHead[i].memTop = mem.head;
			}

			mem.head = heapHead[i].memTop;
			mem.head->size -= heapHead[i].size;
			if (mem.head->size == 0)
				heapHead[i].memTop = mem.head->next;
		}
		exit_spinlock(&heapHead[i].gate);

		alloc.ui = mem.ui + mem.head->size;
	}
	else
	{
		size = ROUNDUP(size,PAGE_SIZE);
		if ((alloc.head = allocMultiKernelPage(size / PAGE_SIZE)) == NULL)
			return NULL;
		setAllocNum(alloc.head,HEAP_NUM() + size / PAGE_SIZE);
	}

	return alloc.head;
}


/*
 * メモリーを開放する
 * parameters : Address
 */
void kfree(void *ptr)
{
	union{
		MEM_HEAD *head;
		uint ui;
	}mem,free,prev,next;
	uint i,m;


	if (ptr == NULL)
		return;

	i = getAllocNum(ptr);
	if (i == 0)
	{
#ifdef DEBUG
		printk("kfree() 0x%x did not allocated : call function=0x%x\n",ptr,*((uint*)&ptr - 1));
#endif
		return;
	}

	if (i < HEAP_NUM())
	{
		free.head = ptr;
#ifdef DEBUG
		/* 引数の妥当性のチェック */
		mem.head = free.head;
		mem.ui &= heapHead[i].size - 1;
		if (mem.ui != 0)
		{
			printk("kfree() : 0x%x is no complete : call function=0x%x\n",free.head, *((uint*)&ptr - 1));
			return;
		}	
#endif
		enter_spinlock(&heapHead[i].gate);
		{
			/* メモリーを接続するリンク位置を検索 */
			mem.head = (MEM_HEAD*)&heapHead[i];
			for (;mem.head->next != NULL; mem.head = mem.head->next)
				if (free.head < mem.head->next)
					break;
			prev = mem;
#ifdef DEBUG
			if ((mem.head != (MEM_HEAD*)&heapHead[i]) && (free.ui < (mem.ui + mem.head->size)) )
			{
				printk("kfree() : 0x%x is already free,call from : 0x%x\n",free.head,*((uint*)&ptr - 1));
				goto EXIT;
			}
#endif
			/* 後ろのメモリーと繋げる */
			next.head = prev.head->next;
			if ((next.ui == (free.ui + heapHead[i].size)) && ((next.ui / PAGE_SIZE) == (free.ui / PAGE_SIZE)) )
			{
				free.head->next = next.head->next;
				free.head->size = next.head->size + heapHead[i].size;
			}
			else
			{
				free.head->next = next.head;
				free.head->size = heapHead[i].size;
			}

			/* つなげたメモリーが１ページになったらページを開放 */
			if (free.head->size == PAGE_SIZE)
			{
				prev.head->next = free.head->next;
				unlinkPhysTable(free.head);
				goto EXIT;
			}

			/* 前のメモリーと繋げる */
			if (((prev.ui + prev.head->size) == free.ui) && ((prev.ui / PAGE_SIZE) == (free.ui / PAGE_SIZE)) )
			{
				prev.head->next = free.head->next;
				prev.head->size += free.head->size;
				
			}
			else
				prev.head->next = free.head;

			/* つなげたメモリーが１ページになったらページを開放 */
			if (prev.head->size == PAGE_SIZE)
			{
				for (mem.head = (MEM_HEAD*)&heapHead[i]; mem.head->next != prev.head; mem.head = mem.head->next);
				mem.head->next = free.head->next;
				unlinkPhysTable(prev.head);
				goto EXIT;
			}
		}
EXIT:	exit_spinlock(&heapHead[i].gate);
	}
	else
	{
		m = (uint)ptr;
		for (i -= HEAP_NUM(); i > 0; --i)
		{
			unlinkPhysTable((void*)m);
			m += PAGE_SIZE;
		}
	}
}


/***********************************************************************************
 *
 * 配列メモリーアロケート
 * 　割当サイズがページサイズより大きいときに使用する
 *
 ***********************************************************************************/


/*
 * 配列メモリーヘッダ
 */
typedef struct{
	int len;
	char *mem[0];
}MEM_ARRAY_HEAD;


/*================================= グローバル ======================================*/


/*
 * 割当
 * ヘッダのみを割り当てる
 * return : メモリー配列ヘッダポインタ or NULL
 */
MEM_ARRAY *allocArray(size_t size)
{
	int len;
	MEM_ARRAY_HEAD *memArray;
	
	/* メモリーポインタ配列をページサイズ単位で割り当てる */
	len = ROUNDUP_DIV(size,PAGE_SIZE);
	memArray = kmalloc(sizeof(MEM_ARRAY_HEAD) + len * sizeof(char*));
	if (memArray == NULL)
		return NULL;

	memArray->len = len;
	memset(memArray->mem,0,len * sizeof(char*));
	
	return (MEM_ARRAY*)memArray;
}


/*
 * 開放
 */
void freeArray(MEM_ARRAY *i_memArray)
{
	MEM_ARRAY_HEAD *memArray = (MEM_ARRAY_HEAD*)i_memArray;
	int i;
	
	/* メモリー配列の開放 */
	for (i = 0; i < memArray->len; ++i)
		if (memArray->mem[i] != NULL)
			unlinkPhysTable(memArray->mem[i]);
	kfree(memArray);
}


/*
 * 再割当
 * return : 新しいメモリー配列ヘッダポインタ or NULL
 */
MEM_ARRAY *reallocArray(MEM_ARRAY *i_memArray,size_t size)
{
	MEM_ARRAY_HEAD *memArray = (MEM_ARRAY_HEAD*)i_memArray;
	MEM_ARRAY_HEAD *newMemArray;

	if (memArray->len * PAGE_SIZE < size)
	{
		newMemArray = (MEM_ARRAY_HEAD*)allocArray(size);
		if (newMemArray == NULL)
			return NULL;
		
		newMemArray->len = ROUNDUP_DIV(size,PAGE_SIZE);
		memcpy(newMemArray->mem,memArray->mem,memArray->len * sizeof(char*));
		memset(&newMemArray->mem[memArray->len],0,(newMemArray->len - memArray->len) * sizeof(char*));
		kfree(memArray);
		
		return (MEM_ARRAY*)newMemArray;
	}
	else
		return (MEM_ARRAY*)memArray;
}


/*
 * 書き込み
 * return : NOERR or error number
 */
int writeArray(MEM_ARRAY *i_memArray,const int i_offset,const size_t size,const void *i_buf)
{
	MEM_ARRAY_HEAD *memArray = (MEM_ARRAY_HEAD*)i_memArray;
	const char *srcBuf = i_buf;
	int offset,cpSize,restSize;
	int i;

#ifdef DEBUG
	/* 書き込みサイズの確認 */
	if (memArray->len * PAGE_SIZE < i_offset * size)
		return -EFBIG;
#endif

	restSize = size;
	offset = i_offset % PAGE_SIZE;
	for (i = i_offset / PAGE_SIZE;; ++i)
	{
		/* メモリー未割当分を割り当てる */
		if (memArray->mem[i] == NULL)
		{
			memArray->mem[i] = kmalloc(PAGE_SIZE);
			if (memArray->mem[i] == NULL)
				return -ENOMEM;
		}
		
		cpSize = (restSize <= PAGE_SIZE - offset)? restSize : PAGE_SIZE - offset;
		memcpy(memArray->mem[i] + offset,srcBuf,cpSize);
		restSize -= cpSize;
		if (restSize == 0)
			break;
		srcBuf += cpSize;
		offset = 0;
	}
	
	return NOERR;
}


/*
 * コピー
 * return : NOERR or error number
 */
void copyArray(MEM_ARRAY *i_memArray,const int i_offset,const size_t size,void *o_buf)
{
	MEM_ARRAY_HEAD *memArray = (MEM_ARRAY_HEAD*)i_memArray;
	char *dstBuf = o_buf;
	int offset,cpSize,restSize;
	int i;

#ifdef DEBUG
	/* 書き込みサイズの確認 */
	if (memArray->len * PAGE_SIZE < i_offset * size)
	{
		printk("%s copyArray() Copy size is bigger!:offset=%d size=%d",__FILE__,i_offset,size);
		return;
	}
#endif

	restSize = size;
	offset = i_offset % PAGE_SIZE;
	for (i = i_offset / PAGE_SIZE;; ++i)
	{
		
		cpSize = (PAGE_SIZE - offset < restSize)? PAGE_SIZE - offset : restSize;
		if (memArray->mem[i] == NULL)
			/* メモリー未割当分は代わりに0を埋める */
			memset(dstBuf,0,cpSize);
		else
			memcpy(dstBuf,memArray->mem[i] + offset,cpSize);
		restSize -= cpSize;
		if (restSize == 0)
			break;
		dstBuf += cpSize;
		offset = 0;
	}
}


/***********************************************************************************
 *
 *　仮想メモリー管理
 * 共通規則
 * ○ 使われていないページエントリーは必ず０クリアーする。
 *
 ***********************************************************************************/


enum{
	PAGE_DIR_SIZE=0x400000,		/* Page directory size */

	/* Page entry flag */
	PAGE_PRESEN=		0x1,	/* Is presence */
	PAGE_RW=			0x2,	/* Is read and write,not read only */
	PAGE_USER=			0x4,	/* Privilege is user,not supervisor */
	PAGE_CASH_DISABLE=	0x10,	/* Disable cash,not able cash */
	PAGE_4M=			0x80,	/* Page size is 4M,not 4K */

	/* ページエントリー記入可領域用フラグ。 */
	PT_COPY_ON_WRITE=0x200,		/* コピーオンライトフラグ。 */
};


/*============================ パブリックメソッド ======================================*/


/*
 * 仮想アドレスからページディレクトリエントリーアドレスを取得する
 * parameters : 仮想アドレス,ページディレクトリアドレス
 * return : ページディレクトリエントリーアドレス
 */
STATIC INLINE uint *getPageDirEntryAddr(uint addr,uint *page_dir)
{
	return &page_dir[addr>>22];
}


/*
 * 仮想アドレスからページテーブルエントリーアドレスを取得する
 * parameters : 仮想アドレス,ページテーブルアドレス
 * return : ページテーブルエントリーアドレス
 */
STATIC INLINE uint *getPageEntryAddr(uint addr,uint *ptbl)
{
	return &ptbl[(addr>>12)&0x3ff];
}


/*
 * ページディレクトリを再読み込みする
 * parameters : page directory address
 */
STATIC INLINE void flashPageDir(uint *pdir)
{
	asm volatile(
		"movl	%%eax,%%cr3"
		::"a"(pdir)
	);
}


/*
 * 不在ページかどうか
 * parameters : 1 = 存在,0 = 不在
 */
STATIC INLINE int isPagePresen(uint entry)
{
	return (entry & PAGE_PRESEN) != 0;
}

/*
 * ページエントリーをコピーオンライトに設定する
 * parameters : page entry address
 */
STATIC INLINE void setCopyOnWrite(uint *pentry)
{
	*pentry |= PT_COPY_ON_WRITE;			/* コピーオンライトフラグを設定する。 */
	*pentry &= ~PAGE_RW;					/* 書込み禁止にする。 */
}

STATIC INLINE void setCopyOnWritePageTable(uint *pentry)
{
	*pentry |= PT_COPY_ON_WRITE;			/* コピーオンライトフラグを設定する。 */
}

/*
 * return : 設定されいたら 0 以外
 */
STATIC INLINE int isSetCopyOnWrite(uint pageTbl)
{
	return pageTbl & PT_COPY_ON_WRITE;
}

/*
 * ページエントリーにページメモリーを割り当てる
 * return : 0 or error number
 */
STATIC int setPageMemory(uint fault_addr,MM_STRUCT *mm_struct)
{
	void *alloc_page;
	uint *dentry = getPageDirEntryAddr(fault_addr,mm_struct->pageDir);
	uint *pentry,entry;
	int rest;


	/* ページテーブルの割り当て */
	if(*dentry == 0){
		*dentry = (uint)allocKernelPage();
		if (*dentry == 0){
			return -ENOMEM;
		}
		memset ((void*)*dentry,0,PAGE_SIZE);
		*dentry |= PAGE_PRESEN | PAGE_RW | PAGE_USER;
	}

	pentry = getPageEntryAddr(fault_addr,(uint*)(*dentry & ~(PAGE_SIZE - 1)));
	
	/* ページメモリーの割当て */
	alloc_page = allocUserPage(pentry);
	if (alloc_page == NULL){
		return -ENOMEM;
	}
	enter_spinlock(&mm_struct->pageDirGate);
	{
		/* バックアップあり */
		entry = *pentry;
		if (entry != 0){
			rest = restorePage(pentry,alloc_page);
			if (rest != 0){
				exit_spinlock(&mm_struct->pageDirGate);
				unlinkPhysTable(alloc_page);
				return rest;
			}
		}

		*pentry = (uint)alloc_page | PAGE_PRESEN | PAGE_RW | PAGE_USER;
	}
	exit_spinlock(&mm_struct->pageDirGate);
	
	return 0;
}


/*
 * コピーオンライトのページエントリーにページメモリーを割り当てる
 * return : 0 or error number
 */
STATIC int setPageMemoryCopyOnWriteEntry(uint fault_addr,MM_STRUCT *mm_struct)
{
	void *alloc_page;
	uint *dentry = getPageDirEntryAddr(fault_addr,mm_struct->pageDir);
	uint *pentry = getPageEntryAddr(fault_addr,(uint*)(*dentry & ~(PAGE_SIZE - 1)));


	/* 物理メモリー参照カウントを確認。 */
	if(delAndGetPhysRefCount((void*)*pentry) > 1)
	{
		/*
		 * ページディレクトリエントリーにコピーオンライトフラグが設定されている場合は、ページテーブルを割り当てる。
		 */
		if((*dentry & PT_COPY_ON_WRITE) != 0)
		{
			if(delAndGetPhysRefCount((void*)*dentry) > 1)
			{
				alloc_page = allocKernelPage();
				if (alloc_page == NULL)
					return -ENOMEM;
				memcpy(alloc_page,(void*)(*dentry & ~(PAGE_SIZE - 1)),PAGE_SIZE);
				*dentry = ((*dentry & (PAGE_SIZE - 1)) | (uint)alloc_page) & ~PT_COPY_ON_WRITE;
				pentry = getPageEntryAddr(fault_addr,alloc_page);
			}
			else *dentry &= ~PT_COPY_ON_WRITE;
		}

		/* 物理ページを割当て、ページ内容をコピーする。 */
		alloc_page = allocUserPage(pentry);
		if (alloc_page == NULL)
			return -ENOMEM;
		memcpy(alloc_page,(void*)(*pentry&~(PAGE_SIZE-1)),PAGE_SIZE);
		*pentry = (uint)alloc_page | PAGE_PRESEN | PAGE_RW | PAGE_USER;
	}
	else
		*pentry = (*pentry & ~PT_COPY_ON_WRITE) | PAGE_RW;
	
	return 0;
}


/*
 * ページエントリーがコピーオンライトかどうか
 * return : 1 = yes or 0 = no
 */
STATIC int isCopyOnWrite(uint fault_addr,MM_STRUCT *mm_struct)
{
	uint *dentry = getPageDirEntryAddr(fault_addr,mm_struct->pageDir);
	uint *pentry = getPageEntryAddr(fault_addr,(uint*)(*dentry & ~(PAGE_SIZE - 1)));


	return (*pentry & PT_COPY_ON_WRITE) != 0;
}


/*============================ グローバルメソッド ======================================*/


/*
 * 割当てたアドレスの最後を取得する
 * return : last allocate addrress
 */
uint getLastLinearAddr()
{
	return ((MM_STRUCT*)get_current_task()->mm_struct)->lastPageAddr;
}


/***********************************************************************************
 *
 *　ページバックアップ
 * ページエントリーのバックアップ時のレイアウト。
 *  0bit=0,1〜23bit=保存ブロック番号,24〜31bit=ブロック参照カウント
 *
 ***********************************************************************************/


enum{
	PT_TYPE_PAGING = 0x9a,	/* ページバックアップデバイスパーティション番号（仮）。 */
	PB_START_SECTER = 2,	/* バックアップ開始セクター。 */
	BIT_NUM=8,				/* バイトビット数。 */
};


/* PRIVATE,ビットマップでバイト値から空きブロックの位置を求める。 */
static uchar emptyBlock[]={
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,0,
};
static uchar *pbBitMap;			/* PRIVATE,空きブロックビットマップ。 */
static int pbBitMapCurrent;		/* PRIVATE,ビットマップ中の現在のバイト位置。 */
static int pbBitMapSize;		/* PRIVATE,ビットマップサイズ。 */
static int pbDin;				/* PRIVATE,バックアップデバイス inode */
static int pbSectors;			/* PRIVATE,ページサイズ/セクターサイズ */


/*
 * PRIVATE
 * 空きページバックアップを取得する。
 * return : ブロック or failed=-1
 */
STATIC int getEmptyBackup()
{
	int bit;
	int i;


	i=pbBitMapCurrent;

	do
	{
		if(pbBitMap[i]!=0xff)
		{
			bit=emptyBlock[pbBitMap[i]];
			pbBitMap[i]|=1<<bit;
			pbBitMapCurrent=i;

			return (i*BIT_NUM+bit)*pbSectors+PB_START_SECTER;
		}

		if(++i==pbBitMapSize)i=0;
	}while(i!=pbBitMapCurrent);

	return -1;
}


/*
 * PRIVATE
 * ページエントリーからブロック番号を得る。
 * parameters : page entry
 * return : block number
 */
STATIC INLINE uint getBlock(uint pentry)
{
	return (pentry&0xffffff)>>1;
}


/*
 * PRIVATE
 * ページエントリーにブロック番号を設定する。
 * parameters : page entry address,block number
 */
STATIC INLINE void setBlock(uint *pentry,uint block)
{
	*pentry&=0xff000000;
	*pentry|=block<<1;
}


/*
 * PRIVATE
 * 該当ブロックを空きにセットする。
 * parameters : block number
 */
STATIC INLINE void setBlockEmpty(uint pentry)
{
	int i;


	i=(getBlock(pentry)-PB_START_SECTER)/pbSectors;
	pbBitMap[i/BIT_NUM]&=(uchar)~(1<<(i%BIT_NUM));
}


/*
 * machine depend
 * PUBLIC
 * バックアップページの参照カウントを増やす
 * parameters : page entry
 */
STATIC void incBackupCount(uint *pentry)
{
	union{
		uint ui;
		uchar uc[4];
	}value;


	value.ui = *pentry;
	++value.uc[3];
	*pentry = value.ui;
}


/*
 * machine depend
 * PUBLIC
 * バックアップページの参照カウントを減らす
 * parameters : page entry
 */
STATIC INLINE void decBackupCount(uint *pageEnt)
{
	union{
		uint ui;
		uchar uc[4];
	}value;


	value.ui = *pageEnt;
	if (value.uc[3] == 0)
		setBlockEmpty(*pageEnt);
}


/*
 * PUBLIC
 * メモリーページをバックアップする
 * parameters : バックアップ物理アドレス,プロセステーブル,ページエントリアドレス
 * returns : 0 or Error number
 */
STATIC int backupPage(uint phaddr,PROC *proc,uint *pageEnt)
{
	int blk;


	enter_spinlock(&((MM_STRUCT*)proc->mm_struct)->pageDirGate);
	{
		/* すでにメモリーは開放されている。 */
		if (((*pageEnt & PAGE_PRESEN) == 0) || (phaddr != (*pageEnt & ~(PAGE_SIZE - 1))) )
			goto EXIT;

		/* page tableを不在に設定 */
		*pageEnt &= ~PAGE_PRESEN;

		/* 横取りされるプロセスが実行中ならpage tableをTBLからフラッシュする */
		if((proc->cpu!=get_current_cpu())&&(cputask[proc->cpu].current_task==proc))
			issue_interrupt_to_cpu(proc->cpu,FLASH_PAGEDIR_VECT);

		/* Store page. */
		if ((blk = getEmptyBackup()) == -1)
			goto ERR;
		if (phaddr < KERNEL_END)
		{
			if (write_cache(pbDin,(void*)phaddr,pbSectors,blk) != pbSectors)
				goto ERR;
		}
		else
		{
			int error=0;
			uint *pdir;
			uint *pentry;
			uint tmp;

			pdir=((MM_STRUCT*)get_current_task()->mm_struct)->pageDir;
			pentry=getPageEntryAddr(USER_BEG,(uint*)(*getPageDirEntryAddr(USER_BEG,pdir)&~(PAGE_SIZE-1)));
			tmp=*pentry;
			*pentry=phaddr|PAGE_PRESEN|PAGE_USER|PAGE_RW;
			flashPageDir(pdir);

			if(write_cache(pbDin,(void*)USER_BEG,pbSectors,blk)!=pbSectors)error=1;

			*pentry=tmp;
			flashPageDir(pdir);
			if(error==1)goto ERR;
		}

		/* バックアップステイトの割当て。 */
		if(*pageEnt&PT_COPY_ON_WRITE)*pageEnt=2;
		else *pageEnt=1;
		*pageEnt<<=24;
		setBlock(pageEnt,blk);
	}
EXIT:
	exit_spinlock(&((MM_STRUCT*)proc->mm_struct)->pageDirGate);

	return 0;

ERR:
	*pageEnt|=PAGE_PRESEN;
	exit_spinlock(&((MM_STRUCT*)proc->mm_struct)->pageDirGate);

	return -1;
}


/*
 * PUBLIC
 * バックアップページをメモリーに戻す
 * parmeters : device block,memory buffer
 * return : 0 or error=-1
 */
STATIC int restorePage(uint *pentry,void *buf)
{
	int rest;
	
	
	/* デバイスから読み込み。 */
	rest = read_cache(pbDin,buf,pbSectors,getBlock(*pentry));
	if (rest != pbSectors)
		return rest;

	/* 参照カウンタを減らす。 */
	decBackupCount(pentry);

	return 0;
}


/*
 * PUBLIC
 * バックアップページをメモリーに戻す
 * parmeters : device block,memory buffer
 * return : 0 or error number
 */
STATIC int getFromBackup(uint pentry,void *buf)
{
	int rest;
	
	/* デバイスから読み込み。 */
	rest = read_cache(pbDin,buf,pbSectors,getBlock(pentry));
	if (rest != pbSectors){
		return rest;
	}

	return 0;
}


/*
 * GLOBAL
 * ページバックアップ用デバイスをマウントする
 * parameters : device path
 * return : error number
 */
int initBackupDev(const char *dev_path)
{
	DEV_STAT dev_stat;
	int rest;

	/* Device open. */
	rest = mountPageBackupDev(dev_path, (void**)&pbDin);
	if (rest < 0){
		return rest;
	}

	/* パーティションタイプの確認。 */
	rest = get_devstat(pbDin, &dev_stat);
	if (rest < 0){
		return rest;
	}
	if (dev_stat.prt_type != PT_TYPE_PAGING){
		return -ENODEV;
	}
	pbSectors = PAGE_SIZE / dev_stat.sect_size;

	/* ビットマップの割当て。 */
	pbBitMapSize = (dev_stat.all_sect - PB_START_SECTER) / pbSectors / BIT_NUM;
	if ((pbBitMap = (char*)allocMultiKernelPage(ROUNDUP_DIV(pbBitMapSize, PAGE_SIZE))) == NULL){
		return -ENOMEM;
	}
	memset(pbBitMap, 0, pbBitMapSize);
	pbBitMapCurrent = 0;

	return NOERR;
}


/***********************************************************************************
 *
 * ページ操作関数
 *
 * ページアウトの条件
 * ・参照カウントが１であること
 *
 * ユーザースタックの配置
 *  0xfebfffff エラーナンバー
 *  0xfebffffb 環境変数
 *                 ↓
 *               引数
 *                 ↓
 *             環境変数アドレス配列
 *             引数アドレス配列
 *             環境変数アドレス配列ポインタ
 *             引数アドレス配列ポインタ
 *             引数カウント
 *
 ***********************************************************************************/


//================================== PRIVATE ============================================


// ページを割り当てて親プロセスのページをコピーする
// return : 0 ro error number
STATIC int copyKernelPage(uint *parentPageDir, uint *pageDir, void **o_kernelStack)
{
	void *kernelStack;
	uint *kStackPageTbl;

	// 親のページディレクトリを子にコピーする
	memcpy(pageDir, parentPageDir, PAGE_SIZE);

	// カーネルスタックを割り当てる
	kStackPageTbl = allocKernelPage();
	if (kStackPageTbl == NULL){
		return -ENOMEM;
	}
	memset(kStackPageTbl, 0, PAGE_SIZE);
	pageDir[KERNEL_STACK_BEG / PAGE_DIR_SIZE] = (uint)kStackPageTbl | PAGE_PRESEN | PAGE_RW;
	kernelStack = allocKernelPage();
	if (kernelStack == NULL){
		unlinkPhysTable(kStackPageTbl);
		return -ENOMEM;
	}
	kStackPageTbl[PAGE_SIZE / sizeof(uint) - 1] = (uint)kernelStack | PAGE_PRESEN | PAGE_RW;

	*o_kernelStack = kernelStack;

	return 0;
}


// カーネルスレッド作成でユーザーページを初期化する
static void initUserPage(MM_STRUCT *mmStruct)
{
	// ページディレクトリの初期化
//	memset(&mmStruct->pageDir[USER_BEG / PAGE_DIR_SIZE], 0, PAGE_SIZE - (USER_BEG / PAGE_DIR_SIZE) * sizeof(uint));
	
	// メモリー管理構造体の初期化
	mmStruct->lastPageAddr = USER_BEG;
	mmStruct->firstStackAddr = USER_END;
}


// ユーザーページを割り当てて親プロセスのページをコピーする
// return : 0 ro error number
STATIC int copyUserPage(MM_STRUCT *parentMmStruct, MM_STRUCT *mmStruct)
{
	uint *parentPageDir = parentMmStruct->pageDir;
	uint *pageDir = mmStruct->pageDir;
	uint *parentPageTbl;
	void *userStack;
	uint *pageTbl;
	int lastDir,firstPage;
	int c_dir,c_page;
	int rest;

	/*
	 * User領域をコピーオンライトに設定する。
	 * 親のページテーブルエントリーが書き込み可なら、書き込み不可にしてコピーオンライトフラグを設定する。
	 * 子のページディレクトリエントリーにコピーオンライトフラグを設定する。
	 * ユーザースタックのあるページテーブルとユーザースタックページは新たに割り当てる。
	 */
	// ユーザーテキストデータ割当ラストページディレクトリインデックス
	lastDir = ROUNDUP_DIV(parentMmStruct->lastPageAddr, PAGE_DIR_SIZE);
	for (c_dir = USER_BEG / PAGE_DIR_SIZE,c_page = 0; c_dir < lastDir; ++c_dir){
		if (pageDir[c_dir] == 0){
			continue;
		}

		/* ページエントリーにコピーオンライトを設定する */
		parentPageTbl = (uint*)(pageDir[c_dir] & ~(PAGE_SIZE - 1));
		for (c_page = 0; c_page < PAGE_SIZE / sizeof(uint); ++c_page){
			if (parentPageTbl[c_page] == 0){
				continue;
			}

			if (isPagePresen(parentPageTbl[c_page])){
				setCopyOnWrite(&parentPageTbl[c_page]);
				linkPhysTable((void*)(parentPageTbl[c_page] & ~(PAGE_SIZE - 1)));
			}
			else{
				incBackupCount(&parentPageTbl[c_page]);		/* バックアップ中 */
			}
		}

		/* ディレクトリエントリーにコピーオンライトフラグを立てる。 */
		setCopyOnWritePageTable(&pageDir[c_dir]);
		setCopyOnWritePageTable(&parentPageDir[c_dir]);
		linkPhysTable((void*)(pageDir[c_dir] & ~(PAGE_SIZE - 1)));
	}

	mmStruct->lastPageAddr = parentMmStruct->lastPageAddr;

	/*
	 * ユーザースタックの割当て
	 * 割り当てはユーザースタック先頭からユーザースタック最後まで
	 */
	firstPage = (parentMmStruct->firstStackAddr & (PAGE_DIR_SIZE - 1)) / PAGE_SIZE;
	for (c_dir = parentMmStruct->firstStackAddr / PAGE_DIR_SIZE; c_dir < USER_SP_BEG / PAGE_DIR_SIZE; ++c_dir){
		if (pageDir[c_dir] == 0){
			continue;
		}

		parentPageTbl = (uint*)(pageDir[c_dir] & ~(PAGE_SIZE - 1));
		pageTbl = allocKernelPage();
		if (pageTbl == NULL){
			rest = -ENOMEM;
			goto ERR;
		}
		pageDir[c_dir] = (uint)pageTbl | PAGE_PRESEN | PAGE_RW | PAGE_USER;
		memset(pageTbl, 0, PAGE_SIZE);
		
		for (c_page = firstPage; c_page < PAGE_SIZE / sizeof(uint); ++c_page){
			if (parentPageTbl[c_page] == 0){
				continue;
			}

			userStack = allocUserPage(&pageTbl[c_page]);
			if (userStack == NULL){
				rest = -ENOMEM;
				goto ERR;
			}
			pageTbl[c_page] = (uint)userStack | PAGE_PRESEN | PAGE_RW | PAGE_USER;
			if (isPagePresen(parentPageTbl[c_page])){
				memcpy(userStack,(void*)(parentPageTbl[c_page] & ~(PAGE_SIZE - 1)), PAGE_SIZE);
			}
			else{
				rest = getFromBackup(parentPageTbl[c_page], userStack);
				if (rest < 0){
					goto ERR;
				}
			}
		}
		firstPage = 0;
	}
	mmStruct->firstStackAddr = parentMmStruct->firstStackAddr;

	return 0;
ERR:
	for (c_dir = USER_SP_BEG / PAGE_DIR_SIZE - 1; (pageDir[c_dir] != 0) && (isSetCopyOnWrite(pageDir[c_dir]) == 0); --c_dir){
		pageTbl = (uint*)(pageDir[c_dir] & ~(PAGE_SIZE - 1));
		for (c_page = PAGE_SIZE / sizeof(uint) - 1; (0 <= c_page) && (pageTbl[c_page] != 0); --c_page){
			unlinkPhysTable((void*)(pageTbl[c_page] & ~(PAGE_SIZE - 1)));
		}
		unlinkPhysTable(pageTbl);
	}
	return rest;
}


/*
 * ページとページテーブルをページ単位で開放する。
 * 〔契約〕
 * 	ユーザーのテキスト・データページは常に全開放される。
 * 	ユーザースタックは常にスタック先頭ページから開放される。
 * 	カーネルスタックは常に全開放される。
 * 〔パラメータ〕
 * 	メモリー管理構造体,ページ境界ファーストアドレス,ページ境界ラストアドレス
 */
STATIC void releasePage(MM_STRUCT *mms, uint firstAddr, uint lastAddr)
{
	uint *pageDir = mms->pageDir;
	uint *pageTbl;
	int lastDir;
	int limDir;
	int firstPage;
	int lastPage;
	int c_dir,c_page;


	enter_spinlock(&mms->pageDirGate);
	{
		lastDir = ROUNDUP_DIV(lastAddr,PAGE_DIR_SIZE);
		limDir = lastAddr / PAGE_DIR_SIZE;
		firstPage = (firstAddr & (PAGE_DIR_SIZE - 1)) / PAGE_SIZE;
		for (c_dir = firstAddr / PAGE_DIR_SIZE; c_dir < lastDir; ++c_dir){
			if (pageDir[c_dir] == 0){
				continue;
			}

			pageTbl = (uint*)(pageDir[c_dir] & ~(PAGE_SIZE - 1));
	
			/* ページエントリーの開放 */
			if (c_dir == limDir){
				lastPage = ROUNDUP_DIV((lastAddr & (PAGE_DIR_SIZE - 1)),PAGE_SIZE);
			}
			else{
				lastPage = PAGE_SIZE / sizeof(uint);
			}
			for (c_page = firstPage; c_page < lastPage; ++c_page){
				if (pageTbl[c_page] == 0){
					continue;
				}

				/* ページが存在する */
				if (pageTbl[c_page] & PAGE_PRESEN){
					unlinkPhysTable((void*)pageTbl[c_page]);
				}
				/* ページがバックアップされている */
				else{
					decBackupCount(&pageTbl[c_page]);
				}
			}
			
			/* ページエントリーのクリアー */
			if (isSetCopyOnWrite(pageDir[c_dir]) == 0){
				for (c_page = firstPage; c_page < lastPage; ++c_page){
					pageTbl[c_page] = 0;
				}
			}

			firstPage = 0;

			/*
			 * 全テーブルエントリーを開放したらページディレクトリも開放する。
			 */
			if ((lastAddr == mms->lastPageAddr) || (lastPage == PAGE_SIZE / sizeof(uint))){
				unlinkPhysTable((void*)pageDir[c_dir]);
				pageDir[c_dir] = 0;
			}
		}
	}
	exit_spinlock(&mms->pageDirGate);
}


/*
 * 引数の環境変数の総バイト数を計算する
 * parameters : 引数,環境変数,引数カウントバッファ
 * return : 総バイト数
 */
STATIC int calcArgLen(char **argv,char **envp,int *argc,int *envc)
{
	int size=0;
	int i;


	for (i = 0; argv[i] != NULL; ++i)
		size += strlen(argv[i]) + 1;			/* 引数サイズ。 */
	size += (i + 1) * sizeof(char*);			/* 引数アドレス配列サイズ。 */
	size += sizeof(char**);						/* 引数アドレス配列ポインタ。 */
	*argc = i;

	for (i = 0; envp[i] != NULL; ++i)
		size += strlen(envp[i]) + 1;			/* 環境変数サイズ。 */
	size += (i + 1) * sizeof(char*);			/* 環境変数アドレス配列サイズ。 */
	size += sizeof(char**);						/* 環境変数アドレス配列ポインタ。 */
	*envc = i;

	return ROUNDUP(size+sizeof(int),sizeof(int));		/* +argc. */
}


/*
 * 文字列をコピーして文字列サイズ+1を返す。
 * parameters : コピー先バッファ,コピー元文字列
 * return : 文字列サイズ
 */
STATIC INLINE int cpystr(char *s1,const char *s2)
{
	int i;

	i=0;
	while((s1[i]=s2[i])!='\0')++i;

	return i+1;
}


//================================== PUBLIC =============================================


/*
 * 親プロセスのメモリーコンテキストをコピーする
 * parameters : isKernelThreadにはカーネルスレッドを作成するときに"0"以外を設定する
 * return : New MM_STRUCT address or error=NULL
 */
int forkPage(MM_STRUCT *parentMmStruct, int isKernelThread, void **o_mmStruct, void **o_kernelStack)
{
	uint *pageDir;
	MM_STRUCT *mmStruct;
	int rest;

	mmStruct = kmalloc(sizeof(MM_STRUCT));
	if (mmStruct == NULL){
		return -ENOMEM;
	}
	mmStruct->pageDirGate = 0;

	pageDir = allocKernelPage();
	if (pageDir == NULL){
		kfree(mmStruct);
		return -ENOMEM;
	}
	mmStruct->pageDir = pageDir;

	// カーネルページをコピーする
	rest = copyKernelPage(parentMmStruct->pageDir, pageDir, o_kernelStack);
	if (rest < 0){
		goto  ERR;
	}

	if (isKernelThread != 0){
		// ユーザーページをクリアーする
		initUserPage(mmStruct);
	}
	else{
		enter_spinlock(&parentMmStruct->pageDirGate);
		{
			// ユーザーページをコピーする
			rest = copyUserPage(parentMmStruct, mmStruct);
			if (rest < 0){
				exit_spinlock(&parentMmStruct->pageDirGate);
				goto  ERR;
			}
		}
		exit_spinlock(&parentMmStruct->pageDirGate);
	}
	
	/* ページディレクトリを更新する。 */
	flashPageDir(parentMmStruct->pageDir);

	*o_mmStruct = mmStruct;
	
	return 0;
ERR:
	kfree(mmStruct);
	unlinkPhysTable(pageDir);
	return rest;
}


/*
 * 実行ファイルをメモリーにロードする
 * 引数のEXEC_FDポインタがNULLならメモリーを0クリアーする。
 * 追加予定機能
 * ○ すでにロード済の同実行ファイルの読み取りセクションを割当て、書込みセクションをコピーオンライトにする。
 * parameters：EXEC_FDポインタ、割り当てバイト数、ロードバイト数、ロード開始リニアアドレス、ページアクセス属性（読込みのみ=LB_R,読み書き=LB_WR）
 * return : 0 or error=-1
 */
int loadBinary(EXEC_FD *open_f,size_t size,size_t load_size,uint address,int attribute)
{
	void *page;
	uint *pdir;
	uint *ptbl;
	uint *pentry;
	uint addr,last;
	MM_STRUCT *mms = (MM_STRUCT*)get_current_task()->mm_struct;


	pdir = mms->pageDir;
	last = address + size;

/*	enter_spinlock(&mms->pageDirGate);*/
	{
		/* ロードサイズ分メモリーを割り当てる。 */
		for(addr=address;addr<last;addr+=PAGE_SIZE)
		{
			if((ptbl=(uint*)(*getPageDirEntryAddr(addr,pdir)&~(PAGE_SIZE-1)))==NULL)
			{
				/* ページテーブルの割当て。 */
				if((ptbl = allocKernelPage()) == NULL)
					goto ERR;
				memset(ptbl,0,PAGE_SIZE);
				*getPageDirEntryAddr(addr,pdir)=(uint)ptbl|PAGE_PRESEN|PAGE_USER|PAGE_RW;
			}

			/* メモリーを割り当て属性を設定する。 */
			pentry=getPageEntryAddr(addr,ptbl);
			if ((page = allocUserPage(pentry))==NULL)goto ERR;
			*pentry=(uint)page|PAGE_PRESEN|PAGE_USER|PAGE_RW;
		}
	}
/*	exit_spinlock(&mms->pageDirGate);*/

	/* ページディレクトリを更新する。 */
	flashPageDir(pdir);

	/* ファイルからメモリーへ読み込む。 */
	if(exec_read(open_f,(void*)address,load_size)!=load_size)return -1;
	memset((char*)address+load_size,0,size-load_size);

	/* ページ属性が読込みなら設定し直す。 */
	if(attribute==LB_R)
	{
		for(addr=address;addr<last;addr+=PAGE_SIZE)
		{
			ptbl=(uint*)(*getPageDirEntryAddr(addr,pdir)&~(PAGE_SIZE-1));
			*getPageEntryAddr(addr,ptbl)&=~PAGE_RW;
		}

		/* ページディレクトリを更新する。 */
		flashPageDir(pdir);
	}

	/* ラストページアドレスを更新する。 */
	if (mms->lastPageAddr < last)
		mms->lastPageAddr = ROUNDUP(last,PAGE_SIZE);

	return 0;

ERR:
/*	exit_spinlock(&mms->pageDirGate);*/

	return -1;
}


/*
 * ユーザースタックを設定する
 * parameters : 引数,環境変数
 * return : スタック先頭アドレス
 */
uint setUserStack(char **argv,char **envp)
{
	void *buf;
	uint stack;
	char *str;
	char *tmp=NULL;
	uint diff;
	int argSize;
	int argc,envc;
	uint esp;
	MM_STRUCT *mms = (MM_STRUCT*)get_current_task()->mm_struct;
	int i;


	/* 引数がNULLなら仮引数を与える。 */
	if (argv == NULL){
		argv = &tmp;
	}
	if (envp == NULL){
		envp=&tmp;
	}

	/* "sizeof(SYS_INFO)+sizeof(int)"はシステム情報格納用とmain()のリターンアドレス格納用。 */
	argSize = calcArgLen(argv,envp,&argc,&envc);
	if (PAGE_SIZE < argSize + sizeof(SYS_INFO) + sizeof(int)){
#ifdef DEBUG
		PRINT_ERR("setUserStack() size over error!");
#endif
		return 0;
	}

	buf = kmalloc(argSize + sizeof(SYS_INFO) + sizeof(int));
	if(buf == NULL){
		return 0;
	}
	diff = USER_ESP_BEG - (uint)buf - (argSize + sizeof(SYS_INFO));
	stack = (uint)buf;

	*(int*)stack = argc;														/* argc. */
	stack += sizeof(int);
	*(uint*)stack = stack + sizeof(char**) * 2 + diff;							/* argv. */
	stack += sizeof(char**);
	*(uint*)stack = stack + sizeof(char**) + sizeof(char*) * (argc + 1) + diff;	/* envp. */
	stack += sizeof(char**);

	str = (char*)((char**)stack + (argc + envc + 2));

	/* argvのコピー。 */
	for(i = 0; i < argc; ++i){
		((char**)stack)[i] = str + diff;
		str += cpystr(str,argv[i]);
	}
	((char**)stack)[i] = NULL;
	stack += sizeof(char*) * (i + 1);

	/* envpのコピー。 */
	for(i = 0; i < envc; ++i){
		((char**)stack)[i] = str + diff;
		str += cpystr(str,envp[i]);
	}
	((char**)stack)[i] = NULL;

	/* バッファからスタックにコピー。 */
	memcpy((void*)(USER_ESP_BEG - argSize - sizeof(SYS_INFO)), buf, argSize);

	kfree(buf);

	/* 新esp */
	esp = USER_ESP_BEG - argSize - sizeof(SYS_INFO) - sizeof(int);

	/* 割当以降のスタックページを開放する */
	releasePage(mms, mms->firstStackAddr, ROUNDDOWN(esp,PAGE_SIZE));

	/* 先頭のスタックアドレスを設定する */
	mms->firstStackAddr = ROUNDDOWN(esp,PAGE_SIZE);

	return esp;
}


/*
 * ユーザーのテキストとデータページを開放する。
 * parameters : メモリー管理構造体
 */
void releaseUserPage(void *mm_struct)
{
	MM_STRUCT *mms = (MM_STRUCT*)get_current_task()->mm_struct;


	releasePage(mms,USER_BEG,mms->lastPageAddr);
}


/*
 * ユーザーのカーネルページとページディレクトリを開放する
 * parameters : メモリー管理構造体
 */
void releasePageDir(void *mm_struct)
{
	MM_STRUCT *mms = mm_struct;
	
	/* ユーザテキスト・データページを開放する。 */
	releasePage(mms,USER_BEG,mms->lastPageAddr);

	/* ユーザスタックを開放する。 */
	releasePage(mms,mms->firstStackAddr,USER_END);

	/* カーネルスタックページを開放する。 */
	releasePage(mm_struct,KERNEL_STACK_BEG,KERNEL_END);

	/* ページディレクトリを開放する。 */
	unlinkPhysTable(mms->pageDir);

	kfree(mm_struct);
}


/*
 * ページフォルト処理
 * todo
 *  ユーザースタックページ不在の処理の見直し。(制限を設ける等)
 * parameters : page fault address,Exception frame
 */
STATIC void printContext(uint errnum,uint addr,uint eip,uint cs,uint esp,uint ss)
{
#ifdef DEBUG	
	printSyscallEntry(1);
#endif
	if(cs == KERNEL_CODE_DES){
		printk("error=%x fault=%x eip=%x cs=%x process=%x\n",
		        errnum,addr,eip,cs,get_current_task());
		decrementNest();
	}
	else{
		printk("error=%x fault=%x eip=%x cs=%x esp=%x ss=%x process=%x\n",
		        errnum,addr,eip,cs,esp,ss,get_current_task());
	}
}


void pageFault(INTERRUPT_FRAME frame)
{
	/* エラーコードビットマスク。 */
	enum{
		PRESEN		= 1,
		RW_MODE		= 2,
		USER_MODE	= 4,
		RSVBIT		= 8,
	};
	MM_STRUCT *mm_struct = get_current_task()->mm_struct;
	int kernelAreaFault,userAreaFault;
	int rest;
	uint faultAddr = getFualtAddr();

	sti();

	/* ユーザーの特権ページアクセス違反 */
	kernelAreaFault = (faultAddr < USER_BEG  ) ||
	                  (IOMAP_BEG <= faultAddr);
	userAreaFault = (mm_struct->lastPageAddr < faultAddr                  ) &&
	                (faultAddr < mm_struct->firstStackAddr - PAGE_SIZE * 2);
	if (kernelAreaFault || userAreaFault){
		/* ユーザーシグナルハンドラからの戻りの場合。 */
		if (faultAddr == (uint)returnUserHandler){
			decrementNest();
			returnUserHandler();
		}
		printk("Page privilege violation!\n");
		printContext(frame.error,faultAddr,frame.eip,frame.cs,frame.user_esp,frame.user_ss);
		forceSendSignal(get_current_task(), SIGSEGV, TASK_SIGNAL_WAIT);
		return;
	}

	/* ページ不在 */
	if ((frame.error & PRESEN) == 0){
		/* ページエントリーにページメモリーを割り当てる */
		rest = setPageMemory(faultAddr,mm_struct);
		if (rest != 0)
		{
			printk("error code = %d ",rest);
			PRINT_ERR("setPageMemory() error!");
			forceSendSignal(get_current_task(), SIGSEGV, TASK_SIGNAL_WAIT);
			return;
		}
		else{
			/* 割当先頭アドレスの更新 */
			if ((mm_struct->firstStackAddr - PAGE_SIZE * 2 <= faultAddr) &&
			    (faultAddr < mm_struct->firstStackAddr                 )    ){
				mm_struct->firstStackAddr = ROUNDDOWN(faultAddr,PAGE_SIZE);
			}
		}
	}
	else{
		/* コピーオンライトかどうか */
		if(isCopyOnWrite(faultAddr,mm_struct)){
			/* コピーオンライトのページエントリーにページメモリーを割り当てる */
			rest = setPageMemoryCopyOnWriteEntry(faultAddr,mm_struct);
			if (rest != 0){
				PRINT_ERR("setPageMemoryCopyOnWriteEntry() error!");
				forceSendSignal(get_current_task(), SIGSEGV, TASK_SIGNAL_WAIT);
				return;
			}
		}
		/* ページ書き込み違反 */
		else{
			printk("\nRead only page violation!\n");
			printContext(frame.error,faultAddr,frame.eip,frame.cs,frame.user_esp,frame.user_ss);
			forceSendSignal(get_current_task(), SIGSEGV, TASK_SIGNAL_WAIT);
			return;
		}
	}

	/* TBLを更新する。 */
	flashPageDir(mm_struct->pageDir);
	
	return;
}


/*
 * ユーザーバッファアドレスをチェックする
 * 主な目的はカーネルを破壊させないようにすることと、カーネル内で例外が発生して
 * メモリーリークを起こすのを防ぐこと。
 * return : NOERR or ERR
 */
int checkMem(const void *memory,size_t size)
{
	int kernelArea,noAllocArea;
	const char *mem = memory;
	MM_STRUCT *mm_struct = get_current_task()->mm_struct;


	kernelArea = ((uint)mem < USER_BEG         ) ||
	             (IOMAP_BEG <= (uint)mem       ) ||
	             (IOMAP_BEG < (uint)mem + size);
	noAllocArea = ((uint)mem < mm_struct->firstStackAddr - PAGE_SIZE) &&
	              (mm_struct->lastPageAddr +1 < (uint)mem + size   );
	if (kernelArea || noAllocArea){
		return ERR;
	}
	else{
		return NOERR;
	}
}


/*
 * ページディレクトリを再読み込みする
 * parameters : memory manage structure
 */
void updateVm(void *mm_struct)
{
	flashPageDir(((MM_STRUCT*)get_current_task()->mm_struct)->pageDir);
}


/*
 * ページディレクトリアドレスを取得する
 * return : page directory address
 */
uint *getPageDir()
{
	return ((MM_STRUCT*)get_current_task()->mm_struct)->pageDir;
}


/*
 * ユーザー領域・カーネルスタックの論理アドレスから物理アドレスを取得する
 */
uint getPhysicalFromLogicalAddr(PROC *proc, uint logicalAddr)
{
	MM_STRUCT *mmStruct = proc->mm_struct;
	uint *pageTbl = (uint*)(mmStruct->pageDir[logicalAddr / PAGE_DIR_SIZE] & ~(PAGE_SIZE - 1));
	
	if (pageTbl == NULL){
		return 0;
	}
	else{
		uint pageEntry = pageTbl[(logicalAddr % PAGE_DIR_SIZE) / PAGE_SIZE];
		
		if (pageEntry & PAGE_PRESEN){
			 return (pageEntry & ~(PAGE_SIZE - 1)) + (logicalAddr % PAGE_SIZE);
		}
		else{
			return 0;
		}
	}
}


// ユーザーハンドラに移行するためカーネルスタックとユーザースタックを入れ替える、"doSigHandler"から呼ばれる。
// return : NOERR or error number
int doUserHandler(int sigNum, SIG_ACTION sigaction)
{
	uint *kernelPageTbl;
	uint *userPageTbl;
	uint *kernelStk;
	uint *userStk;
	uint userEsp;
	PROC *proc = get_current_task();
	MM_STRUCT *mmStruct = (MM_STRUCT*)proc->mm_struct;
	int emptyUser;
	int emptykernel;
	int error = NOERR;

	// カーネルスタックとユーザースタック領域を確保する。
	kernelStk = allocKernelPage();
	if (kernelStk == NULL){
		return -ENOMEM;
	}
	userPageTbl = (uint*)(*getPageDirEntryAddr(USER_ESP_BEG - 1, mmStruct->pageDir) & ~(PAGE_SIZE - 1));
	userStk = allocUserPage(&userPageTbl[PAGE_SIZE / sizeof(uint) - 1]);
	if (userStk == NULL){
		unlinkPhysTable(kernelStk);
		return -ENOMEM;
	}

	// ユーザースタックのコンテキストを設定する。
	userStk[PAGE_SIZE / sizeof(uint) - 1] = sigNum;
	userStk[PAGE_SIZE / sizeof(uint) - 2] = (uint)returnUserHandler;
	userEsp = USER_ESP_BEG - (sizeof(uint) * 2);
	
	// ユーザースタックページを入れ替える。
	for (emptyUser = PAGE_SIZE / sizeof(uint) - 2; userPageTbl[emptyUser] != 0; --emptyUser){
		if (emptyUser <= 0){
			error = -ENOSPC;
			goto ERR;
		}
	}
	userPageTbl[emptyUser] = userPageTbl[PAGE_SIZE / sizeof(uint) - 1];
	userPageTbl[PAGE_SIZE / sizeof(uint) - 1] = (uint)userStk | PAGE_PRESEN | PAGE_RW | PAGE_USER;
	// ２ページ目を書込み禁止にしてページフォルトを起こすようにする。
	userPageTbl[PAGE_SIZE / sizeof(uint) - 2] &= ~PAGE_RW;

	// カーネルスタックページの空きに現在のページを退避する。
	kernelPageTbl = (uint*)(*getPageDirEntryAddr(KERNEL_ESP_BEG - 1, mmStruct->pageDir) & ~(PAGE_SIZE - 1));
	for (emptykernel = PAGE_SIZE / sizeof(uint) - 2; kernelPageTbl[emptykernel] != 0; --emptykernel){
		if (emptykernel <= 0){
			error = -ENOSPC;
			goto ERR;
		}
	}
	kernelPageTbl[emptykernel] = kernelPageTbl[PAGE_SIZE / sizeof(uint) - 1];

	// ユーザーハンドラへ移行する。
	callUserHandler((uint)kernelStk | PAGE_PRESEN | PAGE_RW, &kernelPageTbl[PAGE_SIZE / sizeof(uint) - 1],
		sigaction, userEsp, KERNEL_ESP_BEG - FPU_SAVE_SIZE - (PAGE_SIZE / sizeof(uint) - 1 - emptykernel) * PAGE_SIZE, &proc->espBack);

	// ユーザースタック２ページ目を書込み可能に戻す。
	userPageTbl[PAGE_SIZE / sizeof(uint) - 2] |= PAGE_RW;
	// ユーザースタックページを戻す。
	userPageTbl[PAGE_SIZE / sizeof(uint) - 1] = userPageTbl[emptyUser];
	userPageTbl[emptyUser] = 0;
	
	kernelPageTbl[emptykernel] = 0;
ERR:
	unlinkPhysTable(kernelStk);
	unlinkPhysTable(userStk);

	return error;
}


// カーネルスタックを元に戻し、"callUserHandler"呼び出し元に復帰する、"pageFault"から呼ばれる。
void returnUserHandler()
{
	uint *kernelPageTbl;
	PROC *proc = get_current_task();
	MM_STRUCT *mmStruct = (MM_STRUCT*)proc->mm_struct;
	int empty;

	// カーネルスタックページを探す。
	kernelPageTbl = (uint*)(*getPageDirEntryAddr(KERNEL_ESP_BEG - 1, mmStruct->pageDir) & ~(PAGE_SIZE - 1));
	for (empty = PAGE_SIZE / sizeof(uint) - 2; (kernelPageTbl[empty] != 0) && (0 < empty); --empty){;
	}

	returnCallUserHandler(proc->espBack, kernelPageTbl[empty + 1], &kernelPageTbl[PAGE_SIZE / sizeof(uint) - 1]);

	// ここには戻って来ないはず。
	ASSERT(0);
}


/*
 * 起動時のカーネルページテーブルの設定
 * return : MM_STRUCTアドレス or error=NULL
 */
void *initPaging()
{
	void *stack;/*,*user_page;*/
	uint *pdir,*ptbl;
	MM_STRUCT *mstruct;
	int i;


	/* カーネルページディレクトリの設定 */
	if((pdir=(uint*)allocKernelPage())==NULL)return NULL;
	memset(pdir,0,PAGE_SIZE);
	for(i=0;i<KERNEL_STACK_BEG/PAGE_DIR_SIZE;++i)
		pdir[i]=i*PAGE_DIR_SIZE|PAGE_PRESEN|PAGE_RW|PAGE_4M;

	/* カーネルスタックページを割り当てる */
	if((ptbl=(uint*)allocKernelPage())==NULL)return NULL;
	memset(ptbl,0,PAGE_SIZE);
	pdir[KERNEL_STACK_BEG/PAGE_DIR_SIZE]=(uint)ptbl|PAGE_PRESEN|PAGE_RW;
	if((stack=allocKernelPage())==NULL)return NULL;
	ptbl[PAGE_SIZE/sizeof(uint)-1]=(uint)stack|PAGE_PRESEN|PAGE_RW;

	/* メモリーマップドIOページ用ディレクトリの設定 */
	for(i=IOMAP_BEG/PAGE_DIR_SIZE;i<PAGE_SIZE/sizeof(uint);++i)
	   	pdir[i]=i*PAGE_DIR_SIZE|PAGE_PRESEN|PAGE_RW|PAGE_4M|PAGE_CASH_DISABLE;

	/* ページング開始 */
	asm volatile(
			"movl	%%eax,%%cr3\n"			/* Set page directory address */
			"movl	$0x10,%%eax\n"
			"movl	%%eax,%%cr4\n"			/* 4Mページ拡張 */
			"movl	%%cr0,%%eax\n"
			"orl	$0x80010000,%%eax\n"	/* Enable paging,Enable wp(スーパーバイザからの書き込み保護) */
			"movl	%%eax,%%cr0\n"
			"jmp	1f\n"
		"1:"
		::"a"(pdir)
	);

	/* メモリー管理構造体の初期化。 */
	if((mstruct=(MM_STRUCT*)kmalloc(sizeof(MM_STRUCT)))==NULL)return NULL;
	mstruct->pageDir = pdir;
	mstruct->pageDirGate = 0;
	mstruct->firstStackAddr = USER_SP_BEG;

	return (void*)mstruct;
}


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

/*
 * ユーザーページを指定のアドレスまで割り当てる。
 */
int sys_brk(void *addr)
{
	MM_STRUCT *mm_struct = get_current_task()->mm_struct;


	if ((uint)addr < mm_struct->lastPageAddr)
		return 0;
	if (mm_struct->firstStackAddr <= (uint)addr)
		return -ENOMEM;

	mm_struct->lastPageAddr = ROUNDUP((uint)addr,PAGE_SIZE);

	return 0;
}


/***********************************************************************************
 *
 * 初期化関数
 *
 ***********************************************************************************/

/*
 * GLOBAL
 * メモリーシステムの初期化
 */
void initMm()
{
	/* Find size of physical memory  */
	printk("Memory size = %d MB\n",findAllMemsize()/0x100000);

	/* 物理メモリーテーブルの設定 */
	initPhysMemTable();
}
/************************************************************************/
int test_mm(uint addr)
{
	PHYS_MEM_TABL *mtbl = getPhysTableFromAddr((void*)addr);
	printk("ref_count = %d\n", mtbl->ref_count);

	return 0;
}
/*************************************************************************/
