Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: bitmaster am 29. June 2006, 16:12
-
Also ich muss mein OS kräftig umschreiben sonst ist es bald nicht mehr existens fähig. Die neueren CPUs (und der 64-bit Modus) unterstützen nämlich kein Hardwaremultitasking mehr. Jetzt steh ich doof da. Ich muss also zu Softwaremultitasking wechseln. Aber davon habe ich überhaubt keine Ahnung. Hat man dann kein TSS oder wie? Wie speichert man die Register und wo? Please help me!!!
bitmaster
-
hmm? ich dachte immer der Prozessor emuliert das von alleine.
Sicher das der OS-Programmierer das machen muss?
-
hmm? ich dachte immer der Prozessor emuliert das von alleine.
Sicher das der OS-Programmierer das machen muss?
ROFLMAO :lol:
Ne mal im ernst, du hast schon noch ein TSS benutzt es aber nichtmehr zum speichern der Register sondern nur noch, um SS0 und ESP0 bei Interupts zu bekommen. Evtl. hilf dir das tutorial von beyond infinity (http://www.osdever.net/tutorials/multitasking.php?the_id=84). Wenn nicht dann einfach fragen, was du nicht verstehst :)
-
hmm? ich dachte immer der Prozessor emuliert das von alleine.
Sicher das der OS-Programmierer das machen muss?
Ja, ich bin mir da ganz sicher. Im Intel Manual steht sogar drin, dass im 64-Bit Modus kein Hardwaremultitasking mehr möglich ist. Und der AMD64 kann das sogar im 32-Bit Modus nicht mehr (soweit ich weiß). Deswegen muss ich zu Softwaremultitasking wechseln, sonst ist mein OS nicht zufunftssicher. Hat einer 'ne Ahnung wie das mit Softwaremultitasking funktioniert?
danke!!!
-
Hat einer 'ne Ahnung wie das mit Softwaremultitasking funktioniert?
Siehe mein Post, welches du mit Sicherheit überlesen hast :)
-
Da die 64 Bit CPUs von AMD 100% abwärtskompatibel zu allen 32bit x86ern ist geht da natürlich auch noch Hardwaremultitasking in den "alten" Modi.
Softwaremultitasking funktioniert im Prinzip ganz einfach. Du rufst ja in Regelmäßigen Zeitabständen den Timerinterupt auf. Bei diesem wird im Stack CS:EIP,SS:ESP und eFlags abgelegt die beim IRET wieder ausgelesen werden. Wenn du nun vor dem IRET deine alten Register von Task A abgespeichert hast und die von Task B lädst und dabei auf deine Stackwerte umänderst, springst du beim IRET in deine Task B ;)
-
Aso, danke für die vielen Antworten. Also ich lade einmal mit ltr ein TSS und mehr habe ich dann auch nicht, oder wie? Und mein IRQ0 macht dann das oder wie? :
pusha
push ds
push es
push fs
push gs
kA was dann
pop gs
pop fs
pop es
pop ds
popa
iret
Aber dann habe ich doch die vorherigen Register wieder? Und wieso überhaubt dann ein TSS? Hmm... blick da nicht so ganz durch.
bitmaster
-
Aber dann habe ich doch die vorherigen Register wieder?
Nicht, wenn du bei "kA was dann" den Stack wechselst. Das ist der Trick beim Softwaremultitasking.
Und wieso überhaubt dann ein TSS? Hmm... blick da nicht so ganz durch.
Das TSS brauchst du für einen Privilegwechsel von Ring 3 nach Ring 0. (Die Schwierigkeit ist SS und CS gleichzeitig zu ändern.) Anders kommst du nicht in den Kernel.
-
OK, also das mit den anderen Werten durch den anderen Stack verstehe ich jetzt. Aber wie wechsle ich denn den Stack? Und wie bekomme ich dann das neue CS?
bitmaster
-
Stack wechseln:
mov [alter_stack], esp
mov esp, [neuer_stack]
Das neue CS bekommst entweder durch einen far-jump oder bei einem Privilegwechsel durch ein iret.
-
Stack wechseln:
mov [alter_stack], esp
mov esp, [neuer_stack]
Das neue CS bekommst entweder durch einen far-jump oder bei einem Privilegwechsel durch ein iret.
Hmm.. Also brauche ich nur sp und nicht ss ändern? Hä? Und muss also eine Zeichenkette voller Stackwerte von jedem Task haben? Und wenn ich einen Task starte muss ich auf seinem Stack vorher alle Werte für die Register draufpushen um nacher auch durch iret z.B. den cs und eip an der richtig Position zu haben?
-
Hmm.. Also brauche ich nur sp und nicht ss ändern? Hä?
Doch. Wenn du nicht sicherstellen kannst, dass deine Programme mit ss irgendwelchen Unfug machen, dann solltest du ss auch jedes mal ändern.
Und muss also eine Zeichenkette voller Stackwerte von jedem Task haben?
Korrekt. Ich würds nicht Zeichenkette nennen, sondern Speicherbereich. Und das ist ja eigentlich nix besonderes, denn ich gehe mal davon aus, dass all deine Tasks irgendwie irgendwo eigenen Speicher haben. ;)
Und wenn ich einen Task starte muss ich auf seinem Stack vorher alle Werte für die Register draufpushen um nacher auch durch iret z.B. den cs und eip an der richtig Position zu haben?
Jupp, genau so geht es.
-
Gut, das habe ich jetzt kapiert. Aber was meintest du mit schwer wenn der Ring gewechselt wird? In den Deskritoren der Segmente steht doch der Ring, das hat doch dann nichts mit dem Taskwechsel zu tun?
EDIT: Also so habe ich es bis jetzt verstanden:
;Task wechsel:
IRQ0:
cli
pusha
push ds
push es
push fs
push gs
mov eax,ss
mov [mem],eax
mov [mem],sp
mov eax,[mem]
mov ss,eax
mov sp,[mem]
mov al,20h
out 20h,al
pop fs
pop es
pop ds
popa
sti
iret
;[mem] ist jeweils ein anderer Speicherbereich!!!
Aber wenn ich jetzt einen Task zum aller ersten mal starte, dann holt der IRQ0 Werte vom Stack die ich aber noch nie gespeichert habe??? komisch, scheck gerade gar nichts mehr...
-
So, ich speichere im IRQ0 die aktuellen Register. Wechsle dann den Stack. Hole dann vom Stack Werte zu Register. Nee, halt stop. In diesem Stack habe ich ja noch gar keine Register Werte. Was jetzt?
-
Du musst beim erstellen eines neues Tasks den Stack so aufsetzten, dass es halt stimmt :) Eigentlich alles wie in dem tutorial das ich gepostet hab beschrieben.
-
Du musst beim erstellen eines neues Tasks den Stack so aufsetzten, dass es halt stimmt :) Eigentlich alles wie in dem tutorial das ich gepostet hab beschrieben.
Aso, gut dann habe ich das jetzt glaube ich verstanden. Nur verstehe ich immer noch nicht wieso wir überhaubt ein TSS brauchen. Das ganze kann ich doch auch ohne TSS machen. Was meinst du mit ss0 und esp0?
Ach ja, und so tu ich einen Task für den ersten Start vorbereiten:
cli ;IRQ0 nicht aufrufen
push ss
push esp
mov eax,neuer Stack Selector
mov ss,eax
mov esp,neuer Stackpointer
push 0000001000000010b ;eflags
push 1 << 3 ;cs
push task1 ;eip
push 0 ;eax
push 0 ;ecx
push 0 ;edx
push 0 ;ebx
push ? ;esp was muss hier hin?
push 0 ;ebp
push 0 ;esi
push 0 ;edi
push 2 << 3 ;ds
push 2 << 3 ;es
push 2 << 3 ;fs
push 2 << 3 ;gs
pop esp
pop ss
sti ;jetzt kann der IRQ0 wieder aufgerufen werden
So, was muss ich bei push ? ;esp was muss hier hin?
schreiben? Was muss bei dem ? hin? Weil da muss ja ein gültiger Wert hin, sonst ist esp ja kaput.
Ist das bis jetzt alles richtig?
danke!!!
-
Nur verstehe ich immer noch nicht wieso wir überhaubt ein TSS brauchen. Das ganze kann ich doch auch ohne TSS machen. Was meinst du mit ss0 und esp0?
esp0/ss0 sind nur beim software-taskswitching mit änderung des Priviledgelevels wichtig, da soe dort aus dem TSS entnommen werden und eben als neues SS/ESP genommen werden (des is dann der ich nenns mal "kernel-level task stack"). Auf dem Stack werden dann des alte SS/ESP, die EFlags, CS/EIP gesichert, welche beim iret widerhergestellt werden.
Beim softwaretaskswitching ohne änderung des Priviledgelevels wird der Stack nicht gewechselt, sondern nur EFlags/CS/EIP auf dem Stack gesichert, welche auch beim iret wiederhergestellt werden.
Zusätzlich zu den bereits gesicherten Regs musst dann halt noch die Generalpurpose regs sichern.
So, was muss ich bei push ? ;esp was muss hier hin?
schreiben? Was muss bei dem ? hin? Weil da muss ja ein gültiger Wert hin, sonst ist esp ja kaput.
Du musst das push esp weglassen, da bei pusha/popa esp nicht gesichert/wiederhergestellt wird (Das scheint in dem Tutorial falsch zu sein...).
-
@bluecode: danke
aber:
esp0/ss0 sind nur beim software-taskswitching mit änderung des Priviledgelevels wichtig, da soe dort aus dem TSS entnommen werden und eben als neues SS/ESP genommen werden (des is dann der ich nenns mal "kernel-level task stack").
Und woher weiß ich, wann ich die esp0/ss0 auslesen muss und wann nicht.
Auf dem Stack werden dann des alte SS/ESP, die EFlags, CS/EIP gesichert, welche beim iret widerhergestellt werden.
iret läd aber ss und esp nicht. Sondern nur die EFlags, cs/eip.
Zusätzlich zu den bereits gesicherten Regs musst dann halt noch die Generalpurpose regs sichern.
Was ist das?
Du musst das push esp weglassen, da bei pusha/popa esp nicht gesichert/wiederhergestellt wird (Das scheint in dem Tutorial falsch zu sein...).
Doch, pusha und popa verändern auch das esp.
bitmaster
-
iret läd aber ss und esp nicht. Sondern nur die EFlags, cs/eip.
Doch genau das macht iret, wenn in den EFlags das PL != das PL von CS ist ;) Damit hat sich wohl auch die erste Frage erledigt
Zusätzlich zu den bereits gesicherten Regs musst dann halt noch die Generalpurpose regs sichern.
Was ist das?
halt eax, ebx, ecx, edx (esi, edi, ebp)
Du musst das push esp weglassen, da bei pusha/popa esp nicht gesichert/wiederhergestellt wird (Das scheint in dem Tutorial falsch zu sein...).
Doch, pusha und popa verändern auch das esp.
sry, habs grad auch nochma nachgeschaut. pusha sichert zwar esp, aber popa ignoriert den gepushten wert einfach => 0 setzten.
-
sry, habs grad auch nochma nachgeschaut. pusha sichert zwar esp, aber popa ignoriert den gepushten wert einfach => 0 setzten.
Wie kommst du denn darauf?
-
Intel Manual #2 unter popa (bei mir Seite 633):
The value on the stack for the ESP or SP register is ignored. Instead, the ESP or SP register is incremented after each register is loaded.
-
Intel Manual #2 unter popa (bei mir Seite 633):
The value on the stack for the ESP or SP register is ignored. Instead, the ESP or SP register is incremented after each register is loaded.
ou, stimmt
bei mir stehts im Assemblerbuch von Addison-Wesley auf Seite 97 auch:
popa mach folgendes:
pop (e)di
pop (e)si
pop (e)bp
add (e)sp, (4)2
pop (e)bx
pop (e)dx
pop (e)cx
pop (e)ax
Hmm... ich habe das zwar die ganze Zeit gelesen, aber wohl irgendwie nicht wahr genommen. ^^
Ich habs jetzt mit dem Softwaremultitasking geschaft. Der Kernel wird ausgeführt, dann wird zum IRQ0 gesprungen, der Führt dann zum Task, dann wird wieder der IRQ0 aufgerufen, der Führt wieder zum Kernel, dann wird wieder der IRQ0 aufgerufen, der Führt wieder zum Task usw.
Juhu, ich habs jetzt entlich kapiert und es funktioniert. Jetzt kommt aber wieder mein berühmtes - aber - ^^
Aber ich kapiere das nicht mit den: esp0, ss0, esp1, ss1, esp2 und ss2. Wozu sind die da? Jeder Task (auch der Kernel) hat bei mir einen neuen Stack. Brauchen die jetzt auch noch jeder 3 Stacks? Und wann soll ich die 0, 1 und 3 sachen denn nutzen?
danke!!!
-
Aber ich kapiere das nicht mit den: esp0, ss0, esp1, ss1, esp2 und ss2. Wozu sind die da? Jeder Task (auch der Kernel) hat bei mir einen neuen Stack. Brauchen die jetzt auch noch jeder 3 Stacks? Und wann soll ich die 0, 1 und 3 sachen denn nutzen?
Ok stell dir vor, du würdest den Inahlt der Register auf dem normalen Stack einer App speichern, was könnte die dann ganz leicht machen? Genau, in den EFlags zB das PL ändern, oder das CS ändern. Das wären alles keine so tollen Sachen ;) Deshalb wechselt die CPU beim taskswitchen von CPL3 nach CPL0 automatisch den Stack und pusht da die alten Register (bei denen man nun SS3 und ESP3 sagt) SS3, ESP3, EFlags, CS, EIP.
Aber eigentlich ist das beim Hardware taskswitching auch nicht anders :wink:
-
Aber eigentlich ist das beim Hardware taskswitching auch nicht anders
Jo, da macht die CPU das ja alles von selbst (weshalb ich Hardwaretasking eigentlich auch inteligenter finde). Aber ich habe bis jetzt sowieso alles nur Privilegstufe Null. Ich muss mich da in der Materie noch mal einarbeiten.
danke!!!
-
Ok stell dir vor, du würdest den Inahlt der Register auf dem normalen Stack einer App speichern, was könnte die dann ganz leicht machen? Genau, in den EFlags zB das PL ändern, oder das CS ändern. Das wären alles keine so tollen Sachen ;) Deshalb wechselt die CPU beim taskswitchen von CPL3 nach CPL0 automatisch den Stack und pusht da die alten Register (bei denen man nun SS3 und ESP3 sagt) SS3, ESP3, EFlags, CS, EIP.
Aber eigentlich ist das beim Hardware taskswitching auch nicht anders :wink:
Da kann ich nicht ganz zustimmen.
Wenn man den Stack des Programmes zum Speichern der Register benutzt dann passiert das alles in dieser Reihenfolge:
- Task läuft
- Task wird unterbrochen
- Register werden auf Taskstack gesichert
jetzt mach das OS was es will (z.B. Taskwechsel oder Bluescreen) ...
- Register werden vom Stack zurückgesichert
- Task wird fortgesetzt
Der Task hat also keine Möglichkeit die EFlags oder etwas andere zu verändern, weil er niemals ausgeführt wird wenn sich die Register auf dem Stack befinden.
ABER:
Ein voller Stack würde die Registersicherung eines Interruptaufrufes natürlich nicht mehr aufnehmen können.
Ungültige werte im SS oder ESP Register des Tasks sind auch möglich.
Deshalb besitzt jeder Ring einen eigenen Stack.
-
Der Task hat also keine Möglichkeit die EFlags oder etwas andere zu verändern, weil er niemals ausgeführt wird wenn sich die Register auf dem Stack befinden.
Da kann ich wiederum nicht zustimmen :-D Schonmal an Multithreading gedacht? Da könnte dann ein Thread die EFlags,etc. des anderen verändern.
-
Deshalb besitzt jeder Ring einen eigenen Stack.
Also was jetzt genau? Jeder Ring nur einen Stack? Also 3 Stacks, oder jeder Task 3 Stacks?
bitmaster
-
Deshalb besitzt jeder Ring einen eigenen Stack.
Also was jetzt genau? Jeder Ring nur einen Stack? Also 3 Stacks, oder jeder Task 3 Stacks?
Lass dich nicht unnötig verwirren: Benutz nur Ring 0 (Kernel) und Ring 3 (User). Dann brauchst du nen normalen User-Stack, nen Stack auf dem die Register gespeichert werden und evtl. (falls dein kernel viel stack verbraucht) auch noch nen eigenen kernel-stack. Das macht 2 stacks pro task + evtl. einen für den kernel (pro prozessor, falls du mehrere Prozessoren unterstützen willst)..