Auslesen des PCI Config Space
-
Hallo supernicky,
wenn Du Unterlagen benötigst über die Ansteuerung der Hardware, findest du hier entsprechende Erklärungen und Daten: http://henkessoft.de/OS_Dev/OSDEV Ressourcen.htmlHat es geklappt mit dem PCI-Bus?
-
Hallo Erhard,
Es geht langsam voran...
ich habe noch ein Problem beim auslesen des Datenregisters wenn das Funktionsregister größer NULL ist.
Dazu habe ich ein Beispiel aus dem Buch von Benjamin Lunt.#define PCI_ADDR 0x0CF8#define PCI_DATA 0x0CFC bit32u read_pci(bit8u bus, bit8u dev, bit8u func, bit8u port, bit8u len) { bit32u ret; const bit32u val = 0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (port & 0xFC); outpd(PCI_ADDR, val); ret = inpd(PCI_DATA+(port & 0x3)); ret &= (0xFFFFFFFF >> ((4-len) * 8)); return ret;}
Der Aufruf sieht in seiner Schleife wie folgt aus:
for (i=0; i<64; i++) pcidata[i] = read_pci(pci_bus, pci_dev, pci_func, (i<<2),sizeof(bit32u));
Mein Problem ist der Parameter "port" (Bytewert).
2x shift links löscht gleichzeitig Bit 0 und 1.In Zeile 12 wird port mit 0xFC verknüpft was nochmals Bit 0 und 1 löscht(zur Sicherheit?)
In Zeile 14 wird port jedoch nochmal mit 0x3 AND verknüpft was diesmal Bit 7bis 2 löschtSomit ist der Wert von Port immer NULL oder nicht?Ich verstehe an der folgenden Zeile nicht welcher Wert zum Adressregister 0xCFC addiert werden soll..
ret = inpd(PCI_DATA+(port & 0x3));
Das Auslesen der Vendor ID funktioniert immer, da der Wert von port hier ja immer NULL ist.
Ich bitte wie immer um Erleuchtung
Gruß, Nicky
-
Hast Du Dir schon unsere Funktion in PrettyOS angeschaut?
uint32_t pci_config_read(uint8_t bus, uint8_t device, uint8_t func, uint8_t reg_off, uint8_t length) { uint8_t reg = reg_off & 0xFC; // bit mask: 11111100b uint8_t offset = reg_off % 0x04; // remainder of modulo operation provides offset outportl(PCI_CONFIGURATION_ADDRESS, 0x80000000 | (bus << 16) | (device << 11) | (func << 8) | reg); // use offset to find searched content uint32_t readVal = inportl(PCI_CONFIGURATION_DATA) >> (8 * offset); switch (length) { case 1: readVal &= 0x000000FF; break; case 2: readVal &= 0x0000FFFF; break; case 4: readVal &= 0xFFFFFFFF; break; } return readVal; }
Wichtig sind die Konstanten in pci.h:
#define PCI_CONFIGURATION_ADDRESS 0x0CF8 // Address I/O Port #define PCI_CONFIGURATION_DATA 0x0CFC // Data I/O Port #define PCI_VENDOR_ID 0x00 // length: 0x02 reg: 0x00 offset: 0x00 #define PCI_DEVICE_ID 0x02 // length: 0x02 reg: 0x00 offset: 0x02 #define PCI_COMMAND 0x04 #define PCI_STATUS 0x06 #define PCI_REVISION 0x08 #define PCI_CLASS 0x0B #define PCI_SUBCLASS 0x0A #define PCI_INTERFACE 0x09 #define PCI_HEADERTYPE 0x0E #define PCI_BAR0 0x10 #define PCI_BAR1 0x14 #define PCI_BAR2 0x18 #define PCI_BAR3 0x1C #define PCI_BAR4 0x20 #define PCI_BAR5 0x24 #define PCI_CAPLIST 0x34 #define PCI_IRQLINE 0x3C
Anwendungsbeispiele findest Du in pci.c:
PCIdev->deviceID = pci_config_read(bus, device, func, PCI_DEVICE_ID, 2); PCIdev->classID = pci_config_read(bus, device, func, PCI_CLASS, 1); PCIdev->subclassID = pci_config_read(bus, device, func, PCI_SUBCLASS, 1); PCIdev->interfaceID = pci_config_read(bus, device, func, PCI_INTERFACE, 1); PCIdev->revID = pci_config_read(bus, device, func, PCI_REVISION, 1); PCIdev->irq = pci_config_read(bus, device, func, PCI_IRQLINE, 1);
Die Port-Funktionen findest Du in util.h:
static inline uint32_t inportl(uint16_t port) { uint32_t ret_val; __asm__ volatile ("inl %1, %0" : "=a" (ret_val) : "d"(port)); return ret_val; } static inline void outportl(uint16_t port, uint32_t val) { __asm__ volatile ("outl %0, %1" : : "a"(val), "d"(port)); }
Durch Kombination von register und offset können wir als Konstante einen Wert übergeben, den wir in der Funktion zerlegen.
uint8_t reg = reg_off & 0xFC; // bit mask: 11111100b uint8_t offset = reg_off % 0x04; // remainder of modulo operation provides offset
Ich denke, unsere pci_config_read ist selbsterklärend strukturiert:
Wir setzen bus/device/func/reg,
wir suchen mit dem Offset in dem erhaltenen Wert durch entsprechenden Rechtsshift,
wir setzen eine Längenmaske, damit wir keinen Blödsinn aus Nachbarfeldern erhalten.
-
Hallo nochmal,
Leider komme ich immer noch zu keinem zufriedenstellenden Ergebnis.
Ich weiß aus einem anderen Programm, das die Vendor ID 0x8086 sein sollte.Mein Programm findet vier von diesen Einträgen, jedoch keinen Eintrag mit
Class Code: 0xC und Sub Class: 0x3Ich stelle mal meinen Programmcode rein, vielleicht kann hier noch jemand helfen.
Hier das "Hauptprogramm" geschrieben in NASM, ausgeführt im P-Mode (32Bit)call clearscreen ;Bildschirm löschen mov ecx, 300000d ;Schleifenzähler .einlesen: push ecx ;B = Bus, D = Device, R = Register und F = Funktion ;Startadresse ausgeben B/D/R/F alles 0 mov eax, [adresse] mov edx, [adressport] out dx, eax ;Aktiviere PCI BUS mit B/D/R/F = 0 mov edx, [datenport] in eax, dx ;Register NULL einlesen für Vendor ID cmp ax, 0xFFFF ;Wenn AX = FFFF dann kein Gerät je .nextdevice mov edi, pci_conf ;EDI auf Anfang der PCI Struktur stosd ;DWORD schreiben call show_pci ;Struktur füllen .nextdevice: call next_device ;Nächstes Gerät call clear_pciport ;Konfigurations-Register-Nummer auf NULL pop ecx loop .einlesen
pci_conf ist eine einfache Struktur mit einer Groesse von 256 Byte
Hier ein Auszug vom Aufbau:pci_conf: vendor dw 0 device dw 0 command dw 0 status dw 0 rev db 0 interface db 0 subclass db 0 classcode db 0 cacheline db 0 latency db 0 header db 0 selftest db 0 base_0 dd 0 base_1 dd 0 base_2 dd 0 base_3 dd 0 base_4 dd 0 base_5 dd 0 cardbus dd 0 sub_vendor dw 0 subsystem dw 0 rombase dd 0 c_list db 0 res_1 db 0,0,0 res_2 dd 0 interrupt_line db 0 interrupt_pin db 0
Nun kommt glaube ich, die wichtigste Funktion.
show_pci
Diese durchläuft alle Konfigurationeregister (0 - 63) und schreibt alle DWORD'S
nacheinander in die Struktur pci_confshow_pci: call clear_pciport ;Register Number löschen ;11111111_11111111_11111111_xxxxxx11 ;x = Registernummer pushad push dword [adresse] ;Adresse z.B. 0x80003B00 auf Stack call itoh ;umwandeln nach Hexadezimal push eax ;Beginn der Hexzahl auf Stack call prints ;Adresse auf Schirm ausgeben! call crlf ;Zeilenumbruch einfügen mov edi, pci_conf ;EDI zeigt auf die Struktur mov ecx, 63d ;Zähler mit 63 initialisieren .eintragen: mov edx, [adressport] ;Adresse mit gefundener Vendor ID mov eax, [adresse] ;in Adressregister schreiben out dx, eax mov edx, [datenport] ;Datenport einlesen in eax, dx stosd ;4 Byte aus Register schreiben in die Struktur call set_next_pciport ;Nächste Register Nummer loop .eintragen
Der Code ist nicht soviel. Ich hoffe es kann mir jemand sagen ob ich etwas
vergessen habe.
Falls noch was fehlt einfach bescheid geben.. ich ergänze dann entsprechend.Hier mal ein Auszug von meinem Bildschirm was mein Programm zeigt:
0x80000000 Vendor: 0x8086 SubC_Code: 0 C_Code: 6 Base: 0x0 IRQ: 0 0x80003800 Vendor: 0x8086 SubC_Code: 1 C_Code: 6 Base: 0x0 IRQ: 0 0x80003900 Vendor: 0x8086 SubC_Code: 1 C_Code: 1 Base: 0x0 IRQ: 0 0x80003A00 Vendor: 0x8086 SubC_Code: 0 C_Code: 0 Base: 0x0 IRQ: 4 0x80003B00 Vendor: 0x8086 SubC_Code: 128 C_Code: 6 Base: 0x0 IRQ: 1 0x80004000 Vendor: 0x5333 SubC_Code: 0 C_Code: 3 Base: 0xF8000000 IRQ: 0 0x80005000 Vendor: 0x1011 SubC_Code: 0 C_Code: 2 Base: 0xEC01 IRQ: 1
Leider nichts brauchbares dabei...
Nicky
-
Aber so richtig unplausibel ist die Bildschirmausgabe ja nicht. Zumindest ist nicht alles völlig blödsinnig, 0x8086 als Vendor bekommst Du ja. Ich würde den Fehler an der Stelle vermuten, wo Du die anderen Werte liest.
-
Hallo Mr X,
ich habe mein Programm mal auf dem Laptop starten lassen.
Hier werden mir wohl alle Geräte mit Sub Class 12 (0xC) und Class Code 0x3 angezeigt.
Insgesamt vier Geräte. 3x UHCI und 1x xHCI
UHCI mit einer 16 Bit Base Adresse (0x1821; 0x1841 und 0x1861) und
xHCI mit einer 32 Bit Base Adresse (0xF6504000)Kann das vielleicht nur an Virtual PC liegen?
Jedes mal das Programm auf Disk ziehen und umstöpseln ist nicht gerade
hilfreich und ziemlich zeitaufwändigAber es geht schonmal...
Nicky
-
Virtual PC gibt die Hardware deines PCs nicht wieder, sondern hat seine eigene, die er emuliert.
-
Sollten aber nicht gerade deshalb die Geräte auf gleiche Weise
zu finden sein wie auf echter Hardware?
-
Nein, eben deshalb ist es andere Hardware (nämlich immer die gleiche, je nach Emulator). Hier ist eine Übersicht über die Hardware von VPC: http://www.lowlevel.eu/wiki/Microsoft_Virtual_PC
-
Hallo,
das Auslesen klappt nun sehr gut.
Nach einigen Test mit Virtual PC und Virtual Box habe ich herausgefunden, das
die Basisadresse beim Linear Frame Buffer meiner Grafikkarte beiVPC : 0xF8000000
VBOX: 0xE0000008liegt. Diesen Wert finde ich auch im Register Basis Adresse der VGA Karte im PCI Bus.
Ich möchte dort gern den Wert 0x1000000 (16MB) schreiben, da ich nicht immer mit 4GB Ram ausgestattet binNach dem Schreiben erhalte ich jedoch beim nochmaligen Auslesen des Registers immer den Wert 0x0.
Nach meine Lektüre ist es durchaus möglich andere Werte einzutragen.
Ist es bei Grafikkarten vielleicht nicht "vorgesehen" einen anderen Wert einzutragen?
Hat jemand einen Tipp für mich?
Gruß, Nicky
-
Ich möchte dort gern den Wert 0x1000000 (16MB) schreiben, da ich nicht immer mit 4GB Ram ausgestattet bin
Auch wenn Du nur ein Byte RAM hättest - der virtuelle Adressraum ist 4 GiB groß beim x86. Mit Paging stellt sich die Frage daher eigentlich nicht.
-
Soll ich mit Paging den Bereich umleiten?
-
Du kannst ihn umleiten, musst Du aber nicht. Wie gesagt, der Adressraum ist hinreichend groß und der Speicher, den Du hier beschreibst, ist der der VGA. Der ist bloß in den normalen Adressraum gemappt.
-
Unterscheide zwischen dem physischen und dem virtuellen Adressraum.
-
Hätte ich das mal eher gewußt...
Ich bin immer davon ausgegangen das man physisch auch den Speicher haben muss den man ansprechen will.Bin jetzt jedenfalls am schreiben der Ausgaberoutine im 256 Farbmodus.
Danke nochmal