Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: bitmaster am 24. December 2006, 19:03
-
Hi,
ich möchte jetzt endlich mal FPU-Programmierung in meinem OS ermöglichen. Aber dazu müssen ja die FPU-Register bei einem Task-Wechsel gespeichert und geladen werden. Wie genau macht ihr das in eurem OS? Ach ja, die xmms Register müssen ja auch gespeichert werden. Aber wie genau sollte man das realisieren bzw. wie habt ihr das gemacht?
thx
bitmaster
-
Jo, gibt nen Bit, dass die CPU dazu veranlasst, einen Interrupt bei der nächsten Benutzung eines FPU/MMX oder was auch immer die Register nutzt auszuführen. Da kannst du dann die Register speichern.
-
Wenn du nur den 64bit modus unterstützt brauchst du die fpu register nicht speichern, da es iirc im richtigen 64bit longmode nur die SSE Befehle gibt (und nichtmehr die alten FPU Befehle und Register).
-
Ich glaub das stimmt so nicht bluecode.
Die FPU Befehle sind im Longmode vorhanden, wenn bit 29 in CPUID Funktion 0x80000001 gesetzt ist (AMD Prozessoren - AMD Manual rev 3.07 volume 7 page 211) - Intel weiß ich grad nicht.
Außerdem, da greifen MMX und SSE Befehle teilweise auf auf die FPU Register zu.
Weiterhin gäbe es keine Möglichkeit mehr mit direkter Prozessorunterstützung sinus, cosinus, tangens, ... berechnen. Jeder müsste dann dazu den Taylor Algorithmus (ich glaub so heißt der) implementieren um die zu berechnen.
-
übrigens was mir gerade so einfällt du könntest auch bei jedem Task Switch die FPU Register (und ev. auch XMM Register) am Stack speichern :wink:
lg,
Toaster
jo, aber wie?
bitmaster
-
@T0ast3r: Oh, cool. Ich wusste gat nicht das es dafüt Befehle gibt.
thx
bitmaster
-
Das ist aber extrem langsam da nur sehr wenige Threads/Tasks gleichzeitig FPU Befehle nutzen. Setzen des Bits und Sichern im Interrupt Handler ist wesentlich schneller.
-
@Korona: OK, aber wann genau wird der Interrupt aufgerufen?
thx
bitmaster
-
Ich habe jetzt meine Implementierung nicht mehr im Kopf und gerade keinen Zugriff auf die Intel Manuals, aber im PM Mode Tutorial (http://www.jay-code.de/scripts/htmlgenerator.php?page=pmode&directory=tutorials) steht folgendes:
TS = Task Switched
Dieses Bit wird von Prozessor automatisch bei jedem Taskwechsel gesetzt. Dadurch soll ermöglich werden,
das MMX- und SSE-Register erst dann gesichert werden müssen, sofern der neue Task auch Befehle benutzt, die
diese Register verändert. Sobald ein solcher Befehl ausgeführt wird, prüft der Prozessor ob das Bit gesetzt ist.
Wenn das der Fall ist, wird eine Exception ausgelöst, die es dem Betriebssystem dann ermöglicht die Register zu
sichern.
Demnach musst du das Bit beim Taskswitch setzen (ich gehe davon aus, dass du Software Tasking benutzt, beim Hardware Tasking sollte das automatisch passieren) und dann im Interrupt Handler #16 die Register mit fsave bzw. fxsave sichern.
-
Also ich verwende Softwaremultitasking (Hardwaremultitasking gibt es im 64 Bit mode nicht mehr).
Aber das mit dem TS habe ich noch nicht kapiert. Also ich setze bei jedem Taskwechsel das TS Bit. Und was habe ich dann davon? Wann wird es gelöscht und wenn ich bei jedem Taskwechsel das TS Bit setze, woher weiß ich dann ob die FPU benutzt wurde? :?
thx
bitmaster
-
Disclaimer: Ich hab das noch nie benutzt, aber ich interpretiere einfach mal die obigen Posts für dich. ;)
wenn ich bei jedem Taskwechsel das TS Bit setze, woher weiß ich dann ob die FPU benutzt wurde? :?
Wenn du eine Exception bekommen hast, wurde die FPU benutzt.
Wann wird es gelöscht
Ich hätte jetzt gesagt, am sinnvollsten im Exception Handler, nachdem du die alten Werte gesichert hast. Zusammen mit dem Setzen beim Taskwechsel bekommst du dann genau beim ersten FPU-Aufruf nach einem Taskwechsel eine Exception, in der du den alten Inhalt sichern und die richtigen Werte laden kannst.
So klingt das für mich jedenfalls schlüssig, aber vielleicht hab ich auch was falsch verstanden.
-
Das heißt wenn ich das TS Bit setze wird nach einem FPU Befehl ein IRQ ausgelöst? Und was ist wenn mehr als 1 FPU Befehl aufgerufen wird? Wird bei jedem FPU Befehl ein IRQ aufgerufen?
thx
bitmaster
-
Nachdem die Register gesichert sind kannst du das Bit ja einfach wieder auf 0 setzen.
-
Also dann verstehe ich den Sinn des TS Bit nicht ganz.
Also wann genau wird der FPU IRQ aufgerufen? Wann muss ich das TS setzten, wann löschen und wann den Inhalt der Register speichern?
*verwirrt*
bitmaster
EDIT: Vielleicht hat jemand von euch ein gutes Tutorial über FPU (google hilft mir irgendwie nicht)???
-
Bei jedem Taskswitch setzt du das Bit. Wenn das Bit gesetzt ist, wird immer wenn du einen FPU Befehl nutzt die Exception (Nummer 7) ausgelöst. Wenn diese Exception ausgelöst wird, löschst du das Bit wieder. Falls ein anderer Task/Thread als der aktuelle die FPU Register zuletzt benutzt hat, musst du jetzt die FPU Register speichern und die FPU Register des aktuellen Tasks wieder laden.
Also:
Bei jedem Taskwechsel: Bit setzen
Wenn die Exception 7 ausgelöst wird: Bit löschen.
-> Wenn FPU_REGISTER_TASK != AKTUELLER_TASK
----> Aktueller Inhalt der FPU gehört zu nem anderen Task ergo: Register für den anderen Task speichern, Register für den aktuellen Task laden
----> FPU_REGISTER_TASK = AKTUELLER_TASK
EDIT: Hm, ich habe mal mein OS wieder rausgekramt und mir die Implementierung angeguckt. Ich könnte dir C Beispielcode dafür geben, eigentlich ist das aber sauleicht und das kannst du auch ohne Beispielcode schaffen denke ich :D.
-
@Korona: OK, vielen dank.
Ich könnte dir C Beispielcode dafür geben
Nee lass mal, C verwirt mich nur.
Ich hab jetzt auch was gefunden:
Performance Considerations. When system software supports multitasking,
it must be able to save the processor state for one task
and load the state for another. For performance reasons, the
media and/or x87 processor state is usually saved and loaded
only when necessary. System software can save and load this
state at the time a task switch occurs. However, if the new task
does not use the state, loading the state is unnecessary and
reduces performance.
The task-switch bit (CR0.TS) is provided as a lazy contextswitch
mechanism that allows system software to save and load
the processor state only when necessary. When CR0.TS=1, a
device-not-available exception (#NM) occurs when an attempt
is made to execute a 128-bit media, 64-bit media, or x87
instruction. System software can use the #NM exception
handler to save the state of the previous task, and restore the
state of the current task. Before returning from the exception
handler to the media or x87 instruction, system software must
clear CR0.TS to 0 to allow the instruction to be executed. Using
this approach, the processor state is saved only when the
registers are used.
In legacy mode, the hardware task-switch mechanism sets
CR0.TS=1 during a task switch (see Task Switched (TS) Bit on
page 54 for more information). In long mode, the hardware taskswitching
is not supported, and the CR0.TS bit is not set by the
processor. Instead, the architecture assumes that system
software handles all task-switching and state-saving functions.
If CR0.TS is to be used in long mode for controlling the save and
restore of media or x87 state, system software must set and
clear it explicitly.
Aber ich habe noch eine blöde Frage. Was ist der Unterschied zwischen ST0 - ST7 und FPR0 - FPR7?
Wenn ich FXSAVE und FXRSTOR benutze dann brauch ich doch die anderen fblablasave fblablastor nicht, oder?
thx
bitmaster
-
FXSAVE und FXRSTOR speichern auch die SSE Register mit, sie speichern also mehr Daten als FNSAVE. (und brauchen daher AFAIK auch 512 Byte) Auf älteren Prozessoren sind sie nicht verfügbar, da es damals kein SSE gab, das dürfte für dein x64 OS aber egal sein xD.
-
Moment mal, habe ich wirklich alles richtig verstanden *selbstfrag* ? Wann genau wird der IRQ ausgelöst? Bevor der erste FPU Befehl ausgeführt wird oder danach?
thx
bitmaster
-
Bevor :D
Das ist kein IRQ sondern eine Exception ^^.
-
Bevor :D
Das ist kein IRQ sondern eine Exception ^^.
:oops:,
thx
bitmaster
-
hm... lol
r0 = st(0) = mm0 (nur 64 Bit)
bitmaster
-
So, ich habs mir heute morgen noch mal genau angeschaut. Ich hatte einen dämlichen Denkfehler mit dem TS-Bit. Jetzt ist alles klar. Ich schäme mich ja fast schon. Vielen dank!!!
bitmaster
-
So, ich bin gerade mit der implementierung beschäftigt. Jetzt reserviere ich für jeden Task zusätzlich 512 Byte. Aber welche Werte müssen diese 512 Byte haben? Kann man die einfach alle auf Null setzen? Hier das Schema für den LongMode:
(http://www.os-64.de/other/fxsave%20and%20fxrstore%20for%20longmode.jpg)
Die blau markierten Sachen kann man ja einfach auf Null setzen. Aber was ist mit sachen wie rip? Soll ich da immer den aktuellen Wert des rip Registers eintragen oder was? Also aus dem AMD-Manual werde ich nicht schlau. Vielleicht wisst ihr mehr *hoff*.
thx
bitmaster
EDIT: Sag mal reagiert die Exception mittels TS Bit nur auf die FPU oder auch auf die SEE, 3DNow etc. Sachen? Mein Buch sagt nur was von FPU. Aber dann wäre das Ganze ja Schwachsinn. Dann könnte ich auch gleich bei jedem Task-Switch die ganzen Register mittels fxsave und fxrstor laden und speichern.
-
R.I.P. Registers grin
Soll heißen?
/Nachdenk, vlt sollt ich ein Tut über das TS Bit schreiben
Ich verwende einfach bei jedem Taskwechsel fxsave und fxrstore, da ich glaube das eine exception mittels TS-Bit bei xmm etc. nicht aufgerufen wird.
thx
bitmaster
-
Task Switched (TS) Bit. Bit 3. When an attempt is made to execute
an x87 or media instruction while TS=1, a device-not-available
exception (#NM) occurs. Software can use this mechanism
sometimes referred to as lazy context-switchingto save the
unit contexts before executing the next instruction of those
types. As a result, the x87 and media instruction-unit contexts
are saved only when necessary as a result of a task switch.
When a hardware task switch occurs, TS is automatically set to
1. System software that implements software task-switching
rather than using the hardware task-switch mechanism can still
use the TS bit to control x87 and media instruction-unit context
saves. In this case, the task-management software uses a MOV
CR0 instruction to explicitly set the TS bit to 1 during a task
switch. Software can clear the TS bit by either executing the
CLTS instruction or by writing to the CR0 register directly.
Long-mode system software can use this approach even though
the hardware task-switch mechanism is not supported in long
mode.
-
Die Exception sollte bei jedem Zugriff ausgelöst werden. Egal ob jetzt durch SSE, FPU usw. Steht ja auch in deinem zitierten Text.
-
Die Exception sollte bei jedem Zugriff ausgelöst werden. Egal ob jetzt durch SSE, FPU usw. Steht ja auch in deinem zitierten Text.
Jo, ich habs jetzt. Klappt wunderbar.
thx for help
bitmaster