ジャンクの価値は?

ジャンクの価値?

N君がある日TLを見ていると、とても興味深いtweetが流れてきました。


Alphaマシンなどは一般的にはジャンク品という扱いになると思います。

ジャンク品 (パーソナルコンピュータ) - Wikipediaではジャンク品(ジャンクひん、junk)とは、本来、「故障品」「不要品」「がらくた」の意味のことである。と解説してありました。

さて、一般的にジャンクと呼ばれるコンピュータの価値はいかほどなのでしょうか?

Nくんの家のジャンク

Nくんの家には足の踏み場もないほどにジャンク品のコンピュータが転がっています。
ちょっとだけ紹介してみます。みなさん、いくつご存知でしょうか?

NEC SX-8i


S-4/20LとEWS4800 430とAlphaStation DS15
GENIALstation 737
Sgi Tezro Fuel O2+ O2 Octane2 Power Indigo2 Indy Indigo
Sgi Onyx350
Sgi Power Indigo2
EWS48000 430 EWS4800 530
RS/6000 170
Alpha Server DS25
Alpha station XP1000
VAXstation 4000 Model 96
Apple PowerMac9700 Power Express
Omron Luna68K

HP c8000
AlphaStation 200 4/166

ここには写っていませんが、他にもDaystar millenniumとかNextStation Turbo ColorだとかSgiAltix 350とかAltix 330だとかSun Ultra25とか色々あります。

"コンピュータ OSなければ ただの箱" とはよく言ったもので、OSももちろんあります。

BSD/OS


IRIX
AIX HP-UX Tru64 UNIX UX/4800 MacOS X Server SnowLeopard

特にUX/4800やTru64 UNIXBSD/OSMac OS X Serverはライセンスを入力しないと動きません...
が、メディアはともかくライセンスを手に入れるのがとても難しいです。(おそらくハードを手に入れるよりも)
また、HP-UXはライセンスが無くても動きますが、OnlineJFSなどで遊ぶにはEnterprise OEのメディアを手に入れないといけません…

どうやって手に入れたのか?

東京大学などではComputer Zooなどで研究室単位?で集めているようですが、私のような個人の力では残念ながらヤフオクなどでコツコツ買い集めるしかありません…

そんな私にもコンピュータを譲ってくださる篤志家がいらっしゃいまして、@tsutsuiiからはEWS4800/430を@syuu1228からはAlphaStation 200 4/166を@impreza_gf8からはAlpha Station XP1000をいただきました。(とてもありがとうございます)

ジャンク=一期一会?

さて本題です。私のコレクション、いくらの価値があるのでしょうか?
これらのコンピューターは一般的にはヤフオクなどでオークションという形で販売されます。相場はあって無いようなものです。
値段は読めません。一期一会の出会いかもしれません。
かって、こんなことがありました...
"NECのEWS4800というコンピューターシリーズの後期のモデルにEWS4800/570EXという機種があります。このマシンはEWS4800の中では最速のマシンです。
以前一度だけオークションに出品され、私は入札したのですが、お金がなくて負けてしまいました。(最終的に7万円くらいだったかな?)
先にも後にも、このマシンをオークションで見たのはその一度だけです… 未だに痛い思い出です。"

もちろんその逆で、オークションで高値で購入したけれども、その後リースアウトしたものが大量に出回ってたたき売られることもあります…

ジャンク=Priceless?

私の母親など普通の人から見てみれば、これらのコンピューターはただの”粗大ごみ”かもしれません。
ですが、私がこれらのコンピューターを持っていたおかげで、たくさんのひとに出会い、アドバイスを頂き、勉強をし、友達ができ、とても楽しい思い出をたくさん作ることが出来ました!
togetter.com
www.slideshare.net

ですので、@LabDrunkerさんの質問にはこう答えたいと思います。
確かにそのAlphaStationは高いです。ですが、今後その値段で買える保証はどこにもありません。
もしかしたらAlphaStation XP1000などが出品されるかもしれません。
ちなみにAlphaStation DS15は12万円程度しましたが、それ以上のリターンを与えてくれました。

昔の人は言いました...
bokete.jp

でも、お金は大事だよ〜
ジャンクは用法用量を守って正しくお使いください。

それではHappy Hacking

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

相変わらず謎マシンいじりが最近の趣味の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関係者の方々

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

OpenBSD/sgi、IP26カーネルインストールにまつわるトラブル

! WARNING !
ここに記述してある事は、100%役に立ちません。
完全に備忘録です。

OpenBSD/sgiでIP26のカーネルは正式サポートされていません。
したがって、外部のサイト(ftp.jaist.ac.jp等)から
カーネルの入ったbase57.tgzをダウンロードしても、
IP26のカーネルは含まれていません。

インストール時に、最後に「カーネル無いよ」と警告が出ます。

Set name(s)? (or 'abort' or 'done') [done] 
Cannot determine prefetch area. Continue without verification? [no] yes
Installing base57.tgz   100% |**************************| 56003 KB    03:49    
Extracting etc.tgz      100% |**************************|   110 KB    00:00    
Location of sets? (disk http nfs or 'done') [http] done
Are you *SURE* your install is complete without 'bsd.IP26'? [no] yes
Time appears wrong.  Set to 'Sun Sep  6 08:56:36 JST 2015'? [yes] 
Saving configuration files...done.
Making all device nodes...sh(8003) in realloc(): error: chunk info corrupted
Abort trap 
done.
Installing boot loader in volume header.
Writing file /mnt/usr/mdec/boot-IP26
sgivol: stat /mnt/usr/mdec/boot-IP26: No such file or directory

WARNING: Boot install failed. Booting from disk will not be possible

この問題を解決するには、手動でカーネルブートローダーを設置してやる必要があります。
(NFS BOOTは試していません)

処理を行っているのは、

Super User's BSD Cross Reference: /OpenBSD/distrib/sgi/ramdisk/install.md

ですので、まずはboot-IP26を作ってやらなければなりません。

Super User's BSD Cross Reference: /OpenBSD/sys/arch/sgi/stand/boot64/Makefile
Super User's BSD Cross Reference: /OpenBSD/distrib/sgi/iso/Makefile
Super User's BSD Cross Reference: /OpenBSD/distrib/sets/lists/base/md.sgi

に追記をしてリリースイメージ作成時にboot-IP26を作るようにします。

boot-IP26の書き込み方法ですが、
自分は、"WARNING: Boot install failed. Booting from disk will not be possible"のエラーが出ても無視して処理を続行し、
インストール作業を終了させて#が出た後に、

# /mnt/usr/mdec/sgivol -w boot /mnt/usr/mdec/boot-IP26 sd0
Writing file /mnt/usr/mdec/boot-IP26
File /mnt/usr/mdec/boot-IP26 has 101640 bytes
disklabel shows 17783112 sectors with 512 bytes per sector
checksum: 00000000
root part: 0
swap part: 1
bootfile: /bsd

Volume header files:
boot     offset    2 blocks, length   101640 bytes (199 blocks)

SGI partitions:
 0:a blocks 17779977 first     3135 type  4 (BSD4.2)
 8:i blocks     3135 first        0 type  0 (Volume Header)
10:k blocks 17783112 first        0 type  6 (Volume)
# mv /mnt/usr/mdec/bsd.IP26 /mnt/bsd

カーネルの設置とブートローダーの書き込みを行いました。

で、どーやって肝心のboot-IP26やbsd.IP26を持ってくるか?ですが、
ftpで転送をかけました...
が、OpenBSDramdisk kernel内で使えるようにするため?か、
このftpは制限がかかっていて、使いづらい事この上ないです...

# ftp
usage: ftp [-o output] ftp://[user:password@]host[:port]/file[/] ...
       ftp [-o output] http://host[:port]/file ...
       ftp [-o output] file:file ...
       ftp [-o output] host:/file[/] ...

Super User's BSD Cross Reference: /OpenBSD/usr.bin/ftp/main.c

どうやってもユーザーとパスワードを設定してftpサーバにログインすることができなかったので、
anonymous ftpサーバをたてました。

# ftp -o boot-IP26 192.168.1.22:/pub/boot-IP26 
Connected to 192.168.1.22.
220 macbook2006 FTP server (NetBSD-ftpd 20100320) ready.
331 Guest login ok, type your name as password.
230 Guest login ok, access restrictions apply.
Remote system type is UNIX.
Using binary mode to transfer files.
200 Type set to I.
250 CWD command successful.
Retrieving /pub/boot-IP26
local: boot-IP26 remote: boot-IP26
150 Opening BINARY mode data connection for 'boot-IP26' (101640 bytes).
226 Transfer complete.
101640 bytes received in 0.18 seconds (536.81 KB/s)
221-
    Data traffic for this session was 101640 bytes in 1 file.
    Total traffic for this session was 102251 bytes in 1 transfer.
221 Thank you for using the FTP service on macbook2006.

ブートローダーとカーネル設置してようやくHDDから起動出来るようになったと思ったら、

>> scsi(0)disk(1)rdisk(0)partition(8)boot
1024+36576+3920+1544+320 entry: 0xa80000002fff4b90

OpenBSD/sgi-IP26 ARCBios boot version 1.6
arg 0: scsi(0)disk(1)rdisk(0)partition(8)boot
arg 1: ConsoleIn=serial(0)
arg 2: ConsoleOut=serial(0)
arg 3: SystemPartition=scsi(0)disk(1)rdisk(0)partition(8)
arg 4: OSLoader=sash
arg 5: OSLoadPartition=scsi(0)disk(1)rdisk(0)partition(0)
arg 6: OSLoadFilename=/bsd
Boot: scsi(0)disk(1)rdisk(0)partition(0)/bsd
3577576+722344 [78+201600+120374]=0x468958
ARCS64 Firmware
Found SGI-IP26, setting up.
Initial setup done, switching console.
[ using 322760 bytes of bsd ELF symbol table ]
Copyright (c) 1982, 1986, 1989, 1991, 1993
	The Regents of the University of California.  All rights reserved.
Copyright (c) 1995-2015 OpenBSD. All rights reserved.  http://www.OpenBSD.org

OpenBSD 5.7-stable (GENERIC-IP26) #1: Tue Mar 10 00:34:22 MDT 2015
    naruaki@fuel.my.domain:/usr/src/sys/arch/sgi/compile/GENERIC-IP26
real mem = 536870912 (512MB)
rsvd mem = 1064960 (2MB)
avail mem = 526630912 (502MB)
mainbus0 at root: POWER Indigo2 R8000
...
warning: /dev/console does not exist

応答しません。これはIP26カーネルが不安定なため、

Making all device nodes...sh(8003) in realloc(): error: chunk info corrupted
Abort trap 

インストール時にMAKEDEVが動かないため、/dev/consoleが作られないためです。
しかたがないので、再度ramdisk kernelから起動して、
MAKEDEV stdで/dev/consoleを作ってやります。

Welcome to the OpenBSD/sgi 5.7 installation program.
(I)nstall, (U)pgrade, (A)utoinstall or (S)hell? S
# mount /dev/sd0a /mnt
# cd /mnt/dev
# ls
MAKEDEV rsd0d   rsd0h   rsd0l   rsd0p   sd0d    sd0h    sd0l    sd0p
rsd0a   rsd0e   rsd0i   rsd0m   sd0a    sd0e    sd0i    sd0m
rsd0b   rsd0f   rsd0j   rsd0n   sd0b    sd0f    sd0j    sd0n
rsd0c   rsd0g   rsd0k   rsd0o   sd0c    sd0g    sd0k    sd0o
# ./MAKEDEV std
# ls
MAKEDEV mem     rsd0d   rsd0i   rsd0n   sd0c    sd0h    sd0m    stdin
console null    rsd0e   rsd0j   rsd0o   sd0d    sd0i    sd0n    stdout
klog    rsd0a   rsd0f   rsd0k   rsd0p   sd0e    sd0j    sd0o    tty
kmem    rsd0b   rsd0g   rsd0l   sd0a    sd0f    sd0k    sd0p    zero
ksyms   rsd0c   rsd0h   rsd0m   sd0b    sd0g    sd0l    stderr

ようやく独り立ち出来るようになりましたが、

System Maintenance Menu

1) Start System
2) Install System Software
3) Run Diagnostics
4) Recover System
5) Enter Command Monitor

Option? 5
Command Monitor.  Type "exit" to return to the menu.
>> scsi(0)disk(1)rdisk(0)partition(8)boot
1024+36576+3920+1544+320 entry: 0xa80000002fff4b90

OpenBSD/sgi-IP26 ARCBios boot version 1.6
arg 0: scsi(0)disk(1)rdisk(0)partition(8)boot
arg 1: ConsoleIn=serial(0)
arg 2: ConsoleOut=serial(0)
arg 3: SystemPartition=scsi(0)disk(1)rdisk(0)partition(8)
arg 4: OSLoader=sash
arg 5: OSLoadPartition=scsi(0)disk(1)rdisk(0)partition(0)
arg 6: OSLoadFilename=/bsd
Boot: scsi(0)disk(1)rdisk(0)partition(0)/bsd
3577576+722344 [78+201600+120374]=0x468958
ARCS64 Firmware
Found SGI-IP26, setting up.
Initial setup done, switching console.
[ using 322760 bytes of bsd ELF symbol table ]
Copyright (c) 1982, 1986, 1989, 1991, 1993
	The Regents of the University of California.  All rights reserved.
Copyright (c) 1995-2015 OpenBSD. All rights reserved.  http://www.OpenBSD.org

OpenBSD 5.7-stable (GENERIC-IP26) #1: Tue Mar 10 00:34:22 MDT 2015
    naruaki@fuel.my.domain:/usr/src/sys/arch/sgi/compile/GENERIC-IP26
real mem = 536870912 (512MB)
rsvd mem = 1064960 (2MB)
avail mem = 526630912 (502MB)
mainbus0 at root: POWER Indigo2 R8000
cpu0 at mainbus0: MIPS R8000 CPU rev 0.0 75 MHz, R8010 FPU rev 0.1
cpu0: cache L1-I 16KB D 16KB direct, L2 2048KB direct
int0 at mainbus0 addr 0x1fbd9000
tcc0 at mainbus0: streaming cache revision 0
imc0 at mainbus0: revision 5
gio0 at imc0
grtwo0 at gio0 addr 0x1f000000: GR5-XZ
grtwo0: device has not been setup by firmware!
hpc0 at gio0 addr 0x1fb80000: SGI HPC3 (onboard)
zs0 at hpc0 offset 0x00059830 irq 29: 85230
zstty0 at zs0 channel 1: console
zstty1 at zs0 channel 0
pckbc0 at hpc0 offset 0x00059840 irq 28
sq0 at hpc0 offset 0x00054000 irq 3: Seeq 80c03, address 08:00:69:08:a8:e8
wdsc0 at hpc0 offset 0x00044000 irq 1: WD33C93B, 20.0 MHz, burst DMA
wdsc0: microcode revision 0x0d, fast SCSI
scsibus0 at wdsc0: 8 targets, initiator 0
sd0 at scsibus0 targ 1 lun 0: <SEAGATE, ST19171N, 0023> SCSI2 0/direct fixed serial.SEAGATE_ST19171N_LA718859
sd0: 8683MB, 512 bytes/sector, 17783112 sectors
wdsc1 at hpc0 offset 0x0004c000 irq 2: WD33C93B, 20.0 MHz, burst DMA
wdsc1: microcode revision 0x0d, fast SCSI
scsibus1 at wdsc1: 8 targets, initiator 0
haltwo at hpc0 offset 0x00058000 irq 12 not configured
pione at hpc0 offset 0x00059800 irq 5 not configured
panel0 at hpc0 offset 0x00059850 irq 9: power button
dsclock0 at hpc0 offset 0x00060000
eisa0 at imc0 irq 27
vscsi0 at root
scsibus2 at vscsi0: 256 targets
softraid0 at root
scsibus3 at softraid0: 256 targets
boot device: sd0
root on sd0a (7a66b1019d03f648.a) swap on sd0b dump on sd0b
WARNING: / was not properly unmounted
Automatic boot in progress: starting file system checks.
Can't open 7a66b1019d03f648.a: No such file or directory
CAN'T CHECK FILE SYSTEM.
7a66b1019d03f648.a: UNEXPECTED INCONSISTENCY; RUN fsck_ffs MANUALLY.
Automatic file system check failed; help!
Enter pathname of shell or RETURN for sh: 
# fsck -y                                                               
Can't open 7a66b1019d03f648.a: No such file or directory
# exit
WARNING: R/W mount of / denied.  Filesystem is not clean - run fsck
mount_ffs: 7a66b1019d03f648.a on /: filesystem must be mounted read-only; you may need to run fscWARNING: R/W mount of / denied.  Filesystem is not clean - run fsck
k
mount_ffs: 7a66b1019d03f648.a on /: filesystem must be mounted read-only; you may need to run fsck
setting tty flags
pfctl: /dev/pf: No such file or directory
pfctl: /dev/pf: No such file or directory
starting network
/etc/rc[362]: cannot create /dev/random: Read-only file system
pfctl: /dev/pf: No such file or directory
dd: /dev/random: Read-only file system
chmod: /var/db/host.random: Read-only file system
dd: /var/db/host.random: Read-only file system
dd: /etc/random.seed: Read-only file system
chmod: /etc/random.seed: Read-only file system
rm: dmesg.boot: Read-only file system
rm: utmp: Read-only file system
install: utmp: Read-only file system
rm: *: Read-only file system
/etc/rc[385]: cannot create /var/run/dmesg.boot: Read-only file system
starting early daemons: syslogd pflogd(failed).
starting RPC daemons:.
WARNING: R/W mount of / denied.  Filesystem is not clean - run fsck
mount_ffs: 7a66b1019d03f648.a on /: filesystem must be mounted read-only; you may need to run fsck
savecore: no core dump
checking quotas: done.
kvm_mkdb: can't dbopen /var/db/kvm_bsd.tmp: Read-only file system
kvm_mkdb: will try again using /bsd instead
kvm_mkdb: can't dbopen /var/db/kvm_bsd.tmp: Read-only file system
dev_mkdb: /var/run/dev.tmp: Read-only file system
chmod: /dev/tty[pqrstuvwxyzPQRST]*: No such file or directory
chown: /dev/tty[pqrstuvwxyzPQRST]*: No such file or directory
clearing /tmp
rm: [a-km-pr-uw-zA-Z]*: Read-only file system
kern.securelevel: 0 -> 1
mktemp: cannot make temp file /tmp/_motd.NdqG9uwqJa: Read-only file system
creating runtime link editor directory cache.
ldconfig: /var/run/ld.so.hints.VEtMUTQhpl: Read-only file system
preserving editor files.
Bus error 
starting network daemons: smtpdpanic: trap: utlbmod: invalid pte
Stopped at      Debugger+0x4:   jr      ra
Debugger+0x8:    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!

起動して5秒でpanic!

どっかのシリーズもののAVみたいなオチがつきましたが、現状そんな感じです。

OpenBSD/sgi、カーネルのクロスコンパイルと起動方法について

この記事はOpenBSD Advent Calendar 2015 247 日目目の記事です。
もう2015年も247日過ぎてしまったんですね(白目)。

最近、(また)OpenBSD/sgiをいじっています。
MIPS R8000という変態MIPSで遊ぶためです。

R8000 - Wikipedia

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

が変態度が高くて良い感じです。

とかもMIPSっぽく無い感じ。

イカスぜ!

現在のところR8000が完全に動くOSはIRIXだけです。
キャッシュハンドリング、基板(メモリ)のファーストモード、スローモードの設定、TLBの設定を正しく実装すれば
OpenBSDでも起動するはず...
一年計画で動けば良いなぁ...

んで、OpenBSD/sgiをいじるにあたり、基本的な事を備忘録としてまとめておきました。

カーネルのクロスコンパイルについて

MIPSは(現代のCPUに比べて)とっても遅いので、←重要
実機でカーネルコンパイルをしまくるような場合はとっても時間がかかって大変です。
そこで、クロスコンパイル環境を構築してカーネルコンパイルをします。
ビバ、INTEL

やりかたは、

しゅううさんの記事

d.hatena.ne.jp

に書いてあるのですが、

make obj

TARGET=sgi make cross-tools

cd /usr/src/sys/arch/sgi/conf;config コンフィグ名

cd ../compile/コンフィグ名

make CC=/usr/cross/sgi/usr/mips64-unknown-openbsd4.5/bin/mips64-unknown-openbsd4.5-cc LD=/usr/cross/sgi/usr/mips64-unknown-openbsd4.5/bin/mips64-unknown-openbsd4.5-ld depend

make CC=/usr/cross/sgi/usr/mips64-unknown-openbsd4.5/bin/mips64-unknown-openbsd4.5-cc LD=/usr/cross/sgi/usr/mips64-unknown-openbsd4.5/bin/mips64-unknown-openbsd4.5-ld

エラーが出ずにコンパイルが終了すればOK

http://d.hatena.ne.jp/syuu1228/20090805

でOKです。

/compile/コンフィグ名のディレクトリにbsdという名前でカーネルが出来ています。

カーネルの起動方法について(サーバ編)

カーネルコンパイルしたら、コンパイルしたカーネルをネットブートで起動させましょう。

まずはサーバ編です。
サーバOSはNetBSDを選択しました。

やりかたは

Setting up the tftpd server, Diskless NetBSD HOW-TOの記事

Setting up the tftpd server, Diskless NetBSD HOW-TO

に書いてあるのですが、

/etc/inetd.confを編集して、

/tftpboot/にコンパイルしたカーネルを置いて

$ ls /tftpboot/
bsd.rd.IP26         hack.bsd.rd.IP26    kernel_5_3          kernel_5_7

/etc/bootptabにネットブートさせる為の記述をします。

iris:\
  :ht=ether:\
  :hn:\
  :bs=auto:\
  :bf=bsd.rd.IP26:\
  :ip=192.168.1.11:\
  :sm=255.255.255.0:\
  :rp=/tftpboot/:\
  :ha=080069********:

MACアドレス

                         Running power-on diagnostics...


Initialized tod clock.

System Maintenance Menu

1) Start System
2) Install System Software
3) Run Diagnostics
4) Recover System
5) Enter Command Monitor

Option? 5
Command Monitor.  Type "exit" to return to the menu.
>> 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

>> printenv
AutoLoad=Yes
rbaud=9600
TimeZone=PST8PDT
console=g
diskless=0
dbaud=ÿÿÿÿÿ
volume=80
sgilogo=y
autopower=y
netaddr=192.168.2.13
eaddr=08:00:69:**:**:**
boottune=1
cpufreq=75
SystemPartition=scsi(0)disk(1)rdisk(0)partition(8)
OSLoadPartition=scsi(0)disk(1)rdisk(0)partition(0)
OSLoadFilename=/unix
OSLoader=sash
NoAutoLoad=CONSOLE OPEN FAILED.
ConsoleOut=serial(0)
ConsoleIn=serial(0)

で確認します。

自分の環境だけ?かもしれませんが、
/tftpbootにカーネルを置いても

>> bootp()bsd.rd.IP26

TFTP error: Access violation (code 2)
Unable to execute bootp()bsd.rd.IP26: invalid argument

といって起動しない事がありました。
そう言う場合は、/tftpbootに置くカーネルパーミッションをchmod 755などで変更したら、うまく起動しました。

カーネルの起動方法について(クライアント編)

クライアント編です。

やりかたは

OpenBSD/sgiのサイトの記事

INSTALL.sgi

に書いてありますが、

The PROM, by default, will configure itself to use the IP address set in
the `netaddr' environment variable. To force the PROM to always get an
address from a bootp or dhcp server, clear the variable (`unsetenv netaddr')
before attempting to boot from the network. Alternatively, you may want to
make sure the value of this variable is correct.

Note that, if the `netaddr' variable is unset, the PROM will initialize it
to the address obtained from the bootp or dhcp server.

http://ftp.openbsd.org/pub/OpenBSD/snapshots/sgi/INSTALL.sgi

が注意点で
unsetenv netaddrをしないと

>> bootp()bsdr.d.IP26
Warning: 'netaddr' is set to the default address 192.0.2.1.
Use 'setenv' to reset it to an Internet address on your network.
No server for bsdr.d.IP26.  
Your netaddr environment variable may be set incorrectly, or
the net may be too busy for a connection to be made.
Unable to execute bootp()bsdr.d.IP26:  could not connect to server

とダダをこねます。

OSをインストールしたら、

Option? 5
Command Monitor.  Type "exit" to return to the menu.
>> setenv OSLoadFilename /bsd
>> setenv SystemPartition dksc(0,3,8)
>> setenv OSLoadPartition dksc(0,3,0)
>> dksc(0,3,8)boot

1024+36576+3920+1544+320 entry: 0xa8000000012f4000

OpenBSD/sgi-IP27 ARCBios boot version 1.6
arg 0: dksc(0,3,8)boot
arg 1: ConsoleIn=/dev/tty/ioc30
arg 2: ConsoleOut=/dev/tty/ioc30
arg 3: SystemPartition=dksc(0,3,8)
arg 4: OSLoader=sash
arg 5: OSLoadPartition=dksc(0,3,0)
arg 6: OSLoadFilename=/bsd
arg 7: OSLoadOptions=
Boot: dksc(0,3,0)/bsd
cannot open dksc(0,3,0)/etc/random.seed: Unknown error: code 20
6495848+726200 [78+328872+204365]=0x765928
ARCS64 Firmware
Found SGI-IP35, setting up.

な感じで起動させましょう

カーネルのリリースイメージの作成について

OpenBSDではクロスコンパイル環境でリリースイメージ or インストールカーネルを作る事ができません。
(自分が不勉強なだけ?ご存知のかたがいらっしゃったら教えてください)

時間がかかりますけど、実機でリリースイメージをつくります。

はっやーい!

やりかたは

OpenBSDのサイトの記事

www.openbsd.org

に書いてありますが、

素の状態だと、/usr/rel以下にはR8000を起動させる為に必要なIP26のカーネルは作成されません。
また、不要なsgiの機種のカーネルがもりもり作成されます。

/usr/rel
$ ls
INSTALL.sgi  boot64       bsd.IP28     bsd.rd.IP22  bsd.rd.IP30  game57.tgz
SHA256       bootecoff    bsd.IP30     bsd.rd.IP27  man57.tgz
base57.tgz   bsd.IP22     bsd.IP32     bsd.rd.IP28  cd57.iso
boot32       bsd.IP27     bsd.mp.IP30  bsd.rd.IP32  comp57.tgz

不要なsgiの機種のカーネルを作る時間はもったいないので、
OpenBSDのソースを"ALLIP"で検索して

Super User's BSD Cross Reference: /OpenBSD/etc/etc.sgi/Makefile.inc
Super User's BSD Cross Reference: /OpenBSD/distrib/sgi/ramdisk/Makefile

機種を追加したり、削ったりして、必要な機種のカーネルのみコンパイルするように設定しましょう。

ありがたいコメントを頂きました。

また、カーネルコンパイルするマシンの時間がずれていると、コンパイルにコケルので

で時刻をあわせましょう。

@tokudahiroshiさん、@ao_kenjiさん、ありがとうございます!

i386のアドレス変換の話

この記事はNetBSD Advent Calendar 2015 112日目の記事です(嘘)。


で外部からの観察ができたので、まとめておきます。

まずmi_switch(9)に以下のような細工をすることで、
CR3レジスタの内容を出力させました。

mi_switch(9) - NetBSD Manual Pages

#include <machine/pcb.h>
        if(strcmp(curproc->p_comm,"hoge") == 0) {
                struct pcb *pcb = lwp_getpcb(l);
                printf("cr3 = %x\n", pcb->pcb_cr3);
        }


http://nxr.netbsd.org/xref/src/sys/kern/kern_synch.c#780
あたりに追記します。

カーネルコンパイルし、mi_switchに細工をしたカーネルで再起動します。

とりあえず何かの仮想アドレスを取得したいので、

#include <stdio.h>
 
char foo [] = "WELCOME TO NETBSD!";
 
int main(void){
 
    printf("address foo = %d\n", foo);
    for(;;) {}
 
}

というプログラムを書き、
gcc -o hoge xxxx.c
コンパイルします。

また、物理アドレスのダンプを取りたいので、
デバイスドライバに頼らないハードウェア操作
を参考に、

int main(int argc,char **argv)
{
  char buff[1024];
  int fd;
  unsigned int st,len;
  int r;
 
  st=strtol(argv[1],NULL,16);
  len=strtol(argv[2],NULL,16);
 
  fd=open("/dev/mem",O_RDONLY);
  if(fd<0)
    {
      fprintf(stderr,"cannot open\n");
      return 1;
    }
  lseek(fd,st,SEEK_SET);
  while(len)
    {
      r=read(fd,buff,len>1024?1024:len);
      if(r<1) break;
      write(1,buff,r);
      len-=r;
    }
  close(fd);
  return 0;
}

のようなコードを書き(コピペ)、
gcc -o physdump xxxx.c
コンパイルしました。

僕はあまりプログラムを書いた事が無いので、

とつぶやいたところ、

とoshimayaさんに教えてもらいました!

1.mi_switchに細工をしてhogeというプログラムを実行すると、
 hogeプロセスのCR3レジスタの内容を出力するカーネル
2.仮想アドレスを取得するプログラム(プログラム名はhoge)
3./dev/mem経由で物理アドレスをダンプするプログラム(プログラム名はphysdump)
の3点セットが揃ったので、動かしてみます。

はじめに、仮想アドレスとCR3レジスタの内容を取得します。
hogeを実行すると

$ ./hoge
address foo = 134519060

とfooの仮想アドレスを表示し、ループに入ります。

すると、コンソールには

cr3 = 5f215000
cr3 = 5f215000
cr3 = 5f215000
cr3 = 5f215000
cr3 = 5f215000
cr3 = 5f215000

という表示が延々出力されるはずです。

これで、
fooの仮想アドレス(134519060)

CR3レジスタに格納されている物理アドレス(0x5f215000)が取得出来ました。

では仮想アドレス->物理アドレスの変換をやってみます!

まず、取得できた仮想アドレス134519060を2進数に変換します。
134519060 -> 0000.1000.0000.0100.1001.1001.0001.0100

CR3レジスタに格納されている物理アドレスはPD(ページディレクトリ)の開始物理アドレスです。
仮想アドレスの上位10ビットはPDのインデックスです。
PDのインデックスの値を2ビット左にシフトしたものと
CR3レジスタに格納されている物理アドレス(PDの開始物理アドレス=0x5f215000)を加算すると、
PDEが格納されている物理アドレスが得られます。

まず、仮想アドレス134519060のPDのインデックスは
0000.1000.00 -> 0000.1000.0000 -> 80
になります。

次に、取得したCR3に格納されている物理アドレス(PDの開始物理アドレス)を指定してダンプしてみます。

# ./physdump 5f215000 100 | hexdump -C
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
...
00000080  67 20 75 5e 00 00 00 00  00 00 00 00 00 00 00 00  |g u^............|
00000090  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
...
00000100

80の所には、67 20 75 5eが格納されていました。
i386はリトルエンディアンなので、逆転させて、
5e 75 20 67がPDEに格納されている内容になります。
5e 75 20 67のうち、下位12ビットはアクセス権やら、キャッシュやらの設定なので、オフします。

したがって、
0x5e752000がPT(ページテーブル)の開始物理アドレスになります。

さらに、PTEに格納されている内容を調べます。

仮想アドレスの中10ビットはPTのインデックスです。
PTのインデックスの値を2ビット左にシフトしたものと
PDEに格納されている物理アドレス(PTの開始物理アドレス=0x5e752000)を加算すると、
PTEが格納されている物理アドレスが得られます。

まず、仮想アドレス134519060のPTのインデックスは
00.0100.1001 -> 01.0010.0100 -> 124
になります。

次に、取得したPDEに格納されている物理アドレス(PTの開始物理アドレス)を指定してダンプしてみます。

# ./physdump 5e752000 200 | hexdump -C
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
...
00000120  25 0c 2e 5e 67 94 68 5e  00 00 00 00 00 00 00 00  |%..^g.h^........|
00000130  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
...
00000200

124の所には、67 94 68 5eが格納されていました。
i386はリトルエンディアンなので、逆転させて、
5e 68 94 67がPTEに格納されている内容になります。
5e 68 94 67のうち、下位12ビットはアクセス権やら、キャッシュやらの設定なので、オフします。

したがって、
0x5e689000がページフレームの開始物理アドレスになります。

最後に、仮想アドレスの下位12ビットはページ内のオフセットアドレスになります。
1001.0001.0100 -> 914

ページフレームの開始物理アドレス(0x5e689000)にオフセットアドレス(0x914)を加算して
ダンプを取ってみると...

# ./physdump 5e689914 30 | hexdump -C
00000000  57 45 4c 43 4f 4d 45 20  54 4f 20 4e 45 54 42 53  |WELCOME TO NETBS|
00000010  44 21 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |D!..............|
00000020  00 00 00 00 00 00 00 00  00 00 00 00 f0 ff bf bf  |................|

となっています。
これはfooの中身ですね。

これで仮想アドレス->物理アドレスマッピングが勉強出来ました!

参考サイトに以下をあげておきます。softwaretechnique.jp

オープンソース開発と、あるフレームバッファコンソールの話

謎マシンいじりが最近の趣味のN君。
某オークションで掘り出し物をゲットしてホクホクです。
「Sunがリリースした最後のSPARCワークステーション
こころがぴょんぴょんするんじゃ~〜〜

さっそくNetBSDをインストールしましたが...

画面描画がおっそーい!(ぜかまし風)

行がスクロールするたびに、動きはもっさり、ゆらゆら画面がゆれています...

なぜこんなに画面描画が遅いのか?さっそく原因を調べてみましょう!

というわけで前置きが長くなりましたが、
"オープンソース開発と、あるフレームバッファコンソールの話"と題しまして、
NetBSD Advent Calendar 2014 - Qiita
23目に寄稿させてもらいます。

このような遊びに必要なものは
動作する実装が公開されているGPU
・例えばXの実装。
・例えばLinuxBSDの実装。
GPU仕様書
になります。

今回は
Xの実装として
xc/programs/Xserver/hw/xfree86/drivers/glint
Linuxの実装として
linux/pm2fb.c at master · torvalds/linux · GitHub
NetBSDの実装として
Cross Reference: /src/sys/dev/pci/pm2fb.c
OpenBSDの実装として
src/sys/arch/sparc64/dev/gfxp.c - view - 1.13
GPU仕様書として
TVP4020 Programmer's Reference User's Guide (Rev. A)
を参考にしました。

また、個人的にはつついさんの書かれた
FreeBSD Press No.12 NetBSDにおけるデバイスドライバの読み方・書き方
の記事を補完する内容だと思っています。
(つついさんの記事は後半でbus_dmaの解説に向かうので)
なので、つついさんの書かれた記事を先に読むと、より理解が深まると思います。

また、しばらく実装の話が続きますので、パッチが出来てからの話に興味が有るかたは、
読み飛ばしてうしろの方から読んでくださってもかまいません。

さて、N君が手に入れたマシンはSun MicrosystemsのUltra 25という機種で、
使われているCPUはUltraSPARC IIIi(SPARC64)です。
[今までに発表した製品]UNIXサーバ S series Sun Ultra 25 : 富士通

ささっていたグラフィックボードはPGX32(Raptor GFX)でした。

PGX32にはPermedia 2というGPUが使われています。
そしてPGX32はNetBSDではpm2fbという名前でドライバが提供されています。
https://wiki.netbsd.org/users/macallan/pm2fb/

カーネルの設定ファイルを見ると...
/sys/arch/sparc64/conf/GENERIC
http://nxr.netbsd.org/xref/src/sys/arch/sparc64/conf/GENERIC?r=1.177#904

# Sun PGX32 / TechSource Raptor GFX 8P
pm2fb*		at pci?

と、ちゃんとカーネルに組み込まれるようになっています。

画面描画が遅いということは、ドライバの性能が悪いのでしょうか?
そこでdmesgを見て確認してみます。

Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
    2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014
    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 7.99.3 (GENERIC) #1: Mon Dec 15 20:27:06 UTC 2014
        naruaki@:/usr/obj.sparc64/sys/arch/sparc64/compile/GENERIC
total memory = 1024 MB
avail memory = 986 MB
timecounter: Timecounters tick every 10.000 msec
mainbus0 (root): SUNW,Ultra-25 (Sun Ultra 25 Workstation): hostid 84581962
cpu0 at mainbus0: SUNW,UltraSPARC-IIIi @ 1336 MHz, CPU id 0
cpu0: manuf 3e, impl 16, mask 34
cpu0: system tick frequency 20 MHz
cpu0: 32K instruction (32 b/l), 64K data (32 b/l), 1024K external (64 b/l)
...
pci10: i/o space, memory space enabled
genfb0 at pci10 dev 1 function 0: Texas Instruments TVP4020 Permedia 2 (rev. 0x11)
genfb0: framebuffer at 0x1000000, size 1280x1024, depth 32, stride 5120
wsdisplay0 at genfb0 kbdmux 1: console (default, vt100 emulation)
wsmux1: connecting to wsdisplay0
wsdisplay0: screen 1-3 added (default, vt100 emulation)
drm at genfb0 not configured

genfb0 at pci10 dev 1 function 0: Texas Instruments TVP4020 Permedia 2 (rev. 0x11)
genfb0: framebuffer at 0x1000000, size 1280x1024, depth 32, stride 5120

アイエエエエ! genfb!? genfbナンデ!?

どうやら、PGX32用のpm2fbではなく、汎用のフレームバッファコンソールドライバのgenfbがアタッチしてしまったようです...
http://wiki.netbsd.org/users/macallan/genfb/
"The genfb driver provides an unaccelerated framebuffer console"
とありますし、画面描画が遅いのもしかたがありませんね...

とはいえ、せっかくの高速なUltraSPARC IIIiが台無しです...
バン (∩`・ω・) バンバン

そこで、このPGX32に使われているGPUの素性を調べてみることにします。
再度dmesgを見てみましょう。

genfb0 at pci10 dev 1 function 0: Texas Instruments TVP4020 Permedia 2 (rev. 0x11)
genfb0: framebuffer at 0x1000000, size 1280x1024, depth 32, stride 5120

おっ?どうやらPermedia 2のTVP4020というチップのようです。

そこで、NetBSDのpm2fbドライバのソースコードを頑張って読んでみます。
http://nxr.netbsd.org/xref/src/sys/dev/pci/pm2fb.c?r=1.25#266

	/*
	 * only card tested on so far is a TechSource Raptor GFX 8P /
	 * Sun PGX32, which happens to be a Permedia 2v
	 */

と書いてありました。
Permedia 2v?Permedia 2と違うのでしょうか?

そこでWikipediaをPermedia 2で検索してみます。
3Dlabs - Wikipedia
"後期にはマイナーチェンジ版のPermedia2Vが発売されている。"

どうやら、NetBSDのpm2fbドライバはPermedia 2v用に書かれているようです...
これでアタッチしなかった理由もわかりました!
でも、もうひとつ気になる点があります...
Permedia 2とPermedia 2vはWikipediaをみると3Dlabsの製品のようです。でもdmesgには
Texas Instruments TVP4020 Permedia 2 (rev. 0x11)
とありました。
Texas Instruments?どういうことなのでしょうか?
そこでXのソースコードを頑張って読んでみます。
http://nxr.netbsd.org/source/xref/xsrc/xfree/xc/programs/Xserver/hw/xfree86/drivers/glint/glint_driver.c?r=1.4#174

    { PCI_VENDOR_TI_CHIP_PERMEDIA2,	 PCI_VENDOR_TI_CHIP_PERMEDIA2,	    RES_SHARED_VGA },
    { PCI_VENDOR_TI_CHIP_PERMEDIA,	 PCI_VENDOR_TI_CHIP_PERMEDIA,	    NULL },
    { PCI_VENDOR_3DLABS_CHIP_R4, PCI_VENDOR_3DLABS_CHIP_R4, RES_SHARED_VGA },
    { PCI_VENDOR_3DLABS_CHIP_PERMEDIA4, PCI_VENDOR_3DLABS_CHIP_PERMEDIA4, RES_SHARED_VGA },
    { PCI_VENDOR_3DLABS_CHIP_PERMEDIA3, PCI_VENDOR_3DLABS_CHIP_PERMEDIA3, RES_SHARED_VGA },
    { PCI_VENDOR_3DLABS_CHIP_PERMEDIA2V, PCI_VENDOR_3DLABS_CHIP_PERMEDIA2V, RES_SHARED_VGA },
    { PCI_VENDOR_3DLABS_CHIP_PERMEDIA2,	 PCI_VENDOR_3DLABS_CHIP_PERMEDIA2,  RES_SHARED_VGA },

PCI_VENDOR_TI_CHIP_PERMEDIA2
PCI_VENDOR_3DLABS_CHIP_PERMEDIA2V
PCI_VENDOR_3DLABS_CHIP_PERMEDIA2

とPermedia 2には
Texas Instruments製のPermedia 2(TVP4020)

3Dlabs製のPermedia 2、Permedia 2V
があることがわかりました。
ああ、すっきりした!

では、GPUの素性もわかってきたので、NetBSDのpm2fbドライバに手をいれて、Permedia 2(TVP4020)で使えないかどうか試してみます!

NetBSDのpm2fbドライバのソースコードを読んでみると、
http://nxr.netbsd.org/xref/src/sys/dev/pci/pm2fb.c?r=1.25#256
のpm2fb_match関数でGPUの判定をしているようですね。
なら、ここを改造してTVP4020もプローブするようにしてみます。

const struct {
	int vendor;
	int product;
	int flags;
} pm2fb_pci_devices[] = {
	{
		PCI_VENDOR_3DLABS,
		PCI_PRODUCT_3DLABS_PERMEDIA2V,
		0
	},
	{
		PCI_VENDOR_TI,
		PCI_PRODUCT_TI_TVP4020,
		1
	},
	{
		0,
		0,
		0
	}
};

という構造体を定義して、

static int
pm2fb_match(device_t parent, cfdata_t match, void *aux)
{
	struct pci_attach_args *pa = (struct pci_attach_args *)aux;

	if (PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY)
		return 0;
	if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_3DLABS)
		return 0;

	/*
	 * only card tested on so far is a TechSource Raptor GFX 8P /
	 * Sun PGX32, which happens to be a Permedia 2v
	 */
	if (/*(PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_3DLABS_PERMEDIA2) ||*/
	    (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_3DLABS_PERMEDIA2V))
		return 100;
	return (0);
}

static int
pm2fb_match(device_t parent, cfdata_t match, void *aux)
{
	struct pci_attach_args *pa = (struct pci_attach_args *)aux;
	int i;

	if (PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY)
		return 0;

	for (i = 0; pm2fb_pci_devices[i].vendor; i++) {
		if ((PCI_VENDOR(pa->pa_id) == pm2fb_pci_devices[i].vendor &&
		     PCI_PRODUCT(pa->pa_id) == pm2fb_pci_devices[i].product))
			return 100;
	}

	return (0);
}

と改造してみました。これならGPUの種類が増えても(増えんけど...)、構造体を定義し直せば、すぐ対応できますね。
PCI_VENDORやPCI_PRODUCTは何ぞや?というと、ハードにはハード固有の情報が定義されていて、それを参照しています。
PCI Vendor and Device Lists
http://nxr.netbsd.org/xref/src/sys/dev/pci/pcidevs.h?r=1.1202#750

int flags;

pm2fb_softc構造体に
http://nxr.netbsd.org/xref/src/sys/dev/pci/pm2fb.c?r=1.25#76

struct pm2fb_softc {
	device_t sc_dev;
...
	pci_chipset_tag_t sc_pc;
	pcitag_t sc_pcitag;
...
	int sc_is_pm2;
};

のようにsc_is_pm2と言う名前でPermedia 2(TVP4020)とPermedia 2vの処理を
分岐させる用のフラグが有ったので、活用してみました。
このフラグの存在を考えると、NetBSDのpm2fbドライバはPermedia 2(TVP4020)とPermedia 2vの両方に
対応しようとはしていたみたいです。
なので、
http://nxr.netbsd.org/xref/src/sys/dev/pci/pm2fb.c?r=1.25#295

	sc->sc_is_pm2 = (PCI_PRODUCT(pa->pa_id) ==
	    PCI_PRODUCT_3DLABS_PERMEDIA2);

	for (i = 0; pm2fb_pci_devices[i].vendor; i++) {
		if (PCI_PRODUCT(pa->pa_id) == pm2fb_pci_devices[i].product) {
			sc->sc_is_pm2 = pm2fb_pci_devices[i].flags ;
			break;
		}
	}

と書き換えてみます。

さぁ、カーネルコンパイルして、起動!

お、GPUを認識してアタッチすることに成功した結果?画面が真っ黒になってブラックアウトするようになりました...
良かった良かった?...

ですが、アタッチされるだけでは、残念ながらうまく動いてくれないようです。
そんな自分に都合の良い展開はエロゲの中だけで現実ではなかなかありませんね...

そもそもフレームバッファコンソールドライバの中でハードに依存している処理は、どのあたりなのでしょうか?

Probe、レジスタの初期化、パレットの設定、RAMDACの設定、ビットマップの描画等が
ハードウェアに依存した内容になっているようです。

そこで、このあたりのPermedia 2(TVP4020)専用の処理をpm2fbドライバに適切に追記すれば、
pm2fbドライバでPermedia 2(TVP4020)とPermedia 2v両方が使えるようになる気がします。

では、Permedia 2(TVP4020)とPermedia 2vの違いは何なのでしょうか?
Linuxのpm2fbドライバのソースコードを読んでみます。
https://github.com/torvalds/linux/blob/master/drivers/video/fbdev/pm2fb.c
pm2fb_probe関数が一番最初の処理っぽいですね。

/**
 * Device initialisation
 *
 * Initialise and allocate resource for PCI device.
 *
 * @param	pdev	PCI device.
 * @param	id	PCI device ID.
 */
static int pm2fb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
	struct pm2fb_par *default_par;
	struct fb_info *info;
...
	switch (pdev->device) {
	case  PCI_DEVICE_ID_TI_TVP4020:
		strcpy(pm2fb_fix.id, "TVP4020");
		default_par->type = PM2_TYPE_PERMEDIA2;
		break;
	case  PCI_DEVICE_ID_3DLABS_PERMEDIA2:
		strcpy(pm2fb_fix.id, "Permedia2");
		default_par->type = PM2_TYPE_PERMEDIA2;
		break;
	case  PCI_DEVICE_ID_3DLABS_PERMEDIA2V:
		strcpy(pm2fb_fix.id, "Permedia2v");
		default_par->type = PM2_TYPE_PERMEDIA2V;
		break;
	}

...

なるほど。
PM2_TYPE_PERMEDIA2VとPM2_TYPE_PERMEDIA2を手がかりに調べてみます。
すると...

static void reset_config(struct pm2fb_par *p)
{
	WAIT_FIFO(p, 53);
	pm2_WR(p, PM2R_CHIP_CONFIG, pm2_RD(p, PM2R_CHIP_CONFIG) &
			~(PM2F_VGA_ENABLE | PM2F_VGA_FIXED));
	pm2_WR(p, PM2R_BYPASS_WRITE_MASK, ~(0L));
...
	switch (p->type) {
	case PM2_TYPE_PERMEDIA2:
		pm2_RDAC_WR(p, PM2I_RD_MODE_CONTROL, 0); /* no overlay */
		pm2_RDAC_WR(p, PM2I_RD_CURSOR_CONTROL, 0);
		pm2_RDAC_WR(p, PM2I_RD_MISC_CONTROL, PM2F_RD_PALETTE_WIDTH_8);
		pm2_RDAC_WR(p, PM2I_RD_COLOR_KEY_CONTROL, 0);
		pm2_RDAC_WR(p, PM2I_RD_OVERLAY_KEY, 0);
		pm2_RDAC_WR(p, PM2I_RD_RED_KEY, 0);
		pm2_RDAC_WR(p, PM2I_RD_GREEN_KEY, 0);
		pm2_RDAC_WR(p, PM2I_RD_BLUE_KEY, 0);
		break;
	case PM2_TYPE_PERMEDIA2V:
		pm2v_RDAC_WR(p, PM2VI_RD_MISC_CONTROL, 1); /* 8bit */
		break;
	}
}

static void set_memclock(struct pm2fb_par *par, u32 clk)
{
	int i;
	unsigned char m, n, p;

	switch (par->type) {
	case PM2_TYPE_PERMEDIA2V:
		pm2v_mnp(clk/2, &m, &n, &p);
		WAIT_FIFO(par, 12);
		pm2_WR(par, PM2VR_RD_INDEX_HIGH, PM2VI_RD_MCLK_CONTROL >> 8);
		pm2v_RDAC_WR(par, PM2VI_RD_MCLK_CONTROL, 0);
		pm2v_RDAC_WR(par, PM2VI_RD_MCLK_PRESCALE, m);
		pm2v_RDAC_WR(par, PM2VI_RD_MCLK_FEEDBACK, n);
		pm2v_RDAC_WR(par, PM2VI_RD_MCLK_POSTSCALE, p);
		pm2v_RDAC_WR(par, PM2VI_RD_MCLK_CONTROL, 1);
		rmb();
		for (i = 256; i; i--)
			if (pm2v_RDAC_RD(par, PM2VI_RD_MCLK_CONTROL) & 2)
				break;
		pm2_WR(par, PM2VR_RD_INDEX_HIGH, 0);
		break;
	case PM2_TYPE_PERMEDIA2:
		pm2_mnp(clk, &m, &n, &p);
		WAIT_FIFO(par, 10);
		pm2_RDAC_WR(par, PM2I_RD_MEMORY_CLOCK_3, 6);
		pm2_RDAC_WR(par, PM2I_RD_MEMORY_CLOCK_1, m);
		pm2_RDAC_WR(par, PM2I_RD_MEMORY_CLOCK_2, n);
		pm2_RDAC_WR(par, PM2I_RD_MEMORY_CLOCK_3, 8|p);
		pm2_RDAC_RD(par, PM2I_RD_MEMORY_CLOCK_STATUS);
		rmb();
		for (i = 256; i; i--)
			if (pm2_RD(par, PM2R_RD_INDEXED_DATA) & PM2F_PLL_LOCKED)
				break;
		break;
	}
}

static void set_video(struct pm2fb_par *p, u32 video)
{
	u32 tmp;
	u32 vsync = video;

	DPRINTK("video = 0x%x\n", video);

	/*
	 * The hardware cursor needs +vsync to recognise vert retrace.
	 * We may not be using the hardware cursor, but the X Glint
	 * driver may well. So always set +hsync/+vsync and then set
	 * the RAMDAC to invert the sync if necessary.
	 */
	vsync &= ~(PM2F_HSYNC_MASK | PM2F_VSYNC_MASK);
	vsync |= PM2F_HSYNC_ACT_HIGH | PM2F_VSYNC_ACT_HIGH;

	WAIT_FIFO(p, 3);
	pm2_WR(p, PM2R_VIDEO_CONTROL, vsync);

	switch (p->type) {
	case PM2_TYPE_PERMEDIA2:
		tmp = PM2F_RD_PALETTE_WIDTH_8;
		if ((video & PM2F_HSYNC_MASK) == PM2F_HSYNC_ACT_LOW)
			tmp |= 4; /* invert hsync */
		if ((video & PM2F_VSYNC_MASK) == PM2F_VSYNC_ACT_LOW)
			tmp |= 8; /* invert vsync */
		pm2_RDAC_WR(p, PM2I_RD_MISC_CONTROL, tmp);
		break;
	case PM2_TYPE_PERMEDIA2V:
		tmp = 0;
		if ((video & PM2F_HSYNC_MASK) == PM2F_HSYNC_ACT_LOW)
			tmp |= 1; /* invert hsync */
		if ((video & PM2F_VSYNC_MASK) == PM2F_VSYNC_ACT_LOW)
			tmp |= 4; /* invert vsync */
		pm2v_RDAC_WR(p, PM2VI_RD_SYNC_CONTROL, tmp);
		break;
	}
}

が見つかりました。

どうやら、
pm2_RDAC_RD関数pm2v_RDAC_WR関数の違いがキモのようです。

static inline void pm2_RDAC_WR(struct pm2fb_par *p, s32 idx, u32 v)
{
	pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, idx);
	wmb();
	pm2_WR(p, PM2R_RD_INDEXED_DATA, v);
	wmb();
}

static inline void pm2v_RDAC_WR(struct pm2fb_par *p, s32 idx, u32 v)
{
	pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff);
	wmb();
	pm2_WR(p, PM2VR_RD_INDEXED_DATA, v);
	wmb();
}

RDAC?
RDACはRAMDACの略のようです。

ではRAMDACとは何なのでしょうか?
Wikipediaで調べてみます。
デジタル-アナログ変換回路 - Wikipedia
RAMDAC - Wikipedia, the free encyclopedia
なるほど。
"ビデオカードにおいて用いられる、映像信号処理用D/A変換回路"の事だったんですね。

そして、Linuxのpm2fbのソースコードを見る限り、Permedia 2(TVP4020)とPermedia 2vの主な違いはRAMDACの設定だけのようです。

つぎはXの実装を調べてみましょう。
http://cvsweb.xfree86.org/cvsweb/xc/programs/Xserver/hw/xfree86/drivers/glint/#dirlist
を見てみると...

おおっ。

pm2_dac.c
pm2ramdac.c

pm2v_dac.c
pm2vramdac.c

ときれいに処理が分かれているソースがあるじゃないですか!

ためしに、pm2_dac.cをのぞいてみます...
http://nxr.netbsd.org/source/xref/xsrc/external/mit/xf86-video-glint/dist/src/pm2_dac.c?r=1.3
PM2DAC_CalculateMNPCForClock関数やPermedia2Init関数、Permedia2Save関数、Permedia2Restore関数などがありますが、
関数名からしてPermedia2Init関数が重要な感じがします。

そしてPermedia2Initで調べてみると...
http://nxr.netbsd.org/source/xref/xsrc/external/mit/xf86-video-glint/dist/src/glint_driver.c?r=1.6#2481

/*
 * Initialise a new mode.  This is currently still using the old
 * "initialise struct, restore/write struct to HW" model.  That could
 * be changed.
 */

static Bool
GLINTModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode)
{
    int ret = -1;
    GLINTPtr pGlint = GLINTPTR(pScrn);
    RamDacHWRecPtr pRAMDAC = RAMDACHWPTR(pScrn);
    RamDacRegRecPtr RAMDACreg;
    GLINTRegPtr glintReg = &pGlint->ModeReg[0];
    GLINTRegPtr glintReg2 = &pGlint->ModeReg[1];

    pScrn->vtSema = TRUE;

    switch (pGlint->Chipset) {
    case PCI_VENDOR_TI_CHIP_PERMEDIA2:
    case PCI_VENDOR_3DLABS_CHIP_PERMEDIA2:
	ret = Permedia2Init(pScrn, mode);
	break;
    case PCI_VENDOR_3DLABS_CHIP_PERMEDIA2V:
	ret = Permedia2VInit(pScrn, mode);
	break;

ビンゴ。

CI_VENDOR_TI_CHIP_PERMEDIA2
CI_VENDOR_3DLABS_CHIP_PERMEDIA2

Permedia2Init関数
PCI_VENDOR_3DLABS_CHIP_PERMEDIA2V

Permedia2VInit関数
をよんでいました。

XやLinuxのpm2fbドライバを調べた結果、
NetBSDのpm2fbドライバは、Permedia 2(TVP4020)用のRAMDACの設定が記述されていないようです。

さっそくNetBSDのpm2fbドライバにPermedia 2(TVP4020)用のRAMDACの処理を追記します。

NetBSDのpm2fbドライバのソースを読んでみると、ヒントとして、
http://nxr.netbsd.org/xref/src/sys/dev/pci/pm2fb.c?r=1.25#1498

/*
 * most of the following was adapted from the xf86-video-glint driver's
 * pm2v_dac.c
 */
static void
pm2fb_set_mode(struct pm2fb_softc *sc, const struct videomode *mode)
{
	int t1, t2, t3, t4, stride;
	uint32_t vclk;
	uint8_t sync = 0;

と書いてありました!
なのでPermedia 2(TVP4020)のRAMDACの設定はXのpm2_dac.cの処理を参考にして書く事に決定します。

まずは、いまのNetBSDのpm2fbドライバのRAMDACに関する処理は、Permedia 2v用の処理なので
関数名を書き換えてっと...

-pm2fb_set_pll(struct pm2fb_softc *sc, int freq)
+pm2vfb_set_pll(struct pm2fb_softc *sc, int freq)
-pm2fb_set_mode(struct pm2fb_softc *sc, const struct videomode *mode)
+pm2vfb_set_dac(struct pm2fb_softc *sc, const struct videomode *mode)

そして追記します。

static int
pm2fb_set_pll(struct pm2fb_softc *sc, int freq)
{
	uint8_t  reg, bm = 0, bn = 0, bp = 0;
	unsigned int  m, n, p, fi, diff, out_freq, bdiff = 1000000;

	for (n = 2; n < 15; n++) {
		for (m = 2 ; m < 256; m++) {
			fi = PM2_EXT_CLOCK_FREQ * m / n;
			if (fi >= PM2_PLL_FREQ_MIN && fi <= PM2_PLL_FREQ_MAX) {
				for (p = 0; p < 5; p++) {
					out_freq = fi >> p;
					diff = abs(out_freq - freq);
					if (diff < bdiff) {
						bm = m;
						bn = n;
						bp = p;
						bdiff = diff;
					}
				}
			}
		}
	}

	pm2fb_write_dac(sc, PM2_DAC_PIXELCLKA_M, bm);
	pm2fb_write_dac(sc, PM2_DAC_PIXELCLKA_N, bn);
	pm2fb_write_dac(sc, PM2_DAC_PIXELCLKA_P, (bp | 0x08));

	do {
		reg = bus_space_read_1(sc->sc_memt, sc->sc_regh,
			PM2_DAC_INDEX_DATA);
	} while (reg == PCLK_LOCKED);

	return 0;
}

/*
 * most of the following was adapted from the xf86-video-glint driver's
 * pm2_dac.c (8bpp only)
 */
static void
pm2fb_set_dac(struct pm2fb_softc *sc, const struct videomode *mode)
{
	int t1, t2, t3, t4, stride;
	uint32_t vclk, tmp;
	uint8_t sync = 0;

	t1 = mode->hsync_start - mode->hdisplay;
	t2 = mode->vsync_start - mode->vdisplay;
	t3 = mode->hsync_end - mode->hsync_start;
	t4 = mode->vsync_end - mode->vsync_start;

	/* first round up to the next multiple of 32 */
	stride = (mode->hdisplay + 31) & ~31;
	/* then find the next bigger one that we have partial products for */
	while ((partprodPermedia[stride >> 5] == -1) && (stride < 2048)) {
		stride += 32;
	}

	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_HTOTAL,
	    ((mode->htotal) >> 2) - 1);
	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_HSYNC_END,
	    (t1 + t3) >> 2);
	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_HSYNC_START,
	    (t1 >> 2));
	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_HBLANK_END,
	    (mode->htotal - mode->hdisplay) >> 2);
	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_HGATE_END,
	    (mode->htotal - mode->hdisplay) >> 2);
	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_SCREEN_STRIDE,
	    stride >> 2);

	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_VTOTAL,
	    mode->vtotal - 2);
	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_VSYNC_END,
	    t2 + t4 - 1);
	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_VSYNC_START,
	    t2 - 1);
	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_VBLANK_END,
	    mode->vtotal - mode->vdisplay);
	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_VIDEO_CONTROL,
	    PM2_VC_VIDEO_ENABLE |
	    PM2_VC_HSYNC_ACT_HIGH | PM2_VC_VSYNC_ACT_HIGH);

	vclk = bus_space_read_4(sc->sc_memt, sc->sc_regh, PM2_VCLKCTL);
	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_VCLKCTL,
	    vclk & 0xfffffffc);

	tmp = bus_space_read_4(sc->sc_memt, sc->sc_regh, PM2_CHIP_CONFIG);
	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_CHIP_CONFIG,
	    tmp & 0xffffffdd);

	pm2fb_write_dac(sc, PM2_DAC_MODE_CONTROL, MOC_BUFFERFRONT);
	pm2fb_set_pll(sc, mode->dot_clock);

	sync = MC_PALETTE_8BIT;

	if (!(mode->flags & VID_PHSYNC))
	    sync |= MC_HSYNC_INV;
	if (!(mode->flags & VID_PVSYNC))
	    sync |= MC_VSYNC_INV;

	pm2fb_write_dac(sc, PM2_DAC_MISC_CONTROL, sync);
	pm2fb_write_dac(sc, PM2_DAC_COLOR_MODE,
	    CM_PALETTE | CM_GUI_ENABLE | CM_RGB);

	sc->sc_width = mode->hdisplay;
	sc->sc_height = mode->vdisplay;
	sc->sc_depth = 8;
	sc->sc_stride = stride;
	aprint_normal_dev(sc->sc_dev, "pm2 using %d x %d in 8 bit, stride %d\n",
	    sc->sc_width, sc->sc_height, stride);
}

static void
pm2fb_set_mode(struct pm2fb_softc *sc, const struct videomode *mode)
{
	if (sc->sc_is_pm2) {
		pm2fb_set_dac(sc, mode);
	} else {
		pm2vfb_set_dac(sc, mode);
	}
}

Permedia 2(TVP4020)とPermedia 2v用の処理をわけるフラグsc_is_pm2を利用して
RAMDACの処理を分岐させています。

さぁ、カーネルコンパイルして、起動!

文字も出てきて、ログインプロンプトも出てきましたが、画面が半分に圧縮?されています。
でも、ブラックアウトしているのに比べれば、大幅な進歩です。
やったネ!

もう一度、追記したソースコードを再度見直してみます...

	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_SCREEN_STRIDE,
-	    stride >> 2);
+	    stride >> 3);

でした!
この謎の右シフト演算は、
Linuxのpm2fbドライバの
http://www.cs.fsu.edu/~baker/devices/lxr/http/source/linux/drivers/video/pm2fb.c#L238

/**
 *	pm2fb_set_par - Alters the hardware state.
 *	@info: frame buffer structure that represents a single frame buffer
 *
 *	Using the fb_var_screeninfo in fb_info we set the resolution of the
 *	this particular framebuffer.
 */
static int pm2fb_set_par(struct fb_info *info)
{
	struct pm2fb_par *par = info->par;
...
	depth = (depth > 32) ? 32 : depth;
	data64 = depth > 8 || par->type == PM2_TYPE_PERMEDIA2V;
...
	hsstart = to3264(info->var.right_margin, depth, data64);
	hsend = hsstart + to3264(info->var.hsync_len, depth, data64);
	hbend = hsend + to3264(info->var.left_margin, depth, data64);
	htotal = to3264(xres, depth, data64) + hbend - 1;
	vsstart = (info->var.lower_margin)
		? info->var.lower_margin - 1
		: 0;	/* FIXME! */
	vsend = info->var.lower_margin + info->var.vsync_len - 1;
	vbend = info->var.lower_margin + info->var.vsync_len +
		info->var.upper_margin;
	vtotal = info->var.yres + vbend - 1;
	stride = to3264(width, depth, 1);
...
	set_pixclock(par, pixclock);
	DPRINTK("Setting graphics mode at %dx%d depth %d\n",
		info->var.xres, info->var.yres, info->var.bits_per_pixel);
	return 0;
}

static u32 to3264(u32 timing, int bpp, int is64)
{
         switch (bpp) {
         case 8:
                 timing >>= 2 + is64;
                 break;
         case 16:
                 timing >>= 1 + is64;
                 break;
         case 24:
                 timing = (timing * 3) >> (2 + is64);
                 break;
         case 32:
                 if (is64)
                         timing >>= 1;
                 break;
         }
         return timing;
}

を参考にしました。

さぁ、カーネルコンパイルして、起動!


おおお〜、きれいに文字が表示されました!

pm2fb0 at pci10 dev 1 function 0: Texas Instruments TVP4020 Permedia 2 (rev. 0x11)
pm2fb0: 8 MB aperture at 0x01800000
no data for est. mode 640x480x67
pm2fb0: pm2fb using 1280 x 1024 in 8 bit, stride 1280
wsdisplay0 at pm2fb0 kbdmux 1: console (default, vt100 emulation)

pm2fb0 at pci10 dev 1 function 0: Texas Instruments TVP4020 Permedia 2 (rev. 0x11)
pm2fb0: 8 MB aperture at 0x01800000

ちゃんとアタッチもされています!

なにより、genfbと比べてHardware accelerationが効いて、体感できるレベルで画面描画がキビキビしています。
ああ、rasopsの歓声が聞こえるようだ...

これで一応NetBSD/sparc64でPermedia 2(TVP4020)が使えるようになりました。
しかし、

真にMachine Independentなドライバを目指すならば、SPARCのようなBig endianアーキテクチャで動いただけで満足してはいけません!
tested on sparc64 only so farで満足するなど、愚の骨頂!

という訳でN君は死蔵していた"ELSA Gloria Synergy"の存在を思い出しました!

ELSA Gloria SynergyはAlpha用のグラフィックカードで、
このカードにも実はPermedia 2(TVP4020)が使われているのです!


そして、PC用からAlpha用に転用できるお手軽グラフィックカードとして、Permedia 2は随分研究されています。
http://moon.hanya-n.org/comp/alpha/hct/graphics.html
そこそこ需要?もありそうですね!

さて、NetBSDのpm2fbドライバはNetBSD/alphaではそもそもカーネルに組み込まれていません。
そこで、カーネル設定ファイル
/sys/arch/alpha/conf/GENERICを編集して

+pm2fb*  at      pci? dev ? function ?           # 3Dlabs Permedia 2 Graphics
+wsdisplay*     at      pm2fb?

と付け足してあげます。

RAMDACの設定は追記してあげたので、問題なく動くはずです。

さぁ、カーネルコンパイルして、起動!


うーん、文字が正しく表示されないようです。

というわけで、フォント(ビットマップ)を描画するpm2fb_putchar関数を調べてみます。
http://nxr.netbsd.org/xref/src/sys/dev/pci/pm2fb.c?r=1.25#970

static void
pm2fb_putchar(void *cookie, int row, int col, u_int c, long attr)
{
	struct rasops_info *ri = cookie;
	struct wsdisplay_font *font = PICK_FONT(ri, c);
	struct vcons_screen *scr = ri->ri_hw;
	struct pm2fb_softc *sc = scr->scr_cookie;
	uint32_t mode;

	if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
		void *data;
		uint32_t fg, bg;
		int uc, i;
		int x, y, wi, he;

		wi = font->fontwidth;
		he = font->fontheight;
...
		if (c == 0x20) {
			pm2fb_rectfill(sc, x, y, wi, he, bg);
		} else {
			uc = c - font->firstchar;
			data = (uint8_t *)font->data + uc * ri->ri_fontscale;

			mode = PM2RM_MASK_MIRROR;
			switch (ri->ri_font->stride) {
				case 1:
					mode |= 3 << 7;
					break;
				case 2:
					mode |= 2 << 7;
					break;
			}

			pm2fb_wait(sc, 8);

			bus_space_write_4(sc->sc_memt, sc->sc_regh,
			    PM2_RE_MODE, mode);
			bus_space_write_4(sc->sc_memt, sc->sc_regh,
			    PM2_RE_CONFIG, PM2RECFG_WRITE_EN);
			bus_space_write_4(sc->sc_memt, sc->sc_regh,
			    PM2_RE_BLOCK_COLOUR, bg);
			bus_space_write_4(sc->sc_memt, sc->sc_regh,
			    PM2_RE_RECT_START, (y << 16) | x);
...
	}
}

むー、結構難しそうな関数ですね。

でも、よく見ると
mode = PM2RM_MASK_MIRROR;
という処理がとってもあやしぃ...

ここで、初めてGPU仕様書を読んでみます。
すると、
RasterizerModeの設定にどのように描画をするか指定できるオプションが有りました!



そこでビットマップの描画方法をLittle endianとBig endianで切りかえてあげます。

static void
pm2fb_putchar(void *cookie, int row, int col, u_int c, long attr)
{
	struct rasops_info *ri = cookie;
	struct wsdisplay_font *font = PICK_FONT(ri, c);
	struct vcons_screen *scr = ri->ri_hw;
	struct pm2fb_softc *sc = scr->scr_cookie;
	uint32_t mode;

	if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
		void *data;
		uint32_t fg, bg;
		int uc, i;
		int x, y, wi, he;

		wi = font->fontwidth;
		he = font->fontheight;

		if (!CHAR_IN_FONT(c, font))
			return;
		bg = ri->ri_devcmap[(attr >> 16) & 0xf];
		fg = ri->ri_devcmap[(attr >> 24) & 0xf];
		x = ri->ri_xorigin + col * wi;
		y = ri->ri_yorigin + row * he;
		if (c == 0x20) {
			pm2fb_rectfill(sc, x, y, wi, he, bg);
		} else {
			uc = c - font->firstchar;
			data = (uint8_t *)font->data + uc * ri->ri_fontscale;

			mode = PM2RM_MASK_MIRROR;
#if BYTE_ORDER == LITTLE_ENDIAN
			switch (ri->ri_font->stride) {
				case 1:
					mode |= 4 << 7;
					break;
				case 2:
					mode |= 3 << 7;
					break;
			}
#else
			switch (ri->ri_font->stride) {
				case 1:
					mode |= 3 << 7;
					break;
				case 2:
					mode |= 2 << 7;
					break;
			}
#endif
			pm2fb_wait(sc, 8);

			bus_space_write_4(sc->sc_memt, sc->sc_regh,
			    PM2_RE_MODE, mode);
			bus_space_write_4(sc->sc_memt, sc->sc_regh,
			    PM2_RE_CONFIG, PM2RECFG_WRITE_EN);
			bus_space_write_4(sc->sc_memt, sc->sc_regh,
			    PM2_RE_BLOCK_COLOUR, bg);
			bus_space_write_4(sc->sc_memt, sc->sc_regh,
			    PM2_RE_RECT_START, (y << 16) | x);
			bus_space_write_4(sc->sc_memt, sc->sc_regh,
			    PM2_RE_RECT_SIZE, (he << 16) | wi);
			bus_space_write_4(sc->sc_memt, sc->sc_regh,
			    PM2_RE_RENDER,
			    PM2RE_RECTANGLE |
			    PM2RE_INC_X | PM2RE_INC_Y | PM2RE_FASTFILL);
			bus_space_write_4(sc->sc_memt, sc->sc_regh,
			    PM2_RE_BLOCK_COLOUR, fg);
			bus_space_write_4(sc->sc_memt, sc->sc_regh,
			    PM2_RE_RENDER,
			    PM2RE_RECTANGLE | PM2RE_SYNC_ON_MASK |
			    PM2RE_INC_X | PM2RE_INC_Y | PM2RE_FASTFILL);

			pm2fb_wait(sc, he);
			switch (ri->ri_font->stride) {
			case 1: {
				uint8_t *data8 = data;
				uint32_t reg;
				for (i = 0; i < he; i++) {
					reg = *data8;
					bus_space_write_4(sc->sc_memt,
					    sc->sc_regh,
					    PM2_RE_BITMASK, reg);
					data8++;
				}
				break;
				}
			case 2: {
				uint16_t *data16 = data;
				uint32_t reg;
				for (i = 0; i < he; i++) {
					reg = *data16;
					bus_space_write_4(sc->sc_memt,
					    sc->sc_regh,
					    PM2_RE_BITMASK, reg);
					data16++;
				}
				break;
			}
			}
		}
		if (attr & 1)
			pm2fb_rectfill(sc, x, y + he - 2, wi, 1, fg);
	}
}

さぁ、カーネルコンパイルして、起動!

おおおおっ、文字がきれいに表示されました!
本当にきれいです!わ〜

むむ、しかし問題発生!
およよ?何故か文字が斜めっていますね。
 ヽ(`Д´)ノ  
  .ヽ`Д´)  
  (ヽ`Д).  
  (  ヽ`)  
  (   ヽ  
な感じ?になってしまっています...
一難去って、また一難...
でも、ゴールはあと少し。頑張ります!

では、なぜ文字が斜めに表示されるのでしょうか?
文字を移動させたりする関数が怪しい気がしますね...
では、さっそく調べてみましょう。

文字を移動させたりする関数は、
pm2fb_bitblt関数になります。
http://nxr.netbsd.org/xref/src/sys/dev/pci/pm2fb.c?r=1.25#860

ん?BitBltというのは何じゃらほい?
というわけでWikipediaで調べてみると...
Bit Block Transfer - Wikipedia
Bit blit - Wikipedia, the free encyclopedia
"描画する画像データ (ビットマップ) を一旦メインメモリに蓄え、最終的な画像データだけをVRAMにまとめて転送する。"だそうです。
なんだか、ゲームの開発みたいな話になってきましたね...

まずは、NetBSDのpm2fbの実装を調べてみます。

static void
pm2fb_bitblt(void *cookie, int xs, int ys, int xd, int yd,
    int wi, int he, int rop)
{
	struct pm2fb_softc *sc = cookie;
	uint32_t dir = 0;
	int rxd, rwi, rxdelta;

	if (yd <= ys) {
		dir |= PM2RE_INC_Y;
	}
	if (xd <= xs) {
		dir |= PM2RE_INC_X;
	}
...
	if (sc->sc_depth == 8) {
		int adjust;
		/*
		 * use packed mode for some extra speed
		 * this copies 32bit quantities even in 8 bit mode, so we need
		 * to adjust for cases where the lower two bits in source and
		 * destination X don't align, and/or where the width isn't a
		 * multiple of 4
		 */
		if (rop == 3) {
			bus_space_write_4(sc->sc_memt, sc->sc_regh,
			    PM2_RE_CONFIG,
			    PM2RECFG_READ_SRC | PM2RECFG_WRITE_EN |
			    PM2RECFG_ROP_EN | PM2RECFG_PACKED | (rop << 6));
		} else {
			bus_space_write_4(sc->sc_memt, sc->sc_regh,
			    PM2_RE_CONFIG,
			    PM2RECFG_READ_SRC | PM2RECFG_READ_DST |
			    PM2RECFG_WRITE_EN | PM2RECFG_PACKED |
			    PM2RECFG_ROP_EN | (rop << 6));
		}
		rxd = xd >> 2;
		rwi = (wi + 7) >> 2;
		rxdelta = (xs & 0xffc) - (xd & 0xffc);
		/* adjust for non-aligned x */
		adjust = ((xd & 3) - (xs & 3));
		bus_space_write_4(sc->sc_memt, sc->sc_regh,
		    PM2_RE_PACKEDDATA_LIMIT,
		    (xd << 16) | (xd + wi) | (adjust << 29));

	} else {
...
	}
	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_RE_RECT_START,
	    (yd << 16) | rxd);
	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_RE_RECT_SIZE,
	    (he << 16) | rwi);
	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_RE_SOURCE_DELTA,
	    (((ys - yd) & 0xfff) << 16) | (rxdelta & 0xfff));
	bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_RE_RENDER,
	    PM2RE_RECTANGLE | dir);
}

次はXの実装を調べます。
http://nxr.netbsd.org/source/xref/xsrc/external/mit/xf86-video-glint/dist/src/pm2_accel.c?r=1.1#505
Permedia2SubsequentScreenToScreenCopy関数

static void
Permedia2SubsequentScreenToScreenCopy(ScrnInfoPtr pScrn, int x1, int y1,
					int x2, int y2, int w, int h)
{
    GLINTPtr pGlint = GLINTPTR(pScrn);
    char align;

    TRACE_ENTER("Permedia2SubsequentScreenToScreenCopy");
    /* We can only use GXcopy for Packed modes */
    if (pGlint->ROP != GXcopy) {
	GLINT_WAIT(5);
	GLINT_WRITE_REG(pGlint->FrameBufferReadMode, FBReadMode);
        Permedia2LoadCoord(pScrn, x2, y2, w, h);
        GLINT_WRITE_REG(((y1-y2)&0x0FFF)<<16 | ((x1-x2)&0x0FFF), FBSourceDelta);
    } else {
  	align = (x2 & pGlint->bppalign) - (x1 & pGlint->bppalign);
	GLINT_WAIT(6);
	GLINT_WRITE_REG(pGlint->FrameBufferReadMode|FBRM_Packed, FBReadMode);
        Permedia2LoadCoord(pScrn, x2>>pGlint->BppShift, y2,
						(w+7)>>pGlint->BppShift, h);
  	GLINT_WRITE_REG(align<<29|x2<<16|(x2+w), PackedDataLimits);
        GLINT_WRITE_REG(((y1-y2)&0x0FFF)<<16 | (((x1 & ~pGlint->bppalign)-(x2 & ~pGlint->bppalign))&0x0FFF), FBSourceDelta);
    }

    GLINT_WRITE_REG(PrimitiveRectangle | pGlint->BltScanDirection, Render);
    TRACE_EXIT("Permedia2SubsequentScreenToScreenCopy");
}

むー、NetBSDのpm2fb_bitblt関数と
XのPermedia2SubsequentScreenToScreenCopy関数やっている事は、ほぼ同じです。

これは詰んだ...

ソウルジェムが濁りそうです...

ですが、あきらめが悪いN君はpm2fb_bitbltをいじくっているうちにある重大な発見をしてしまいました。

static void
pm2fb_bitblt(void *cookie, int xs, int ys, int xd, int yd,
    int wi, int he, int rop)
{
...
	if (sc->sc_depth == 8) {
		int adjust;
		/*
		 * use packed mode for some extra speed
		 * this copies 32bit quantities even in 8 bit mode, so we need
		 * to adjust for cases where the lower two bits in source and
		 * destination X don't align, and/or where the width isn't a
		 * multiple of 4
		 */
		if (rop == 3) {
			bus_space_write_4(sc->sc_memt, sc->sc_regh,
			    PM2_RE_CONFIG,
			    PM2RECFG_READ_SRC | PM2RECFG_WRITE_EN |
			    PM2RECFG_ROP_EN | PM2RECFG_PACKED | (rop << 6));
		} else {
			bus_space_write_4(sc->sc_memt, sc->sc_regh,
			    PM2_RE_CONFIG,
			    PM2RECFG_READ_SRC | PM2RECFG_READ_DST |
			    PM2RECFG_WRITE_EN | PM2RECFG_PACKED |
			    PM2RECFG_ROP_EN | (rop << 6));
		}
		rxd = xd >> 2;
		rwi = (wi + 7) >> 2;
		rxdelta = (xs & 0xffc) - (xd & 0xffc);
		/* adjust for non-aligned x */
#if BYTE_ORDER == LITTLE_ENDIAN
                adjust = 1;
#else
                adjust = ((xd & 3) - (xs & 3));
#endif
		bus_space_write_4(sc->sc_memt, sc->sc_regh,
		    PM2_RE_PACKEDDATA_LIMIT,
		    (xd << 16) | (xd + wi) | (adjust << 29));

	} else {
...
}

なんと、adjustに1を指定してやると、ちゃんと表示されるのです。


そして、Big endianなSPARC64でadjustに1を指定してやると、Little endianなAlphaと同じ現象が発生しました。

この現象をまとめると
・Little endianなAlphaでadjustに1→きれいに表示される
・Big endianなSPARC64でadjustに1→ななめになる。
です。

これは非常に不思議な事です。
GPU仕様書にも、adjustのこの使い方は書いてないからです。


まぁ、結果オーライということで先に進みます。

幾多の難関を乗り越え遂にLittle endianでもBig endianでもNetBSDのpm2fbドライバを動かす事に成功したN君。
「勝ったッ!pm2fb完!」
なのでしょうか?

試しにwsfb-driverでXを動かしてみましょう!

wsfb-driverとは
"wsfb is an Xorg driver for OpenBSD and NetBSD wsdisplay framebuffer devices. This is a non-accelerated driver. "
WSFB(4) manual page
とのことで、フレームバッファコンソールの機能を利用した?Xのドライバです。
(たぶんLinuxのfbdevに近いものだとおもいますが、Linux良く知らないので違っていたらすいません...)

さぁ、startx!

ああっ、SPARC64では、このように画面が崩れてしまいました...

ですが、Alphaだと正しく表示されるようです!

むー。
これは困りました。
何か良い問題解決の手がかりは無いでしょうか?

そこで、OpenBSDの実装を見てみます。
OpenBSDでは、gfxpと言う名前でドライバが提供されているようです。
また、gfxpソースコードが、/sys/arch/sparc64/dev/直下にあることから、
異なるアーキテクチャ間でのgfxpドライバの共有は行われていないようです。
(NetBSDは/sys/dev/pci/直下)
GFXP(4)
http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/arch/sparc64/dev/gfxp.c?rev=1.13&content-type=text/x-cvsweb-markup

/*
 * The Permedia 2 provides two views into its 64k register file.  The
 * first view is little-endian, the second is big-endian and
 * immediately follows the little-endian view.  Since bus_space(9)
 * already does the byte order conversion for us, we use the
 * little-endian view.
 *
 * There are also little-endian and big-endian views into the
 * framebuffer.  These are made available through separate BARs.  We
 * use the big-endian view in this driver to avoid unnecessary byte
 * swapping in rasops(9).
 */
#define PM2_PCI_MMIO		0x10 	/* Registers */
#define PM2_PCI_MEM_LE		0x14 	/* Framebuffer (little-endian) */
#define PM2_PCI_MEM_BE		0x18	/* Framebuffer (big-endian) */

大変興味深いことが書いてあります。
どうやら、Permedia 2はLittle endian用とBig endian用のフレームバッファ(VRAM)を切り替えられるようです。

NetBSDのpm2fbドライバでは
http://nxr.netbsd.org/xref/src/sys/dev/pci/pm2fb.c?r=1.25#328

	pci_mapreg_info(pa->pa_pc, pa->pa_tag, 0x14, PCI_MAPREG_TYPE_MEM,
	    &sc->sc_fb, &sc->sc_fbsize, &flags);

と0x14=Framebuffer (little-endian)を指定していました。
Little endianなAlphaでXが正しく表示されて、
Big endianなSPARC64でXの画面が崩れてしまうのは、コレが原因かもしれません。
なので、

#define PM2_PCI_MEM_LE		0x14 	/* Framebuffer (little-endian) */
#define PM2_PCI_MEM_BE		0x18	/* Framebuffer (big-endian) */

#if BYTE_ORDER == LITTLE_ENDIAN
	pci_mapreg_info(pa->pa_pc, pa->pa_tag, PM2_PCI_MEM_LE, PCI_MAPREG_TYPE_MEM,
 	    &sc->sc_fb, &sc->sc_fbsize, &flags);
#else
        pci_mapreg_info(pa->pa_pc, pa->pa_tag, PM2_PCI_MEM_BE, PCI_MAPREG_TYPE_MEM,
            &sc->sc_fb, &sc->sc_fbsize, &flags);
#endif

と書き換えてみます。
さぁ、カーネルコンパイルして、起動!& startx

おっ、うまくいきましたね。

補足
もし、この取得したフレームバッファ物理アドレスを使って、
どのようにwsfb-driverがXを表示しているか興味が有る方は、
あるmmapの話
をご覧ください。

しかし、これも不思議な現象です。
NetBSDのpm2fbドライバは"tested on sparc64 only so far"で"Permedia 2v"専用でした。
つまり、Little endian用のフレームバッファを指定しても、Permedia 2vはPermedia 2と違って、
wsfb-driverでXが正しく表示できているのでしょうか?

まぁ、結果オーライということで先に進みます。

さて、いよいよ幾多の難関を乗り越え遂にBig endianでもLittle endianでもX環境でも
NetBSDのpm2fbドライバを動かす事に成功したN君。
「勝ったッ!pm2fb完!」
なのでしょうか?

いいえ、まだ先が有ります。

せっかく書いた、NetBSDのpm2fbドライバのパッチをsend-prしましょう。
send-prとはエラーや改善希望の報告システム事です。
WEB上から簡単に出来ます。
http://www.netbsd.org/support/send-pr.html
http://www.netbsd.org/cgi-bin/sendpr.cgi?gndb=netbsd

残念ながら英語で書かなければなりませんが、心配しなくても大丈夫。
N君なんて英検四級です!

どうやらN君は今回のsend-prを書くにあたって、フレームバッファコンソールという
デバイスの性質も考慮して、写真を多用したようです。
また、adjustの件など疑問に思ったこともすべて書ききったようです。
kern/49226: pm2fb are not attached on Spsarc64 and Alpha.

WEB上から投稿すると、"Your electronic mail address: "で指定したメールアドレスに
以下のような内容で受付メールが来ます。

It has the internal identification `kern/49226'.
The individual assigned to look at your
report is: kern-bug-people.

>Category:       kern
>Responsible:    kern-bug-people
>Synopsis:       pm2fb are not attached on Spsarc64 and Alpha.

また、実際に受け付けされた内容もWEB上で確認できます。
http://mail-index.netbsd.org/netbsd-bugs/

しかし、この日は"たまたま"処理が重かったのか、10時間たっても反映されませんでした。

早漏なN君は「あら?うまく届かなかったのかな?」と思って
同じ内容でもう1回send-prしました。
kern/49229: pm2fb are not attached on Sparc64 and Alpha.

そんなことをすると、当然2回分受理されてしまいます...

1回目はSparc64の綴りをSpsarc64とミスしたので、
謝って取り消してもらいました。
http://mail-index.netbsd.org/netbsd-bugs/2014/09/20/msg038368.html
↑これは"revert"より"close"のほうが、適切な表現です。

けっかとして、前の1回目のsend-prは取り消され、2回目に一本化されました。
http://mail-index.netbsd.org/netbsd-bugs/2014/09/24/msg038401.html

さて、send-prも完了しました。
後は返事を待つだけです。
「いつ返事がくるかな?」N君は首を長くして待ちます。

.
..
...
.....
......

来ません!怒!

怒ったN君はtech-kernという別のメーリングリストで、
http://mail-index.netbsd.org/tech-kern/2014/12/08/msg018222.html
"Please point it out if there are any mistakes in my code."
「俺のコードに文句がある奴は出てこいやーっ!」(高田延彦風)

凸(゚Д゚#)ヤンノカゴルァ!!
しました。

すると返事が来ました!
http://mail-index.netbsd.org/tech-kern/2014/12/08/msg018223.html

中指を飛ばすことから始まるコミュニケーション、ス・テ・キ。

そして、議論を重ね
http://mail-index.netbsd.org/tech-kern/2014/12/10/msg018244.html
http://mail-index.netbsd.org/tech-kern/2014/12/10/msg018245.html
http://mail-index.netbsd.org/tech-kern/2014/12/11/msg018247.html
http://mail-index.netbsd.org/tech-kern/2014/12/11/msg018248.html
http://mail-index.netbsd.org/tech-kern/2014/12/12/msg018249.html
http://mail-index.netbsd.org/tech-kern/2014/12/12/msg018250.html
http://mail-index.netbsd.org/tech-kern/2014/12/13/msg018251.html
http://mail-index.netbsd.org/tech-kern/2014/12/13/msg018252.html
http://mail-index.netbsd.org/tech-kern/2014/12/17/msg018264.html
http://mail-index.netbsd.org/tech-kern/2014/12/17/msg018265.html

無事コミットされました。
http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/dev/pci/pm2fb.c.diff?r1=1.26&r2=1.27&only_with_tag=MAIN&f=h

これで、ようやく
「勝ったッ!pm2fb完!」
です。

というわけで、ドライバの読み書きを題材に、オープンソース開発における
・問題の発見
・問題点の詳細な検討
・問題点を解決する手がかりの探し方とソースコードの書き方
・パッチを提供する
・議論をする
・パッチが取り込まれる
の流れを記事として書いてみました。

インターネットを検索すると、オープンソース開発での
「パッチを書いた」とか「コミットされた」とか
断片的な情報は出てくるのですが、はじめから終わりまでを書いた情報は、
なかなか見たことが無かったので「よい記録かな?」と思って筆をとった次第です。
勉強会だと時間の関係上、どうしても切らなければならない事が出てきますから...

フレームバッファコンソールのドライバ開発は、
視覚的に面白いので勉強にちょうど良いと思います。

残念なのはAMDRadeonNvidiaGeForceに収斂してしまって、
仕様の公開された勉強向きのおもしろいGPUの出てくる可能性がほぼ無い事でしょうか...

あしたは、いよいよNetBSD developerのnonakapさんの登場です!
xakaneという声がどこからか聞こえてくる気がしますが、
どんな記事を書いてくださるのか、大変楽しみですね!(・∀・)ニヤニヤ
(中指を飛ばすコミュニケーション)

"サンタさんに髪の毛頼んだら枕元にたくさん落ちてた"
というスレタイを見るたびに"ドキッ"とするお年頃のN君のお話でした。

おしまい。

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