最後の楽園の開拓を(ちょこっとだけ)手伝った話

相変わらず謎マシンいじりが最近の趣味のN君。
家の中の"積みマシン"の電源を入れてみたところ、
珍しいCPUを積んでいる事が分かりました。

>> hinv
                   System: IP26
                Processor: 75 Mhz R8000, with FPU
     Primary I-cache size: 16 Kbytes
     Primary D-cache size: 16 Kbytes
     Secondary cache size: 2 Mbytes
              Memory size: 512 Mbytes
                 Graphics: GR5-XZ
                SCSI Disk: scsi(0)disk(1)
                    Audio: Iris Audio Processor: version A2 revision 1.1.0

MIPS R8000というCPUはMIPS系のCPUの中では、かなり変わり者です。

R8000 - Wikipedia
Power Indigo2 (R8000) - Nekochan Net

"命令キャッシュは16KBで、ダイレクトマップ方式、仮想インデックス・仮想タグ方式で、ラインサイズは32バイトである。"
とか
"TLBはデュアルポートで384エントリあり、3ウェイセットアソシアティブ方式になっている。"
とか
"ストリーミングキャッシュは外付けの1MBから16MBのキャッシュで、R8000の二次キャッシュ、R8010の一次データキャッシュとして機能する。"
とか
"マルチチップで構成されている"
とか
変態度が高くて、イカしています。

暇を見つけて以下記事のようにボチボチ遊んでいたのですが、一部問題が有るものの動き始めました。

OpenBSD/sgi、カーネルのクロスコンパイルと起動方法について - nullnilaki’s blog
OpenBSD/sgi、IP26カーネルインストールにまつわるトラブル - nullnilaki’s blog

というわけで前置きが長くなりましたが、

"最後の楽園の開拓を(ちょこっとだけ)手伝った話"と題しまして、
NetBSD Advent Calendar 2015 - Qiita
23目に寄稿させてもらいます。

自分が触り始めた時は、OpenBSDにはR8000対応のソースは入っているものの、
自分でMakefileを修正しないとカーネルは作成されない状態で、
手つかずのまま3年程度眠っていた状態でした。

また、commit logには、

IP26 kernels currently boot single-user, but don't live long; I am suspecting
a bug in the tcc cache routines, but am currently not able to find it (come
to think of it, my understanding of how this cache works could be wrong, and
of course there is no documentation for it but what can be gathered from
IRIX'  comments and defines).

と書いてある状態でした。

/src/sys/arch/sgi/localbus/tcc.c

で、まずはキャッシュが怪しいかなと思って
(特に命令キャッシュはダイレクトマップ、仮想インデックス、仮想タグ方式なので)
いろいろいじってみたのですが、R8000のマニュアルとOpenBSDのキャッシュのフラッシュ方法が同じだったのと、
カーネルのPanicのしかたがキャッシュに起因するものでは無いような気がして、
キャッシュが原因説は保留しました。

/src/sys/arch/mips64/mips64/cache_tfp_subr.S

さて、よくよく落ち方を観察すると、最終的にPanicはするのですが、
結構良いところまで行く場合が有る事に気付きました。


>> bootp()bsd.rd.IP26
Obtaining bsd.rd.IP26 from server macbook2006
3577592+722344 entry: 0xa800000008010000
ARCS64 Firmware
Found SGI-IP26, setting up.
...
Bus error (core dumped) 
starting early daemons: syslogd pflogd.
starting RPC daemons:.
savecore: /dev/sd0a: Device busy
checking quotas: done.
kvm_mkdb: can't open /dev/ksyms
panic: trap: utlbmod: invalid pte
Stopped at      0xa800000008294424:     jr      ra
0xa800000008294428:      move   zero,zero
RUN AT LEAST 'trace' AND 'ps' AND INCLUDE OUTPUT WHEN REPORTING THIS PANIC!
DO NOT EVEN BOTHER REPORTING THIS WITHOUT INCLUDING THAT INFORMATION!
ddb> 

TLB modified faultについて、調べてみると

‏@koieさん、ありがとうございます!

どうやら、ユーザーランドTLBミス時の処理が良くないのが原因なような気がします。
そこで、今度はユーザーランドTLBミス時の処理を調べる事にしました。

/src/sys/arch/mips64/mips64/exception_tfp.S

utlb_miss:
	.set	noat
	.align	4
	PRE_MFC0_ADDR_HAZARD
	DMFC0	k0, COP_0_VADDR
	MFC0_HAZARD
	DMTC0	k0, COP_0_WORK0
	MTC0_HAZARD
	PTR_SRL	k0, SEGSHIFT
	sltiu	k1, k0, PMAP_SEGTABSIZE
	beqz	k1, _inv_seg
	 NOP
	DMFC0	k1, COP_0_UBASE		# PCB_SEGTAB(CI_CURPROCPADDR(curcpu))
	MFC0_HAZARD
	PTR_SLL	k0, LOGREGSZ
	PTR_ADDU k1, k0
	PTR_L	k1, 0(k1)		# get pointer to page table
	DMFC0	k0, COP_0_WORK0		# saved COP_0_VADDR
	MFC0_HAZARD
	beqz	k1, _inv_seg
	 PTR_SRL k0, PAGE_SHIFT - 2
	andi	k0, ((NPTEPG / 2) - 1) << 2
	PTR_ADDU k1, k0
	lwu	k0, 0(k1)		# get pte
	DMTC0	k0, COP_0_TLB_LO
	MTC0_HAZARD
	TLBW
	ERET

このあたりの処理は、一刻も早く終わらせる為に全部アセンブリ言語で書かれているようです...
ウーン、わからん!呪文みたいだ...
ですが、MIPSの解説本を片手に読み進めていくと、
「なんとなく意味が分かるところ」と「わけがわからないよ
な部分に分かれて来ました。

上記コードの赤文字の部分が「わけがわからないよ」な部分です。
やたらと"2"で引いたり、割ったり、左シフトしたりしています。
アセンブリ言語で書かれていてコードの意味は解らないし、デバックもやりづらいし、
お手上げです。

そこで思いつきました!
"一刻も早く終わらせる為に全部アセンブリ言語で書かれている"
なら
"同じような処理をCで書いている部分"を探して
参考にしてみれば良いんです。

そして、あっちこっち探して、見つけました!

/src/sys/arch/mips64/include/pmap.h

/* User virtual address to pte page entry */
#define	uvtopte(va)	(((va) >> PAGE_SHIFT) & (NPTEPG -1))

あれ?
/src/sys/arch/mips64/include/pmap.hに書かれているマクロでは
"2"で引いたり、割ったり、左シフトしたりしていません。

そこで、mips64/exception_tfp.Sを書き直してみました。

utlb_miss:
	.set	noat
...
	PTR_L	k1, 0(k1)		# get pointer to page table
	DMFC0	k0, COP_0_WORK0		# saved COP_0_VADDR
	MFC0_HAZARD
	beqz	k1, _inv_seg
-       PTR_SRL k0, PAGE_SHIFT - 2 
-       andi    k0, ((NPTEPG / 2) - 1) << 2 
+         PTR_SRL k0, PAGE_SHIFT 
+       andi    k0, NPTEPG - 1 
	PTR_ADDU k1, k0
	lwu	k0, 0(k1)		# get pte
	DMTC0	k0, COP_0_TLB_LO
	MTC0_HAZARD
	TLBW
	ERET

すると...

やったねたえちゃん!
しかし、起動はするものの、SIGABRTが頻発してしまうため、調査を続けることにしました。

そのうち、シルバーウィークがやってきたので、実家に帰る事にしました。

そこで転機がやって来たのです!
そのころには、Miodさん(OpenBSD/sgiのえらい人)とTwitter上でやりとりをしていたので、
メンションもらったついでに、思い切ってパッチを見てもらう事にしました。
この機会を逃すと、また、手元で眠らせてしまう気がしたので...

そしたら、あれよあれよという間に...

OpenBSD/sgiの公式?サポートになったのでした。

今まで動かなかった原因は、

cvsweb.openbsd.org

が原因でした!

これでOpenBSD/sgisgiのマシンに使われていたすべてのMIPS系CPUを制覇した事になります。

何故、自分がこのようなお手伝いを出来たかというと、
R8000が特殊で誰も持っていないという事につきると思います。

実際、Linux/MIPSのページには
"R8000 は現在未サポートです。これはこのプロセッサが一部の SGI の機種のみで使われた
比較的まれなプロセッサで、Linux/MIPS 開発者が誰もこのような機種を持っていない、
ということも理由の一部です。"
と書いてあります。

http://archive.linux.or.jp/JF/JFdocs/MIPS-HOWTO-9.html#ss9.8

また、他のMIPS系のCPUと構成が違うためソースの共有が出来ない部分が多数有り、余計誰も見なかったという事が考えられます。

/src/sys/arch/mips64/mips64/cache_tfp.c
/src/sys/arch/mips64/mips64/cache_tfp_subr.S
/src/sys/arch/mips64/mips64/exception_tfp.S
/src/sys/arch/mips64/mips64/tlb_tfp.S

残念ながら負荷をかけるとSIGBUSやらSIGSEGVやら発生する問題が有るのですが、
おいおい調べていきたいと思います。

まぁ、

が感想です。(日頃お世話になっているし)
それと、MiodさんにR8000の話を振られた時に、タイミングよくパッチを提出
できた事は大きかったと思います。
タイミング、大事です。

謎の/src/sys/arch/mips64/exception_tfp.Sでやたらと
"2"で引いたり、左シフトしたりしている理由は、

The 2 in the original code is log2(pte size); k0 >> PAGE_SHIFT will be 
the pte number. But in the page table, it is stored as an array of 
32-bit words, so we need to shift it to the left by 2. The original 
instructions: 
        PTR_SRL k0, PAGE_SHIFT - 2 
        andi    k0, ((NPTEPG / 2) - 1) << 2 
are equivalent to: 
        PTR_SRL k0, PAGE_SHIFT 
        andi    k0, (NPTEPG / 2) - 1 
        PTR_SLL k0, 2 
and guarantees the address is correctly aligned for the `lwu' 
instruction later. 

だそうでした。

反省点は、10月11月12月とIngressにハマって、まったくマシンいじりを放置していました。

このようなアドバイスを頂いていたのに、@gussunoyoyoさん、すいません...

よく遊び、よく学べということで!

あしたは、いよいよNetBSD developerのnonakapさんの登場です!
今年もどんな記事を書いてくださるのか、大変楽しみですね!(・∀・)ニヤニヤ
(相変わらず中指を飛ばすコミュニケーション)

おしまい。

えっ
NetBSD Advent Calendarなのに、OpenBSDだって!?
(゚ε゚)キニシナイ!!

それと、R8000関係で最近こんなことがありました。

どうやら、この変更が原因

'Re: CVS: cvs.openbsd.org: src' - MARC

らしいのですが、

+	 * Execution of the `sync' instruction is not supported by the
+	 * T-Bus and raises a machine check exception.

を疑問に思いました。

バスから見てCPUの命令ってどういう風に見えるんでしょうね?
おしえてつついさ〜ん!

参考文献など:
はじめて読む MIPS(リローデッド)
OpenBSD/sgi on octane2 - exception.S・tlbhandler.SにおけるGET_CPU_INFO()とコンテキスト保存、割り込みの話 - かーねる・う゛いえむにっき

謝辞:
いつもtwitterでふぁぼってくださる皆様
なまあたたかい目で見守ってくださるつついさんをはじめとしたNetBSD関係者の方々

追加:
つついさんにきいてみたところ、下記のような回答でした。