091023

|



筑波選手権のランキングが出ていた。なんとまた19位!。うーん。やはり神は俺
を19に。筑波選手権のS80クラスで俺ほど19の似合う人間はいないからな。(
MCFAJだと他に2人くらいいた)

さてロガーを外して調べてみると、やはり3端子レギュレータの足にクラックが 入っていた。手で押さえたりすると電源がON/OFFする。うーん。


SH4A続き。頭からキャッシュ落ちしない程度に触れておこう。pmap_kenter_paと pmap_kremoveについて。
void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot)
{
	pt_entry_t *pte, entry;

仮想アドレスはページアラインであることが前提。
	KDASSERT((va & PGOFSET) == 0);
そしてその仮想アドレスはP3のみ。カーネル空間ではP0(0-0x80000000)も
使えるけれど、UVMからは使わない設定にしている。
	KDASSERT(va >= VM_MIN_KERNEL_ADDRESS && va < VM_MAX_KERNEL_ADDRESS);

カーネルのマップはASIDがどう設定されていても参照できるようにPG_SH(共有)
の設定。こう設定できるのは、ユーザ空間を0-0x8000000のU0、カーネル空間で
TLBを参照するのは0xc0000000-0xe0000000のP3と分けているから。P0も使った
場合には共有ページにしてしまうとTLB多重ヒット例外でリセットしてしまう可
能性がある。

もしP0まで使うならカーネル専用のASID(0)に設定する必要があり、さらにその
P0のページは共有設定してはいけない。

そう考えると、先日実装した32bit物理アドレス拡張モード用のpmap_zero_page,
pmap_copy_pageの実装は間違えていて、PG_SHはなしにして、memcpy,memsetの前に
ASIDを0(カーネル)に設定して、その後で元のASIDに戻さないといけない。
そうしないと、もしU0で0-0x2000をマップしていたら、TLB多重ヒット例外だ。

	entry = (pa & PG_PPN) | PG_V | PG_SH | PG_4K;

これは書き込みするかしないかの設定。PG_Dを設定することで、初期ページ書
き込み例外を起こさせないようにしている。

	if (prot & VM_PROT_WRITE)
		entry |= (PG_PR_KRW | PG_D);
	else
		entry |= PG_PR_KRO;

struct vm_pageが設定されていれば(メモリ)キャッシュを有効に。設定して
いなければ(メモリマップドのデバイス)、アンキャッシュド。
	if (PHYS_TO_VM_PAGE(pa))
		entry |= PG_C;


この仮想アドレスに対するページテーブルエントリを__pmap_kernelから取得し
ます。__pmap_kernelはP3エリアを担当する。ここで失敗しないのはUVMが
先だってpmap_growkernelでそのエリアまでページテーブルエントリスロット
を確保しているから。
#define	PMAP_GROWKERNELをデファインしないのはpmap_initにおいて
pmap_growkernel(VM_MAX_KERNEL_ADDRESS);
をしておくのと同じ(全部のページテーブルページスロットを確保しておく)。
	pte = __pmap_kpte_lookup(va);
	KDASSERT(*pte == 0);
PTEを登録。
	*pte = entry;
そしてこれをTLBに乗せておきます。
	sh_tlb_update(0, va, entry);

pmap_kenter_paでは、__pmap_pv_enter()を呼ばない。つまりMD拡張の物理アド
レス-仮想アドレスのマッピングの組を登録しない。これはpmap_kenter_paのマッ
プはpmap_kernel()に固定されているからだ。pmap_enterでは一つの物理アドレ
スのマッピングにいくつかのアドレス空間+そこでの仮想アドレスの組が存在し、
それがキャッシュのエイリアス問題を引きおこすので、それを検出するために
登録する。pmap_kernel()ではそのようなことはないので__pmap_pv_enter()す
る必要がない。

}

void
pmap_kremove(vaddr_t va, vsize_t len)
{
	pt_entry_t *pte;
	vaddr_t eva = va + len;

ここではpmap_kenter_paで登録されたページを削除します。
これらのアサートはpmap_kenter_paと同じく、これらがページサイズのアラインで
あることと、P3にマップされていることを確認。
	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);
ここでpteがNULLということは既にこの仮想アドレスの領域に対するページテー
ブルページスロットが削除されてしまったということだ。それは早すぎる。
		KDASSERT(pte != NULL);
もし既に穴喰いで削除されていたとしたらスキップ。だけれど、こういう機会は
あるのだろうか。
		if (*pte == 0)
			continue;
このページを削除するにあたってキャッシュに対してすることは、
 ライトバックキャッシュであれば、書き戻しておく。

無効化に関して

エイリアスがない場合でも次にこの物理ページを使う時にキャッシュに残って
いれば、それが読めてしまう。それは保護の問題でよくないので無効化したい。

エイリアスがある場合、無効化しなければ、次にこの物理ページを使った場合、
それがpmap_kernel()でなければ、他のインデックスにこの物理ページのキャッ
シュが乗って、エイリアスになる。うっかりそこでキャッシュフラッシュすれ
ばこの古いキャッシュが書き戻されることになってしまうかもしれない。

ということで、ここの方針は常に書き戻し無効化。なのでここの実装はちょっ
と疑問があるけれど、UVMがユーザ領域のマッピングの際にゼロページしていれ
ばこれでいい。(たぶんそうしてるはず...)

PHYS_TO_VM_PAGE(*pte & PG_PPN)はこれがメモリへのマッピングであることを
調べている。(メモリでなければアンキャシュドマッピングだ)
		if (SH_HAS_VIRTUAL_ALIAS && PHYS_TO_VM_PAGE(*pte & PG_PPN))
			sh_dcache_wbinv_range(va, PAGE_SIZE);
		*pte = 0;

そしてこのアドレスに対するTLBのエントリも無効化します。
		sh_tlb_invalidate_addr(0, va);
	}
}