In diesem Artikel wird der Interrupt-Übermittlungsprozess von externen Geräten im x86-System beschrieben. Es versucht Fragen zu beantworten wie:
- Was ist Bild und wofür ist es?
- Was ist APIC und wofür ist es? Was ist der Zweck von LAPIC und I / O APIC?
- Was sind die Unterschiede zwischen APIC, xAPIC und x2APIC?
- Was ist MSI? Was sind die Unterschiede zwischen MSI und MSI-X?
- Welche Rolle spielen die Tabellen $ PIR, MPtable und ACPI?
Wenn Sie die Antwort auf eine dieser Fragen wissen möchten oder einfach nur etwas über die Entwicklung von Interrupt-Controllern wissen möchten, sind Sie herzlich willkommen.
Einführung
Für diejenigen, die nicht wissen, was ein Interrupt ist, hier ein Zitat aus Wikipedia:
Bei der Systemprogrammierung ist ein Interrupt ein Signal an den Prozessor, das von Hardware oder Software ausgegeben wird und ein Ereignis anzeigt, das sofortige Aufmerksamkeit erfordert. Ein Interrupt warnt den Prozessor vor einem Zustand mit hoher Priorität, der die Unterbrechung des aktuellen Codes erfordert, den der Prozessor ausführt. Der Prozessor reagiert, indem er seine aktuellen Aktivitäten unterbricht, seinen Status speichert und eine Funktion ausführt, die als Interrupt-Handler (oder Interrupt-Serviceroutine ISR) bezeichnet wird, um das Ereignis zu behandeln. Diese Unterbrechung ist vorübergehend, und nach Abschluss des Interrupt-Handlers nimmt der Prozessor die normalen Aktivitäten wieder auf.
Es gibt zwei Arten von Interrupts: Hardware-Interrupts und Software-Interrupts (Softirqs):
- Hardware-Interrupts werden von Geräten verwendet, um zu kommunizieren, dass sie vom Betriebssystem beachtet werden müssen. Intern werden Hardware-Interrupts mithilfe elektronischer Warnsignale implementiert, die von einem externen Gerät, das entweder Teil des Computers selbst ist, wie z. B. einem Festplattencontroller oder einem externen Peripheriegerät, an den Prozessor gesendet werden. Wenn Sie beispielsweise eine Taste auf der Tastatur drücken oder die Maus bewegen, werden Hardware-Interrupts ausgelöst, die dazu führen, dass der Prozessor den Tastenanschlag oder die Mausposition liest. Das Einleiten eines Hardware-Interrupts wird als Interrupt-Anforderung (IRQ) bezeichnet.
- Ein Software-Interrupt wird entweder durch einen Ausnahmezustand im Prozessor selbst oder durch einen speziellen Befehl im Befehlssatz verursacht, der bei seiner Ausführung einen Interrupt verursacht. Ersteres wird häufig als Trap oder Ausnahme bezeichnet und wird für Fehler oder Ereignisse verwendet, die während der Programmausführung auftreten und so außergewöhnlich sind, dass sie nicht im Programm selbst behandelt werden können. Beispielsweise wird eine Ausnahme zum Teilen durch Null ausgelöst, wenn der arithmetischen Logikeinheit des Prozessors befohlen wird, eine Zahl durch Null zu teilen, da dieser Befehl ein Fehler ist und unmöglich ist.
Dieser Artikel befasst sich mit Hardware / externen Interrupts IRQ.
Was ist der Zweck von Interrupts? Beispielsweise möchten wir eine Aktion mit einem eingehenden Paket von der Netzwerkkarte ausführen, sobald das Paket eintrifft. Wenn Sie die Netzwerkkarte nicht ständig fragen möchten: "Ist mein Paket angekommen?" und verschwenden Sie Ihre Prozessorzeit, können Sie externe Hardware-Interrupt-IRQ verwenden. Die Interrupt-Leitung eines Geräts sollte mit der INTR-Leitung der CPU verbunden sein. Nach dem Empfang jedes Pakets sendet die Netzwerkkarte ein Signal über diese Leitung. Die CPU erkennt dieses Signal und weiß, dass die Netzwerkkarte Informationen dafür enthält. Erst danach liest die CPU das eingehende Paket.
Aber was sollen wir tun, wenn es viele externe Geräte gibt? Es wäre sehr unproduktiv, für alle eine Menge INTR-Pins auf der CPU zu erstellen.

Um dieses Problem zu lösen, wurde ein spezieller Chip erfunden - ein Interrupt-Controller.
Pic
(
wiki /
osdev )
Der erste Interrupt-Controller-Chip war der
Intel 8259 PIC . Es hatte 8 Eingangsleitungen (IRQ0-7) und 1 Ausgangsleitung (die den Interrupt-Controller mit der INTR-Leitung der CPU verbindet). Wenn eines der Geräte auf seinen Eingangsleitungen unterbrochen wird, sendet der 8259 ein Signal über die INTR-Leitung. Danach weiß die CPU, dass ein Gerät seine sofortige Aufmerksamkeit erfordert, und der Prozessor fragt den PIC, welche der 8 Eingangsleitungen (IRQx) die Quelle dieses Interrupts war. Diese Abfrage hat einen gewissen Overhead, aber jetzt haben wir 8 Interrupt-Leitungen anstelle von 1.

Bald waren 8 Zeilen nicht mehr genug. Um die Gesamtzahl der Interrupt-Leitungen zu erhöhen, wurden zwei 8259-Controller (Master und Slave) in einer Kaskade (Dual PIC) angeschlossen.
IRQs von 0 bis 7 werden mit dem ersten Intel 8259 PIC (Master) und IRQs von 8 bis 15 mit dem zweiten Intel 8259 PIC (Master) verarbeitet. Nur der Master ist an die CPU angeschlossen und kann über die eingehenden Interrupts signalisieren. Wenn auf den Leitungen 8-15 ein Interrupt auftritt, signalisiert der zweite PIC (Slave) dem Master auf der Leitung IRQ2 dies, und danach signalisiert der Master der CPU. Dieser kaskadierte Interrupt entfernt 1 der 16 Leitungen, macht jedoch insgesamt 15 Interrupts für alle externen Geräte.

Dieses Schema wurde von der Community übernommen, und wenn jemand über PIC (Programm Interrupt Controller) spricht, meint er dieses Dual-PIC-System. Nach einiger Zeit wurden die 8259-Controller verbessert und erhielten einen neuen Namen: 8259A. Bei diesen Controllern war das DUAL PIC-System im Chipsatz enthalten. Zu einer Zeit, als der Hauptbus für die Verbindung externer Geräte die ISA war, war dieses System ausreichend. Es war nur erforderlich, dass verschiedene Geräte nicht mit derselben IRQ-Leitung verbunden waren, da ISA-Interrupts nicht gemeinsam genutzt werden können.
Die Geräte-Interrupt-Zuordnung war so ziemlich Standard:
Beispiel (von
hier ):
IRQ 0 - Systemtimer
IRQ 1 - Tastaturcontroller
IRQ 2 - Kaskade (Interrupt vom Slave-Controller)
IRQ 3 - serielle Schnittstelle COM2
IRQ 4 - serielle Schnittstelle COM1
IRQ 5 - Parallelport 2 und 3 oder Soundkarte
IRQ 6 - Diskettenregler
IRQ 7 - Parallelport 1
IRQ 8 - RTC-Timer
IRQ 9 - ACPI
IRQ 10 - offen / SCSI / NIC
IRQ 11 - offen / SCSI / NIC
IRQ 12 - Mauscontroller
IRQ 13 - Mathe-Co-Prozessor
IRQ 14 - ATA-Kanal 1
IRQ 15 - ATA-Kanal 2
Die Konfiguration und Arbeit mit 8259 Chips erfolgt über E / A-Ports:
Die vollständige Dokumentation des 8259A finden Sie
hier .
Der PCI-Bus ersetzte später den ISA-Bus. Leider begann die Anzahl der Geräte die Anzahl von 15 zu überschreiten. Anstelle des statischen ISA-Busses können auch Geräte im PCI-Bus dynamisch zum System hinzugefügt werden, was möglicherweise zu noch mehr Problemen führen kann. Glücklicherweise können Interrupts im PCI-Bus gemeinsam genutzt werden, sodass viele Geräte an eine Interrupt-Leitung IRQ angeschlossen werden können. Um das Problem des Fehlens von Interrupt-Leitungen zu lösen, wurde beschlossen, Interrupts von allen PCI-Geräten zu PIRQ-Leitungen zu gruppieren (Programmable Interrupt Request).
Angenommen, wir haben 4 freie Interrupt-Leitungen am PIC-Controller und 20 PCI-Geräte. Wir können Interrupts von 5 Geräten zu einer PIRQx-Leitung kombinieren und diese PIRQx-Leitungen mit dem PIC-Controller verbinden. In diesem Fall muss der Prozessor bei einem Interrupt auf einer der PIRQx-Leitungen alle an diese Leitung angeschlossenen Geräte nach dem Interrupt fragen, um zu wissen, wer dafür verantwortlich ist, aber am Ende löst er das Problem. Das Gerät, das PCI-Interrupt-Leitungen mit PIRQ-Leitungen verbindet, wird häufig als PIR-Router bezeichnet.
Bei dieser Methode muss sichergestellt werden, dass PIRQx-Leitungen keine Verbindung zu Leitungen mit ISA-Interrupts herstellen (da dies zu Konflikten führt) und dass PIRQx-Leitungen ausgeglichen sind (je mehr Geräte wir mit einer Leitung verbinden, desto mehr Geräte benötigt die CPU abfragen, wann überprüft werden muss, welches Gerät für den Interrupt verantwortlich ist).
Hinweis : Auf dem Bild ist die Zuordnung von PCI-Gerät -> PIR abstrakt dargestellt, da sie im realen Fall etwas komplizierter ist. In der realen Welt verfügt jedes PCI-Gerät über 4 Interrupt-Leitungen (INTA, INTB, INTC, INTD) und bis zu 8 Funktionen, wobei jede Funktion nur einen dieser INTx-Interrupts haben kann. Welche INTx-Leitung von jeder Funktion verwendet wird, hängt von der Chipsatzkonfiguration ab.
Funktionen sind von Natur aus separate logische Blöcke. Beispielsweise kann ein PCI-Gerät eine Smbus-Controller-Funktion, eine SATA-Controller-Funktion und eine LPC-Bridge-Funktion haben. Aus Sicht eines Betriebssystems (OS) ist jede Funktion wie ein separates Gerät mit einem eigenen Konfigurationsbereich (PCI-Konfiguration).
Informationen über ein PIC-Controller-Interrupt-Routing werden vom BIOS mit Hilfe der Tabelle $ PIR und über die Register 3Ch (INT_LN Interrupt Line (R / W)) und 3Dh (INT_PN Interrupt Pin (RO)) von an das Betriebssystem gesendet den PCI-Konfigurationsraum für jede Funktion.
Eine Spezifikation für die $ PIR-Tabelle wurde kürzlich
auf der Microsoft-Website veröffentlicht , ist jedoch derzeit nicht verfügbar. Es ist möglich, den Inhalt der Tabelle anhand der
PCI-BIOS-Spezifikation [4.2.2. Holen Sie sich PCI Interrupt Routing Options] oder von
hier (der letzte Link ist auf Russisch, aber Sie können versuchen, "PCI IRQ Routing Table Specification" zu googeln)
Apic
(
Wiki ,
Osdev )
Die letzte Methode funktionierte, bis Multiprozessorsysteme eintrafen. Von Natur aus kann der PIC nur Interrupts an eine CPU senden, und in einem Multiprozessorsystem ist es erwünscht, CPUs auf ausgeglichene Weise zu laden. Die Lösung für dieses Problem war die neue APIC-Schnittstelle (Advanced PIC).
Für jeden Prozessor wurde ein spezieller Controller namens LAPIC (Local APIC) sowie der
E / A-APIC- Controller zum Weiterleiten von Interrupts von externen Geräten hinzugefügt. Alle diese Steuerungen sind in einem gemeinsamen Bus mit dem Namen APIC zusammengefasst (beachten Sie, dass moderne Systeme für diese Aufgabe einen Standardsystembus anstelle eines separaten APIC-Busses verwenden).
Wenn ein externer Interrupt am E / A-APIC-Eingang eintrifft, sendet die Steuerung eine Interrupt-Nachricht an den LAPIC einer der System-CPUs. Auf diese Weise hilft der E / A-APIC-Controller, die Interrupt-Last zwischen den Prozessoren auszugleichen.
Der erste APIC-Chip war der
82489DX , ein separater Chip, der einen verbundenen LAPIC- und E / A-APIC in sich hatte. Für ein Doppelprozessorsystem wurden drei solcher Chips benötigt: zwei für LAPIC und einer für I / O APIC. Später wurde die LAPIC-Funktionalität direkt in die Prozessoren integriert, und der E / A-APIC-Teil wurde vom 82093AA-Chip getrennt.
Der E / A-APIC
82093AA hatte 24 Eingänge und die APIC-Architektur konnte bis zu 16 CPUs unterstützen. Die Interrupts 0-15 wurden für alte ISA-Interrupts zur Kompatibilität mit älteren Systemen belassen, und die Interrupts 16-23 waren für alle PCI-Geräte vorgesehen. Mit dieser Abgrenzung könnten alle Konflikte zwischen ISA- und PCI-Interrupts leicht vermieden werden. Mit der erhöhten Anzahl freier Interrupt-Leitungen wurde es auch möglich, die Anzahl der PIRQx-Leitungen zu erhöhen.

Die E / A-APIC- und LAPIC-Programmierung erfolgt mit Hilfe von MMIO. LAPIC-Register werden normalerweise an der Adresse 0xFEE00000 und E / A-APIC-Register an der Adresse 0xFE0000 abgelegt, obwohl es möglich ist, sie neu zu konfigurieren.
Wie im PIC-Fall wurden separate Chips am Anfang später Teil des Chipsatzes.
Die APIC-Architektur wurde später modernisiert und ihre neue Variante hieß xAPIC (x - erweitert). Bei voller Abwärtskompatibilität wurde die Gesamtzahl der möglichen CPUs im System auf 256 erhöht.
Der nächste Schritt in der Architekturentwicklung wurde
x2APIC genannt . Die Anzahl der möglichen CPUs im System wurde auf 2 ^ 32 erhöht. Diese Controller können in einem Abwärtskompatibilitätsmodus mit xAPIC oder im neuen x2APIC-Modus arbeiten. In diesem neuen Modus erfolgt die Controller-Programmierung nicht über MMIO, sondern über MSR-Register (die viel schneller sind). Laut
diesem Link ist für diesen Modus IOMMU-Unterstützung erforderlich.
Es ist zu beachten, dass es möglich ist, mehrere E / A-APIC-Controller im System zu haben. Zum Beispiel einer für 24 Interrupts in einer Southbridge und der andere für 32 Interrupts in einer Northbridge. Im Kontext von I / O APIC werden Interrupts normalerweise als GSI (Global System Interrupt) bezeichnet. Das oben genannte System hat also GSIs 0-55.
Wie können wir feststellen, ob eine CPU über ein internes LAPIC verfügt und welche APIC-Architektur sie unterstützt? Es ist möglich, diese Fragen zu beantworten, indem Bit-Flags von der CPUID überprüft werden.
Damit das Betriebssystem LAPIC und I / O APIC erkennen kann, sollte das BIOS Informationen über diese entweder über eine MPtable (alte Methode) oder über eine ACPI-Tabelle (in diesem Fall eine MADT-Tabelle) anzeigen. Neben allgemeinen Informationen sollten sowohl die MPtable als auch die ACPI (in diesem Fall eine DSDT-Tabelle) Informationen zum Interrupt-Routing enthalten. Dies bedeutet Informationen darüber, welches Gerät welche Interrupt-Leitung verwendet (ähnlich der $ PIR-Tabelle).
Sie können über die MPtable in der offiziellen
Spezifikation lesen. Früher war die Spezifikation auf der Intel-Website, aber derzeit ist sie nur in einer Archivversion zu finden. Die ACPI-Spezifikation finden Sie auf der UEFI-Website (aktuelle Version ist
6.2 ). Es ist zu beachten, dass mit ACPI das Interrupt-Routing für Systeme ohne APIC deklariert werden kann (anstatt eine separate $ PIR-Tabelle bereitzustellen).
Msi
(
Wiki )
Die letzte Variante von APIC war gut, aber nicht ohne Nachteile. Alle Interrupt-Leitungen von Geräten machten das System sehr kompliziert und erhöhten somit die Fehlerwahrscheinlichkeit. Der PCI-Express-Bus ersetzte den PCI-Bus, wodurch alle Interrupt-Systeme vollständig vereinfacht wurden. Es gibt überhaupt keine Interrupt-Leitungen. Aus Gründen der Abwärtskompatibilität werden Interrupt-Signale (INTx #) mit einer separaten Art von Nachrichten emuliert. Bei PCI-Interrupt-Leitungen wurde die Verbindung mit physischen Drähten hergestellt. Bei PCI-Express-Interrupt-Leitungen ist eine Verbindung logisch und wird über PCI-Express-Bridges hergestellt. Diese Unterstützung älterer INTx-Interrupts besteht jedoch nur aus Gründen der Abwärtskompatibilität mit dem PCI-Bus. PCI Express führt eine völlig neue Methode zur Interrupt-Zustellung ein - MSI (Message Signaled Interrupts). Bei diesem Verfahren signalisiert ein Gerät den Interrupt einfach durch Schreiben an eine spezielle Stelle im MMIO-Bereich der CPUs LAPIC.

Früher konnte ein einzelnes PCI-Gerät (dh alle seine Funktionen) nur 4 Interrupts haben, jetzt konnten jedoch bis zu 32 Interrupts adressiert werden.
Bei MSI gibt es keine gemeinsame Nutzung von Interrupt-Leitungen: Jeder Interrupt entspricht natürlich seinem Gerät.
MSI-Interrupts lösen auch ein weiteres Problem. Stellen wir uns zum Beispiel eine Situation vor, in der ein Gerät eine Speicherschreibtransaktion ausführt und über den Interrupt signalisieren möchte, dass es abgeschlossen ist. Eine Schreibtransaktion auf dem Bus kann jedoch während der Übertragung verzögert werden (und das Gerät konnte nichts davon wissen). In diesem Fall kommt das Signal über den Interrupt zuerst an die CPU, sodass der Prozessor noch nicht gültige Daten liest. Wenn MSI verwendet wird, werden Informationen über das MSI auf die gleiche Weise wie Datennachrichten übertragen und können daher nicht früher eingehen.
Es ist zu beachten, dass MSI-Interrupts ohne LAPIC nicht funktionieren können, MSIs jedoch E / A-APIC ersetzen können (eine weitere Vereinfachung des Designs).
Nach einiger Zeit wurde die MSI-Methode auf MSI-X erweitert. Jetzt kann jedes Gerät bis zu 2048 Interrupts haben. Es ist jetzt auch möglich anzugeben, welche CPU welchen Interrupt verarbeiten soll. Dies kann für Hochlastgeräte wie z. B. Netzwerkkarten sehr nützlich sein.
Für die MSI-Unterstützung ist keine separate BIOS-Tabelle erforderlich. Das Gerät sollte jedoch seine MSI-Unterstützung über eine der Funktionen in seinem PCI-Konfigurationsbereich anzeigen. Außerdem sollte ein Gerätetreiber die erforderliche Unterstützung für die Arbeit mit dem MSI enthalten.
Fazit
In diesem Artikel haben wir Informationen zur Interrupt-Controller-Entwicklung untersucht und allgemeines theoretisches Wissen über die Interrupt-Übermittlung von externen Geräten im x86-System erhalten.
Im nächsten Teil werden wir üben und sehen, wie jeder der oben genannten Interrupt-Controller unter Linux aktiviert wird.
Im dritten Teil werden wir uns den Coreboot-Code ansehen und herausfinden, welche Einstellungen im Chipsatz für ein korrektes Interrupt-Routing erforderlich sind.
Links:
Anerkannte Segmente
Besonderer Dank geht an Jacob Garber von der
Coreboot- Community, der mir bei dieser Artikelübersetzung geholfen hat.