PCI-Scan



  • Zum Auffinden der USB-Geräte und der Bestimmung von Program Interfaces (UHCI, EHCI, OHCI, ...) benötigen wir einen PCI Scan. Ich habe in Version 108 mal eine erste Testversion erstellt, die bereits USB detektiert. Bitte testet das mal bei euch auf PCs aus, falls möglich, ob die USB-Devices richtig erkannt werden (Linux: lspci).
    http://www.henkessoft.de/OS_Dev/Downloads/108.zip
    http://www.henkessoft.de/OS_Dev/Bilder/PCI-Scan.JPG

    PS:
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-252501.html
    (Diesen Link kann man auch für anderen Testcode, z.B. zum Einblenden in IRC, verwenden)



  • Hallo,

    der PCI Scan scheint bei mir zu funktionieren. Und die Nummern scheinen mir denen von lspci zu stimmen.

    Übrigens, lspci unter Linux macht keinen richtigen "Scan", sondern nutzt sysfs. In /sys/devices/pci0000:00 stehen die ganzen Geräte (bei mir). Verdammt sei diese Philosophie, alles ist eine Datei... 😃



  • der PCI Scan scheint bei mir zu funktionieren. Und die Nummern scheinen mir denen von lspci zu stimmen

    Was heißt "scheint"? Klappt das mit der korrekten Erkennung von USB + Program Interface?



  • Nächster Schritt:
    http://wiki.osdev.org/PCI#Base_Address_Registers - BAR lesen:

    When you want to retrieve the actual base address of a BAR, be sure to mask the lower bits. For Memory Space BARs, you calculate (BAR & 0xFFFFFFF0). For I/O Space BARs, you calculate (BAR & 0xFFFFFFFC).

    To determine the amount of address space needed by a PCI device, you must read the BAR. Devices must return 0 for address bits used internally and 1 for bits you can set. For example, if a device utilizes 16MBytes it will have BAR0 filled with 0xFF000000. You can only modify the hexadecimal FF part. This means you can only map memory on 16Mbyte boundaries. The zeroed part is used internally by the device to access memory.



  • Bis auf das einige meiner DeviceIDs (z.B. 0x07D7, 0x07D8) noch nirgendwo verzeichnet sind, scheint der PCI-Scan zu funktionieren.

    Allerdings gibt es ein kleines Problem (zumindest eines für Erbsenzähler): 🙂

    // http://wiki.osdev.org/PCI#Configuration_Mechanism_.231
    
    |31        |30    24|23 16|15  11|10     8|7      2[b]|1|0|[/b]
    |Enable Bit|Reserved| Bus |Device|Funktion|Register[b]|0|0|[/b]
    

    Wenn dieser Aufbau so stimmt, dann muß folgende Funktion überarbeitet werden:

    // pci.c version 108
    
    static ULONG pci_config_read(ULONG bus, ULONG device, ULONG func, ULONG reg)
    {
        outportl(PCI_CONFIGURATION_ADDRESS,
            0x80000000
            | (bus    << 16)
            | (device << 11)
            | (func   <<  8)
    //        | (reg         ));                  // <- !
            | (reg    <<  2  ));                  // <- !
        return inportl(PCI_CONFIGURATION_DATA);
    }
    

    Dadurch ändert sich auch die "Zählweise" der Register:

    Register Nummer           : 0x00 0x01 0x02 0x03 ...
    entspricht
    Offset im "Config-Space"  : 0x00 0x04 0x08 0x0C ...
    


  • Ja, das stimmt mit Offset = 4 * Register-Nummer, aber siehe hier:

    http://wiki.osdev.org/PCI#PCI_Device_Structure

    register    bits 31-24 	bits 23-16 	bits 15-8       bits 7-0
    00 	    Device ID 	Vendor ID
    04 	    Status 	Command
    08 	    Class code 	Subclass 	Prog IF 	Revision ID
    0C 	    BIST 	Header type 	Latency Timer 	Cache Line Size
    10 	    Base address #0 (BAR0)
    ...
    

    Da steht Register als Überschrift, unten folgen aber die Offsets.

    http://lowlevel.brainsware.org/wiki/index.php/PCI#Header_Type_0x00
    Das gleiche Spiel.

    Wahrscheinlich ist es so mit offset als Parameter und offset&0xFC (11111100) am klarsten:

    int pci_config_readd(int bus,int dev,int func,int offset) {
      int val;
      int address = 0x80000000|(bus<<16)|(dev<<11)|(func<<8)|(offset&0xFC);
      outl(PCI_CONFIG_ADDRESS,address);
      val = inl(PCI_CONFIG_DATA);
      return val;
    }
    


  • Bis auf das einige meiner DeviceIDs (z.B. 0x07D7, 0x07D8) noch nirgendwo verzeichnet sind

    Bus/Device/Function von Mainboard Chipsatz nVIDIA nForce 7100-630i bei lspci?

    devvID: 0x07D8 ergibt nForce 7100-630i (MCP73PV) von NVIDIA Corporation (0x10DE )
    bei 0x07D7 gibt die Datenbank keinen Wert zurück.

    scheint der PCI-Scan zu funktionieren.

    schon wieder "scheint". 😃



  • Erhard Henkes schrieb:

    Wahrscheinlich ist es so mit offset als Parameter und offset&0xFC (11111100) am klarsten

    Aber warum "offset & 0xFC"? Das ist unlogisch wenn "Registernummer" bit 7-0 sein soll.

    Bit 1+0 sind gewissermaßen "reserviert". Wenn z.B. Bit 0 gesetzt ist, dann wird der Aufruf an den nächsten PCI-Bus weitergeleitet (wo auch immer der sich auch befinden mag)*.

    Siehe "Type 1 Configuration"

    * Das ist auch der Grund für "scheint". Eigentlich kenne ich meine eigenen Geräte nur nicht. 🙂



  • Dein Register-Zähler-Ansatz mit dem doppelten Linksshift gefällt mir inzwischen auch am besten, aufgeführt werden aber nicht die von Dir vorgeschlagenen Laufzahlen für die Register 0 ... 15, sondern die jeweils auf vier abgerundeten Offsets 0x0 ... 0x3C (siehe angegebe Links). Dein Ansatz wäre also sehr unüblich.

    Vielleicht wird der Mainboard Chipsatz nicht angezeigt, weil er wahrscheinlich 0:00.0 ist. Der wurde vielleicht nach oben aus dem Bild gescrollt, passiert mir bei meinem vollgestopften Arbeits-PC auch. Da könntest Du zum Überprüfen eine Wartepause - sleepSeconds(...) - in der Ausgabeschleife einbauen, damit Dir nichts entgeht.

    Alte Kisten haben da weniger zu bieten. USB-Erkennung hat bisher bei den von mir getesteten PCs geklappt.

    Man kann übrigens auch in Windows im Arbeitsplatz - Gerätemanager - Ressourcenzusammenfassung - IRQ-Verwendungszusammenfassung nachschauen. Dort werden die ISA- und PCI-Devices aufgeführt (letztere teilweise mit Device-ID).



  • http://wiki.osdev.org/PCI#Configuration_Mechanism_.231
    Register Number Bit 7-2

    http://lowlevel.brainsware.org/wiki/index.php/PCI#Aufbau_des_Adress-I.2FO-Ports
    Registernummer Bit 7-0 <--- sollte als 4-aligned angegeben werden
    Ich habe da mal "bit0 u. bit1 sind 0" darunter geschrieben im wiki.

    (offset & 0xFC) ==> Register Number Bit 7-2 mit Bit 0 und 1 auf 0 gesetzt macht also absolut Sinn, wenn es auch unschön ist.



  • Der PCI-Scan von PrettyOS, eine eigens selbst geschriebene Schleife, ein drittes Programm (Dr. Hardware) und die "Windows Vista Computerverwaltung" liefern allesamt das gleiche Ergebnis:

    B   D   F   DeviceID ("Gerät")       VendorID
    ------------------------------------------------
      0h  0h  0h  07C1 Host Bridge         10DE
      0h  0h  1h  07CB RAM RAM-Contr.      10DE
    (...)
      0h  4h  0h  07FE USB Serial Bus      10DE (OHCI)
      0h  4h  1h  056A USB Serial Bus      10DE (EHCI)
                                           (kein UHCI) :(
    (...)
      2h  0h  0h  06E6 VGA Video           10DE
      4h  0h  0h  3403 Firewire Serial Bus 1106
    

    Damit hat es sich ausge"scheint". 👍

    Nochwas zum Erbsenzählen:

    Wenn in einer Schleife Bit 7-0 hochgezählt werden (ohne das unschöne (xxx & 0xFC)), dann ergibt das folgendes:

    Bit 7-0                    0x00 0x01 0x02 0x03 0x04 0x05 0x06 ...
    entspricht
    Register Nr.               0x00 0x00 0x00 0x00 0x01 0x01 0x01 ...
    und [i]liefert 32 bit[/i] ab
    Offset                     0x00 0x00 0x00 0x00 0x04 0x04 0x04 ...
    [i]im "Config-Space"[/i]
    

    Ein Zugriff auf z.B. Offset (oder Register lt. diversen wikis) 0x02 im "Config-Space" ist nicht möglich. Vorerst ist es besser, bit 1 und bit 0 dieser Struktur im Tutorial als "z.Zt. undokumentiert :(" zu bezeichnen.



  • Der PCI-Scan von PrettyOS, eine eigens selbst geschriebene Schleife, ein drittes Programm (Dr. Hardware) und die "Windows Vista Computerverwaltung" liefern allesamt das gleiche Ergebnis

    Danke! 🙂



  • +gjm+ schrieb:

    Vorerst ist es besser, bit 1 und bit 0 dieser Struktur im Tutorial als "z.Zt. undokumentiert " zu bezeichnen.

    Die sind nicht undefiniert. Es ist - soweit ich weiß - festgelegt, dass ein PCI-Gerät beim Zugriff auf den Konfigurationsadressraum diese beiden Bits nicht überprüfen muss, da sie immer 0 sind. Und, btw, laut dieser Quelle ("PC-Hardwarebuch - Aufbau, Funktionsweise, Programmierung") handelt es sich nicht um eine Registernummer in den Bits 2 bis 7, sondern um einen Offset in diesem Konfigurationsraum, der aber eben an vier ausgerichtet sein muss (der Konfigurationsraum ist laut diesem Buch nur ein anderer Adressraum neben dem I/O- und dem Speicheradressraum).



  • @+gjm+: Eines habe ich bei deiner Argumentation noch nicht verstanden:
    So wie es aussieht ist "Register-Nummer" und "Offset" zahlenmäßig das Gleiche. Das sieht bei Dir irgendwie anders aus. Dein Argument mit ...&FC verstehe ich allerdings.

    Ich lasse es zunächst mal bei dieser "schönen" statischen Funktion:

    static uint32_t pci_config_read(uint32_t bus, uint32_t device, uint32_t func, uint32_t reg)
    {
        outportl(PCI_CONFIGURATION_ADDRESS,
            0x80000000
            | (bus    << 16)
            | (device << 11)
            | (func   <<  8)
            | (reg         ));
        return inportl(PCI_CONFIGURATION_DATA);
    }
    

    Parameter reg wird dann eben der korrekte Offset sein. Das kann man durch eine entsprechende Schleife oder durch saubere Parameterübergabe erreichen. Außerhalb des Moduls wird diese Funktion ja nicht benötigt.



  • XanClic schrieb:

    Es ist - soweit ich weiß - festgelegt, dass ein PCI-Gerät beim Zugriff auf den Konfigurationsadressraum diese beiden Bits nicht überprüfen muss, da sie immer 0 sind.

    Entweder muß das Gerät diese beiden Bits nicht überprüfen -> dann sind sie undefiniert. Oder sie müssen beide Null sein -> dann fehlt die Dokumentation. Aber davon mal abgesehen gibt es spätestens hier Abschnitt 6.5.2, Figur 6.4 ein Problem: Entweder einigt man sich auf die Register-Zählweise, oder man definiert, je nach Bedarf, immer wieder eine neue Struktur.

    Erhard Henkes schrieb:

    So wie es aussieht ist "Register-Nummer" und "Offset" zahlenmäßig das Gleiche.

    Das ist so weil ein Register genau 32 Bit breit ist. Den Offset kann man ja nicht "beliebig" wählen. Es ist ähnlich wie das Auslesen vom CMOS.



  • +gjm+ schrieb:

    Entweder muß das Gerät diese beiden Bits nicht überprüfen -> dann sind sie undefiniert. Oder sie müssen beide Null sein -> dann fehlt die Dokumentation.

    Nicht entweder-oder. Beides. Da sie beide Null sein müssen, darf sich das Gerät die Überprüfung sparen. Wenn sie ungleich Null sind, dann ist das nicht korrekt.



  • Also jetzt will ich es genau wissen. Wozu sind diese beiden Bits überhaupt da? Was passiert, wenn Bit 0 bzw. Bit 1 bzw. beide auf 1 gesetzt werden?



  • Wenn ich mir den QEMU-Quellcode ansehe, dann ist es nicht empfehlenswert, die auf einen anderen Wert als 0 zu setzen, weil QEMU die Adresse so nimmt, wie sie ist (also nicht selbst die Bits 0 und 1 auf 0 setzt). Es scheint aber auch das Lesen von Bytes und Words anstellen von DWords zu erlauben.

    Also, ich zitiere mal aus dem besagten Buch:

    Konfiguration-Lesezugriff: [...] Die Adressbits AD7-AD2 geben die Adresse des zu lesenden Doppelworts im Konfigurationsadressraum der Einheit an, AD1 und AD0 sind gleich 0. [...]

    Somit habe ich da Mist erzählt mit "das Gerät muss das nicht prüfen". Ich bitte dafür um Entschuldigung.

    Aber wie man sieht, ist es einfach eine Adresse in einem speziellen Adressraum und keine Registernummer. Die Adressbits 0 und 1 müssen 0 sein, sonst bekommt man auf QEMU wohl andere Ergebnisse als auf echter Hardware (sind sie auf echter Hardware ungleich 0, dann werden sie anscheinend einfach auf 0 gesetzt; sind sie bei QEMU ungleich 0, dann werden diese Bits mit berücksichtigt).



  • sind sie auf echter Hardware ungleich 0, dann werden sie anscheinend einfach auf 0 gesetzt

    Interessanter Hinweis. Das können wir experimentell prüfen. Simulatoren sollten die Hardware entsprechend nachbilden. Schwächen dieser Programme müssen wir in einem OS nicht zwingend berücksichtigen, wir könnten es aber dennoch machen. Somit ist der ...&FC Ansatz nicht notwendig, zielt aber dennoch in die richtige Richtung. Wenn man ihn bringt, muss man genau erklären, warum man das macht, z.B. für reale HW nicht notwendig, aber wegen Simulationsprogramm ... sinnvoll.



  • @Erhard Henkes:

    Wie tief soll fürs PrettyOS in die Untiefen von "PCI" eingestiegen werden? Bit 1 und Bit 0 sind reserviert und m.E. nur für die/eine PCI-Bridge interessant.

    Wenn z.B. eine angeforderte Busnummer nicht von der/einer PCI-Bridge "verwaltet" (oder "erreicht") wird, dann setzt sie Bit 0 und gibt die Anforderung an die nächste PCI-Bridge weiter.

    Für den PCI-Scan sind beide Bits ohne Bedeutung (da reserviert!). Sie können gesetzt sein oder auch nicht. (Genauso wie bei Bit 30-24).