091020

|



CR85の整備。新品ピストン(+ピン+ベアリング)入れて、リングは中古。一本目これでピストン作って、二本目は中古ピストンに新品リングで。



SH4A続き。おやすみということにしていたけれど、動かないのがどうも気になっ てしかたがないのでUVMいじって強引に動かしました。32bit拡張モードで 512MB全部UVMに乗せました。これで胸のつかえがおりた...。
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) #700: Tue Oct 20 20:57:14 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 = 511 MB
avail memory = 498 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: 32bit physical address mode. TLB-compatible mode.
VPN:80000000 (V) PPN:0 (V) 512MB, bufferd write, cacheable, write-back,
VPN:a0000000 (V) PPN:0 (V) 512MB, bufferd write, uncacheable, write-back,
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% /
# 

uvm_km.cの中の
	uvm_km_alloc()
	uvm_km_alloc_poolpage_cache()
	uvm_km_alloc_poolpage()
uvm_map.cの中の
	uvm_kmapent_alloc()

のuvm_pageallocをuvm_pagealloc_stratにしてP1からアクセスできるエリアに
してやればPMAP_MAP_POOLPAGEにやってくる物理アドレスはP1だけになる。ブー
トして一番最初にPMAP_MAP_POOLPAGEを使うことになるのは
uvm_kmapent_alloc()@uvm_map.cでアロケートされた部分だ。

これをやってやれば、P1からアクセスすることのできないエリアまでUVMに積んだ
としても

#define	PMAP_MAP_POOLPAGE(pa)		SH3_PHYS_TO_P1SEG((pa))

として動く。そして512MB積むとinitでセグメンテーションフォルトになってしまうの
は、新しく書いたpmap_copy_pageのバグだった。昨日のソースから。

		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);
                    ^^^^^ここはdst。これじゃコピーされてないよ!


32bit拡張モード用のpmap_copy_pageについて。

まずは物理アドレスを指定された時にそこにマップしている仮想アドレスの
キャッシュをフラッシュするルーチンを書きました。

pmapの中でのキャッシュの扱いについて。

SHは仮想アドレスインデックス物理アドレスタグキャッシュ。それは仮想アド
レスでキャッシュのインデックスを生成して、そのインデックスに目当ての
キャッシュがあるかどうかは物理アドレスで特定する。

なのでタグの生成にはMMUの助けがいる。MMUはASID(アドレススペース
ID)と仮想アドレスの組からマッチして物理アドレスを返す。OS側でもASIDは
struct pmapの形でpmap+vaの組でこれを表現している。

キャッシュ操作にはICBI,OCBP,OCBI,OCBWB命令と、メモリ割り付けのキャッ
シュアレイの操作がある。キャッシュ命令の場合はその仮想アドレスから物理
アドレスの変換にMMUを使う。ヒットしているかどうかは物理アドレスで同定
するからだ。なので、命令の発行時に目的のアドレススペースになるように
MMUにASIDを設定し、OS側もその例外をハンドルできるようにしておく必要
がある。つまりpmap_activateする必要がある。

メモリ割りつけの場合はMMUの介在がないのでTLBミスも存在しないし、アドレ
ススペースを切り替える必要もない。ただ、ヒットするかどうかはわからない
のでその仮想アドレスから計算されるインデックスの全てのウェイについて操
作しないといけない。ちょっとメモリアクセスが増える。

メモリ割りつけのキャッシュからインデックス操作をする場合、皆殺しなので
悪影響がないようにしないといけない。例えば他のウェイにまだ書き戻されて
ないデータがキャッシュが乗っているのに、これに対して書き戻しをせずに無
効化してしまったらデータをロスしてしまう。(ライトバックの場合)

なので、ライトバックキャッシュの場合インデックス操作では無効化だけとい
うのはしてはいけない。ライトスルーならOK。

もし、該当する仮想アドレスがカーネル空間のものなら、アドレス空間の変更
なしに、ICBI,OCBP,OCBI,OCBWB命令を発行していい。これはページテーブルペー
ジとか、pmap_kenter_paで登録されたkernel_mapのページ。

  + ICBI,OCBP,OCBI,OCBWBを発行するならアドレス空間を考慮する。
  + pmapの関数はカーネル空間。
  + アドレス空間を変更せずにユーザ空間のキャッシュをいじるにはインデッ
  クス操作しかない
  + その際、他のウェイに気をつけろ。

ということだ。実際のところNetBSD/sh3ではインデックス操作には書き戻し無
効化しか用意していない。

enum cache_ops {
	CACHE_INV = 0,
	CACHE_WB = 1,
	CACHE_WBINV = 2,
};

// cache ops for vm-mapped pages.
void
__pmap_dcache_pa(paddr_t pa, enum cache_ops ops)
{
全部同じ関数だけれど、操作側でしたいことを明確にしたくてこうした。
できればsh_cache_ops._dcache_wb_range_indexは追加してもいいだろう。
これはその仮想アドレスがユーザ空間でもうまくいくようにしてある。
カーネル空間専用なら、インデックス操作は必要ない。
	void (*cache_func[])(vaddr_t, vsize_t) = {
		sh_cache_ops._dcache_wbinv_range_index,
		sh_cache_ops._dcache_wbinv_range_index,
		sh_cache_ops._dcache_wbinv_range_index
	};

	struct vm_page *pg;
	struct vm_page_md *pvh;
	struct pv_entry *pv;

もしこの物理アドレスに仮想アドレスがまだマップされてないならなにもする
ことはない。(コピーページの時はないかも?よく調べてない。 ゼロページの時
だとUVMはゼロページしてからマップするのでよくある)

	if ((pg = PHYS_TO_VM_PAGE(pa)) == NULL)
		return;
	pvh = &pg->mdpage;
この物理ページにマップされている仮想アドレスでループ。
	SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) {
アドレス空間を変更せずにインデックス操作でフラッシュ。この他、
pmap_activate(pv->pv_pmap)してキャッシュヒット命令にして、またカーネル
マップに戻すのもありかも?(まだやってない)
	    cache_func[ops](pv->pv_va, PAGE_SIZE);
//	    printf("%s: cache flush pmap=%p va=%lx\n", __FUNCTION__,
//		pv->pv_pmap, pv->pv_va);
	}
}

void
pmap_copy_page(paddr_t src, paddr_t dst)
{
#ifdef SH4A_EXT_ADDR32
物理アドレスがP1/P2からマップされる空間ならP2から処理します。
	if (src < 0x20000000 && dst < 0x20000000)
		goto maped_area;
	struct vm_page *pg_src, *pg_dst;
	vaddr_t dst_va;
	vaddr_t src_va;
	int s = _cpu_intr_suspend();

実際のコピーを今現在の仮想アドレスからではなく、別のマップから行うので
キャッシュの整合性をとります。

コピー元が仮想アドレスでマップされているなら、まず書き戻します。
(write-thruならいらない)
	// If mapped to virtual address, sync cache.
	if ((pg_src = PHYS_TO_VM_PAGE(src)) != NULL)
		__pmap_dcache_pa(src, CACHE_WB);
コピー先が仮想アドレスでマップされているなら、落とします。(実際はインデックス
操作なので書き戻し無効化。)
	if ((pg_dst = PHYS_TO_VM_PAGE(dst)) != NULL)
		__pmap_dcache_pa(dst, CACHE_INV);

ここでコピー元、コピー先の物理アドレスを仮のP0空間のアドレスにとり、ここで
コピー。このページはアンキャッシュドでマップしているのでキャッシュの考慮は
無用。
	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,
	    (dst & PG_PPN) | PG_PR_KRW | PG_V | PG_D | PG_4K | PG_SH);
P0空間でコピー。上のsh_tlb_updateで両方ともTLBに乗せてあるので例外は起きない。
TLB落ちさせないために割り込みも押さえてある。
	memcpy((void *)dst_va, (void *)src_va, PAGE_SIZE);
この仮のマップを落とします。
	sh_tlb_invalidate_addr (0, src_va);
	sh_tlb_invalidate_addr (0, dst_va);
	_cpu_intr_resume (s);
	return;
maped_area:
#endif
これは今迄通りP1/P2からアクセスできるエリア。
	if (SH_HAS_VIRTUAL_ALIAS) {	/* don't polute cache */
SH_HAS_VIRTUAL_ALIASで分けているのは、キャッシュインデックスがアドレス
空間によって変わるから。同じ物理アドレスが別のキャッシュインデックスに
乗っていることもある。ので、この物理ページにマップされる仮想アドレスに
対して全て操作しないといけない。
		/* sync cache since we access via P2. */
ここでも仮想アドレスにマップされていた時のキャッシュの整合性をとります。
		__pmap_dcache_pa(dst, CACHE_INV);
		__pmap_dcache_pa(src, CACHE_WB);
		memcpy((void *)SH3_PHYS_TO_P2SEG(dst),
		    (void *)SH3_PHYS_TO_P2SEG(src), PAGE_SIZE);
	} else {
実質物理キャッシュの場合、完全に物理アドレスでタグづけされるのでキャッシュド
のP1から操作すればいい。(どのアドレス空間からの仮想アドレスでも同じ物理アドレス
には同じインデックスになる。)
		memcpy((void *)SH3_PHYS_TO_P1SEG(dst),
		    (void *)SH3_PHYS_TO_P1SEG(src), PAGE_SIZE);
	}
}