livaの雑記帳

livaの雑記帳

OSとか作ってみたい

USB3.0のデバドラを書いてみた

f:id:liva_h:20171114114104j:plain



ザ!車輪の再発明!!って感じですが、いつものごとく懲りずにやってます。

3ヶ月くらい、ありとあらゆる事を放り出してずっとUSBのデバドラ書いてたのですが、途中ソウルジェムを濁らせたりしながらも、ようやく動いたのでブログに書いて供養しようと思います。

 

話の流れとしては、以下の記事の続き。

raphine.hatenablog.com

 

タイトルは分かり易さを重視したが故に情報量が少ないので、きちんと説明すると、

  • USB3のホストコントローラ(xHCI)のデバドラを自作し、
  • キーボードから入力を受けられる事を実機で確認した

というものです。

 

また、以下の点を注記しておきます。

  • キーボードを動かす事しか考えてないので、USB3の特徴である広帯域通信はやってない。
  • 自作OS向けにデバドラを書いたのではなく、あくまでLinuxの上で動くデバドラを書いた。

まあ前者に関しては、xHCI自体の設計が綺麗なので、たぶん僕のデバドラでもそこそこ帯域は出るんじゃないかなー、なんて思ってるんですが、そもそもBulk TransferとかIsochronous Transferとかは実装してなくて・・・。そのうち気が向いたらやります。

後者に関しては、ちょっと深い理由があって(後述)こうなりました。もちろん、近いうちに今回書いたコードを自作OS向けに移植するつもりですし、移植する事を前提とした設計にしています。

 

前置きが長くなりましたが、ここから本編。

 

用語解説

  • ホストコントローラ:USBデバイスを集中制御するコントローラ。こいつの下にUSBのポートがぶら下がっていて、ポートにデバイスを差すとコントローラがデバイスの制御を始める。デバドラはホストコントローラと通信する事で全てのUSBデバイスを制御する。
  • xHCIUSB3.0/3.1向けのホストコントローラ
  • uio_pci_generic:Linuxにおいては、本来デバドラはカーネル空間で動かす必要があるが、ユーザー空間でもデバドラ開発ができるようにする魔法の仕組み。魔法の仕組み自体はuioで、uio_pci_genericはPCIバイス制御ができるようにするためのもの。

 

事の始まり

端的に言えば、酒の席で余計な事を口走ってしまったんです。

 

「ぼく、xHCIのデバドラ書くことにしました!」

 

この言葉を発する前にちょっとしたやり取りがあったのですが、ここではそれは割愛するとして、、、どんな理由があっても、「男に二言は無い」と言いますし、口に出した以上はやらざるを得ないなぁ、と。

 

今年の春先は、「いかに四月病でも、USB3.0のデバドラなんて絶対書かないぞ!」(2.0と1.1のデバドラ書くのがあまりに大変すぎたから)って思ってたんですがね。何でこんな事言っちゃったんだろう。

 

カレンダー確認したら、この言葉を発したのがちょうど今から3ヶ月前だったわけですが、この時は「まあ大変だけど1,2週間くらい本気でやればできるでしょ」なーんて甘く見てました。。。

当時は調子に乗ってたので、こんな事を呟いてますね。この時の自分、ぶん殴りたい....

 

まあそんなこんなでUSB3.0のホストコントローラのデバドラを書き始める事になったのでした。

 

実装方針

書くのは良いのですが、ただ自分のOS向けにデバドラ書くだけというのも芸がないなぁと思ったので、ちょっと新しいチャレンジをしてみる事にしました。

 

Linuxユーザー空間で動くxHCIデバイスドライバを作る

 

これによって、デバドラの開発効率が大幅に上がり、かつ他の人に気軽に遊んでもらいやすいかなぁ、なんて事を思ったわけです。

前者は分かりやすくて、自分のOSのデバドラとして作ると、デバドラ内やOS内にバグがあった時にOSごとクラッシュしたりして辛いわけですが、ひとまずLinuxアプリケーションとして作れば、libcをバンバン使えますし、(将来的に自分のOSに移植する際はlibcの部分を取り除く必要があるとはいえ、デバッグのためには好きなように使える)クラッシュしてもセグフォするだけなので、すぐ修正して再実行できます。PCリブートして、ファームウェアの画面眺めて、自分のOS起動するのとか、もう僕は待てません。

デバドラを書く時って、少なくとも最初の頃はQEMU上で自分のOSを動かしながらその上でデバドラ動かして、っていう形式を取る事が多いと思うのですが(実機でデバッグするのは大変だから)、この方式ならQEMU上で開発するよりもずっとずっと楽です。実機上での開発なので、QEMUが対応してないハードウェアもデバッグできますし。

唯一問題があるとすれば、割り込みのオーバーヘッドが大きい事なのですが、まあそれは自作OSに移植する際に解決する問題だし、そもそもそれ以前にその程度のオーバーヘッドが問題となるような凄い物を作ってないだろ!という話も。

ユーザー空間でデバドラを書く方法としては、uio_pci_genericを使っています。技術的な話については、以前記事を書いたので、こちらを参考にしてください。

raphine.hatenablog.com

raphine.hatenablog.com

raphine.hatenablog.com

 

暗雲

でまあ、最初はxHCIのデバドラくらいすぐ書けるだろとか舐めてたんですが、なんかちょっとづつ雲行きが怪しくなってきたんですよね。

上で、「どうせこんなの1,2週間で書けるだろ」みたいな事を宣ってたという話を書きましたが、これには一応僕なりの根拠がありまして。以前USB2.0のホストコントローラ(EHCI)のデバドラを書いた時、USB1.1のホストコントローラ(UHCI)と仕様がほぼ一緒だったので、本当に1,2週間で書けてしまったんです。

なので、某セキュリティ・キャンプとか、某サイボウズ・ラボユースの合宿(OBとして参加)とかでも、「もうすぐ書き終わります〜」みたいな余裕ぶっこいた顔をしてましたよ、ええ。

 

でも、蓋を空けてみたら、xHCIEHCIから大幅に仕様が変わっていて、てかよくこんな仕様設計したなぁ、と正直感心してしまったわけですが、まあインターフェースとしては明らかに複雑になってました。

それを仕様書を読みながらチマチマ書いてたら2ヶ月経ってました。はい。

この時は、「千里の道も一歩から、毎日少しでも進捗生めば何時か終わる」って唱えてました。マラソンかよ。

 

想定外

xHCIのデバドラがほぼ書けて、ついにキーボードが使えるようになると思ったのですが、ここで事件が起こります。

 

 

そう、コントローラの下にぶら下がっていたのはキーボードではなく、ハブだったのでした。というわけで、ここからハブのデバドラを書き始める事になります。

 

ま、本当はそんな事する必要は無かったんだけどな!!!

 

May the Force be with me!

論文の学会投稿と重なりつつも、それを見なかった事にしてハブのデバドラを書き続けたらハブのデバドラが書けました。結果、論文の修正と校正プロセスが炎上しました。まあそれはまた別のお話。

キーボードのデバドラは既に書いた事があるので、よし、これ組み合わせれば動くぞ!と思っていたのですが、なぜか動かない。。。Interrupt Endpointよ、なぜ君はデータを送ってこないのか。。。

ここらへんからシステムプログラミングの醍醐味である、念力デバッグの出番です。なんか「う〜〜〜ん!」って迷走瞑想すると、問題のある箇所が分かる!的な。

まあ現実は、QEMU上で走らせてみて、QEMUxHCIエミュレーションコードをトレースして状態遷移を監視してみたり、Linuxのデバドラによって初期化された後のメモリダンプと比較したり・・・実機のデバッグ、タノシイ!!!

最終的には、仕様書眺めてるとなんか一箇所が光り始めたので、そこを確認したらバグが見つかりました。宗教始められるかもしれない。

 

キーボードいっぱいコレクション

大きなバグを潰したら、なんかキーボードの製品毎に違う挙動を示したりするようになったので、原因究明をする事になりました。もちろんデバドラのどこかに問題があって、その問題の発現が製品ごとに微妙に違う、という話であって、製品によって制御を変えなければいけないとかそんな事ではないです。

 

その中で遭遇した面白案件。

 

  • 某Sサプライ社の安物キーボードが初期化時に必ずエラーになる。僕の私物のREALFORCEはちゃんと初期化できるのに。東プレすげぇ!流石高級キーボードは違う!!って夜の研究室で叫んだ。
  • なんかふとした拍子に安物キーボードも動くようになったので、さてはと思ってUSBのコネクタ部分に力を掛けたら100%動くようになった。(=コネクタの接触不良)
  • ちなみに、安物だから悪いのではなく、偶然この個体が悪かっただけでした。(同じ製品の別の個体を試したら何もしなくても動いた)某Sサプライ社さん、誹謗中傷して申し訳ございませんでした。
  • HHKBがキーボードだと?あれはハブだよ?

 

最後について。

研究室にあるHHKBはLiteなので、まあHHKBのパチモンみたいな奴なわけですが、いずれにせよHHKBってUSBポートあるじゃないですか。キーボードが他のデバイスをぶら下げる事はできないので、HHKBの内部構造としては、ハブがあってその下にキーボードが1つぶら下がってる、という物になります。(なんかこう書くと当たり前に見えるけど、デバッグしてた時は完全に失念していたのでした)

 

でね、さっきホストコントローラの下にぶら下がってたのがハブだったって話。

あの時は、このハブがxHCIに内蔵されてるハブ(仕様書内のEmbedded Hub相当)だと思ってたんですよ。

 

 

はい、大嘘です。お前が今見てるそれはHHKBだ!!

まあそんなわけでとりあえずキーボード動かしたいだけならハブのデバドラは書かなくて大丈夫です。HHKB使いでないなら。

僕は諸事情によりあまりHHKBをdisれないのですが、まあでもUSBホストコントローラのデバドラ書く人はHHKBやめてREALFORCE使おうな!

 

動いたぞ

いろいろありましたが、一応動いたので、動画。

左のIntel NUCにUSBキーボードを挿して、NUC上でLinuxを動かし、このLinuxの上で自作xHCIドライバを動かしています。右のMacにキーボードから飛んできたパケットを表示していますが、これは単にNUCのLinuxsshしただけ。

 

一応以下の環境で検証済み。検証環境は他にもあるので、そのうちそいつらでも動かさないと。

  • Intel NUC(Kaby Lake)

BOXNUC7i7BNH 《送料無料》 - 自作PC・PCパーツが豊富!PC専門店【TSUKUMO】

  • なんか強いマシン

X11SAE | Motherboards | Products - Super Micro Computer, Inc.

xHCIモードに設定、USB2.0ポートに繋いでもちゃんと動いた。

 

コードはここに転がしてます。HHKBだと(ハブが挟まると)落ちるバグがあるので、そのうち治します。何も変更加えてないけど動くようになりました。ただの接触不良だったっぽい。

github.com