2.7K Views
December 01, 20
スライド概要
MikanOS(x86-64パソコン用OS)のUSBホストコントローラドライバを開発するにあたり,遭遇した罠を集めました。
サイボウズ・ラボ株式会社で教育向けのOSやCPU、コンパイラなどの研究開発をしています。
USB3.0ドライバ開発の道 USBホストコントローラドライバの難所紹介 2020/11/13 ラボ勉強会 @uchan_nos
MikanOSのUSBドライバ開発の罠たち この資料はMikanOSのUSBホストコントローラドライバを開発す るにあたり,遭遇した罠ポイントを集めたもの MikanOS: @uchan_nosが開発している教育用OS ソースコード https://github.com/uchan-nos/mikanos/ 罠ポイントは思い付いた主なものを載せた これがすべてではない みんなもレッツUSBドライバ開発!
罠リスト QEMUのCRCRのバグ No Opコマンドがエラー 送信イベントが来ない USBデバイスの初期化エラー xHCを始動するとフリーズ 一部機種でデバイス不可視
罠リスト QEMUのCRCRのバグ No Opコマンドがエラー 送信イベントが来ない USBデバイスの初期化エラー xHCを始動するとフリーズ 一部機種でデバイス不可視
Command Ring xHCにコマンドを送るためのキュー CRCR Command Ring OSがpush,xHCがpop 要素はTRBという16バイトの構造体 TRB最終ワードのビット0がcycle bit OSは末尾にLink TRBを配置し,コマン ドリングを1周させる 周回するたびにcycle bitを反転 CRCRがcommand ringの先頭を指す xHCが消費者 TRB TRB 1 1 … OSが生産者 TRB Link TRB 1 0
CRCR: Command Ring Control Register xHCI for USB Requirement Specificationより Command Ring PointerにCRの先頭アドレスを設定 CRR (Command Ring Running) はCR動作中に1になるはず 他のフィールドは0が読み出されることになっている RCS: Reading this flag always returns ‘0’. CS: Reading this bit shall always return ‘0’. CA: Reading this bit always returns ‘0’. Command Ring Pointer: Reading this field always returns ‘0’.
CRCR.CRRで動作チェック Command ringにTRBをpushし,ドアベルレジスタに書き込む →xHCがcommand ringから取得開始する(CRR=1になる) 当時の僕「上手くコマンドが認識されないなあ… そうだ,CRCR.CRRをチェックしてみよう」 QEMUで実験 まったくCRR=1にならない
QEMUのバグ
https://github.com/qemu/qemu/blob/3c8c36c9087da957f580a9bb5ebf7814a753d1c6/hw/usb/hcd-xhci.c#L2867-L2872
static uint64_t xhci_oper_read(void *ptr, hwaddr reg, unsigned size) {
XHCIState *xhci = ptr;
uint32_t ret;
switch (reg) {
…
case 0x18: /* CRCR low */
ret = xhci->crcr_low & ~0xe;
break;
case 0x1c: /* CRCR high */
ret = xhci->crcr_high;
break;
…
~0xe = 1 1 1 0 0 0 1
QEMUではCRCR.CRRをマスクしてしまっている!
加えて,0になるべきフィールドをマスクしてない
罠リスト QEMUのCRCRのバグ No Opコマンドがエラー 送信イベントが来ない USBデバイスの初期化エラー xHCを始動するとフリーズ 一部機種でデバイス不可視
No Opコマンドがエラーになる No Opコマンド:Command ringの動作確認用コマンド このコマンドがうまく動く → Command ringが正しくセットアップされている No Opコマンドを送るとTRB Errorが返ってくる 仕様を読んでも辻褄が合わないぞ?
QEMUにNo Opコマンドが無い QEMUにNo Opコマンドが実装されていないだけだった hikaliumさんがその後パッチ投稿 https://github.com/qemu/qemu/commit/dc2c037fd23ea3dcf2e13afda22c1c64ab56f96b
罠リスト QEMUのCRCRのバグ No Opコマンドがエラー 送信イベントが来ない USBデバイスの初期化エラー xHCを始動するとフリーズ 一部機種でデバイス不可視
USBデバイスとエンドポイント エンドポイント=通信チャネル USBデバイスは最大16個のEPを持つ 必ずEP0=Default Control Pipeを持つ EP0を使ってその他のEPの構成情報を得る xHC側にTransfer Ringを用意し,EPと接続 xHC スロット スロット TR TR TR EPは4種類 転送種別 転送方向 転送の特徴 Control Bidirectional 各種ディスクリプタを読むなど Isochronous Out/In 時間当たりの転送量を保証 Bulk Out/In 記憶装置などの大容量転送 Interrupt Out/In 小容量の定期的な転送 EP EP EP USBデバイス
Transfer Ring USBデバイスに対する送受信のキュー 構造はcommand ringと同じ エンドポイント毎に1つのTR No Op TRBを送信しても送信完了イベ Endpoint Context xHCが消費者 Transfer Ring TRB TRB ントが返ってこない 1 1 … TRBのIOC (Interrupt on Completion) を1にしなければならないのだった OSが生産者 TRB Link TRB 1 0
罠リスト QEMUのCRCRのバグ No Opコマンドがエラー 送信イベントが来ない USBデバイスの初期化エラー xHCを始動するとフリーズ 一部機種でデバイス不可視
USBデバイスの初期化フロー 1. デバイス接続を検知(PORTSC.CCS=1) 2. ポートをリセット 3. スロットを割り当て(Enable Slotコマンド) スロット:xHCが利用するメモリ上の構造体 4. アドレスを割り当て(Address Deviceコマンド) アドレス幅は7ビット 0~127 Address Deviceコマンド:デバイスにアドレスを割り当てる どんな設定をしてもUSB Transaction Errorになる…
Address Deviceコマンドのエラー原因 ポートリセット後,デバイスはデフォルトアドレス0を持つ 同一アドレスを持つ複数のデバイスが存在するとダメ 1. 複数のデバイスを接続して並行に初期化しようとする 初期化が終わらないとデバイス種別を判定できないため, USBキーボードに限定するとしても全デバイスの初期化が必要 2. アドレス0を持つデバイスが複数発生する 3. Address Deviceコマンドがエラー ポートリセット~Address Deviceコマンドまでは 直列に実行する必要がある!
罠リスト QEMUのCRCRのバグ No Opコマンドがエラー 送信イベントが来ない USBデバイスの初期化エラー xHCを始動するとフリーズ 一部機種でデバイス不可視
xHCを始動させるとシステムフリーズ 1. xHCをリセット 2. メモリ構造を生成し初期化 3. xHCのレジスタを設定 メモリ構造のアドレスを設定したり 4. xHCの動作を開始 USBCMDのRun/Stopビットに1を書く Run/Stopに1を書いた少し後に,システム全体がフリーズ 例外ハンドラで文字を書いている途中で止まる(上図) シャットダウンではなく,止まる forループの途中なのでプログラムのミスは疑いにくい (同じロジックを繰り返し実行している)
HC BIOS Owned Semaphore システムがフリーズする=バスロック系? プログラム外に原因がありそう。BIOS? 仕様書をふと眺めているとUSB Legacy Support Capabilityに HC BIOS Owned Semaphoreなるビットを発見 “The BIOS sets this bit to establish ownership of the xHC.” 調べてみると,このビットが1になっていた 隣にHC OS Owned Semaphoreというビットもある “System software sets this bit to request ownership of the xHC.” これらを適切に設定したらフリーズしなくなった!
罠リスト QEMUのCRCRのバグ No Opコマンドがエラー 送信イベントが来ない USBデバイスの初期化エラー xHCを始動するとフリーズ 一部機種でデバイス不可視
一部の機種でUSBデバイスが検出されない osdev-jpメンバーからUSBマウス,キーボードが使えない報告 報告があった機種の1つはAcer Aspire E1-572-A54D 発売日は2013年6月 CPU: i5-4200U (Haswell) 搭載されているxHCのデバイスコードは0x9c31 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 https://github.com/torvalds/linux/blob/3650b228f83adda7e5ee532e2b90429c03f7b9ec/drivers/usb/host/xhci-pci.c#L37 Lynx Point-LPはUSB3.0が普及し始めた頃のチップセット Linuxにusb_enable_intel_xhci_ports()という関数がある Intel製チップセットの場合だけ呼ばれる USB3_PSSENやXUSB2PRという謎のレジスタへ書き込み
USB3_PSSENとXUSB2PR Intel 9あたりの時代のチップセットに載ってるレジスタ USB3_PSSEN: USB 3.0 Port SuperSpeed Enable Register XUSB2PR: xHC USB 2.0 Port Routing Register USB3_PSSENのビットに1を書くと,SuperSpeedが有効に XUSB2PRのビットに1を書くと,EHC→xHCに制御権が移動 EHCIにしか対応しないOSに配慮した過渡期の仕様 USBポート群 XUSB2PR EHC xHC
xHCI仕様書には載っていない xHCI仕様書に”5.2 PCI Configuration Registers (USB)”という節が 一応は存在する でもUSB3_PSSEN/XUSB2PRに関する記述は無い チップセット固有の,しかも過渡期の仕様だから? 2つのレジスタをLinuxと同様に設定したらうまく動いた! PCIコンフィグレー ション空間 BAR USB3_PSSEN XUSB2PR xHCI仕様の範疇 xHCレジスタ 空間 メモリ上の データ構造群