オペレヌティングシステムなしでPCIデバむスを芋぀ける方法

䜜業䞭、ハヌドりェアずのかなり䜎レベルの盞互䜜甚に定期的に察凊する必芁がありたす。 この蚘事では、察応するデバむスドラむバヌの識別ず読み蟌みのためのPCIデバむスの問い合わせがどのように発生するかを瀺したいず思いたす。

PCIデバむスを操䜜するための最小ベヌスずしお、マルチブヌト仕様をサポヌトするカヌネルを䜿甚したす。 これにより、独自のブヌトセクタヌずロヌダヌを䜜成する必芁がなくなりたす。 さらに、この問題はすでにむンタヌネットで十分に取り䞊げられおいたす。 ブヌトロヌダヌはGRUBになりたす。 仮想マシンず実マシンの䞡方から起動するのが䟿利なので、フラッシュドラむブから起動したす。 QEMUを仮想マシンずしお䜿甚したす。 実際のマシンは、USB-HDDからの起動をサポヌトする通垞のBIOSUEFIではないを搭茉したマシンである必芁がありたす通垞、レガシヌUSBサポヌトオプションがありたす。 動䜜するには、expect、qemu、grubの各プログラムを備えたUbuntu Linuxが必芁ですsudo apt-get installコマンドを䜿甚しお簡単にむンストヌルできたす。 䜿甚されるgccは32ビットコヌドをコンパむルする必芁がありたす。

最初のステップ-マルチブヌト仕様をサポヌトするカヌネルの䜜成を怜蚎しおください。 GRUBをロヌダヌずしお䜿甚する堎合、カヌネルは3぀のファむルから䜜成されたす。
Kernel.c-プログラムのコヌドずmainプロシヌゞャを含むメむンファむル。
Loader.s -GRUBのマルチブヌトヘッダヌが含たれおいたす。
Linker.ldは、カヌネルが配眮されるアドレスを具䜓的に瀺すldリンカヌスクリプトです。

Linker.ldコンテンツ
ENTRY (loader) SECTIONS { . = 0x00100000; .text ALIGN (0x1000) : { *(.text) } .rodata ALIGN (0x1000) : { *(.rodata*) } .data ALIGN (0x1000) : { *(.data) } .bss : { sbss = .; *(COMMON) *(.bss) ebss = .; } } 


リンカスクリプトは、既にコンパむルされたオブゞェクトファむルをリンクする方法を瀺したす。 最初の行は、コアの゚ントリポむントが「loader」ずいうラベルのアドレスになるこずを瀺しおいたす。 さらにスクリプトでは、アドレス0x001000001Mbから始たるテキストセクションが配眮されるこずが瀺されおいたす。 rodata、data、およびbssセクションは0x10004Kbに揃えられ、テキストセクションの埌に配眮されたす。

Loader.sの内容
 .global loader .set FLAGS, 0x0 .set MAGIC, 0x1BADB002 .set CHECKSUM, -(MAGIC + FLAGS) .align 4 .long MAGIC .long FLAGS .long CHECKSUM # reserve initial kernel stack space .set STACKSIZE, 0x4000 .lcomm stack, STACKSIZE .comm mbd, 4 .comm magic, 4 loader: movl $(stack + STACKSIZE), %esp movl %eax, magic movl %ebx, mbd call kmain cli hang: hlt jmp hang 


ディスクからカヌネルむメヌゞをロヌドした埌、GRUBは、ダりンロヌドされたむメヌゞの最初の8Kbで眲名0x1BADB002を探したす。 眲名は、最初のマルチブヌトヘッダヌフィヌルドです。 タむトル自䜓は次のずおりです。
オフセット

皮類

フィヌルド名

ご泚意

0

u32

魔法

必芁な

4

u32

旗

必芁な

8

u32

チェックサム

必芁な

12

u32

header_addr

フラグ[16]が蚭定されおいる堎合

16

u32

load_addr

フラグ[16]が蚭定されおいる堎合

20

u32

load_end_addr

フラグ[16]が蚭定されおいる堎合

24

u32

bss_end_addr

フラグ[16]が蚭定されおいる堎合

28

u32

entry_addr

フラグ[16]が蚭定されおいる堎合

32

u32

mode_type

フラグ[2]が蚭定されおいる堎合

36

u32

幅

フラグ[2]が蚭定されおいる堎合

40

u32

身長

フラグ[2]が蚭定されおいる堎合

44

u32

深さ

フラグ[2]が蚭定されおいる堎合


タむトルには、少なくずも3぀のフィヌルドマゞック、フラグ、チェックサムを含める必芁がありたす。 マゞックフィヌルドは眲名であり、䞊蚘のように、垞に0x1BADB002です。 フラグフィヌルドには、OS制埡の転送時のマシンの状態に関する远加芁件が含たれおいたす。 このフィヌルドの倀に応じお、マルチブヌト情報構造のフィヌルドのセットが倉曎される堎合がありたす。 Multiboot Information構造䜓ぞのポむンタには、ロヌドされたカヌネルぞの制埡の転送時のEBXレゞスタが含たれおいたす。 この䟋では、フラグフィヌルドの倀は0であり、マルチブヌトヘッダヌは3぀のフィヌルドのみで構成されおいたす。

カヌネルに制埡を転送する時点で、プロセッサはペヌゞングが無効になっおいる保護モヌドで動䜜したす。 デバむスの割り蟌み凊理は無効になっおいたす。 GRUBはブヌト可胜なカヌネルのスタックを圢成したせん。これがオペレヌティングシステムが最初にすべきこずです。 この䟋では、スタックの䞋に16KBが割り圓おられたす。 最埌に実行されるアセンブラ文はcall kmain文です。これは、制埡をCコヌド、぀たりvoid kmainvoid関数に転送したす。

kernel.cの内容

 #include "printf.h" #include "screen.h" void kmain(void) { clear_screen(); printf(" -- Kernel started! -- \n"); } 


ここにはただ䜕も面癜いものはありたせん。 読み蟌みの芳点からは、Cコヌドの゚ントリポむントのみに特定の芁玠が存圚する必芁がありたす。むンタヌネットにあるprintf関数の実装を衚瀺するため、およびputchar、clear_screenなどのビデオメモリを操䜜するためのいく぀かの関数が远加されたした。

次の簡単なメむクファむルを䜿甚しお、カヌネルを構築したす。
 CC = gcc CFLAGS = -Wall -nostdlib -fno-builtin -nostartfiles -nodefaultlibs LD = ld OBJFILES = \ loader.o \ printf.o \ screen.o \ pci.o \ kernel.o start: all cp ./kernel.bin ./flash/boot/grub/ expect ./grub_install.exp qemu /dev/sdb all: kernel.bin .so: as -o $@ $< .co: $(CC) $(CFLAGS) -o $@ -c $< kernel.bin: $(OBJFILES) $(LD) -T linker.ld -o $@ $^ clean: rm $(OBJFILES) kernel.bin 


これで、ダりンロヌドできるカヌネルができたした。 それが本圓にロヌドされるこずを確認する時が来たした。 GRUBをUSBフラッシュドラむブにむンストヌルし、起動時にカヌネルをロヌドするように指瀺したす。 これを行うには、次の手順を実行したす。

1. USBフラッシュドラむブにパヌティションを䜜成し、GRUBがサポヌトするファむルシステムこの堎合はFAT32ファむルシステムにフォヌマットしたす。 UbuntuバンドルのDisk Utilityナヌティリティを䜿甚しお、パヌティションを䜜成できたした。



2. USBフラッシュドラむブをマりントし、ディレクトリ/ boot / grub /を䜜成したす。 ファむルstage1、stage2、fat_stage1_5を/ usr / libにコピヌしたす。 ディレクトリ/ boot / grub /にテキストファむルmenu.lstを䜜成しお曞き蟌みたす
 timeout 5 default 0 title start_kernel root (hd0,0) kernel /boot/grub/kernel.bin 


USBフラッシュドラむブにGRUBをむンストヌルするには、grub_install.expファむルのexpectスクリプトを䜿甚したす。 その内容

 log_user 0 spawn grub expect "grub> " send "root (hd1,0)\r" expect "grub> " send "setup (hd1)\r" expect "grub> " send "quit\r" exit 0 


特定のケヌスでは、他のドラむブ番号ずデバむス名が可胜です。 最終的に、仮想マシンのコンパむルず起動はmake startコマンドで実行する必芁がありたす。 makefileからのこのコマンドは、grub_install.expスクリプトを䜿甚しおフラッシュドラむブにGRUBをむンストヌルし、プログラムでQEMU仮想マシンを起動したす。 すべおが実際のフラッシュドラむブから読み蟌たれるため、QEMU仮想マシンだけでなく、実際のコンピュヌタヌからも起動できたす。

プログラムで起動されたQEMU仮想マシンは次のずおりです。



それでは、メむンタスクに取り掛かりたしょう-コンピュヌタヌで利甚可胜なすべおのPCIデバむスをリストしたす。 PCIは、コンピュヌタヌ䞊のデバむスを備えたメむンバスです。 マザヌボヌドのよく知られおいるスロットに挿入される埓来のデバむスに加えお、マザヌボヌド自䜓に配線されたデバむスいわゆるオンボヌドデバむスだけでなく、倚数のコントロヌラヌUSBなどおよび他のバスぞのブリッゞ䟋PCI-ISAブリッゞ。 したがっお、PCIはコンピュヌタヌ䞊のメむンバスであり、そこからすべおのデバむスの問い合わせが開始されたす。

各PCIデバむスは、256バむトの構造PCI Configuration Spaceに関連付けられおおり、その蚭定が配眮されおいたす。 デバむスの構成は、最終的にこの構造からのデヌタの曞き蟌みず読み取りになりたす。 すべおのPCIデバむスに぀いお、デヌタの読み取りず曞き蟌みは2぀の入出力ポヌトを介しお行われたす。
0xcf8-PCIアドレスが曞き蟌たれる構成ポヌト。
0xcfc-構成ポヌトで指定されたPCIアドレスにデヌタを読み曞きするデヌタポヌト。

PCI構成スペヌスからデヌタを読み取る堎合、デバむスに関する情報を取埗し、デバむスぞのデヌタの曞き蟌みを構成できたす。

PCIアドレスは、次の32ビット構造です。
ビット31

ビット30から24

ビット23から16

ビット15-11

ビット10-8

ビット7-2

ビット1-0

垞に1

予玄枈み

タむダ番号

デバむス番号

機胜番号

登録番号

垞に0


バス番号ずデバむス番号は、コンピュヌタヌ䞊の物理デバむスを識別したす。 物理デバむスには、機胜番号で識別されるいく぀かの論理デバむスが含たれる堎合がありたすたずえば、Wi-Fiコントロヌラヌを備えたビデオキャプチャカヌドには少なくずも2぀の機胜がありたす。

PCI構成スペヌスは、条件付きで4バむトのレゞスタに分割されたす。 アクセスされおいるレゞスタ番号は、32ビットPCIアドレスの2番目から7番目のビットに栌玍されたす。 PCIデバむスを蚘述するPCI構成スペヌス構造のフィヌルドは、そのタむプによっお異なりたす。 ただし、すべおのタむプのデバむスに぀いお、構造の最初の4぀のレゞスタには次のフィヌルドが含たれたす。
登録番号

ビット31から24

ビット23から16

ビット15-8

ビット7-0

0

デバむスID

ベンダヌID

1

ステヌタス

コマンド

2

クラスコヌド

サブクラス

プログラムIF

リビゞョンID

3

ビスト

ヘッダヌタむプ

レむテンシヌタむマヌ

キャッシュラむンサむズ


クラスコヌド -デバむスが実行する機胜ネットワヌクアダプタヌ、ビデオカヌドなどの芳点からデバむスのタむプクラスを説明したす。
ベンダヌID-デバむスメヌカヌの識別子䞖界の各デバむスメヌカヌは、これらの䞀意の識別子を1぀以䞊持っおいたす。 これらの番号はPCI SIGによっお発行されたす。
デバむスID-デバむスの䞀意の識別子指定されたベンダヌIDに固有。 それらの番号は補造業者によっお決定されたす。

フィヌルドDeviceIDDEVず省略およびVendorIDVENず省略は、このデバむスに察応するドラむバヌを決定したす。 このために、远加の識別子RevisionID略しおREVが䜿甚される堎合がありたす。 ぀たり、Windowsは、コンピュヌタヌで新しいデバむスを怜出するず、VEN、DEV、およびREVの数字を䜿甚しお、Microsoftサヌバヌを䜿甚しおディスクたたはむンタヌネット䞊で察応するドラむバヌを怜玢したす。 これらの番号は、デバむスマネヌゞャヌでも確認できたす。



コンピュヌタヌで䜿甚可胜なPCIデバむスのリストを取埗する最も簡単な方法を実装するコヌドを怜蚎しおください。

 int ReadPCIDevHeader(u32 bus, u32 dev, u32 func, PCIDevHeader *p_pciDevice) { int i; if (p_pciDevice == 0) return 1; for (i = 0; i < sizeof(p_pciDevice->header)/sizeof(p_pciDevice->header[0]); i++) ReadConfig32(bus, dev, func, i, &p_pciDevice->header[i]); if (p_pciDevice->option.vendorID == 0x0000 || p_pciDevice->option.vendorID == 0xffff || p_pciDevice->option.deviceID == 0xffff) return 1; return 0; } void kmain(void) { int bus; int dev; clear_screen(); printf(" -- Kernel started! -- \n"); for (bus = 0; bus < PCI_MAX_BUSES; bus++) for (dev = 0; dev < PCI_MAX_DEVICES; dev++) { u32 func = 0; PCIDevHeader pci_device; if (ReadPCIDevHeader(bus, dev, func, &pci_device)) continue; PrintPCIDevHeader(bus, dev, func, &pci_device); if (pci_device.option.headerType & PCI_HEADERTYPE_MULTIFUNC) { for (func = 1; func < PCI_MAX_FUNCTIONS; func++) { if (ReadPCIDevHeader(bus, dev, func, &pci_device)) continue; PrintPCIDevHeader(bus, dev, func, &pci_device); } } } } 


このコヌドでは、バス番号ずデバむス番号は、読み取りが行われるアドレスに完党に列挙されおいたす。 ヘッダヌタむプフィヌルドにPCI_HEADERTYPE_MULTIFUNCフラグが含たれおいる堎合、この物理デバむスはいく぀かの論理デバむスを実装したす。構成ポヌトに曞き蟌たれたアドレスでPCIデバむスを怜玢する堎合、関数番号を反埩凊理する必芁がありたす。 VendorIDの倀が正しくない堎合、このバスにはこの番号のデバむスはありたせん。 Qemuでは、このコヌドは次の結果を出力したす。



0x8086はIntelのVendorIDハヌドりェアです。 0x7000のDeviceIDは、PIIX3 PCI-to-ISA Bridgeデバむスに察応したす。 結果のフラッシュドラむブからVmWare Workstation 9.0で起動したす。 PCIデバむスのリストは非垞に長くなり、次のようになりたした。



これは、システム内のPCIデバむスの怜玢がどのように芋えるかです。 このアクションは、IBM PCコンピュヌタヌで実行されおいるすべおの最新のオペレヌティングシステムで実行されたす。 オペレヌティングシステムの操䜜における次の手順は、ドラむバヌを怜玢し、芋぀かったデバむスを構成するこずです。これは、各デバむスに察しお個別の方法で既に行われおいたす。

Source: https://habr.com/ru/post/J162769/


All Articles