PCI-Scan
-
fricky sprach im IRC das Thema PCI-Aktivierung an:
A PCI device ignores all IO and memory access from the PCI bus until it has been activated, steht z.b. dort: http://ecos.sourceware.org/docs-latest/ref/ecos-pci-library.html
weiterhin:
<fricky> erhard: zum testen: scanne den PCI-bus nach einem EHCI controller
<*ehenkes> muss erstmal einer da sein
<*ehenkes> if( pciProgrInterface==0x20 ) { printformat("EHCI"); }
<fricky> an offset 2 der memory-mapped i/o-register befindet sich das register HCIVERSION
<*ehenkes> du bekommst von dem: bus:device.function usw.
<fricky> wenn du dort einen 16-bit lesezugriff machst, sollte der wert 0x100 zu lesen sein
-
Ich habe nun auch die Bestimmung der BARs / Memory Size versuchsweise implementiert.
/// TEST PCI-SCAN BEGIN /// Erweiterte Version siehe unten ... /// TEST PCI-SCAN END
Typisches Ergebnis bezüglich USB-Devices:
0:3.0 IRQ 5 USB OHCI BAR0: CFFFD000h size: 4096 0:3.1 IRQ 11 USB OHCI BAR0: CFFFE000h size: 4096 0:3.2 IRQ 5 USB EHCI BAR0: CFFFF000h size: 4096
Sieht als Einstieg nicht schlecht aus. Klappt aber nur, weil die letzten Bits 00b sind. Die letzten 4 Bits müssen daher noch gesondert ausgewertet werden: (siehe Beitrag weiter oben)
Ein Speicherbereich hat eine Mindestgröße von 16 Byte und ist immer ein Vielfaches von 2. Dadurch stehen die 4 unteren Bits für zusätzliche Codierungen zur Verfügung. Bit 0 gibt Auskunft darüber, ob es sich um eine IO Adresse (der Zugriff muß über Port-Befehle erfolgen) oder eine Memory Adresse handelt. Handelt es sich um eine Memory Adresse, geben Bit 1 und 2 den Adresstyp an. 00 signalisiert, dass es sich um eine gewöhnliche 32 bit Adresse handelt. Die Kombination 01 bedeutet, dass die Adresse unterhalb der aus alten Zeiten bekannten 1 Megabyte Grenze liegt und die Kombination 10 wird für eine 64 bit Adresse verwednet. Bei einer 64 bit Adresse werden zwei „Base Address“ Einträge zur Adressbestimmung gebraucht. Bit 3 schließlich gibt Auskunft darüber, ob auf diesen Adressbereich im Prefetch-Mode zugegriffen werden darf oder nicht. Ein gesetztes Bit bedeutet dabei, dass nicht im Prefetch-Mode zugegriffen werden darf. Im Prefetch-Mode speichert die PCI-Bridge (d.h. der Baustein, der den PCI-Bus an den Processorbus ankoppelt) Daten zwischen, bevor die eigentliche Übertragung zur CPU (lesen oder schreiben) gestartet wird.
Sobald das stabil steht, schiebe ich es als Test-Version 110 hoch.
-
In einem Fall ergab die Auswertung eines PCI-Gerätes den IRQ 255. Macht das Sinn? Wohl nicht.
bus:dev.func: 1:0.1 device: 4170h vendor: 1002h IRQ: 255
-
//... struct pciBasicAddressRegister { uint32_t baseAddress; size_t memorySize; uint8_t memoryType; }; //...
/// TEST PCI-SCAN BEGIN uint16_t pciDevice = 0; // device uint16_t pciVendor = 0; // vendor uint8_t pciClass = 0; // device class uint8_t pciSubclass = 0; // device subclass uint8_t pciProgrInterface = 0; // program interface uint8_t pciIRQLine = 0; // IRQ line (interrupt line) uint8_t bus = 0; // max. 256 uint8_t device = 0; // max. 32 uint8_t func = 0; // max. 8 struct pciBasicAddressRegister pciBAR[6]; // base address, size, type of BAR0 - BAR5 uint32_t pciBar = 0; for(bus=0;bus<8;++bus) { for(device=0;device<32;++device) { for(func=0;func<8;++func) { pciVendor = pci_config_read( bus, device, func, PCI_VENDOR_ID ); pciDevice = pci_config_read( bus, device, func, PCI_DEVICE_ID ); pciClass = pci_config_read( bus, device, func, PCI_CLASS ); pciSubclass = pci_config_read( bus, device, func, PCI_SUBCLASS ); pciProgrInterface = pci_config_read( bus, device, func, PCI_INTERFACE ); pciIRQLine = pci_config_read( bus, device, func, PCI_IRQLINE ); pciBAR[0].baseAddress = pci_config_read( bus, device, func, PCI_BAR0 ); pciBAR[1].baseAddress = pci_config_read( bus, device, func, PCI_BAR1 ); pciBAR[2].baseAddress = pci_config_read( bus, device, func, PCI_BAR2 ); pciBAR[3].baseAddress = pci_config_read( bus, device, func, PCI_BAR3 ); pciBAR[4].baseAddress = pci_config_read( bus, device, func, PCI_BAR4 ); pciBAR[5].baseAddress = pci_config_read( bus, device, func, PCI_BAR5 ); if( pciVendor != 0xFFFF ) { if(pciIRQLine!=255) { printformat("%d:%d.%d\t dev:%x vend:%x IRQ:%d ", bus, device, func, pciDevice, pciVendor, pciIRQLine); } else // "255 is defined as meaning "unknown" or "no connection" to the interrupt controller" { printformat("%d:%d.%d\t dev:%x vend:%x IRQ:-- ", bus, device, func, pciDevice, pciVendor); } if( (pciClass==0x0C) && (pciSubclass==0x03) ) // USB { printformat(" USB "); if( pciProgrInterface==0x00 ) { printformat("UHCI "); } if( pciProgrInterface==0x10 ) { printformat("OHCI "); } if( pciProgrInterface==0x20 ) { printformat("EHCI "); } if( pciProgrInterface==0x80 ) { printformat("no HCI "); } if( pciProgrInterface==0xFE ) { printformat("any "); } for(i=0;i<6;++i) // check USB BARs { pciBAR[i].memoryType = pciBAR[i].baseAddress & 0x01; if(pciBAR[i].baseAddress) { if(pciBAR[i].memoryType == 0) { printformat("%d:%X MEM ", i, pciBAR[i].baseAddress & 0xFFFFFFF0 ); } if(pciBAR[i].memoryType == 1) { printformat("%d:%X I/O ", i, pciBAR[i].baseAddress & 0xFFFFFFFC ); } /// TEST Memory Size Begin cli(); pci_config_write_dword ( bus, device, func, PCI_BAR0 + 4*i, 0xFFFFFFFF ); pciBar = pci_config_read( bus, device, func, PCI_BAR0 + 4*i ); pci_config_write_dword ( bus, device, func, PCI_BAR0 + 4*i, pciBAR[i].baseAddress ); sti(); pciBAR[i].memorySize = (~pciBar | 0x0F) + 1; printformat("sz:%d ", pciBAR[i].memorySize ); /// TEST Memory Size End } } } printformat("\n"); } // Bit 7 in header type (Bit 23-16) --> multifunctional if( !(pci_config_read(bus, device, 0, PCI_HEADERTYPE) & 0x80) ) { break; // --> not multifunctional } } } } printformat("\n"); /// TEST PCI-SCAN END
Damit man mehr ausdrucken kann in einer Monitorzeile habe ich in printformat(...) %X auf acht Zeichen und %x auf vier Zeichen Hexadezimal-Ausgabe gesetzt. %x macht z.B. Sinn bei VendorID und DeviceID.
Bei USB gegeb ich nun aus:
USB UHCI 4:0000D400h I/O sz: 32
USB UHCI 4:0000D000h I/O sz: 32von rechts nach links: Subclass Program-Interface BAR-Nr. Memory-Address
Memory-Type(MEM oder I/O) Memory-SizeHier zwei konkrete Beispiele:
http://www.henkessoft.de/OS_Dev/Bilder/USB_UHCI_IO_32.JPG (UHCI IO 32 Byte)
http://www.henkessoft.de/OS_Dev/Bilder/USB_OHCI_EHCI_MEM_4096.JPG (OHCI u. EHCI MEM 4096 Byte)
-
Erhard Henkes schrieb:
In einem Fall ergab die Auswertung eines PCI-Gerätes den IRQ 255. Macht das Sinn?
Das würde "Sinn" machen bei PCI-Geräten, die keinen Interrupt brauchen (i.e. 0xFF -> nicht vorhanden, nicht gebraucht). Hast du mal auf dem Testrechner im BIOS alle Optionen de/aktiviert, die in etwa heißen "plug and play os" oder "pci-autoconfig" o.ä? Mein BIOS kennt solche Optionen nicht, da der Rechner nicht dafür ausgelegt ist, nach dem Kauf noch großartig aufgerüstet zu werden. Aber der PCI-Scan sollte dann völlig neue Ergebnisse bringen.
-
Ja, 255 bedeutet nicht da, hab ich im Code ergänzt, mache da jetzt "IRQ:--"
Genaue Daten sind: 1:0.1 dev:4170h vend:1002h IRQ:255 (jetzt: IRQ:--)
-
Meine teuer bezahlten "RAM-Controller" und "PCI-PCI-Bridges" haben/nutzen auch keinen Interrupt. Hast du den PCI-Scan mal mit geänderten BIOS-Einstellungen gemacht? Das Ergebnis interessiert mich.
Hier noch ein Bit Twiddle Hack:
uint32_t pci_config_read( uint8_t bus, uint8_t device, uint8_t func, uint16_t content ) { (...) uint8_t reg = reg_off & 0xFC; // bit mask: 11111100b // uint8_t offset = reg_off % 0x04; // remainder of modulo operation provides offset uint8_t offset = reg_off & 0x03; // x & 0x03 == x % 0x04 // aber das wird der Kompiler eh so machen :) (...) }
-
Bit Twiddle Hack
Danke! Interessanter Link.
-
Das mit dem Auslesen der Register beginnend ab dem genutzten BAR, nachfolgend nur am Beipsiel USB EHCI, wie von fricky gewünscht, klappt nun ebenso:
/// TEST PCI-SCAN BEGIN settextcolor(15,0); uint16_t pciDevice = 0; // device uint16_t pciVendor = 0; // vendor uint8_t pciClass = 0; // device class uint8_t pciSubclass = 0; // device subclass uint8_t pciProgrInterface = 0; // program interface uint8_t pciIRQLine = 0; // IRQ line (interrupt line) uint8_t bus = 0; // max. 256 uint8_t device = 0; // max. 32 uint8_t func = 0; // max. 8 struct pciBasicAddressRegister pciBAR[6]; // base address, size, type of BAR0 - BAR5 uint32_t pciBar = 0; uint32_t EHCI_data = 0; for(bus=0;bus<8;++bus) { for(device=0;device<32;++device) { for(func=0;func<8;++func) { pciVendor = pci_config_read( bus, device, func, PCI_VENDOR_ID ); pciDevice = pci_config_read( bus, device, func, PCI_DEVICE_ID ); pciClass = pci_config_read( bus, device, func, PCI_CLASS ); pciSubclass = pci_config_read( bus, device, func, PCI_SUBCLASS ); pciProgrInterface = pci_config_read( bus, device, func, PCI_INTERFACE ); pciIRQLine = pci_config_read( bus, device, func, PCI_IRQLINE ); pciBAR[0].baseAddress = pci_config_read( bus, device, func, PCI_BAR0 ); pciBAR[1].baseAddress = pci_config_read( bus, device, func, PCI_BAR1 ); pciBAR[2].baseAddress = pci_config_read( bus, device, func, PCI_BAR2 ); pciBAR[3].baseAddress = pci_config_read( bus, device, func, PCI_BAR3 ); pciBAR[4].baseAddress = pci_config_read( bus, device, func, PCI_BAR4 ); pciBAR[5].baseAddress = pci_config_read( bus, device, func, PCI_BAR5 ); if( pciVendor != 0xFFFF ) { if(pciIRQLine!=255) { printformat("%d:%d.%d\t dev:%x vend:%x IRQ:%d ", bus, device, func, pciDevice, pciVendor, pciIRQLine); } else // "255 is defined as meaning "unknown" or "no connection" to the interrupt controller" { printformat("%d:%d.%d\t dev:%x vend:%x IRQ:-- ", bus, device, func, pciDevice, pciVendor); } if( (pciClass==0x0C) && (pciSubclass==0x03) ) // USB { printformat(" USB "); if( pciProgrInterface==0x00 ) { printformat("UHCI "); } if( pciProgrInterface==0x10 ) { printformat("OHCI "); } if( pciProgrInterface==0x20 ) { printformat("EHCI "); } if( pciProgrInterface==0x80 ) { printformat("no HCI "); } if( pciProgrInterface==0xFE ) { printformat("any "); } for(i=0;i<6;++i) // check USB BARs { pciBAR[i].memoryType = pciBAR[i].baseAddress & 0x01; if(pciBAR[i].baseAddress) { if(pciBAR[i].memoryType == 0) { printformat("%d:%X MEM ", i, pciBAR[i].baseAddress & 0xFFFFFFF0 ); } if(pciBAR[i].memoryType == 1) { printformat("%d:%X I/O ", i, pciBAR[i].baseAddress & 0xFFFFFFFC ); } /// TEST Memory Size Begin cli(); pci_config_write_dword ( bus, device, func, PCI_BAR0 + 4*i, 0xFFFFFFFF ); pciBar = pci_config_read( bus, device, func, PCI_BAR0 + 4*i ); pci_config_write_dword ( bus, device, func, PCI_BAR0 + 4*i, pciBAR[i].baseAddress ); sti(); pciBAR[i].memorySize = (~pciBar | 0x0F) + 1; printformat("sz:%d ", pciBAR[i].memorySize ); /// TEST EHCI Data Begin if( (pciProgrInterface==0x20) && pciBAR[i].baseAddress ) { for(j=0;j<24;++j) { EHCI_data = *((volatile uint8_t*)( (pciBAR[i].baseAddress & 0xFFFFFFF0) + j )); if(!j) { printformat("\n"); } else { printformat("\t"); } printformat("BAR%d+%d: %x ",i, j, EHCI_data); } } /// TEST EHCI Data Begin /// TEST Memory Size End } } } printformat("\n"); } // Bit 7 in header type (Bit 23-16) --> multifunctional if( !(pci_config_read(bus, device, 0, PCI_HEADERTYPE) & 0x80) ) { break; // --> not multifunctional } } } } printformat("\n"); /// TEST PCI-SCAN END
Zwei Beispiele:
http://www.henkessoft.de/OS_Dev/Bilder/PCI_USB_EHCI_Scan_001.JPG
http://www.henkessoft.de/OS_Dev/Bilder/PCI_USB_EHCI_Scan_002.JPGDie Bedeutung der 24 Bytes nach der verwendeten BAR findet man in der EHCI-Spezifikation:
CAPLENGTH - Capability Registers Length
Address: Base+ (00h)
Default Value Implementation Dependent
Attribute: RO
Size: 8 bits
This register is used as an offset to add to register base to find the beginning of the Operational Register Space.HCIVERSION - Host Controller Interface Version Number
Address: Base+ (02h)
Default Value: 0100h
Attribute RO
Size: 16 bits
This is a two-byte register containing a BCD encoding of the EHCI revision number supported by this host controller. The most significant byte of this register represents a major revision and the least significant byte is the minor revision.HCSPARAMS - Structural Parameters
Address: Base+ (04h)
Default Value Implementation Dependent
Attribute RO
Size: 32 bits
This is a set of fields that are structural parameters: Number of downstream ports, etc.HCCPARAMS - Capability Parameters
Address: Base+ (08h)
Default Value Implementation Dependent
Attribute RO
Size: 32 bits
Multiple Mode control (time-base bit functionality), addressing capabilityHCSP-PORTROUTE - Companion Port Route Description
Address: Base+ (0Ch)
Default Value Implementation Dependent
Attribute RO
Size: 60 bits
This optional field is valid only if Port Routing Rules field in the HCSPARAMS register is set to a one.Hier die Zusammenfassung:
These registers specify the limits, restrictions and capabilities of the host controller implementation.
Table 2-5. Enhanced Host Controller Capability RegistersOffset Size Mnemonic Power Well Register Name 00h 1 CAPLENGTH Core Capability Register Length 01h 1 Reserved Core N/A 02h 2 HCIVERSION Core Interface Version Number 04h 4 HCSPARAMS Core Structural Parameters 08h 4 HCCPARAMS Core Capability Parameters 0Ch 8 HCSP-PORTROUTE Core Companion Port Route Description
Quelle: "Enhanced Host Controller Interface Specification for Universal Serial Bus", Date: March 12, 2002, Revision: 1.0, Intel Corporation
Es gibt inzwischen auch ein Addendum für Revision 1.1
Dort ist der Standardwert für HCIVERSION z.B. 0x110 (Base+2, 16 bits), auch bei HCCPARAMS (Base+8, 32 bits) wurden viele Details geändert.
-
Die erhöhte Auflösung der Textausgabe auf 80 Spalten * 50 Zeilen ist hier bei modernen Rechnern recht hilfreich, hier ein ca. 5 Jahre alter, ziemlich gut ausgebauter Rechner:
http://www.henkessoft.de/OS_Dev/Bilder/PCI-Scan3.PNGMan sieht allerdings, dass die Bestimmung der Speichergröße bei UHCI leider noch eine Macke hat (negative Werte). Die Byte des Enhanced Host Controller Capability Registers müssen auch noch entsprechend zusammengefasst und dargestellt werden. HCIVERSION ist hier auf jeden Fall auch 0x0100.
-
Erhard Henkes schrieb:
Man sieht allerdings, dass die Bestimmung der Speichergröße bei UHCI leider noch eine Macke hat (negative Werte).
Laut Datenblatt (Kapitel 11, Seite 469 ) für "dev:27C8h vend:8086h" sind Offset 10h-1Fh des "Config Space" in etwa "reserviert". D.h. daß man dort eine Speichergröße nicht auslesen kann.
-
Die PCI-Daten wurden nun noch in eine PCI-Device-Struct eingelesen, damit diese dauerhaft zur Verfügung stehen (Rev. 14 SVN):
typedef struct pciBasicAddressRegister { uint32_t baseAddress; size_t memorySize; uint8_t memoryType; }pciBar_t; typedef struct pciDev { uint8_t number; uint8_t bus; uint8_t device; uint8_t func; uint16_t vendorID; uint16_t deviceID; uint8_t classID; uint8_t subclassID; uint8_t interfaceID; uint8_t revID; uint8_t irq; pciBar_t bar[6]; }pciDev_t;
Ich habe die gültigen PCI Devices auch durchlaufend nummeriert, damit diese mit einer Größe identifizierbar sind.