PCI-Scan



  • +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).


  • Mod

    @+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.


  • Mod

    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).


  • Mod

    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).


  • Mod

    Wie tief soll fürs PrettyOS in die Untiefen von "..." eingestiegen werden?

    Diese Frage gilt grundsätzlich an vielen Stellen. Bisher habe ich einen didaktischen und zielorientierten Ansatz gewählt. Für PCI bedeutet dies, dass wir im ersten Ansatz die USB Devices mit ihren Program Interfaces sowie ihren BAR feststellen wollen.


  • Mod

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

    Tests auf echter Hardware (PC):

    1. (reg ));
    2. (reg + 1 ));
    3. (reg + 2 ));
    4. (reg + 3 ));

    ---> in allen vier Fällen folgendes Bild:
    http://www.henkessoft.de/OS_Dev/Bilder/PCI-Scan2.JPG

    Obige Aussage trifft demnach zu und wir können uns das überflüssige und nicht verständlich ... & 0xFC bei entsprechender Dokumentation sparen. 🙂

    Daher folgender Vorschlag für die Funktion im Sourcecode mit entsprechendem Kommentar:

    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         )); // Bit 0 and Bit 1 is reserved and is automatically set to 0.
                               // Therefore, we do not need & 0xFC, but this mask should be used for simulations.
        return inportl(PCI_CONFIGURATION_DATA);
    }
    


  • Obige Aussage trifft demnach zu ...

    Dann sollte man besser schreiben, daß echte HW Bit 7-2 beachtet und Bit 1-0 ignoriert (beim PCI-Scan). 🙂


  • Mod

    echte HW Bit 7-2 beachtet und Bit 1-0 ignoriert

    Ja, eindeutig präziser. Danke!

    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         )); // Real hardware ignores bits 0 and 1, and reads only bits 7-2.
                               // Hence, we do not really need "& 0xFC".
                               // This mask should be used for tests with simulation software.
    
        return inportl(PCI_CONFIGURATION_DATA);
    }
    

  • Mod

    Wenn ich mir den PCI Scan auf dem Monitor (80*24) anschaue, hätte ich gerne eine kleinere - also besser aufgelöste - Text-Darstellung. Dafür benötigen wir aber VM86, leider.



  • Die Spezifikation nicht gelesen, auf einem einzigen PC getestet, und es dann anders machen als alle anderen?


  • Mod

    ... und es dann anders machen als alle anderen?

    Ja, genau, bis wir wissen, dass unser Vorgehen falsch ist. Erst dann wird ... & 0xFC eingebaut. Schlimmer ist es, einfach diesen Masker einzubauen, obwohl man ihn nicht braucht. Kernfrage ist also, ob man ihn wirklich braucht.

    Daher die konkrete Frage an Dich: Wo steht, dass man notwendigerweise ... & 0xFC verwenden muss, wenn man eine Register-Nummer übergibt, weil man ansonsten falsche Daten zurück erhält? 😉

    Hier wird z.B. Bit 0 der Register-Nummer beschrieben:
    http://tldp.org/LDP/tlk/dd/pci.html (Absatz 6.5.2, PCI-PCI Bridges)

    Unter 6.6.4 findet man "Finding Out How Much PCI I/O and PCI Memory Space a Device Needs" (interessant für den Schritt BARs ermitteln):

    Each PCI device found is queried to find out how much PCI I/O and PCI Memory address space it requires. To do this, each Base Address Register has all 1's written to it and then read. The device will return 0's in the don't-care address bits, effectively specifying the address space required.

    ...

    Figure 6.10: PCI Configuration Header: Base Address Registers

    There are two basic types of Base Address Register, the first indicates within which address space the devices registers must reside; either PCI I/O or PCI Memory space. This is indicated by Bit 0 of the register. Figure 6.10 shows the two forms of the Base Address Register for PCI Memory and for PCI I/O.

    To find out just how much of each address space a given Base Address Register is requesting, you write all 1s into the register and then read it back. The device will specify zeros in the don't care address bits, effectively specifying the address space required. This design implies that all address spaces used are a power of two and are naturally aligned.

    For example when you initialize the DECChip 21142 PCI Fast Ethernet device, it tells you that it needs 0x100 bytes of space of either PCI I/O or PCI Memory. The initialization code allocates it space. The moment that it allocates space, the 21142's control and status registers can be seen at those addresses.

    Bild (Figure 6.10): http://tldp.org/LDP/tlk/dd/pci-bars.gif



  • +gjm+ schrieb:

    Bit 1 und Bit 0 sind reserviert

    Wenn in Intelmanuals oder so "reserviert" steht, dann heißt das nicht "auf irgendwas setzen". Das heißt immer - wenn nicht ausdrücklich anders gesagt - "dieses Bit ist auf 0 zu setzen". Denn reserviert heißt zumindest in diesem Fall: "In der Zukunft könnten wir das für was anderes brauchen, und damit dein Programm dann noch funktioniert, musst du das auf 0 setzen."
    Natürlich mag das bei PCI anders sein, dass das auf echter Hardware automatisch auf 0 gesetzt wird. Aber reserviert heißt meiner Meinung nach keinesfalls undefiniert.


  • Mod

    XanClic hat mich davon unterrichtet, dass qemu auch Zwischenwerte von 0x0, 0x4, 0x8, 0xC ... für die Ausgabe von Daten zulässt. Während also echte Hardware dies auf die nächst-tiefere Register-Nummer "abrundet", liefert qemu falsche Werte zurück. Ein Test eines "korrekten" OS ergäbe dann falsche Werte auf qemu.

    Es kann im Umkehrschluss aber auch sein, dass mit qemu etwas funktioniert, was dann auf echter HW versagt.

    Folgerung: Daher sollte man - wie alle anderen auch - reg & 0xFC verwenden, damit auf echter HW und auf qemu das Verhalten gleich ist.

    Es geht also eher um den Ausgleich dieses "bugs" (man könnte es auch "feature" nennen) von qemu.

    Ich schlage vor, wir warten noch die Antworten und Ideen hier ab:
    http://forum.osdev.org/viewtopic.php?f=1&t=21098
    http://forum.osdev.org/viewtopic.php?f=1&t=21097

    Da qemu z.B. die Standardsimulation für tyndur ist, während ich bisher Bochs bevorzugt habe, spricht einiges dafür, diese Kröte "reg & 0xFC" einfach zu schlucken mit dem Hinweis im Kommentar auf die Kompatibilität mit qemu. 😉



  • Erhard Henkes schrieb:

    Ein Test eines "korrekten" OS ergäbe dann falsche Werte auf qemu.

    Nein, denn ein "korrektes" OS greift nicht auf Adressen im Konfigurationsadressraum zu, die nicht an vier ausgerichtet sind. 😉


  • Mod

    Nein, denn ein "korrektes" OS greift nicht auf Adressen im Konfigurationsadressraum zu, die nicht an vier ausgerichtet sind.

    Macht PrettyOS ja auch nicht, aber mit dem Masker 0xFC wird es das überhaupt nicht mehr können. Das ist der Punkt dafür. Es geht hier um doppelte Absicherung. 😃



  • XanClic schrieb:

    Wenn in Intelmanuals oder so "reserviert" steht, dann heißt das nicht "auf irgendwas setzen". Das heißt immer - wenn nicht ausdrücklich anders gesagt "dieses Bit ist auf 0 zu setzen".

    Quelle? Eine würde schon reichen. 🙂 Grade das würde ja bedeuten, daß bestehender Code umgeschrieben werden muß, falls ein reserviertes Bit plötzlich eine dokumentierte Bedeutung kriegt. Reservierte Bits dürfen weder gelöscht noch gesetzt werden. Das muß im Code entsprechend berücksichtigt werden.


Anmelden zum Antworten