PCI BIOS32の探し方

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;
				}
			}