この記事は,自作OS Advent Calendar 2020 - Adventarの13日目の記事です.
USBキーボードドライバを実装しました.
主に読んだもの
xHCIの仕様書です.
USBキーボードドライバの作成手順が載っています.
USB3.2の仕様書です.
OSに関するフォーラムです.xHCIやUSBに関する質問は少ないですが,それでもいくつか存在します.
手順
以下では eXtensible Host Controller Interface for Universal Serial Bus (xHCI) Requirements Specification May 2019 Revision 1.2 を単にxHCIの仕様書と呼称します.
xHCを探す
xHCはPCIデバイスの一種です *1.xHCの検索方法は以下のページを参考にしてください.
PCI設定空間の内容を読み取る
BAR0(とBAR1)が,xHCIのMMIO空間の基準アドレスとなります.
HCの所有権をBIOSからOSに移す
xHCIの仕様書の4.22.1に詳細があります.ただし,USBLEGSUP
がxECP + 00h
に存在すると仕様書には書かれていますが,実際はこの位置に存在しないこともあります.USBLEGSUP
はxHCI Extended Capabilityというものの一つで,これはリンクリストの形態でメモリ上に存在します.USBLEGSUP
がxECP + 00h
に存在せず,このリンクリストの途中に存在する場合がありますが,この場合にHCの所有権を移動させる必要があるかは不明です.
HCの初期化
xHCIの仕様書の4.2に手順が示されています.割り込みは任意と書かれていますが,割り込みを用いない場合でもイベントリングは必要です.
ポート,スロット,エンドポイントの初期化
xHCIの仕様書の4.3に手順が示されています.
キーボードからのキー入力の受信
これについては,紹介した「USB 3.0 ホストドライバ自作入門」の第6章に詳しく載っています.
注意するべき事柄
トランスファーリングは各エンドポイント毎に用意する
各エンドポイントは異なるトランスファーリングを扱うことになります.
物理アドレスも連続しているメモリが必要になる
「メモリ確保なんて仮想アドレスで連続していればいいだろ」と思っていた時期が僕にもありました.実際はそうではありません.ハードウェアはDMAによって物理メモリに対して直接値を読み書きします.場合によってはページ境界を跨ぐ場合も存在します.確保したメモリが物理メモリ上でも必ず連続するようなメモリアロケータを用意するべきです.
QEMUは始めから接続されているUSBデバイスのPort Status Change TRBを発行しない
通常のコンピュータでは,起動時に接続されているUSBデバイスに対するPort Status Change TRBが,HCが起動 (run) した後に発行されますが,QEMUは始めから接続されているデバイスに対するTRBを発行しません.HCを起動した後,各ポートの接続状況を確認する必要があります.
また,HCを起動した後にコマンドリングやイベントリングが正しく設定されているかを確認するには,コマンドリングにNoop TRBを発行すると良いです.このTRBはイベントリングにCommand Completion TRBを発行する以外,何も行ないません.間違えてトランスファーリングのNoop TRBを発行しないようにしてください.
ポート番号は1からはじまる
ポートのレジスタのオフセットは index - 1 となります.
便利なもの
QEMUのデバッグ出力,トレーサ
詳細はこちらを確認してください.
DPRINTF
とトレースをどちらも有効にしておくべきです.これらの有無によってバグ取りに掛ける時間が大幅に変わります.
QEMUのソースコード
上記のデバッグ出力やトレーサとの合わせ技ですが,デバッグ出力やトレーサの出力をgrep
を用いてソースコード内で検索すると,なぜそのメッセージが表示されたのか,そのメッセージはどのような意味を持つのかがすぐにわかります.これは非常に便利です.
作成物
kernel/src/device/pci/xhci
に,xHCIやUSBを扱うコードがあります.
謝辞
現在,USBドライバを含む,多種多様なドライバの作成というテーマで,サイボウズ・ラボユースにおいて開発を行なっています.メンターの内田さんには,様々な質問に答えていただいたり,バグ取りを手伝っていただいたりしています.本当にありがとうございます.
*1:Class = 0x0C, Sub = 0x03, Interface = 0x30