PCIにアクセスするのにPCI BIOS使った方が良いのかな〜と思って,まずは,PCI BIOS32を探すコードを書いてみました.
参考文献は昨日買った「PCIバス&PCI‐Xバスの徹底研究」
最初に探すのは,PCI BIOS32サービス・ディレクトリで,アドレス0xE0000〜0xFFFFFのどこかにいます.
0xFFFF0はリセットベクタらしいので,実際は0xFFFF0まで探せばOKです.
PCI BIOS32サービス・ディレクトリは16バイトのテーブルで,以下のような形になってます.
オフセット | サイズ | 説明 |
---|---|---|
0x0 | 4 | シグネチャで,値は"_32_" |
0x04 | 4 | PCI BIOS32サービス・ディレクトリのエントリポイント |
0x08 | 1 | PCI BIOS32サービス・ディレクトリのリビジョン(0x0) |
0x09 | 1 | データ構造長 |
0x0a | 1 | チェックサム(データ構造全体の値を足して,0になる) |
0x0b | 5 | 予約済み(全部0) |
これを,cで表現したのが,以下の共用体です.
共用体にしたのは,テーブルの各バイトを足してチェックサムを計算するときに,こっちのほうが簡単だからです.
union pci_bios32 { struct { u_int8_t sig[4]; // _32_. u_int32_t entry; // entry point. u_int8_t rev; // revision. u_int8_t len; // length. u_int8_t checksum; // checksum. u_int8_t reserved[5]; // reserved. } fields; char data[16]; };
実際に探すのは,以下の関数です.
static bool find_pci_bios32(void) { unsigned long addr = 0; union pci_bios32 *bios32; int sum, i; int len; for (addr = 0xe0000; addr < 0xffff0; addr += 16) { bios32 = (union pci_bios32 *) addr; if (bios32 != NULL && bios32->fields.sig[0] == '_' && bios32->fields.sig[1] == '3' && bios32->fields.sig[2] == '2' && bios32->fields.sig[3] == '_') { len = bios32->fields.len * 16; if (len) { for (i = 0, sum = 0; i < len; i++) sum += bios32->data[i]; if (!sum) { printk("found pci bios32 at 0x%x\n", addr); printk("PCI BIOS32 entry point is 0x%x\n", bios32->fields.entry); printk("PCI BIOS32 revision is 0x%x\n", bios32->fields.rev); return true; } } } } printk("pci bios32 not found\n"); return false; }
最初に,union pci_bios32のポインタにアドレスをキャストして,シグネチャのチェックをします.
シグネチャが"_32_"なら次はチェックサムを計算します.
for (addr = 0xe0000; addr < 0xffff0; addr += 16) { bios32 = (union pci_bios32 *) addr; if (bios32 != NULL && bios32->fields.sig[0] == '_' && bios32->fields.sig[1] == '3' && bios32->fields.sig[2] == '2' && bios32->fields.sig[3] == '_') {
データ構造長フィールド*16がこのテーブルの長さになります.
そして,テーブルのデータを1バイトずつ足し算していき,結果が0になれば,このアドレスがサービス・ディレクトリだということになります.
len = bios32->fields.len * 16; if (len) { for (i = 0, sum = 0; i < len; i++) sum += bios32->data[i]; if (!sum) { printk("found pci bios32 at 0x%x\n", addr); printk("PCI BIOS32 entry point is 0x%x\n", bios32->fields.entry); printk("PCI BIOS32 revision is 0x%x\n", bios32->fields.rev); return true; } }