091018

|


SH4A続き。そうもSH4Aばかりいじってもいられないのでちょっとここでおやす
み。割り込みがあがっているように見えたのはscifintrで無限ループに入って
いた。SH4(775x)のSCIFはFIFOのカウンタがRX,TX一つのレジスタだったのが、
SH4A(778x) では別々のレジスタになっていたのだ。これでなんとかSH4互換モード
では動くようになった。

ちょっと以下俺コンテキストセーブ。内容はあってないかも。ちょっとロガー なんとかしないとBSに間に合わない。
Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
    2006, 2007, 2008, 2009
    The NetBSD Foundation, Inc.  All rights reserved.
Copyright (c) 1982, 1986, 1989, 1991, 1993
    The Regents of the University of California.  All rights reserved.

NetBSD 5.99.20 (RENESAS7785LCR) #643: Mon Oct 19 20:46:10 JST 2009
        uch@alexandrite:/usr/src/sys/arch/evbsh3/compile/RENESAS7785LCR
general exception handler:      324 byte
TLB miss exception handler:     272 byte
interrupt exception handler:    288 byte
total memory = 127 MB
avail memory = 120 MB
Renesas evaluation board R0P7785LC0011RL
mainbus0 (root)
cpu0 at mainbus0: SH4A 599.999 MHz PCLOCK 49.999 MHz
cpu0: 32KB/32B 4-way set-associative Instruction cache.
cpu0: 32KB/32B 4-way set-associative Data cache.
cpu0: U0, P0, P3 write-back; P1 write-back
cpu0: full-associative 4 ITLB, 64 UTLB entries
cpu0: multiple virtual storage mode, SQ access: kernel, wired 7
cpu0: 29bit physical address mode. TLB-compatible mode.
shb0 at mainbus0
scif0 at shb0
scif0: console
rn_init: radix functions require max_keylen be set
root on md0a dumps on md0b
mountroot: trying ffs...
root file system type: ffs
WARNING: no TOD clock present
WARNING: using filesystem time
WARNING: CHECK AND RESET THE DATE!
init: copying out flags `-s' 3
init: copying out path `/sbin/init' 11
erase ^?, werase ^W, kill ^U, intr ^C

mount_kernfs: kernfs on /kern: Operation not supported by device
# df
Filesystem   1K-blocks       Used      Avail %Cap Mounted on
/dev/md0a          3811       1323       2488  34% /
# ls
.profile     etc          kern         mnt2         tmp          usr.install
bin          install      libexec      sbin         upgrade      var
dev          install.sub  mnt          targetroot   usr
# 


さて、ここで物理アドレス32bitモードにして512MB全部UVMに積みたい。全ての
アドレスがP1/P2から参照できなくなるので

SH3_PHYS_TO_P1SEG((pa))
SH3_P1SEG_TO_PHYS((va))

のマクロを使ったところをなんとかしないといけない。512MBまでならPMBを使っ
てP1/P2全てにメモリをマップしてしまい、その領域のデバイスメモリはP3から
マップしてやればなんとかなりそうだけれど。

ここでSH4互換モードのまま
#define	PMAP_MAP_POOLPAGE(pa)		SH3_PHYS_TO_P1SEG((pa))
#define	PMAP_UNMAP_POOLPAGE(va)		SH3_P1SEG_TO_PHYS((va))

このデファインをなくしてみた。こうなるとUVMの方で、適当にとってきたペー
ジをpmap_kenter_paでマップする。pmap_kenter_paはpmap_kernel()だけのマッ
ピングをひき受けるルーチンで、これは'wired'。このwiredはTLBのワイヤード
ではなく、仮想アドレス-物理アドレスの組が変更されることがないことを意味
する。pmap_kenter_paでマッピングされているページはシェルが立ちあがった
以降で300ページ弱ある。

つまりpool(9)がP1からP3に移る(独自のアロケータを設定してpool_initしなければ)。

しかしこの変更したカーネルではinit起動後、0〜10秒でランダムなタイミング
でリセットしてしまう。キャッシュをオフにしても同じだ。SH4AのLDTLB命令は
TLBミス例外の後にMMUCR.URCを1を加算してからLDTLB命令を発行しないといけ
ない。これをちゃんとやってもだめ。(SH4Aでは他にメモリ割りつけのTLBをP2
からいじる必要はないP1でいい)

P3になったとして、例外が起こって、VAでpmap_kernel()のページテーブルペー
ジスロットを参照する。pmap_kernel()はP1にある。スロットは
pmap_kenter_paで既に登録したところなので絶対にある。そこのPTEもある。の
でそれをTLBに乗せるだけだ。

手間がかかるだけで問題はないはずなのだけど...。

これらはページテーブルページのインデックス、オフセットを仮想アドレスから
とりだすためのもの。
#define	__PMAP_PTP_SHIFT	22
#define	__PMAP_PTP_TRUNC(va)						\
	(((va) + (1 << __PMAP_PTP_SHIFT) - 1) & ~((1 << __PMAP_PTP_SHIFT) - 1))
#define	__PMAP_PTP_PG_N		(PAGE_SIZE / sizeof(pt_entry_t))
#define	__PMAP_PTP_INDEX(va)	(((va) >> __PMAP_PTP_SHIFT) & (__PMAP_PTP_N - 1))
#define	__PMAP_PTP_OFSET(va)	((va >> PGSHIFT) & (__PMAP_PTP_PG_N - 1))

これがカーネルのアドレス空間。P3のだけとしている。
VM_MIN_KERNEL_ADDRESS(0xc0000000)からVM_MAX_KERNEL_ADDRESS(0xe0000000)まで。
カーネル空間からはP0(0x0-0x80000000)も扱えるけれど、そこは使っていない。
使わなければ0x80000000より上ならカーネル空間、下ならユーザ空間と切りわけ
できて楽。実際それで空間を識別している。
struct pmap __pmap_kernel;
これはUVMに登録するための正式な名前にしている。
struct pmap *const kernel_pmap_ptr = &__pmap_kernel;
これはカーネル仮想空間のリミット。pmap_growkernelが呼び出されるとどんどん
増えていく。
STATIC vaddr_t __pmap_kve;	/* VA of last kernel virtual */
これらはkloaderが次にロードするカーネルをストアする領域をかき集める時のヒント
のために用意されている。なくてもいい。
// kloader uses avail_start and avail_end.
paddr_t avail_start;		/* PA of first available physical page */
paddr_t avail_end;		/* PA of last available physical page */
これはTLB例外が起きた時にできるだけ早くTLBにロードできるようにpmap_activate
で有効化されたアドレス空間のページテーブルページスロットを差している。
これはTTBレジスタ使った方が効率的かも。
/* For the fast tlb miss handler */
pt_entry_t **curptd;		/* p1 va of curlwp->...->pm_ptp */

これはstruct pmap自体をアロケートするためのプール。
/* pmap pool */
STATIC struct pool __pmap_pmap_pool;

これはstruct vm_pageのうちMD部分の拡張にあたる、
//	struct vm_page_md {
//		SLIST_HEAD(, pv_entry) pvh_head;
//		int pvh_flags;
//	};
につながれるリストになる。物理アドレスと仮想アドレスのマッピングが確定した
時に、そのvm_pageにつながれる。
/* pv_entry ops. */
struct pv_entry {
	struct pmap *pv_pmap;
	vaddr_t pv_va;
	SLIST_ENTRY(pv_entry) pv_link;
};

vm_pageはそのアドレスがメモリである時に(デバイスじゃない)、物理アドレス
に対して設定される。それに対して仮想アドレスは一意に決まらないので(複数
のプロセスから共有されているかもしれない)、アドレス空間(pv_pmap)、その
中でのオフセット(pv_va)の組で登録しておくことで、物理アドレスから、どこ
の仮想アドレスにマップしているかをリストをたぐればわかるようにしてある。

pmapとvaがあればそのpmapのページテーブルページからたどってPTEを探せば
物理アドレスは一意に決まる。

こういうマクロは好きじゃないな。俺が書いたのかもしれないが。
#define	__pmap_pv_alloc()	pool_get(&__pmap_pv_pool, PR_NOWAIT)
#define	__pmap_pv_free(pv)	pool_put(&__pmap_pv_pool, (pv))

これはMD拡張になるpv_entryを登録、抹消するためのもの。
STATIC void __pmap_pv_enter(pmap_t, struct vm_page *, vaddr_t);
STATIC void __pmap_pv_remove(pmap_t, struct vm_page *, vaddr_t);

これはそのpv_entryをプールからとってきたりするもの
STATIC void *__pmap_pv_page_alloc(struct pool *, int);
STATIC void __pmap_pv_page_free(struct pool *, void *);
そのプール。
STATIC struct pool __pmap_pv_pool;
ここでプールのアロケータに独自のを指定している。これはP1アドレスでpv_entry
をとるためのものだ。
STATIC struct pool_allocator pmap_pv_page_allocator = {
	__pmap_pv_page_alloc, __pmap_pv_page_free, 0,
};

/* ASID ops. */
STATIC int __pmap_asid_alloc(void);
STATIC void __pmap_asid_free(int);
STATIC struct {
	uint32_t map[8];
	int hint;	/* hint for next allocation */
} __pmap_asid;

これはPTEをアロケートする。PTPスロットがなければスロットにページテーブルページ
をアロケートして、そこからのオフセットを返す。
/* page table entry ops. */
STATIC pt_entry_t *__pmap_pte_alloc(pmap_t, vaddr_t);

これは今迄のマップを変更した時のものだけど、その実装はちょっと不明なと
ころがある。
/* pmap_enter util */
STATIC bool __pmap_map_change(pmap_t, vaddr_t, paddr_t, vm_prot_t,
    pt_entry_t);

void
pmap_bootstrap(void)
{
uvm_pageboot_allocはこの中でpmap_steal_memoryを呼び出しそのアドレスを確定する。
	/* Steal msgbuf area */
	initmsgbuf((void *)uvm_pageboot_alloc(MSGBUFSIZE), MSGBUFSIZE);
ここでインデックス0を最初とできるのはvmparm.hで
#define	VM_PHYSSEG_STRAT	VM_PSTRAT_BSEARCH
と指定しているので、uvm_page_physloadがアドレス順にしてくれているから。
	avail_start = ptoa(vm_physmem[0].start);
	avail_end = ptoa(vm_physmem[vm_nphysseg - 1].end);
	__pmap_kve = VM_MIN_KERNEL_ADDRESS;

	pmap_kernel()->pm_refcnt = 1;
	pmap_kernel()->pm_ptp = (pt_entry_t **)uvm_pageboot_alloc(PAGE_SIZE);
	memset(pmap_kernel()->pm_ptp, 0, PAGE_SIZE);

	/* Enable MMU */
	sh_mmu_start();
	/* Mask all interrupt */
	_cpu_intr_suspend();
	/* Enable exception for P3 access */
	_cpu_exception_resume(0);
}

vaddr_t
pmap_steal_memory(vsize_t size, vaddr_t *vstart, vaddr_t *vend)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_physseg *bank;
	int i, j, npage;
	paddr_t pa;
	vaddr_t va;

	KDASSERT(!uvm.page_init_done);

	size = round_page(size);
	npage = atop(size);

#ifdef SH4A_EXT_ADDR32
uvm_pageboot_allocが必ずP1/P2にマップされるエリアのメモリを返す用に
その領域からのメモリを返します。これは
#define	VM_PHYSSEG_MAX		4
#define	VM_NFREELIST		2
#define	VM_FREELIST_P1ACCESS	1
のようにして、別々のフリーリストにメモリを登録した。
	// To assure uvm_pageboot_alloc allocates P1/P2 mapped area.
	for (i = 0, bank = &vm_physmem[i]; i < vm_nphysseg; i++, bank++)
		if ((npage <= bank->avail_end - bank->avail_start) &&
		    (bank->free_list == VM_FREELIST_P1ACCESS))
			break;
#else
	for (i = 0, bank = &vm_physmem[i]; i < vm_nphysseg; i++, bank++)
		if (npage <= bank->avail_end - bank->avail_start)
			break;
#endif
	KDASSERT(i != vm_nphysseg);

	/* Steal pages */
	pa = ptoa(bank->avail_start);
	bank->avail_start += npage;
	bank->start += npage;

	/* GC memory bank */
	if (bank->avail_start == bank->end) {
		/* Remove this segment from the list. */
		vm_nphysseg--;
		KDASSERT(vm_nphysseg > 0);
		for (j = i; i < vm_nphysseg; j++)
			vm_physmem[j] = vm_physmem[j + 1];
	}

	va = SH3_PHYS_TO_P1SEG(pa);
#ifdef SH4A_EXT_ADDR32
	assert(pa < 0x20000000);
#endif
	memset((void *)va, 0, size);

	return (va);
}

vaddr_t
pmap_growkernel(vaddr_t maxkvaddr)
{
//	printf("%s %d\n", __FUNCTION__, uvm.page_init_done);
	int i, n;

	if (maxkvaddr <= __pmap_kve)
		return (__pmap_kve);

	i = __PMAP_PTP_INDEX(__pmap_kve - VM_MIN_KERNEL_ADDRESS);
	__pmap_kve = __PMAP_PTP_TRUNC(maxkvaddr);
	n = __PMAP_PTP_INDEX(__pmap_kve - VM_MIN_KERNEL_ADDRESS);

	/* Allocate page table pages */
	for (;i < n; i++) {
		if (__pmap_kernel.pm_ptp[i] != NULL)
			continue;

		if (uvm.page_init_done) {
#ifdef SH4A_EXT_ADDR32
P1/P2アドレスが欲しいので、uvm_pagealloc_stratでフリーリストを指定して獲得
			struct vm_page *pg = uvm_pagealloc_strat(NULL, 0, NULL,
			    UVM_PGA_USERESERVE | UVM_PGA_ZERO,
			    UVM_PGA_STRAT_ONLY, VM_FREELIST_P1ACCESS);
			assert(VM_PAGE_TO_PHYS(pg) < 0x20000000);
#else
			struct vm_page *pg = uvm_pagealloc(NULL, 0, NULL,
			    UVM_PGA_USERESERVE | UVM_PGA_ZERO);
#endif
			if (pg == NULL)
				goto error;
			__pmap_kernel.pm_ptp[i] = (pt_entry_t *)
			    SH3_PHYS_TO_P1SEG(VM_PAGE_TO_PHYS(pg));
		} else {
			pt_entry_t *ptp = (pt_entry_t *)
			    uvm_pageboot_alloc(PAGE_SIZE);
			if (ptp == NULL)
				goto error;
			__pmap_kernel.pm_ptp[i] = ptp;
			memset(ptp, 0, PAGE_SIZE);
		}
	}

	return (__pmap_kve);
 error:
	panic("pmap_growkernel: out of memory.");
	/* NOTREACHED */
}

void
pmap_virtual_space(vaddr_t *start, vaddr_t *end)
{
//	printf("%s\n", __FUNCTION__);
	*start = VM_MIN_KERNEL_ADDRESS;
	*end = VM_MAX_KERNEL_ADDRESS;
}

void
pmap_init(void)
{
//	printf("%s\n", __FUNCTION__);
	/* Initialize pmap module */
#ifdef PMAP_MAP_POOLPAGE
	pool_init(&__pmap_pmap_pool, sizeof(struct pmap), 0, 0, 0, "pmappl",
	    &pool_allocator_nointr, IPL_NONE);
#else
プールがデフォルトでP3になるときにはアロケータを指定してstruct pmapをP1に
とるようにした。
	// allocate pmap to P1.
	pool_init(&__pmap_pmap_pool, sizeof(struct pmap), 0, 0, 0, "pmappl",
	    &pmap_pv_page_allocator, IPL_NONE);
#endif
	pool_init(&__pmap_pv_pool, sizeof(struct pv_entry), 0, 0, 0, "pvpl",
	    &pmap_pv_page_allocator, IPL_NONE);
	pool_setlowat(&__pmap_pv_pool, 16);
#if defined SH4 || defined SH4A //XXX should be  SH_HAS_VIRTUAL_ALIAS
	if (SH_HAS_VIRTUAL_ALIAS) {
		/*
		 * XXX
		 * Disable sosend_loan() in src/sys/kern/uipc_socket.c
		 * on SH4 to avoid possible virtual cache aliases and
		 * unnecessary map/unmap thrashing in __pmap_pv_enter().
		 * (also see comments in __pmap_pv_enter())
		 * 
		 * Ideally, read only shared mapping won't cause aliases
		 * so __pmap_pv_enter() should handle any shared read only
		 * mappings like ARM pmap.
		 */
		sock_loan_thresh = -1;
	}
#endif
}
pmap_t
pmap_create(void)
{
//	printf("%s\n", __FUNCTION__);
	pmap_t pmap;
	struct vm_page *pg;

	pmap = pool_get(&__pmap_pmap_pool, PR_WAITOK);
	memset(pmap, 0, sizeof(struct pmap));
	pmap->pm_asid = -1;
	pmap->pm_refcnt = 1;
	/* Allocate page table page holder (512 slot) */
#ifdef SH4A_EXT_ADDR32
ページテーブルページスロットはP1にとるようにします。
	pg = uvm_pagealloc_strat(NULL, 0, NULL,
	    UVM_PGA_USERESERVE | UVM_PGA_ZERO,
	    UVM_PGA_STRAT_ONLY, VM_FREELIST_P1ACCESS);
	assert(VM_PAGE_TO_PHYS(pg) < 0x20000000);
#else
	pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE | UVM_PGA_ZERO);
#endif
ここでstruct pmapとページテーブルページスロットが別々になっているのはペー
ジテーブルページスロットは2KBなので、それと細いヘッダとプールを分けるこ
とでアロケータの効率を計る目的だろう。
	pmap->pm_ptp = (pt_entry_t **)SH3_PHYS_TO_P1SEG(VM_PAGE_TO_PHYS(pg));

	return (pmap);
}

void
pmap_destroy(pmap_t pmap)
{
//	printf("%s\n", __FUNCTION__);
	int i;

	if (--pmap->pm_refcnt > 0)
		return;

	/* Deallocate all page table page */
	for (i = 0; i < __PMAP_PTP_N; i++) {
		vaddr_t va = (vaddr_t)pmap->pm_ptp[i];
		if (va == 0)
			continue;
#ifdef DEBUG	/* Check no mapping exists. */
		{
			int j;
			pt_entry_t *pte = (pt_entry_t *)va;
			for (j = 0; j < __PMAP_PTP_PG_N; j++, pte++)
				KDASSERT(*pte == 0);
		}
#endif /* DEBUG */
		/* Purge cache entry for next use of this page. */
これはページテーブルページ。もう使われないのでinvalidateでいい。
が、ページテーブルページはP1にとっている。共有もしていない。次に
同じ物理ページをとるにしても、uvm_pagealloc->pmap_page_zeroでinvalidate
されるから、いらないのでは?
		if (SH_HAS_VIRTUAL_ALIAS)
			sh_dcache_inv_range(va, PAGE_SIZE);
		/* Free page table */
		uvm_pagefree(PHYS_TO_VM_PAGE(SH3_P1SEG_TO_PHYS(va)));
	}
	/* Deallocate page table page holder */
	if (SH_HAS_VIRTUAL_ALIAS)
		sh_dcache_inv_range((vaddr_t)pmap->pm_ptp, PAGE_SIZE);
	uvm_pagefree(PHYS_TO_VM_PAGE(SH3_P1SEG_TO_PHYS((vaddr_t)pmap->pm_ptp)));

	/* Free ASID */
	__pmap_asid_free(pmap->pm_asid);

	pool_put(&__pmap_pmap_pool, pmap);
}

void
pmap_reference(pmap_t pmap)
{
//	printf("%s\n", __FUNCTION__);
	pmap->pm_refcnt++;
}

void
pmap_activate(struct lwp *l)
{
//	printf("%s\n", __FUNCTION__);
	pmap_t pmap = l->l_proc->p_vmspace->vm_map.pmap;

	if (pmap->pm_asid == -1)
		pmap->pm_asid = __pmap_asid_alloc();

	KDASSERT(pmap->pm_asid >=0 && pmap->pm_asid < 256);

	sh_tlb_set_asid(pmap->pm_asid);
これはTLB例外の効率化のため。
	curptd = pmap->pm_ptp;
}

void
pmap_deactivate(struct lwp *l)
{
//	printf("%s\n", __FUNCTION__);
	/* Nothing to do */
}

int
pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
//	printf("%s pa=%lx va=%lx\n", __FUNCTION__, pa, va);
	struct vm_page *pg;
	struct vm_page_md *pvh;
	pt_entry_t entry, *pte;
	bool kva = (pmap == pmap_kernel());

	/* "flags" never exceed "prot" */
	KDASSERT(prot != 0 && ((flags & VM_PROT_ALL) & ~prot) == 0);

	pg = PHYS_TO_VM_PAGE(pa);
	entry = (pa & PG_PPN) | PG_4K;
	if (flags & PMAP_WIRED)
		entry |= _PG_WIRED;

	if (pg != NULL) {	/* memory-space */
		pvh = &pg->mdpage;
		entry |= PG_C;	/* always cached */

		/* Seed modified/reference tracking */
		if (flags & VM_PROT_WRITE) {
			entry |= PG_V | PG_D;
			pvh->pvh_flags |= PVH_MODIFIED | PVH_REFERENCED;
		} else if (flags & VM_PROT_ALL) {
			entry |= PG_V;
			pvh->pvh_flags |= PVH_REFERENCED;
		}

		/* Protection */
		if ((prot & VM_PROT_WRITE) && (pvh->pvh_flags & PVH_MODIFIED)) {
			if (kva)
				entry |= PG_PR_KRW | PG_SH;
			else
				entry |= PG_PR_URW;
		} else {
			/* RO or COW page */
			if (kva)
				entry |= PG_PR_KRO | PG_SH;
			else
				entry |= PG_PR_URO;
		}

		/* Check for existing mapping */
		if (__pmap_map_change(pmap, va, pa, prot, entry))
			return (0);

		/* Add to physical-virtual map list of this page */
		__pmap_pv_enter(pmap, pg, va);

	} else {	/* bus-space (always uncached map) */
		if (kva) {
			entry |= PG_V | PG_SH |
			    ((prot & VM_PROT_WRITE) ?
			    (PG_PR_KRW | PG_D) : PG_PR_KRO);
		} else {
			entry |= PG_V |
			    ((prot & VM_PROT_WRITE) ?
			    (PG_PR_URW | PG_D) : PG_PR_URO);
		}
	}

	/* Register to page table */
	if (kva)
		pte = __pmap_kpte_lookup(va);
	else {
		pte = __pmap_pte_alloc(pmap, va);
		if (pte == NULL) {
			if (flags & PMAP_CANFAIL)
				return ENOMEM;
			panic("pmap_enter: cannot allocate pte");
		}
	}

	*pte = entry;

	if (pmap->pm_asid != -1)
		sh_tlb_update(pmap->pm_asid, va, entry);

	if (!SH_HAS_UNIFIED_CACHE &&
	    (prot == (VM_PROT_READ | VM_PROT_EXECUTE)))
		sh_icache_sync_range_index(va, PAGE_SIZE);

	if (entry & _PG_WIRED)
		pmap->pm_stats.wired_count++;
	pmap->pm_stats.resident_count++;

	return (0);
}

/*
 * bool __pmap_map_change(pmap_t pmap, vaddr_t va, paddr_t pa,
 *     vm_prot_t prot, pt_entry_t entry):
 *	Handle the situation that pmap_enter() is called to enter a
 *	mapping at a virtual address for which a mapping already
 *	exists.
 */
bool
__pmap_map_change(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot,
    pt_entry_t entry)
{
//	printf("%s\n", __FUNCTION__);
	pt_entry_t *pte, oentry;
	vaddr_t eva = va + PAGE_SIZE;

	if ((pte = __pmap_pte_lookup(pmap, va)) == NULL ||
	    ((oentry = *pte) == 0))
		return (false);		/* no mapping exists. */

	if (pa != (oentry & PG_PPN)) {
		/* Enter a mapping at a mapping to another physical page. */
		pmap_remove(pmap, va, eva);
		return (false);
	}

	/* Pre-existing mapping */

	/* Protection change. */
	if ((oentry & PG_PR_MASK) != (entry & PG_PR_MASK))
		pmap_protect(pmap, va, eva, prot);

	/* Wired change */
	if (oentry & _PG_WIRED) {
		if (!(entry & _PG_WIRED)) {
			/* wired -> unwired */
			*pte = entry;
			/* "wired" is software bits. no need to update TLB */
			pmap->pm_stats.wired_count--;
		}
	} else if (entry & _PG_WIRED) {
		/* unwired -> wired. make sure to reflect "flags" */
		pmap_remove(pmap, va, eva);
		return (false);
	}

	return (true);	/* mapping was changed. */
}

/*
 * void __pmap_pv_enter(pmap_t pmap, struct vm_page *pg, vaddr_t vaddr):
 *	Insert physical-virtual map to vm_page.
 *	Assume pre-existed mapping is already removed.
 */
void
__pmap_pv_enter(pmap_t pmap, struct vm_page *pg, vaddr_t va)
{
	struct vm_page_md *pvh;
	struct pv_entry *pv;
	int s;

	s = splvm();
	if (SH_HAS_VIRTUAL_ALIAS) {
		/*
		 * Remove all other mappings on this physical page
		 * which have different virtual cache indexes to
		 * avoid virtual cache aliases.
		 *
		 * XXX We should also handle shared mappings which
		 * XXX have different virtual cache indexes by
		 * XXX mapping them uncached (like arm and mips do).
		 */

ここではvm_page(物理アドレス)に対してすでに他のアドレス空間でマップされ
ていて、キャッシュエイリアスが生じる場合に、その他のマップを外している。
pmap_removeでこの領域のキャッシュは書き戻される。コメントにもある通りこ
ういう事態が見つかった時にはこのページをアンキャッシュドにするという選
択もあるけれど、これが一番シンプルな解決方。

vm_pageにMDリストを追加するのはこれがやりたいため。
 again:
		pvh = &pg->mdpage;
		SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) {
			if (sh_cache_indexof(va) !=
			    sh_cache_indexof(pv->pv_va)) {
				pmap_remove(pv->pv_pmap, pv->pv_va,
				    pv->pv_va + PAGE_SIZE);
				goto again;
			}
		}
	}

	/* Register pv map */
	pvh = &pg->mdpage;
	pv = __pmap_pv_alloc();
	pv->pv_pmap = pmap;
	pv->pv_va = va;

	SLIST_INSERT_HEAD(&pvh->pvh_head, pv, pv_link);
	splx(s);
}

void
pmap_remove(pmap_t pmap, vaddr_t sva, vaddr_t eva)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_page *pg;
	pt_entry_t *pte, entry;
	vaddr_t va;

	KDASSERT((sva & PGOFSET) == 0);

	for (va = sva; va < eva; va += PAGE_SIZE) {
		if ((pte = __pmap_pte_lookup(pmap, va)) == NULL ||
		    (entry = *pte) == 0)
			continue;

		if ((pg = PHYS_TO_VM_PAGE(entry & PG_PPN)) != NULL)
			__pmap_pv_remove(pmap, pg, va);

		if (entry & _PG_WIRED)
			pmap->pm_stats.wired_count--;
		pmap->pm_stats.resident_count--;
		*pte = 0;

		/*
		 * When pmap->pm_asid == -1 (invalid ASID), old entry attribute
		 * to this pmap is already removed by pmap_activate().
		 */
		if (pmap->pm_asid != -1)
			sh_tlb_invalidate_addr(pmap->pm_asid, va);
	}
}

/*
 * void __pmap_pv_remove(pmap_t pmap, struct vm_page *pg, vaddr_t vaddr):
 *	Remove physical-virtual map from vm_page.
 */
void
__pmap_pv_remove(pmap_t pmap, struct vm_page *pg, vaddr_t vaddr)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_page_md *pvh;
	struct pv_entry *pv;
	int s;

	s = splvm();
	pvh = &pg->mdpage;
	SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) {
		if (pv->pv_pmap == pmap && pv->pv_va == vaddr) {
			if (SH_HAS_VIRTUAL_ALIAS ||
			    (SH_HAS_WRITEBACK_CACHE &&
				(pg->mdpage.pvh_flags & PVH_MODIFIED))) {
				/*
				 * Always use index ops. since I don't want to
				 * worry about address space.
				 */
				sh_dcache_wbinv_range_index
				    (pv->pv_va, PAGE_SIZE);
			}

			SLIST_REMOVE(&pvh->pvh_head, pv, pv_entry, pv_link);
			__pmap_pv_free(pv);
			break;
		}
	}
#ifdef DEBUG
	/* Check duplicated map. */
	SLIST_FOREACH(pv, &pvh->pvh_head, pv_link)
	    KDASSERT(!(pv->pv_pmap == pmap && pv->pv_va == vaddr));
#endif
	splx(s);
}

void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot)
{
//	printf("%s va=%lx pa=%lx\n", __FUNCTION__, va, pa);
	pt_entry_t *pte, entry;

	KDASSERT((va & PGOFSET) == 0);
	KDASSERT(va >= VM_MIN_KERNEL_ADDRESS && va < VM_MAX_KERNEL_ADDRESS);
	entry = (pa & PG_PPN) | PG_V | PG_SH | PG_4K;
	if (prot & VM_PROT_WRITE)
		entry |= (PG_PR_KRW | PG_D);
	else
		entry |= PG_PR_KRO;

	if (PHYS_TO_VM_PAGE(pa))
		entry |= PG_C;

これが失敗しない理由がちょっとわからない。先んじてpmap_growkernelをしてる?
	if ((pte = __pmap_kpte_lookup(va)) == NULL) {
		printf ("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%s no pte\n",
		    __FUNCTION__);
	}

	KDASSERT(*pte == 0);
	*pte = entry;

	sh_tlb_update(0, va, entry);
//	sh4a_wired_counter_alloc (va);どのくらいエントリがあるのかカウント。
}

void
pmap_kremove(vaddr_t va, vsize_t len)
{
//	printf("%s va=%lx len=%lx\n", __FUNCTION__, va, len);
	pt_entry_t *pte;
	vaddr_t eva = va + len;

	KDASSERT((va & PGOFSET) == 0);
	KDASSERT((len & PGOFSET) == 0);
	KDASSERT(va >= VM_MIN_KERNEL_ADDRESS && eva <= VM_MAX_KERNEL_ADDRESS);

	for (; va < eva; va += PAGE_SIZE) {
		pte = __pmap_kpte_lookup(va);
		KDASSERT(pte != NULL);
		if (*pte == 0)
			continue;

		if (SH_HAS_VIRTUAL_ALIAS && PHYS_TO_VM_PAGE(*pte & PG_PPN))
			sh_dcache_wbinv_range(va, PAGE_SIZE);
		*pte = 0;

		sh_tlb_invalidate_addr(0, va);
//		sh4a_wired_counter_dealloc (va);どのくらいエントリがあるのかカウント。
	}
}

bool
pmap_extract(pmap_t pmap, vaddr_t va, paddr_t *pap)
{
//	printf("%s\n", __FUNCTION__);
	pt_entry_t *pte;

単純にアドレス空間と仮想アドレスのペアから物理アドレスを返すルーチン。
	/* handle P1 and P2 specially: va == pa */
	if (pmap == pmap_kernel() && (va >> 30) == 2) {
		if (pap != NULL)
			*pap = va & SH3_PHYS_MASK;

		return (true);
	}

	pte = __pmap_pte_lookup(pmap, va);
	if (pte == NULL || *pte == 0) {
//		printf("%s va=%lx failed\n", __FUNCTION__, va);
		return (false);
	}

	if (pap != NULL)
		*pap = (*pte & PG_PPN) | (va & PGOFSET);

	return (true);
}

void
pmap_protect(pmap_t pmap, vaddr_t sva, vaddr_t eva, vm_prot_t prot)
{
//	printf("%s\n", __FUNCTION__);
	bool kernel = pmap == pmap_kernel();
	pt_entry_t *pte, entry, protbits;
	vaddr_t va;

	sva = trunc_page(sva);

	if ((prot & VM_PROT_READ) == VM_PROT_NONE) {
		pmap_remove(pmap, sva, eva);
		return;
	}

	switch (prot) {
	default:
		panic("pmap_protect: invalid protection mode %x", prot);
		/* NOTREACHED */
	case VM_PROT_READ:
		/* FALLTHROUGH */
	case VM_PROT_READ | VM_PROT_EXECUTE:
		protbits = kernel ? PG_PR_KRO : PG_PR_URO;
		break;
	case VM_PROT_READ | VM_PROT_WRITE:
		/* FALLTHROUGH */
	case VM_PROT_ALL:
		protbits = kernel ? PG_PR_KRW : PG_PR_URW;
		break;
	}

	for (va = sva; va < eva; va += PAGE_SIZE) {

		if (((pte = __pmap_pte_lookup(pmap, va)) == NULL) ||
		    (entry = *pte) == 0)
			continue;

		if (SH_HAS_VIRTUAL_ALIAS && (entry & PG_D)) {
			if (!SH_HAS_UNIFIED_CACHE && (prot & VM_PROT_EXECUTE))
				sh_icache_sync_range_index(va, PAGE_SIZE);
			else
				sh_dcache_wbinv_range_index(va, PAGE_SIZE);
		}

		entry = (entry & ~PG_PR_MASK) | protbits;
		*pte = entry;

		if (pmap->pm_asid != -1)
			sh_tlb_update(pmap->pm_asid, va, entry);
	}
}

void
pmap_page_protect(struct vm_page *pg, vm_prot_t prot)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_page_md *pvh = &pg->mdpage;
	struct pv_entry *pv;
	struct pmap *pmap;
	vaddr_t va;
	int s;

	switch (prot) {
	case VM_PROT_READ | VM_PROT_WRITE:
		/* FALLTHROUGH */
	case VM_PROT_ALL:
		break;

	case VM_PROT_READ:
		/* FALLTHROUGH */
	case VM_PROT_READ | VM_PROT_EXECUTE:
		s = splvm();
		SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) {
			pmap = pv->pv_pmap;
			va = pv->pv_va;

			KDASSERT(pmap);
			pmap_protect(pmap, va, va + PAGE_SIZE, prot);
		}
		splx(s);
		break;

	default:
		/* Remove all */
		s = splvm();
		while ((pv = SLIST_FIRST(&pvh->pvh_head)) != NULL) {
			va = pv->pv_va;
			pmap_remove(pv->pv_pmap, va, va + PAGE_SIZE);
		}
		splx(s);
	}
}

void
pmap_unwire(pmap_t pmap, vaddr_t va)
{
//	printf("%s\n", __FUNCTION__);
	pt_entry_t *pte, entry;
このwireは物理アドレス-仮想アドレスのwire。メモリじゃないメモリマップドデバイス
だとvm_pageがない。しかしそういったマッピングはワイヤードに決まってる。

	if ((pte = __pmap_pte_lookup(pmap, va)) == NULL ||
	    (entry = *pte) == 0 ||
	    (entry & _PG_WIRED) == 0)
		return;

	*pte = entry & ~_PG_WIRED;
	pmap->pm_stats.wired_count--;
}

void
pmap_procwr(struct proc	*p, vaddr_t va, size_t len)
{
//	printf("%s\n", __FUNCTION__);
	if (!SH_HAS_UNIFIED_CACHE)
		sh_icache_sync_range_index(va, len);
}

void
pmap_zero_page(paddr_t phys)
{
//	printf("%s pa=%lx\n", __FUNCTION__, phys);
#ifdef SH4A_EXT_ADDR32
P1/P2アドレスならP2からやります。
	if (phys < 0x20000000)
		goto maped_area;
	struct vm_page *pg;
	struct vm_page_md *pvh;
	struct pv_entry *pv;

	bool vm_mapped = FALSE;
この物理ページに対応するvm_pageを探します。
	if ((pg = PHYS_TO_VM_PAGE(phys)) != NULL) {
		pvh = &pg->mdpage;
		// For each vm page which maps this physical page.
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
そのvm_pageに登録されている物理アドレスから0クリアと思ったけれど、これ
はだめだ。アドレス空間変更しないと。これじゃpmap_kenrel()のvaをゼロクリ
アしてしまう。アドレス空間変更してゼロにして、他のが見つかったら、そこ
のキャッシュをinvalidate.
		SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) {
			memset ((void *)pv->pv_va, 0, PAGE_SIZE);
			vm_mapped = TRUE;
		}
	}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
	if (!vm_mapped) {
どこにもマップされていない。実際はほとんどこのケース。UVMはまずpmap_zero_page
してからpmap_enterする。
		// Mapped to P0 area
使ってないP0エリアにアンキャッシュドでマップしてそこでゼロクリア。
		vaddr_t va = 0;
		sh_tlb_update (0, va,
		    (phys & PG_PPN) | PG_PR_KRW | PG_V | PG_D | PG_4K | PG_SH);
		memset ((void *)va, 0, PAGE_SIZE);
マップを外します。
		sh_tlb_invalidate_addr (0, 0);
	}
	return;
maped_area:
#endif
	if (SH_HAS_VIRTUAL_ALIAS) {	/* don't polute cache */
		/* sync cache since we access via P2. */
これは皆殺し。ここでvm_pageを引っぱって、もしそvm_pageがあるならそこか
らinvalidateがいいだろう。
		sh_dcache_wbinv_all();
		memset((void *)SH3_PHYS_TO_P2SEG(phys), 0, PAGE_SIZE);
	} else {
		memset((void *)SH3_PHYS_TO_P1SEG(phys), 0, PAGE_SIZE);
	}
}

ここも大体同じ方針。ここもアドレス空間をよく考えて全部書き直し。
void
pmap_copy_page(paddr_t src, paddr_t dst)
{
//	printf("***************************************%s\n", __FUNCTION__);
#ifdef SH4A_EXT_ADDR32
	if (src < 0x20000000 && dst < 0x20000000)
		goto maped_area;
//	printf("************TMPMAP**********************%s\n", __FUNCTION__);
	struct vm_page *pg_src, *pg_dst;
	vaddr_t dst_va;
	vaddr_t src_va;
	bool vm_mapped = FALSE;
	struct pv_entry *pv_src = 0;
	struct pv_entry *pv_dst = 0;

	pg_src = PHYS_TO_VM_PAGE(src);
	pg_dst = PHYS_TO_VM_PAGE(dst);
	if (pg_src && pg_dst) {
		struct vm_page_md *pvh;
		pvh = &pg_src->mdpage;
		pv_src = SLIST_FIRST(&pvh->pvh_head);
		pvh = &pg_dst->mdpage;
		pv_dst = SLIST_FIRST(&pvh->pvh_head);
		if (pv_src && pv_dst) {
			dst_va = pv_dst->pv_va;
			src_va = pv_src->pv_va;
			memcpy ((void *)dst_va, (void *)src_va, PAGE_SIZE);
			vm_mapped = TRUE;
		}
	}
	if (!vm_mapped) {	// Temporary mapped to P0
		if (pv_src)
			sh_dcache_wb_range (pv_src->pv_va, PAGE_SIZE);
		if (pv_dst)
			sh_dcache_inv_range (pv_dst->pv_va, PAGE_SIZE);
		src_va = 0;
		dst_va = PAGE_SIZE;
		sh_tlb_update(0, src_va,
		    (src & PG_PPN) | PG_PR_KRW | PG_V | PG_D | PG_4K | PG_SH);
		sh_tlb_update(0, dst_va,
		    (src & PG_PPN) | PG_PR_KRW | PG_V | PG_D | PG_4K | PG_SH);
		memcpy((void *)dst_va, (void *)src_va, PAGE_SIZE);
		sh_tlb_invalidate_addr (0, src_va);
		sh_tlb_invalidate_addr (0, dst_va);
	}
	return;
maped_area:
#endif
	if (SH_HAS_VIRTUAL_ALIAS) {	/* don't polute cache */
		/* sync cache since we access via P2. */
		sh_dcache_wbinv_all();
		memcpy((void *)SH3_PHYS_TO_P2SEG(dst),
		    (void *)SH3_PHYS_TO_P2SEG(src), PAGE_SIZE);
	} else {
		memcpy((void *)SH3_PHYS_TO_P1SEG(dst),
		    (void *)SH3_PHYS_TO_P1SEG(src), PAGE_SIZE);
	}
//	printf("DONE***************************************%s\n", __FUNCTION__);
}

bool
pmap_is_referenced(struct vm_page *pg)
{
//	printf("%s\n", __FUNCTION__);
	return ((pg->mdpage.pvh_flags & PVH_REFERENCED) ? true : false);
}

bool
pmap_clear_reference(struct vm_page *pg)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_page_md *pvh = &pg->mdpage;
	struct pv_entry *pv;
	pt_entry_t *pte;
	pmap_t pmap;
	vaddr_t va;
	int s;

	if ((pg->mdpage.pvh_flags & PVH_REFERENCED) == 0)
		return (false);

	pg->mdpage.pvh_flags &= ~PVH_REFERENCED;

	s = splvm();
	/* Restart reference bit emulation */
	SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) {
		pmap = pv->pv_pmap;
		va = pv->pv_va;

		if ((pte = __pmap_pte_lookup(pmap, va)) == NULL)
			continue;
		if ((*pte & PG_V) == 0)
			continue;
		*pte &= ~PG_V;

		if (pmap->pm_asid != -1)
			sh_tlb_invalidate_addr(pmap->pm_asid, va);
	}
	splx(s);

	return (true);
}

bool
pmap_is_modified(struct vm_page *pg)
{
//	printf("%s\n", __FUNCTION__);
	return ((pg->mdpage.pvh_flags & PVH_MODIFIED) ? true : false);
}

bool
pmap_clear_modify(struct vm_page *pg)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_page_md *pvh = &pg->mdpage;
	struct pv_entry *pv;
	struct pmap *pmap;
	pt_entry_t *pte, entry;
	bool modified;
	vaddr_t va;
	int s;

	modified = pvh->pvh_flags & PVH_MODIFIED;
	if (!modified)
		return (false);

	pvh->pvh_flags &= ~PVH_MODIFIED;

	s = splvm();
	if (SLIST_EMPTY(&pvh->pvh_head)) {/* no map on this page */
		splx(s);
		return (true);
	}

	/* Write-back and invalidate TLB entry */
	if (!SH_HAS_VIRTUAL_ALIAS && SH_HAS_WRITEBACK_CACHE)
		sh_dcache_wbinv_all();

	SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) {
		pmap = pv->pv_pmap;
		va = pv->pv_va;
		if ((pte = __pmap_pte_lookup(pmap, va)) == NULL)
			continue;
		entry = *pte;
		if ((entry & PG_D) == 0)
			continue;

		if (SH_HAS_VIRTUAL_ALIAS)
			sh_dcache_wbinv_range_index(va, PAGE_SIZE);

		*pte = entry & ~PG_D;
		if (pmap->pm_asid != -1)
			sh_tlb_invalidate_addr(pmap->pm_asid, va);
	}
	splx(s);

	return (true);
}

paddr_t
pmap_phys_address(paddr_t cookie)
{
//	printf("%s\n", __FUNCTION__);
	return (sh3_ptob(cookie));
}

#if defined SH4 || defined SH4A //XXX should be  SH_HAS_VIRTUAL_ALIAS
/*
 * pmap_prefer(vaddr_t foff, vaddr_t *vap)
 *
 * Find first virtual address >= *vap that doesn't cause
 * a virtual cache alias against vaddr_t foff.
 */
void
pmap_prefer(vaddr_t foff, vaddr_t *vap)
{
//	printf("%s\n", __FUNCTION__);
	vaddr_t va;

	if (SH_HAS_VIRTUAL_ALIAS) {
		va = *vap;

		*vap = va + ((foff - va) & sh_cache_prefer_mask);
	}
}
#endif /* SH_HAS_VIRTUAL_ALIAS */

/*
 * pv_entry pool allocator:
 *	void *__pmap_pv_page_alloc(struct pool *pool, int flags):
 *	void __pmap_pv_page_free(struct pool *pool, void *v):
 */
void *
__pmap_pv_page_alloc(struct pool *pool, int flags)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_page *pg;
#ifdef SH4A_EXT_ADDR32
	pg = uvm_pagealloc_strat(NULL, 0, NULL, UVM_PGA_USERESERVE,
	    UVM_PGA_STRAT_ONLY, VM_FREELIST_P1ACCESS);
	assert(VM_PAGE_TO_PHYS(pg) < 0x20000000);
#else
	pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE);
#endif
	if (pg == NULL)
		return (NULL);

	return ((void *)SH3_PHYS_TO_P1SEG(VM_PAGE_TO_PHYS(pg)));
}

void
__pmap_pv_page_free(struct pool *pool, void *v)
{
//	printf("%s\n", __FUNCTION__);
	vaddr_t va = (vaddr_t)v;

	/* Invalidate cache for next use of this page */
	if (SH_HAS_VIRTUAL_ALIAS)
		sh_icache_sync_range_index(va, PAGE_SIZE);
	uvm_pagefree(PHYS_TO_VM_PAGE(SH3_P1SEG_TO_PHYS(va)));
}

/*
 * pt_entry_t __pmap_pte_alloc(pmap_t pmap, vaddr_t va):
 *	lookup page table entry. if found returns it, else allocate it.
 *	page table is accessed via P1.
 */
pt_entry_t *
__pmap_pte_alloc(pmap_t pmap, vaddr_t va)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_page *pg;
	pt_entry_t *ptp, *pte;

	if ((pte = __pmap_pte_lookup(pmap, va)) != NULL)
		return (pte);

	/* Allocate page table (not managed page) */
#ifdef SH4A_EXT_ADDR32
	pg = uvm_pagealloc_strat(NULL, 0, NULL,
	    UVM_PGA_USERESERVE | UVM_PGA_ZERO,
	    UVM_PGA_STRAT_ONLY, VM_FREELIST_P1ACCESS);
	assert(VM_PAGE_TO_PHYS(pg) < 0x20000000);
#else
	pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE | UVM_PGA_ZERO);
#endif
	if (pg == NULL)
		return NULL;

	ptp = (pt_entry_t *)SH3_PHYS_TO_P1SEG(VM_PAGE_TO_PHYS(pg));
	pmap->pm_ptp[__PMAP_PTP_INDEX(va)] = ptp;

	return (ptp + __PMAP_PTP_OFSET(va));
}

/*
 * pt_entry_t *__pmap_pte_lookup(pmap_t pmap, vaddr_t va):
 *	lookup page table entry, if not allocated, returns NULL.
 */
pt_entry_t *
__pmap_pte_lookup(pmap_t pmap, vaddr_t va)
{
////	printf("%s\n", __FUNCTION__);
	pt_entry_t *ptp;

	if (pmap == pmap_kernel())
		return (__pmap_kpte_lookup(va));

	/* Lookup page table page */
	ptp = pmap->pm_ptp[__PMAP_PTP_INDEX(va)];
	if (ptp == NULL)
		return (NULL);

	return (ptp + __PMAP_PTP_OFSET(va));
}


/*
 * pt_entry_t *__pmap_kpte_lookup(vaddr_t va):
 *	kernel virtual only version of __pmap_pte_lookup().
 */
pt_entry_t *
__pmap_kpte_lookup(vaddr_t va)
{
	pt_entry_t *ptp;
//	printf("%s va=%lx, %lx\n", __FUNCTION__, va, __PMAP_PTP_INDEX(va-VM_MIN_KERNEL_ADDRESS));
	ptp = __pmap_kernel.pm_ptp[__PMAP_PTP_INDEX(va-VM_MIN_KERNEL_ADDRESS)];
	if (ptp == NULL)
		return NULL;

//	printf("%s found %p\n", __FUNCTION__, ptp + __PMAP_PTP_OFSET(va));
	return (ptp + __PMAP_PTP_OFSET(va));
}

/*
 * bool __pmap_pte_load(pmap_t pmap, vaddr_t va, int flags):
 *	lookup page table entry, if found it, load to TLB.
 *	flags specify do emulate reference and/or modified bit or not.
 */
bool
__pmap_pte_load(pmap_t pmap, vaddr_t va, int flags)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_page *pg;
	pt_entry_t *pte;
	pt_entry_t entry;

	KDASSERT((((int)va < 0) && (pmap == pmap_kernel())) ||
	    (((int)va >= 0) && (pmap != pmap_kernel())));

	/* Lookup page table entry */
	if (((pte = __pmap_pte_lookup(pmap, va)) == NULL) ||
	    ((entry = *pte) == 0))
		return (false);

	KDASSERT(va != 0);

	/* Emulate reference/modified tracking for managed page. */
	if (flags != 0 && (pg = PHYS_TO_VM_PAGE(entry & PG_PPN)) != NULL) {
		if (flags & PVH_REFERENCED) {
			pg->mdpage.pvh_flags |= PVH_REFERENCED;
			entry |= PG_V;
		}
		if (flags & PVH_MODIFIED) {
			pg->mdpage.pvh_flags |= PVH_MODIFIED;
			entry |= PG_D;
		}
		*pte = entry;
	}

	/* When pmap has valid ASID, register to TLB */
	if (pmap->pm_asid != -1)
		sh_tlb_update(pmap->pm_asid, va, entry);

	return (true);
}

/*
 * int __pmap_asid_alloc(void):
 *	Allocate new ASID. if all ASID is used, steal from other process.
 */
int
__pmap_asid_alloc(void)
{
//	printf("%s\n", __FUNCTION__);
	struct proc *p;
	int i, j, k, n, map, asid;

	/* Search free ASID */
	i = __pmap_asid.hint >> 5;
	n = i + 8;
	for (; i < n; i++) {
		k = i & 0x7;
		map = __pmap_asid.map[k];
		for (j = 0; j < 32; j++) {
			if ((map & (1 << j)) == 0 && (k + j) != 0) {
				__pmap_asid.map[k] |= (1 << j);
				__pmap_asid.hint = (k << 5) + j;
				return (__pmap_asid.hint);
			}
		}
	}

	/* Steal ASID */
	LIST_FOREACH(p, &allproc, p_list) {
		if ((asid = p->p_vmspace->vm_map.pmap->pm_asid) > 0) {
			pmap_t pmap = p->p_vmspace->vm_map.pmap;
			pmap->pm_asid = -1;
			__pmap_asid.hint = asid;
			/* Invalidate all old ASID entry */
			sh_tlb_invalidate_asid(pmap->pm_asid);

			return (__pmap_asid.hint);
		}
	}

	panic("No ASID allocated.");
	/* NOTREACHED */
}

/*
 * void __pmap_asid_free(int asid):
 *	Return unused ASID to pool. and remove all TLB entry of ASID.
 */
void
__pmap_asid_free(int asid)
{
	int i;

	if (asid < 1)	/* Don't invalidate kernel ASID 0 */
		return;

	sh_tlb_invalidate_asid(asid);

	i = asid >> 5;
	__pmap_asid.map[i] &= ~(1 << (asid - (i << 5)));
}