2013-05-03 16 views
6

In einer Post-Boot-Umgebung (kein Betriebssystem), wie würde man den BSP (erster Kern/Prozessor) verwenden, um IPIs für die APs (alle anderen Kerne/Prozessoren) zu erstellen? Im Wesentlichen, wie wacht man auf und setzt den Befehlszeiger für die anderen Kerne, wenn man von einem startet?Wie Sie mit dem APIC IPIs erstellen, um die APs für SMP in der x86-Assembly zu aktivieren?

+0

[Diese Frage] (http://stackoverflow.com/questions/1622388/running-code-on-different-processor-x86-assembly) zeigt den grundlegenden Algorithmus zum Starten eines anderen Prozessors. Ist es das, wonach Sie suchen, oder wollen Sie Details darüber, wie Sie das IPI spezifisch senden? (In beiden Fällen würde ich Sie auf das [OSDev-Wiki] (http://wiki.osdev.org) und [Foren] (http://forum.osdev.org) hinweisen, die viele nützliche Informationen enthalten.) – ughoavgfhw

+0

Minimal Beispiel: http://stackoverflow.com/a/33651438/895245 –

Antwort

9

WARNUNG: Ich habe hier 80x86 angenommen. Wenn es nicht 80x86 ist, dann weiß ich nicht :-)

Zuerst müssen Sie herausfinden, wie viele andere CPUs existieren und welche ihre APIC IDs sind, und die physikalische Adresse der lokalen APICs bestimmen. Dazu parsen Sie ACPI-Tabellen (siehe MADT/APIC in der ACPI-Spezifikation). Wenn Sie keine gültigen ACPI-Tabellen finden können (z. B. Computer ist zu alt), gibt es eine ältere "MultiProcessor Specification", die ihre eigenen Tabellen mit denselben Informationen definiert. Beachten Sie, dass die "MultiProcessor Specification" jetzt veraltet ist (und es gibt einige Computer mit Dummy-Multiprozessor-Tabellen), weshalb Sie zuerst die ACPI-Tabellen überprüfen müssen.

Der nächste Schritt ist zu bestimmen, welche Art von lokalen APIC Sie haben. Es gibt 3 Fälle - alte externe "82489DX" lokale APICs (nicht in die CPU selbst eingebaut), xAPIC und x2APIC.

Beginnen Sie mit der Überprüfung der CPUID, um festzustellen, ob der lokale APIC x2APIC ist. Wenn Sie zwei Möglichkeiten haben, können Sie x2APIC verwenden oder den "xAPIC compatibility mode" verwenden. Für den "xAPIC-Kompatibilitätsmodus" können Sie nur 8-Bit-APIC-IDs verwenden und sind nicht in der Lage, Computer mit vielen CPUs (z. B. 255 oder mehr CPUs) zu unterstützen. Ich würde empfehlen, x2APIC zu verwenden (auch wenn Sie sich nicht für Computer mit vielen CPUs interessieren), da es schneller ist. Wenn Sie den x2APIC-Modus verwenden, müssen Sie den lokalen APIC in diesen Modus umschalten.

Andernfalls, wenn es nicht x2APIC ist, lesen Sie das Versionsregister des lokalen APIC. Wenn die Version des lokalen APICs 0x10 oder höher ist, dann ist es xAPIC, und wenn es 0x0F oder niedriger ist, dann ist es ein externer "82489DX" lokaler APIC.

Die alten externen "82489DX" lokalen APICs wurden in 80486 und älteren Computern verwendet, und diese sind extrem selten (sie waren vor 20 Jahren sehr selten, dann starben die meisten und/oder wurden ersetzt und weggeworfen). Da eine andere Sequenz zum Starten anderer CPUs verwendet wird und Computer mit diesen lokalen APICs äußerst selten sind (z. B. werden Sie wahrscheinlich nie in der Lage sein, Ihren Code zu testen), ist es sehr sinnvoll, diese Computer nicht zu unterstützen. Wenn Sie diese alten Computer überhaupt unterstützen; Ich würde empfehlen, sie als "single-CPU only" zu behandeln und einfach keine anderen CPUs zu starten, wenn der lokale APIC "82489DX" ist. Aus diesem Grund werde ich nicht die Methode beschreiben, mit der sie hier gestartet wurden (sie wird in Intels "MultiProcess Specification" beschrieben, wenn Sie neugierig sind).

Für xAPIC und x2APIC ist die Sequenz zum Starten einer anderen CPU im Wesentlichen die gleiche (nur unterschiedliche Zugriffsmöglichkeiten auf die lokalen APIC - MSRs oder Speicherabbild). Ich würde empfehlen, z. B. Funktionszeiger zu verwenden, um diese Unterschiede zu verbergen; damit später Code eine "send IPI" -Funktion über aufrufen kann. der Funktionszeiger, ohne darauf zu achten, ob der lokale APIC x2APIC oder xAPIC ist.

Um die andere CPU tatsächlich zu starten, müssen Sie eine Folge von IPIs (Inter Processor Interrupts) senden. Intels Methode geht so:

Send an INIT IPI to the CPU you're starting 
Wait for 10 ms 
Send a STARTUP IPI to the CPU you're starting 
Wait for 200 us 
Send another STARTUP IPI to the CPU you're starting 
Wait for 200 us 
Wait for started CPU to set a flag (so you know it started) 
    If flag was set by other CPU, other CPU was started successfully 
    Else if time-out, other CPU failed to start 

Es gibt 2 Probleme mit Intels Methode. Oft wird die andere CPU von der ersten STARTUP IPI gestartet, und in einigen Fällen kann dies zu Problemen führen (zB wenn der Startcode der anderen CPU etwa total_CPUs++; tut, dann kann jede CPU sie zweimal ausführen. Um dieses Problem zu vermeiden, können Sie zusätzliche hinzufügen Synchronisation (z. B. wartet andere CPU auf ein Flag "Ich weiß, dass Sie begonnen haben", das von der ersten CPU gesetzt wird, bevor es fortfährt.) Das zweite Problem mit Intels Methode ist die Messung dieser Verzögerungen.In der Regel startet ein OS die anderen CPUs, ermittelt dann, welche Funktionen die CPUs unterstützen und welche Hardware danach vorhanden ist, und verfügt nicht über eine genaue Zeiteinstellung, um diese 200 us-Verzögerungen genau zu messen.

Um diese Probleme zu vermeiden; Ich verwende eine alternative Methode, die so geht:

Send an INIT IPI to the CPU you're starting 
Wait for 10 ms 
Send a STARTUP IPI to the CPU you're starting 
Wait for started CPU to set a flag (so you know it started) with a short timeout (e.g. 1 ms) 
    If flag was set by other CPU, other CPU was started successfully 
    Else if time-out 
     Send another STARTUP IPI to the CPU you're starting 
     Wait for started CPU to set a flag with a long timeout (e.g. 200 ms) 
      If flag was set by other CPU, other CPU was started successfully 
      Else if time-out, other CPU failed to start 
If CPU started successfully 
    Set flag to tell other CPU it can continue 

Beachten Sie auch, dass Sie CPUs einzeln starten müssen. Ich habe gesehen, wie Leute alle CPUs gleichzeitig mit der Funktion "Broadcast IPI to all but self" gestartet haben - das ist falsch und kaputt und zweifelhaft (tun Sie es nur, wenn Sie Firmware schreiben). Das Problem dabei ist, dass einige CPUs fehlerhaft sein können (z. B. ihren BIST/eingebauten Selbsttest nicht bestanden haben) und einige CPUs möglicherweise deaktiviert sind (z. B. Hyper-Threading, wenn Hyper-Threading in der Firmware deaktiviert ist); und die Methode "Broadcast IPI to all but self" kann CPUs starten, die niemals gestartet werden sollten.

Schließlich kann es bei Computern mit einer großen Anzahl von CPUs relativ lange dauern, sie alle zu starten, wenn Sie sie einzeln starten. Zum Beispiel, wenn es 11 ms dauert, um jede CPU zu starten und es 128 CPUs gibt, dann würde es 1,4 Sekunden dauern. Wenn Sie schnell booten möchten, gibt es Möglichkeiten, dies zu vermeiden. Zum Beispiel kann die erste CPU die zweite CPU starten, dann können die 1. und 2. CPU die 3. und 4. CPU starten, dann können diese vier CPUs die nächsten vier CPUs usw. starten. Auf diese Weise können Sie 128 CPUs in 77 ms starten statt 1,4 Sekunden.

Hinweis: Ich würde nur empfehlen, die CPUs einzeln zu starten und sicherzustellen, dass sie funktioniert, bevor Sie irgendeine Art von "parallelem Start" versuchen (Sie können sich später darum kümmern, nachdem Sie wissen, dass der Rest funktioniert).

Die Adresse, mit der die anderen CPU/s beginnen, wird im Feld "vector" des STARTUP IPI codiert. Die CPU startet die Ausführung des Codes (im Realmodus) mit CS = vector * 256 und IP = 0. Das Vektorfeld ist 8-Bit, die höchste Startadresse, die Sie verwenden können, ist 0x000FF000 (0xFF00: 0x0000 in Realmodus). Dies ist jedoch der Legacy-ROM-Bereich (in der Praxis müsste die Startadresse niedriger sein). Normalerweise würden Sie ein kleines Stück Startup-Code in eine geeignete Adresse kopieren; wobei der Startcode die Synchronisation handhabt (z. B. das Setzen eines "Ich gestarteten" Flags, das eine andere CPU sehen kann und darauf wartet, dass es fortgesetzt werden kann) und dann die Aktivierung des geschützten/langen Modus und das Einrichten eines Stapels vor dem Sprung zu einem Eintrag zeigen Sie in den normalen Code des Betriebssystems. Dieses kleine Stück Startup-Code heißt "AP-CPU-Start-Trampolin". Dies macht auch den "parallelen Start" etwas kompliziert; da jede CPU, die gestartet wird, ihre eigenen/separaten Synchronisationsflags und Stapel benötigt; und da diese Dinge normalerweise mit Variablen in dem Trampolin implementiert sind (z. B. mov esp,[cs:stackTop]), bedeutet dies, dass sie mit mehreren Trampolinen enden.

+0

Wie Sie auf eine bestimmte Zeit warten? In Verbindung stehende http://stackoverflow.com/questions/9971405/assembly-display-in-screen-and-system-sleep –