Interrupts von externen GerÀten in einem x86-System. Teil 3. Konfigurieren des Interrupt-Routings im Chipsatz anhand des Coreboot-Beispiels

Wir werden weiterhin in Betracht ziehen, Interrupts von externen GerÀten im x86-System zu konfigurieren.
In Teil 1 (Die Entwicklung von Interrupt-Controllern ) haben wir die theoretischen Grundlagen von Interrupt-Controllern und allgemeine Begriffe untersucht. In Teil 2 ( Linux-Kernel-Boot-Optionen ) haben wir untersucht, wie das Betriebssystem in der Praxis zwischen Controllern wĂ€hlt. In diesem Teil werden wir uns ansehen, wie das BIOS das IRQ-Routing fĂŒr Interrupt-Controller im Chipsatz konfiguriert.

Keine modernen BIOS-Entwicklungsunternehmen (AwardBIOS / AMIBIOS / Insyde) legen den Quellcode ihrer Programme offen. Aber zum GlĂŒck gibt es Coreboot , ein Projekt, das das proprietĂ€re BIOS durch freie Software ersetzt. In seinem Code werden wir sehen, wie das Interrupt-Routing im Chipsatz konfiguriert ist.




Theorie


Erfrischen und ergĂ€nzen Sie zunĂ€chst unser theoretisches Wissen. In Teil 1 haben wir einen gemeinsamen Interrupt-Pfad fĂŒr den Fall von PIC und APIC identifiziert.

Bild:



APIC:



In diesen Figuren ist das PCI-GerĂ€t → PIR-Mapping abstrakt dargestellt, tatsĂ€chlich ist es etwas komplizierter. In Wirklichkeit hat jedes PCI-GerĂ€t 4 Interrupt-Leitungen (INTA #, INTB #, INTC #, INTD #). Jedes PCI-GerĂ€t kann bis zu 8 Funktionen haben und jede Funktion hat bereits einen INTx # Interrupt. Welche Zeile von INTx # jede Funktion des GerĂ€ts abruft, ist entweder in der Hardware festgelegt oder wird durch die Konfiguration des GerĂ€ts bestimmt.



Funktionen sind im Wesentlichen separate logische Blöcke. Beispielsweise kann in einem PCI-GerĂ€t eine Smbus-Controller-Funktion, eine SATA-Controller-Funktion oder eine LPC-Bridge-Funktion vorhanden sein. Auf der Betriebssystemseite ist jede Funktion ein separates GerĂ€t mit einem eigenen Konfigurationsbereich fĂŒr die PCI-Konfiguration.

Im einfachsten (und hĂ€ufigsten) Fall eines PCI-GerĂ€ts gibt es nur eine Funktion, deren Interrupt ĂŒber die INTA # -Leitung erfolgt. Im Allgemeinen verfĂŒgt das GerĂ€t jedoch möglicherweise sogar ĂŒber mehr als 4 Funktionen (wie bereits erwĂ€hnt, 8). Einige davon mĂŒssen dann auf einer INTx # -Leitung platziert werden (PCI-Interrupts können die Leitung gemeinsam nutzen). Außerdem kann bei PCI-GerĂ€ten, die im Chipsatz enthalten sind, durch Schreiben in spezielle Register hĂ€ufig angegeben werden, welche Funktionen welche INTx # -Leitungen verwenden (und ob sie ĂŒberhaupt verwendet werden).

Wir systematisieren unser Wissen und bezeichnen den Pfad (Routing) von Unterbrechungen von jeder PCI-Funktion ĂŒber INTx # → PIRQy → IRQz, wobei:

  • INTx # - Zeile INT # (INTA #, INTB #, INTC #, INTD #) des PCI-GerĂ€ts, das die Funktion verwenden wird
  • PIRQy - die PIRQ-Leitung (PIRQA, PIRQB, ...) von der PIR, mit der die INTx # -Leitung verbunden ist
  • IRQz - IRQ-Leitung (0, 1, 2, ...) am Interrupt-Controller (APIC / PIC), der mit der PIRQy-Leitung verbunden ist

Warum können Sie nicht einfach ĂŒberall eine Verbindung herstellen? INTA # → PIRQA, INTB # → PIRQB, ...?


Warum ĂŒberhaupt Routing einrichten? Angenommen, wir möchten nicht alle Interrupt-Leitungen von allen PCI-GerĂ€ten auf die gleichen PIRQ-Leitungen ĂŒbertragen. Sagen wir Folgendes:

  • INTA # → PIRQA
  • INTB # → PIRQB
  • INTC # → PIRQC
  • INTD # → PIRQD

Wie oben erwĂ€hnt, ist der hĂ€ufigste Fall, wenn ein PCI-GerĂ€t eine Funktion hat und seine Unterbrechung mit der INTA # -Leitung verbunden ist (warum sollte der GerĂ€teentwickler es anders starten?). Wenn wir uns also plötzlich dazu entschließen, alle Zeilen so zu starten, wie wir sie geschrieben haben, werden fast alle Interrupts von GerĂ€ten in PIRQA-Zeilen unterteilt. Nehmen wir an, sie ist auf IRQ16 gelandet. Jedes Mal, wenn der Prozessor darĂŒber informiert wird, dass ein Interrupt auf der IRQ16-Leitung aufgetreten ist, muss er die Treiber aller an die IRQ16-Leitung (PIRQA) angeschlossenen GerĂ€te abfragen, ob sie einen Interrupt dafĂŒr haben. Wenn es viele solcher GerĂ€te gibt, beschleunigt dies natĂŒrlich nicht die Systemreaktion auf Unterbrechungen. Und die PIRQB-PIRQD-Leitungen sind in diesem Fall grĂ¶ĂŸtenteils inaktiv. Zur Verdeutlichung die Abbildung, die das Problem veranschaulicht:



Aber alles könnte so gemacht werden:



Das Bild ist etwas verwirrend, aber der Punkt ist, dass wir einfach die INTx # -Linien mit PIRQy mit Round-Robin verbinden (PIRQA, PIRQB, PIRQC, PIRQD, PIRQA, PIRQB, PIRQC, PIRQD, PIRQA, PIRQB, PIRQC, PIRQD). ..)

Es ist zu beachten, dass hier nicht nur berĂŒcksichtigt werden muss, dass auf jeder PIRQ-Leitung die gleiche Anzahl von PCI-Funktionen geladen ist. Schließlich können einige Funktionen sehr selten und einige dauerhaft Interrupts erzeugen (z. B. Ethernet-Controller). In diesem Fall kann sogar die Zuweisung einer separaten PIRQ-Leitung fĂŒr Interrupts mit einer solchen Funktion durchaus gerechtfertigt sein.

Auf der Grundlage des Vorstehenden hat der BIOS-Entwickler unter anderem die Aufgabe sicherzustellen, dass die PIRQ-Leitungen gleichmĂ€ĂŸig mit Interrupts geladen werden.

Was soll das BIOS ĂŒberhaupt tun?


Wir systematisieren in der Abbildung:



  • 1) Geben Sie an, welche INTx # -Zeile jede Funktion von PCI-GerĂ€ten abruft
    Bei externen PCI-GerĂ€ten wird dieser Punkt nicht ausgefĂŒhrt, bei den im Chipsatz enthaltenen Funktionen von PCI-GerĂ€ten jedoch möglicherweise.
  • 2) Konfigurieren Sie die INTx # → PIRQy-Zuordnung fĂŒr jedes PCI-GerĂ€t
    Es ist erwÀhnenswert, dass es mehr als vier Standard-PIRQy-Signale geben kann (PIRQA, PIRQB, PIRQC, PIRQD). Zum Beispiel 8: PIRQA-PIRQH.

PIRQy-Signale gehen auf die IRQz-Leitung des ausgewĂ€hlten Interrupt-Controllers (APIC / PIC). Da wir alle möglichen Lademethoden unterstĂŒtzen möchten (siehe Teil 2 ), mĂŒssen wir beide Zuordnungen ausfĂŒllen:

  • 3a) FĂŒllen Sie das Mapping PIRQy → IRQz1 fĂŒr die Kommunikation PIR → I / O APIC aus
    In der Regel ist dies jedoch nicht erforderlich, da die PIRQy-Leitungen auf der APIC-Leitung festgelegt sind. Die ĂŒbliche Lösung ist PIRQA → IRQ16, PIRQB → IRQ17, ... Die einfachste Lösung, weil Wenn Sie PIRQy-Leitungen auf Controller-Leitungen ≄ 16 platzieren, mĂŒssen Sie sich keine Gedanken ĂŒber Konflikte mit untrennbaren Interrupts von ISA-GerĂ€ten machen.
  • 3b) FĂŒllen Sie das Mapping PIRQy → IRQz2 fĂŒr die Kommunikation PIR → PIC
    Dies muss angegeben werden, wenn wir das Routing ĂŒber den PIC-Controller verwenden. Es gibt keine so eindeutige Lösung wie im Fall von APIC, da im Fall von PIC die Möglichkeit von Konflikten mit untrennbaren Interrupts von ISA-GerĂ€ten bekannt sein sollte.

Der letzte vierte Punkt ist erforderlich, um das Betriebssystem bei der Bestimmung des Interrupt-Routings zu unterstĂŒtzen. Das GerĂ€t selbst verwendet diese Register normalerweise nicht.

  • 4) FĂŒllen Sie die Interrupt Line / Interrupt Pin-Register fĂŒr jede PCI-Funktion aus
    Im Allgemeinen wird das Interrupt-Pin-Register automatisch ausgefĂŒllt und ist normalerweise schreibgeschĂŒtzt. Daher muss beim AusfĂŒllen höchstwahrscheinlich nur das Interrupt-Line-Register ausgefĂŒllt werden. Dies muss bereitgestellt werden, wenn wir das Routing ĂŒber den PIC-Controller verwenden, ohne dem Betriebssystem eine Tabelle zu Routing-Interrupts zur VerfĂŒgung zu stellen (siehe erneut Teil 2 ). Wenn Tabellen bereitgestellt werden und diese Zuordnung mit Routing-Tabellen ($ PIR / ACPI) ĂŒbereinstimmt, verlĂ€sst das Betriebssystem diese hĂ€ufig.

Es ist zu beachten, dass wir die Tabellen $ PIR / MPtable / ACPI noch nicht berĂŒhren und ĂŒberlegen, wie die Chipsatzregister in Bezug auf Routing-Interrupts konfiguriert werden, bevor die Steuerung an den Systemloader ĂŒbertragen wird. Interrupt-Tabellen sind ein Thema fĂŒr einen separaten Artikel (möglicherweise einen zukĂŒnftigen).

Also werden die theoretischen Grundlagen studiert, endlich fangen wir an zu ĂŒben!

Übe


Als Beispiel fĂŒr Artikel in dieser Serie verwende ich ein benutzerdefiniertes Board mit einem Intel Haswell i7-Prozessor und einem LynxPoint-LP-Chipsatz. Auf diesem Board habe ich Coreboot in Verbindung mit SeaBIOS gestartet. Coreboot bietet eine hardwarespezifische Initialisierung, und die SeaBIOS-Nutzdaten bieten eine BIOS-Schnittstelle fĂŒr Betriebssysteme. In diesem Artikel werde ich nicht den Prozess der Konfiguration von Coreboot beschreiben, sondern nur anhand eines Beispiels zeigen, welche BIOS-Einstellungen im Chipsatz vorgenommen werden sollten, um IRQ-Interrupts von externen GerĂ€ten weiterzuleiten.

Da sich das Coreboot-Projekt aktiv entwickelt, sodass der Artikel immer auf dem neuesten Stand ist, werden wir den Code am Beispiel der neuesten festen Version 4.9 (Release 2018-12-20) betrachten.

Das Motherboard, das meinem am nĂ€chsten kommt, ist Google Beltino mit Panther-Variante. Der Hauptordner fĂŒr dieses Motherboard ist der Ordner "src \ mainboard \ google \ bearino" . Alle Einstellungen sind hier und der fĂŒr diese Karte spezifische Code konzentriert.

Beginnen wir also damit, herauszufinden, wo die oben genannten Elemente konfiguriert sind:

1) Geben Sie an, welche INTx # -Zeile jede Funktion von PCI-GerÀten abruft


Diese Informationen werden in der Datei „src / mainboard / google / bearino / romstage.c“ in der Struktur rcba_config ĂŒber die DxxIP-Register (Device xx Interrupt Pin Register (IP)) definiert. Dieses Register zeigt an, welcher Pin INTx # (A / B / C / D) jede der GerĂ€tefunktionen einen Interrupt ausgibt.

Mögliche Optionen (siehe Datei "src / southbridge / intel / lynxpoint / pch.h" ):

0h = No interrupt 1h = INTA# 2h = INTB# 3h = INTC# 4h = INTD# 

Es wird angenommen, dass mehrere Funktionen denselben Pin verwenden.

Es wird angenommen, dass Funktionen den Pin möglicherweise nicht fĂŒr Unterbrechungen verwenden (keine Unterbrechung).
Alles, wie wir in der Abbildung am Anfang des Artikels gesehen haben.

Der vollstĂ€ndige Code ist fĂŒr den von uns bezeichneten Artikel verantwortlich:

 /* Device interrupt pin register (board specific) */ RCBA_SET_REG_32(D31IP, (INTC << D31IP_TTIP) | (NOINT << D31IP_SIP2) | (INTB << D31IP_SMIP) | (INTA << D31IP_SIP)), RCBA_SET_REG_32(D29IP, (INTA << D29IP_E1P)), RCBA_SET_REG_32(D28IP, (INTA << D28IP_P1IP) | (INTC << D28IP_P3IP) | (INTB << D28IP_P4IP)), RCBA_SET_REG_32(D27IP, (INTA << D27IP_ZIP)), RCBA_SET_REG_32(D26IP, (INTA << D26IP_E2P)), RCBA_SET_REG_32(D22IP, (NOINT << D22IP_MEI1IP)), RCBA_SET_REG_32(D20IP, (INTA << D20IP_XHCI)), 

Betrachten Sie zum besseren VerstÀndnis einige Beispiele:

Beispiel 1:

Das GerÀt 0x1d (29 in Dezimalzahl) hat eine Funktion (EHCI-Controller).

Weisen Sie in diesem Fall INTA # einen Interrupt zu.

00: 1d.0 - INTA #

 RCBA_SET_REG_32(D29IP, (INTA << D29IP_E1P)), 

Beispiel 2:
Das GerÀt 0x1f (31 in Dezimalzahl) hat die Funktionen Thermosensor-Controller (00: 1f.6), SATA-Controller 2 (00: 1f.2), SMBus-Controller (00: 1f.3), SATA-Controller 1 (00: 1f) .2). Wir möchten nur den SMBus-Controller, den SATA-Controller 1 und den Thermosensor-Controller verwenden.

00: 1f.2 - INTA # (SATA-Controller 1)
00: 1f.3 - INTB # (SMBus-Controller)
00: 1f.2 - Kein Interrupt (SATA-Controller 2 wird nicht verwendet)
00: 1f.6 - INTC # (Thermosensorregler)

FĂŒr diese Konfiguration sollten Sie schreiben:

 RCBA_SET_REG_32(D31IP, (INTC << D31IP_TTIP) | (NOINT << D31IP_SIP2) | (INTB << D31IP_SMIP) | (INTA << D31IP_SIP)), 

Beispiel 3:

In einem GerĂ€t sind mehr als 4 Funktionen erforderlich. Im 0x1c-GerĂ€t ist jede Funktion fĂŒr den PCI Express-Port verantwortlich. Damit die Ports 0-5 funktionieren und Interrupts gleichmĂ€ĂŸig auf die Leitungen verteilt werden, können Sie Folgendes konfigurieren:

00: 1c.0 - INTA # (PCI Express Port 0)
00.1c.1 - INTB # (PCI Express Port 1)
00.1c.2 - INTC # (PCI Express Port 2)
00.1c.3 - INTD # (PCI Express Port 3)
00.1c.4 - INTA # (PCI Express Port 4)
00.1c.5 - INTB # (PCI Express Port 5)
00.1c.6 - Kein Interrupt (Port nicht verwendet)
00.1c.7 - Kein Interrupt (Port nicht verwendet)

 RCBA_SET_REG_32(D28IP, (INTA << D28IP_P1IP) | (INTB << D28IP_P2IP) | (INTC << D28IP_P3IP) | (INTD << D28IP_P4IP) | (INTA << D28IP_P5IP) | (INTB << D28IP_P6IP) | (NOINT << D28IP_P7IP) | (NOINT << D28IP_P8IP)), 

2) Konfigurieren Sie die INTx # → PIRQy-Zuordnung fĂŒr jedes PCI-GerĂ€t


Diese Informationen sind auch in der Datei "src \ mainboard \ google \ bearino \ romstage.c" definiert.
in der Struktur rcba_config, jedoch bereits ĂŒber die Register DxxIR (Device xx Interrupt Route Register).

Die Informationen in diesem Register zeigen, mit welcher PIRQx-Leitung (A / B / C / D / E / F / G / H) jede INTx # -Unterbrechungsleitung verbunden ist.

 /* Device interrupt route registers */ RCBA_SET_REG_32(D31IR, DIR_ROUTE(PIRQG, PIRQC, PIRQB, PIRQA)),/* LPC */ RCBA_SET_REG_32(D29IR, DIR_ROUTE(PIRQD, PIRQD, PIRQD, PIRQD)),/* EHCI */ RCBA_SET_REG_32(D28IR, DIR_ROUTE(PIRQA, PIRQB, PIRQC, PIRQD)),/* PCIE */ RCBA_SET_REG_32(D27IR, DIR_ROUTE(PIRQG, PIRQG, PIRQG, PIRQG)),/* HDA */ RCBA_SET_REG_32(D22IR, DIR_ROUTE(PIRQA, PIRQA, PIRQA, PIRQA)),/* ME */ RCBA_SET_REG_32(D21IR, DIR_ROUTE(PIRQE, PIRQF, PIRQF, PIRQF)),/* SIO */ RCBA_SET_REG_32(D20IR, DIR_ROUTE(PIRQC, PIRQC, PIRQC, PIRQC)),/* XHCI */ RCBA_SET_REG_32(D23IR, DIR_ROUTE(PIRQH, PIRQH, PIRQH, PIRQH)),/* SDIO */ 

Beispiel 1:

Das 0x1c-GerÀt (28 im Dezimalsystem) sind die PCIe-Ports, wie wir bereits herausgefunden haben.

Wir stellen eine "direkte" Verbindung her:

  • INTA # → PIRQA
  • INTB # → PIRQB
  • INTC # → PIRQC
  • INTD # → PIRQD

 RCBA_SET_REG_32(D28IR, DIR_ROUTE(PIRQA, PIRQB, PIRQC, PIRQD)) 

Beispiel 2:

GerÀt 0x1d (29 in Dezimalzahl) - Eine Funktion (EHCI-Controller) auf INTA #, andere Zeilen werden nicht verwendet.

Verbinden Sie die INTA # -Leitung mit PIRQD:

 RCBA_SET_REG_32(D29IR, DIR_ROUTE(PIRQD, PIRQD, PIRQD, PIRQD)) 

In diesem Fall ist nur der erste PIRQD-Datensatz (fĂŒr INTA #) sinnvoll, der Rest macht keinen Sinn.

3a) FĂŒllen Sie das Mapping PIRQy → IRQz1 (PIR → APIC) aus.


Wie bereits erwÀhnt, wird die Zuordnung hier hÀufig behoben, und dieser Fall ist keine Ausnahme.

  • PIRQA → IRQ16
  • PIRQB → IRQ17
  • ...
  • PIRQH → IRQ23

3b) FĂŒllen Sie das Mapping PIRQy → IRQz2 (PIR → PIC) aus.


In coreboot wird der Inhalt zum FĂŒllen dieser Register in der Datei devicetree.cb im Motherboard-Ordner "src \ mainboard \ google \ bearino \" definiert.

devicetree.cb (der Name devicetree fĂŒr die Kommunikation mit einem Ă€hnlichen Konzept im Linux-Kernel und "cb" steht fĂŒr coreboot) ist eine spezielle Datei, die die Konfiguration dieses Motherboards widerspiegelt: Welcher Prozessor, welcher Chipsatz werden verwendet, welche GerĂ€te sind in welchen enthalten? aus usw. DarĂŒber hinaus können in dieser Datei spezielle Informationen zur Konfiguration des Chipsatzes angegeben werden. Dies ist genau der Fall, den wir brauchen:

 register "pirqa_routing" = "0x8b" register "pirqb_routing" = "0x8a" register "pirqc_routing" = "0x8b" register "pirqd_routing" = "0x8b" register "pirqe_routing" = "0x80" register "pirqf_routing" = "0x80" register "pirqg_routing" = "0x80" register "pirqh_routing" = "0x80" 

Diese Zeilen geben die Zuordnung PIRQy → IRQz2 an. Im Code werden sie nach dem Parsen der Datei devicetree.cb in die Variablen "config-> pirqX_routing" umgewandelt.

Die Variable "config-> pirqa_routing = 0x8b" bedeutet, dass die PIRQA mit der IRIC11-Interrupt-Leitung (0x0b = 11) des PIC-Controllers verbunden ist. Das höhere Bit (0x80) bedeutet jedoch, dass kein Interrupt-Routing durchgefĂŒhrt wird. Meiner Erfahrung nach ist dies ein Fehler. StandardmĂ€ĂŸig lohnt es sich, das PIC-Routing zu aktivieren. Das Betriebssystem selbst kann zu E / A-APIC wechseln, indem dieses Bit bei Bedarf auf 1 gesetzt wird.

Das heißt, in diesem Fall wĂ€re es korrekter zu schreiben:

 register "pirqa_routing" = "0x0b" register "pirqb_routing" = "0x0a" register "pirqc_routing" = "0x0b" register "pirqd_routing" = "0x0b" register "pirqe_routing" = "0x80" # not used register "pirqf_routing" = "0x80" # not used register "pirqg_routing" = "0x80" # not used register "pirqh_routing" = "0x80" # not used 

Wir haben die letzten 4 Interrupts nicht aktiviert, weil Der IRQ0-Interrupt wird immer unter dem System-Timer verwendet und ist eindeutig nicht verfĂŒgbar (siehe Allgemeine Informationen zu IBM-PC-kompatiblen Interrupts ).

Wenn wir uns jedoch Punkt 2) genauer ansehen, werden wir feststellen, dass einige PCI-GerÀte die PIRQE-PIRQH-Leitungen verwenden. Wenn Sie sie also nicht angeschlossen lassen, ist dies der richtige Weg, um GerÀte zu beschÀdigen.

Es ist also besser, so etwas zu schreiben:
 register "pirqa_routing" = "0x03" register "pirqb_routing" = "0x04" register "pirqc_routing" = "0x05" register "pirqd_routing" = "0x06" register "pirqe_routing" = "0x0a" register "pirqf_routing" = "0x0b" register "pirqg_routing" = "0x0e" register "pirqh_routing" = "0x0f" 


Das eigentliche FĂŒllen der entsprechenden Register erfolgt in der Datei src \ southbridge \ intel \ lynxpoint \ lpc.c in der Funktion pch_pirq_init.

Code-Snippet, das fĂŒr das AusfĂŒllen des Registers verantwortlich ist:

 /* Get the chip configuration */ config_t *config = dev->chip_info; pci_write_config8(dev, PIRQA_ROUT, config->pirqa_routing); pci_write_config8(dev, PIRQB_ROUT, config->pirqb_routing); pci_write_config8(dev, PIRQC_ROUT, config->pirqc_routing); pci_write_config8(dev, PIRQD_ROUT, config->pirqd_routing); pci_write_config8(dev, PIRQE_ROUT, config->pirqe_routing); pci_write_config8(dev, PIRQF_ROUT, config->pirqf_routing); pci_write_config8(dev, PIRQG_ROUT, config->pirqg_routing); pci_write_config8(dev, PIRQH_ROUT, config->pirqh_routing); 

Registeradresskonstanten werden in derselben pch.h- Datei beschrieben

 #define PIRQA_ROUT 0x60 #define PIRQB_ROUT 0x61 #define PIRQC_ROUT 0x62 #define PIRQD_ROUT 0x63 #define PIRQE_ROUT 0x68 #define PIRQF_ROUT 0x69 #define PIRQG_ROUT 0x6A #define PIRQH_ROUT 0x6B 

Die Zuordnung von PIRQy → IRQz2 fĂŒr diesen Chipsatz wird in die PIRQy_ROUT-Register auf das LPC-PCI-GerĂ€t (Adresse 00: 1f.0) geschrieben. Es ist zu beachten, dass hĂ€ufig nicht alle 15 IRQz2-Leitungen pro PIC verwendet werden dĂŒrfen, sondern nur ein Teil (z. B. 3,4,5,6,7,9,10,11,12,14,15). Die Beschreibung dieser Register sollte Informationen darĂŒber enthalten, welche IRQs verfĂŒgbar sind, um ihnen Interrupts von PIRQ-Leitungen zuzuweisen. Das von uns oben vorgeschlagene Mapping ist also nur möglich, wenn die Zuordnung von PIRQ auf der Leitung IRQ3, IRQ4, IRQ5, IRQ6, IRQ10, IRQ11, IRQ14, IRQ15 verfĂŒgbar ist. Wenn wir uns jedoch den Kommentar vor der Funktion pch_pirq_init genau ansehen, werden wir feststellen, dass es sich um Folgendes handelt:

 /* PIRQ[n]_ROUT[3:0] - PIRQ Routing Control * 0x00 - 0000 = Reserved * 0x01 - 0001 = Reserved * 0x02 - 0010 = Reserved * 0x03 - 0011 = IRQ3 * 0x04 - 0100 = IRQ4 * 0x05 - 0101 = IRQ5 * 0x06 - 0110 = IRQ6 * 0x07 - 0111 = IRQ7 * 0x08 - 1000 = Reserved * 0x09 - 1001 = IRQ9 * 0x0A - 1010 = IRQ10 * 0x0B - 1011 = IRQ11 * 0x0C - 1100 = IRQ12 * 0x0D - 1101 = Reserved * 0x0E - 1110 = IRQ14 * 0x0F - 1111 = IRQ15 * PIRQ[n]_ROUT[7] - PIRQ Routing Control * 0x80 - The PIRQ is not routed. */ 

4) FĂŒllen Sie die Interrupt Line / Interrupt Pin-Register fĂŒr jede PCI-Funktion aus


Im PCI-Konfigurationsraum (jede PCI hat Funktionen gemĂ€ĂŸ dem Standard) gibt es 2 Register, die fĂŒr uns von Interesse sind:

  • 3Ch: Interrupt Line - Hier mĂŒssen Sie die IRQz2-Nummer (eine Nummer von 0 bis 15) schreiben, die Interrupt-Nummer, die die Funktion bei Verwendung des PIC-Controllers eventuell abruft
  • 3Dh: Interrupt Pin - Zeigt an, welche Zeile INTx # (A / B / C / D) die Funktion verwendet

Beginnen wir mit dem letzten. Das Interrupt-Pin-Register wird basierend auf den von uns in Absatz 1 vorgenommenen Chipsatzeinstellungen (DxxIP-Registern) automatisch gefĂŒllt und ist schreibgeschĂŒtzt.

Sie mĂŒssen also nur noch das Interrupt Line-Register mit einem IRQz2-Interrupt fĂŒr jede PCI-Funktion fĂŒllen.

Wenn Sie das Mapping PIRQy → IRQz2 (Punkt 3b) und das Mapping INTx # → PIRQy (Punkt 2) kennen, können Sie das Interrupt Line-Register fĂŒr jede Funktion einfach ausfĂŒllen und wissen, welchen INTx # Interrupt es verwendet (Punkt 1).

In Coreboot werden die Interrupt Line-Register auch in der Datei src \ southbridge \ intel \ lynxpoint \ lpc.c in der Funktion pch_pirq_init gefĂŒllt:

 /* Eric Biederman once said we should let the OS do this. * I am not so sure anymore he was right. */ for (irq_dev = all_devices; irq_dev; irq_dev = irq_dev->next) { u8 int_pin=0, int_line=0; if (!irq_dev->enabled || irq_dev->path.type != DEVICE_PATH_PCI) continue; int_pin = pci_read_config8(irq_dev, PCI_INTERRUPT_PIN); switch (int_pin) { case 1: /* INTA# */ int_line = config->pirqa_routing; break; case 2: /* INTB# */ int_line = config->pirqb_routing; break; case 3: /* INTC# */ int_line = config->pirqc_routing; break; case 4: /* INTD# */ int_line = config->pirqd_routing; break; } if (!int_line) continue; pci_write_config8(irq_dev, PCI_INTERRUPT_LINE, int_line); } 

Aus irgendeinem Grund impliziert dieser Code, dass die Zuordnung auf jeden Fall INTA # → PIRQA, INTB # → PIRQB, INTC # → PIRQC, INTD # → PIRQD ist. Obwohl wir in der Praxis gesehen haben, dass es anders sein kann (siehe Absatz 2).

Im Allgemeinen "Eric Biederman hat einmal gesagt", und wir haben es ĂŒberall kopiert:

 $ grep "Eric Biederman once said" -r src/ src/southbridge/intel/fsp_bd82x6x/lpc.c: /* Eric Biederman once said we should let the OS do this. src/southbridge/intel/i82801gx/lpc.c: /* Eric Biederman once said we should let the OS do this. src/southbridge/intel/i82801ix/lpc.c: /* Eric Biederman once said we should let the OS do this. src/southbridge/intel/lynxpoint/lpc.c: /* Eric Biederman once said we should let the OS do this. src/southbridge/intel/sch/lpc.c: /* Eric Biederman once said we should let the OS do this. 

Im Allgemeinen kĂŒmmert sich Coreboot nicht wirklich um die UnterstĂŒtzung Ă€lterer Interrupts. Sie sollten sich also nicht ĂŒber diesen Fehler wundern. Wenn Sie ein modernes Betriebssystem laden, wird Sie dies nicht stören. Wenn Sie jedoch plötzlich Linux mit den Optionen "acpi = off nolapic" laden mĂŒssen, ist dies kaum möglich.

Fazit


Abschließend wiederholen wir die typischen Informationen, die im Chipsatz fĂŒr das Routing von PCI-Interrupts konfiguriert werden mĂŒssen:

  1. Geben Sie an, welche INTx # -Zeile jede PCI-Funktion zieht
  2. Konfigurieren Sie die INTx # → PIRQy-Zuordnung fĂŒr jedes PCI-GerĂ€t
  3. FĂŒllen Sie die Zuordnung PIRQy → IRQz1 (PIR → APIC) und die Zuordnung PIRQy → IRQz2 (PIR → PIC).
  4. FĂŒllen Sie fĂŒr jede PCI-Funktion die Register Interrupt Line / Interrupt Pin des PCI-Konfigurationsbereichs aus.

Source: https://habr.com/ru/post/de440304/


All Articles