uchan-nos / os-from-zero

『ゼロからのOS自作入門』(内田公太著、マイナビ出版)のサポートサイトです

Home Page:https://zero.osdev.jp/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

【USBドライバー実装】QEMU上でEventRingにTRBが積もらないときのデバッグ方法?

paulsohn opened this issue · comments

Rust移植を取り組んでいるため、day06cのUSBドライバーを最初から実装することにしました。
QEMU環境でテストしていますが、ポートリセット後、Event RingにPort Status Change Event TRBが入ってこない問題があります。
Event Ringとして割り当てたバッファの先頭を読み取ると、ゼロになっています。

QEMU起動で --trace usb_xhci_port_* オプションを入れると、portsc.current_connect_statusがセットされている第5,6ポートのうち第5ポートがリセットされ、次のようなログを確認できました。(第6ポートはWaiting Addressedフェーズに入ります。)

2373@1700803829.384328:usb_xhci_port_write port 5, off 0x0000, val 0x00020ef1
2373@1700803829.384352:usb_xhci_port_reset port 5, warm 0
2373@1700803829.384357:usb_xhci_port_link port 5, pls 0
2373@1700803829.384359:usb_xhci_port_notify port 5, bits 0x200000

このことからポートリセット自体は問題なくできているはずですが、Event Ringの登録がどこか間違っていてTRBを受け取れていないように思います。
そこで、Event Ringが正しく登録されているか、ちゃんとTRBを発行しているかをデバッグしたいのですが、どうすればいいでしょうか?

(追記)
スレッドを見つけましたが、あちらに出ているようなログの出し方がわかれば役に立つでしょうか。
スレッドの問題はQEMU自体のバグらしく、私の場合と関係があるかはわかりません(事情によりQEMU以外の環境のテストは困難な状態です。)

なお、元のC++コードをビルドしたところマウスが正しく認識されているので、QEMUの問題ではなさそうです。

別のスレッドに出ているようにBusy Bitもクリア(W1C)してみましたが、大差ありませんでした。

@paulsohn
確認ですが、Event Ring と Command Ring は正しくセットアップできていますか?
Event Ring と Command Ring の動作確認用の「No Op Command」というものがあるので、まだ試してないのであれば、是非試してください。
XHCI 仕様書の「6.4.3.1 No Op Command TRB」に説明があります。

Command Ring に No Op Command を積み、Event Ring に成功メッセージが帰ってくれば、Command Ring と Event Ring のセットアップが出来ています。
No Op Command がうまく動かないのであれば、セットアップがうまく行っていない可能性が高いです。

※あまりにも古い QEMU では No Op Command が実装されていないので、新しめのバージョンを使ってくださいね。
qemu/qemu@dc2c037

ご回答ありがとうございます。
Command RingにNoOpを積むようにセットしてみました。

今回は、これらのリンクを参照し、QEMUを

--trace "usb_xhci_queue_event"
--trace "usb_xhci_fetch_trb"

オプションで起動させてみました。

NoOpTRBをCommand Ringから拾っているところ(usb_xhci_fetch_trb)まではQEMUログに出力されますが、
その後は同様、イベントログは発生せず、Event RingからTRBを取り出すこともできません。

今までの結果から見ると、Event Ringのセットアップが上手く行っていないように見えます。

インタラプターのdequeueポインターとセグメントテーブルレジスターの値は問題なく入れているはずです。
何かビットの活性化を見落としているのではないかと思っています。

ERST構成後、ERSTBZ→ERSTBA順に更新すべきだったのを、逆にしていたのが原因でした。

解決おめでとうございます。

ERSTBZ ではなく ERSTSZ ですかね。Event Ring Segment Table のサイズ(SZ)を表すデータ構造ですね。

後からこのIssueを見た方への補足情報

xHCI仕様書の"4.9.4 Event Ring Management"にERSTBAを設定する前に他の初期化を終わらせておくように書いてあります。

  • Prior to writing the ERST Base Address (ERSTBA) register system software shall:
    • Initialize the Event Ring Segments that will be referenced by the Event Ring Segment Table (ERST) to ‘0’.
    • Initialize the ERST by initializing the ERST.BaseAddress and ERST.Size fields of each element in the table. The ERST.BaseAddress field shall point to the associated Event Ring Segment, and the ERST.Size field shall indicate the number of TRBs supported by the segment.
    • Write the ERST Size (ERSTSZ) Register with the number of valid entries in the ERST and Event Ring Dequeue Pointer (ERDP) Register with the value of ERST(0).BaseAddress.

概要を日本語に訳すと

  • ERSTBA に書く前に以下のことをせよ
    • イベントリングセグメントを 0 に初期化
    • ERST.BaseAddress と ERST.Size フィールドを初期化
    • ERSTSZ レジスタと ERDP レジスタをセット