Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: El-Tomato am 28. September 2011, 04:13
-
Moin,
da ich im Moment in der Uni das Fach "Betriebssysteme" habe, bin ich gerade dabei ein eigenes OS zusammen zu puzzeln, um die Abläufe da etwas besser zu verstehen. :P
Nun bin ich soweit, dass ich nen kleinen PMode Kernel habe und dabei Multitasking zu implementieren.
Dabei bin ich auf das Tutorial auf den Wiki Seiten gestoßen.
Zuerst mal größtes Lob und besten Dank, dass ihr OS-Dev Neulingen, wie mir, nen Anlaufpunkt bietet. :-)
Los geht's..
Ich habe (zum rum-experimentieren) eine .c Datei mit einer Einstiegsfunktion "mt_install()" und eine "mt_task_switch()", die vom PIC aufgerufen wird.
Nun bin ich nach eurem Tutorial (http://www.lowlevel.eu/wiki/Teil_6_-_Multitasking) vorgegangen und habe die entsprechenden Funktionen implementiert.
Jetzt hänge ich aber bei
struct cpu_state* handle_interrupt(struct cpu_state* cpu)
Ich verstehe einfach nicht woher sie den cpu-parameter bekommt und wohin er den cpu_state returnt. Und was die Punkte darstellen sollen, versteh ich auch nicht :D
Angenommen, ich möchte einen simplen context-switch in die "task_a()"-Funktion machen .. Wie krieg ich das gebacken?
Wenn die vom PIC, zyklisch aufgerufene Funktion "mt_task_switch()" die "handle_interrupt(struct cpu_state* cpu)" aufrufen sollte, kann ich dann als Parameter eine
globale Variable übergeben und den Rückgabewert wieder in dieser Speichern?
zu guter letzt steht im Tutorial folgendes:
...
// Handler aufrufen
// Der Rueckgabewert ist der Prozessorzustand des moeglicherweise
// gewechselten Tasks. Wir muessen also den Stack dorthin wechseln
// um die Register wiederherzustellen.
push %esp
call handle_interrupt
mov %eax, %esp
// CPU-Zustand wiederherstellen
...
Wie kann ich das in C-Code verwursten? Ich habs schon mit inline ASM versucht, bekomme schon bei "push %esp" einen kernel panic.
Generell weiß ich auch noch nicht so wirklich was der gegebene Code macht. :?
Aktuell sieht mein Code folgendermaßen aus:
#include <system.h>
#include <stdio.h>
struct cpu_state {
// Von Hand gesicherte Register
uint32_t eax;
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
uint32_t esi;
uint32_t edi;
uint32_t ebp;
uint32_t intr;
uint32_t error;
// Von der CPU gesichert
uint32_t eip;
uint32_t cs;
uint32_t eflags;
uint32_t esp;
uint32_t ss;
};
struct cpu_state* current_cpu = 0;
static uint8_t stack_a[4096];
static uint8_t stack_b[4096];
static int current_task = -1;
static int num_tasks = 2;
static struct cpu_state* task_states[2];
void task_a(void)
{
while (1) {
printf("A");
}
}
void task_b(void)
{
while (1) {
printf("B");
}
}
struct cpu_state* init_task(uint8_t* stack, void* entry)
{
/*
* CPU-Zustand fuer den neuen Task festlegen
*/
struct cpu_state new_state = {
.eax = 0,
.ebx = 0,
.ecx = 0,
.edx = 0,
.esi = 0,
.edi = 0,
.ebp = 0,
//.esp = unbenutzt (kein Ring-Wechsel)
.eip = (uint32_t) entry,
/* Ring-0-Segmentregister */
.cs = 0x08,
//.ss = unbenutzt (kein Ring-Wechsel)
/* IRQs einschalten (IF = 1) */
.eflags = 0x202,
};
/*
* Den angelegten CPU-Zustand auf den Stack des Tasks kopieren, damit es am
* Ende so aussieht als waere der Task durch einen Interrupt unterbrochen
* worden. So kann man dem Interrupthandler den neuen Task unterschieben
* und er stellt einfach den neuen Prozessorzustand "wieder her".
*/
struct cpu_state* state = (void*) (stack + 4096 - sizeof(new_state));
*state = new_state;
return state;
}
void init_multitasking(void)
{
task_states[0] = init_task(stack_a, task_a);
task_states[1] = init_task(stack_b, task_b);
}
/*
* Gibt den Prozessorzustand des naechsten Tasks zurueck. Der aktuelle
* Prozessorzustand wird als Parameter uebergeben und gespeichert, damit er
* beim naechsten Aufruf des Tasks wiederhergestellt werden kann
*/
struct cpu_state* schedule(struct cpu_state* cpu)
{
/*
* Wenn schon ein Task laeuft, Zustand sichern. Wenn nicht, springen wir
* gerade zum ersten Mal in einen Task. Diesen Prozessorzustand brauchen
* wir spaeter nicht wieder.
*/
if (current_task >= 0) {
task_states[current_task] = cpu;
}
/*
* Naechsten Task auswaehlen. Wenn alle durch sind, geht es von vorne los
*/
current_task++;
current_task %= num_tasks;
/* Prozessorzustand des neuen Tasks aktivieren */
cpu = task_states[current_task];
return cpu;
}
//wird am Anfang aufgerufen, Observer-Pattern ..
int mt_install()
{
timer_add_handler((fp)&mt_task_switch);
init_multitasking();
return 0;
}
//wird zyklisch vom PIC aufgerufen
int mt_task_switch()
{
//hier fehlt mir die Idee .. Wir überrede ich meine Möhre zum Task-Switch?
return 0;
}
Kann mir wer nen Wink in die richtige Richtung geben?
Danke schonmal ;)
MfG
-
Die handle_interrupt()-Funktion wird vom Interrupt-Handler-Stub (in der Assembler-Datei) aufgerufen.
Den CPU-Zustand kriegt sie auch von da und gibt ihn dorthin zurück.
Was meinst du mit Punkten? Die Sterne? Die geben an, dass es sich um Zeiger handelt.
Also: handle_interrupt() ruft deinen Scheduler auf, der übernimmt den alten CPU-Zustand (speichert den aktuellen Zustand für den noch aktuellen Task), wechselt den Task und gibt den neuen CPU-Zustand (also den gespeicherten Zustand des neuen Tasks) zurück. Das handle_interrupt() ändert den CPU-Zustand dann auf den des neuen Tasks.
Alle deine Tasks müssen also eine "struct cpu_state" speichern.
Gruß,
Svenska
-
Hallo El-Tomato,
hast du auch den Teil 5 gelesen? Der Assembler-Teil gehört in intr_common_handler, und die kannst du nicht einfach so in eine C-Funkiton(auch nicht per inline-asm) stecken, weil sie mit einem 'iret' und nicht 'ret' endet, und du außerdem genau wissen must wie der stack aussieht, wo du dir innerhalb einer c-funktion nicht 100%ig sicher sein kanst.
Der Interupthandler wird in dem von dir gezeigten asm-teil aufgerufen. Der parameter (esp) wird auf den stack gepusht, und der rückgabe wert landet in eax, das ist Konvention. Du sicherst allso den CPU zustand auf dem stack, lässt dir von handle_interrupt() nen neuen stack pointer zurückgeben und stellst den cpu zustand von dem neuen stack wieder her.
-
Hallo, danke für die Antworten :)
Habe ich das richtig verstanden?
Wenn der PIC feuert, und die IRQ Routine ausführt, werden die Register auf den Stack gepusht, der IRQ Handler ausgeführt und anstatt danach den alten Zustand zu popen, schiebe ich die Werte des nächsten Tasks in die CPU?
Mein aktueller IRQ Handler sieht wie folgt aus:
extern _irq_handler
irq_common_stub:
pusha
push ds
push es
push fs
push gs
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov eax, esp
push eax
mov eax, _irq_handler
call eax
pop eax
pop gs
pop fs
pop es
pop ds
popa
add esp, 8
iret
(Ich habe mich dabei an das Tutorial von Brandon F. gehalten)
Sehe ich das richtig?
Nachdem _irq_handler aufgerufen wurde, und eax gepopt wurde, müsste ich, statt des vorherigen Zustands, den des nächsten Tasks in die Register, gs, fs, es und ds schieben?
MfG
-
Ja, das hast du richtig verstanden.
Wobei du nicht nur die paar Segmentrigister, sondern alle Register mit denen des neuen Tasks laden musst. Und da die sich alle auf dem Stack befinden reicht es statt pop eax, einfach ESP mit dem Wert zu laden den der Task letztes mal an _irq_handler übergeben hat.
-
Gut, also sollte das so funktionieren:
Wenn der PIC die C-Funktion aufruft, hole ich mir aus dem Struct des nächsten Tasks, die ESP und switche dann die Register. Richtig?
Das ASM Unterprogramm würde dann so aussehen:
global _switch_task
_switch_task:
pop eax ;parameter vom stack nehmen
pusha
xchg esp, eax
popa
ret
und Aufruf über C-Funktion "switch_task(uint32_t neuer_esp)", oder?
Das tuts nur leider nicht :( Sobald ich switch_task aufrufe, bleibt alles stehen ...
Wenn ich mir mit
uint32_t curESP;
asm ( "mov %%esp, %0;" : "=r"(curESP));
den ESP hole und switch_task(curESP);
aufrufe, sollte er ja eigentlich normal weiter funktionieren ..
Selbst das tut er nicht. Das ganze System steht einfach still :/ Woran könnte das liegen?
MfG
-
pop eax ;parameter vom stack nehmen
Der Parameter liegt nicht oben auf dem Stack. Deswegen kommst du da nicht mit einem einfachen pop ran. Stattdessen kannst du den Wert so auslesen:
mov eax, [esp + 4]
Edit in eigener Sache: *yay* vierstellig (http://i.imgur.com/kJFYd.gif)
-
Jetzt krieg ich nen Out of Bounds Panic :-o
global _switch_task
_switch_task:
mov eax, [esp + 4] ;parameter vom stack nehmen
pusha
xchg esp, eax
popa
ret
Ich verlier grad die Lust :D
-
Was meinst du damit? Exception 5 wird von deinem OS gefangen?
-
genau das passiert ..
Ich steh gerade total auf dem Schlauch :/
-
Eine Möglichkeit wären falsch eingestellte IRQs, das heißt PIC falsch konfiguriert. Das glaube ich allerdings nicht.
Wahrscheinlicher ist, dass die CPU an eine falsche Adresse springt. Wie genau verwaltest du die Stacks? Insbesondere: Wie erstellst du den Stack zu dem du springst? switch_task(curESP) ist auf jeden Fall nicht korrekt, weil curESP ja auf nichts zeigt, das von einem popa gefolgt von einem ret verarbeitet werden kann.
-
Wenn ich mir mit
uint32_t curESP;
asm ( "mov %%esp, %0;" : "=r"(curESP));
den ESP hole und switch_task(curESP);
aufrufe, sollte er ja eigentlich normal weiter funktionieren ..
Selbst das tut er nicht. Das ganze System steht einfach still :/ Woran könnte das liegen?
nein, das kann so nicht gehen.
nach dem du esp gespeichert hast, wird mindestens noch der parameter, eip und die allzweicregister(pusha) auf den stack gepuscht (esp also verändert),
wenn du jetzt esp mit dem gespeicherten wert lädst, hast du all das auf dem neuen stack noch gar nicht drauf, und popa lädt irgend einen müll. Viel schlimmer ist aber
das ret auch irgend einen müll vom stack nach eip lädt. also einen 'jmp RANDOM' ausführt.
-
Am IRQ kann es nicht liegen, da ich den Switch im Moment noch per Hand starte ..
OK, den quatsch mit dem curESP hab ich jetzt verworfen.
Ich habe mich meist an den Einsteiger-Guide Teil 6 gehalten ( http://www.lowlevel.eu/wiki/Teil_6_-_Multitasking ) gehalten und auch zwei Tasks (task_a() und task_b()) initialisiert.
Das Problem wird wahrscheinlich sein, dass der esp im task_state[0] = 0 ist :D (Da bin ich mir sogar sicher! :P)
Aber wie kriege ich den Task zum Laufen?
Mein aktueller Code sieht aus wie folgt:
(die blabla() ist im Moment die Funktion, mit der ich den Task Switch anstoßen will .. )
UPDATE: Nochmal mit Syntax Highlighting: http://pastebin.com/5aQJzeUn
#include <system.h>
#include <stdio.h>
//Prototypen
int mt_task_switch();
static uint8_t stack_a[4096];
static uint8_t stack_b[4096];
static int current_task = -1;
static int num_tasks = 2;
static struct cpu_state* task_states[2];
void task_a(void)
{
while (1) {
printf("A");
}
}
void task_b(void)
{
while (1) {
printf("B");
}
}
struct cpu_state* init_task(uint8_t* stack, void* entry)
{
/*
* CPU-Zustand fuer den neuen Task festlegen
*/
struct cpu_state new_state = {
.eax = 0,
.ebx = 0,
.ecx = 0,
.edx = 0,
.esi = 0,
.edi = 0,
.ebp = 0,
//.esp = unbenutzt (kein Ring-Wechsel)
.eip = (uint32_t) entry,
/* Ring-0-Segmentregister */
.cs = 0x08,
//.ss = unbenutzt (kein Ring-Wechsel)
/* IRQs einschalten (IF = 1) */
.eflags = 0x202,
};
/*
* Den angelegten CPU-Zustand auf den Stack des Tasks kopieren, damit es am
* Ende so aussieht als waere der Task durch einen Interrupt unterbrochen
* worden. So kann man dem Interrupthandler den neuen Task unterschieben
* und er stellt einfach den neuen Prozessorzustand "wieder her".
*/
struct cpu_state* state = (void*) (stack + 4096 - sizeof(new_state));
*state = new_state;
return state;
}
void init_multitasking(void)
{
task_states[0] = init_task(stack_a, task_a);
task_states[1] = init_task(stack_b, task_b);
}
/*
* Gibt den Prozessorzustand des naechsten Tasks zurueck. Der aktuelle
* Prozessorzustand wird als Parameter uebergeben und gespeichert, damit er
* beim naechsten Aufruf des Tasks wiederhergestellt werden kann
*/
struct cpu_state* schedule(struct cpu_state* cpu)
{
/*
* Wenn schon ein Task laeuft, Zustand sichern. Wenn nicht, springen wir
* gerade zum ersten Mal in einen Task. Diesen Prozessorzustand brauchen
* wir spaeter nicht wieder.
*/
if (current_task >= 0) {
task_states[current_task] = cpu;
}
/*
* Naechsten Task auswaehlen. Wenn alle durch sind, geht es von vorne los
*/
current_task++;
current_task %= num_tasks;
/* Prozessorzustand des neuen Tasks aktivieren */
cpu = task_states[current_task];
return cpu;
}
struct cpu_state* handle_interrupt(struct cpu_state* cpu)
{
printf("handle_interrupt called, finally :P\n");
struct cpu_state* new_cpu = cpu;
if (cpu->intr == 0x20) {
new_cpu = schedule(cpu);
}
return new_cpu;
}
int mt_install()
{
init_multitasking();
timer_add_handler((fp)&mt_task_switch);
}
int mt_task_switch()
{
return 0;
}
void blabla()
{
printf("Task_A esp = %i\n", task_states[0]->esp);
printf("Task_B esp = %i\n", task_states[1]->esp);
//switch_task(task_states[0]->esp);
}
Meine Frage ist jetzt, woher bekomme ich den ESP des Tasks, wenn der quasi noch uninitialisiert ist?!
-
Im Tutorial wird der Task Switch hier (Zeile 67) (http://git.tyndur.org/?p=tutorial.git;a=blob;f=kernel/int_stub.S;h=e08236ea967e959a42bc9bf05042fd8c7786640d;hb=HEAD#l67) ausgelöst.
Solang du innerhalb von C bist kannst du nicht einfach mal den stack aus tauschen, weil du dich nicht auf einen bestimmten zustand verlassen kannst.
Deshalb macht man das im assembler stub des Interupthandlers.
-
Ich glaube ich komme der Sache näher ..
Den Code habe ich schon gefunden ..
Mein Problem ist einfach nur, dass ich den interrupt handler etwas anders integriert habe .. (Dump davon gibts weiter unten ..)
statt cpu_state benutze ich folgendes Struct:
struct regs
{
unsigned int gs, fs, es, ds;
unsigned int edi, esi, ebp, esp, ebx, edx, ecx, eax;
unsigned int int_no, err_code;
unsigned int eip, cs, eflags, useresp, ss;
};
Mein IRQ Handler sieht wie folgt aus:
struct regs* irq_handler(struct regs *r)
{
struct regs* new_cpu = r;
void (*handler)(struct regs *r);
handler = irq_routines[r->int_no - 32];
if (handler)
{
handler(r);
}
if (r->int_no >= 40)
{
outportb(0xA0, 0x20);
}
if (r->int_no == 0x20) {
new_cpu = schedule(r);
}
outportb(0x20, 0x20);
return new_cpu;
}
Und das hier ist der irq stub, der den irq_handler aufruft
irq_common_stub:
pusha
push ds
push es
push fs
push gs
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push esp
mov eax, _irq_handler
call eax
call _irq_handler
pop eax
mov esp, eax
pop gs
pop fs
pop es
pop ds
popa
add esp, 8
iret
Jetzt zum Problem:
sobald der Interrupt kommt, und schedule() aufgerufen wird, scheint er alles richtig in die Register zu laden (siehe dump unten),
aber danach verabschiedet sich das Ding mit einem Fehler 0, General Protection Fault.
Der Dump sieht wie folgt aus:
CPU HALT
gs: 0, fs: 0, es: 0, ds: 0
edi: 0, esi: 0, esp: 1065599
eax: 0, ebx: 1085824, ecx: 0, edx: 1065599
int_no: 13, err_code: 0
eip: 1063348, cs: 0, ss: 1065599
eflags: 66054, useresp: 1050769
Hat wer ne Idee?
-
Du rufst in irq_common_stub zweimal irq_handler handler auf. Einmal sollte genügen.
Außerdem wechselst du nicht den Stack im irq_handler (wegen des pop eax).
Wenn das nicht das Problem behebt, solltest du genauer analysieren, wo genau die Exception auftritt. Du kannst zum Beispiel den Emulator befragen (qemu z.B. mit den Parameter -d int aufrufen und dann in der Datei qemu.log bzw. /tmp/qemu.log nachschauen), oder deiner eigenen Ausgabe vertrauen (das eip: 1063348). Interessant ist vor allem welcher Befehl ausgeführt wird.
-
Sry, hatte einige Zeilen auskommentiert und wollte die im Code Tag löschen ..
Da lief was falsch .. Mein Code sieht eigentlich so aus:
irq_common_stub:
pusha
push ds
push es
push fs
push gs
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push esp
call _irq_handler
mov esp, eax
pop gs
pop fs
pop es
pop ds
popa
add esp, 8
iret
Sry, mein Fehler :/
-
Wenn das so ist, würde ich sagen, dass der Fehler wahrscheinlich nicht in dem Code liegt, den du eben gepostet hast.
Die Ausgabe "cs: 0" sieht allerdings verdächtig aus. Du solltest überprüfen, was du auf den Stack legst bzw. was du mit dem Stack machst. Oder die Ausgabefunktion.
-
Also ich habe jetzt Code aus dem Tutorial Teil 6 nochmal komplett rüber kopiert und nur "cpu_state" durch "regs" getauscht ..
Jetzt steht im CS der Wert 8.
Trotzdem bekomme ich einen General Protection Fault :/
Ich total ratlos :? :?
Edit:
Ich hab jetzt die Funktion, welche den Fehler verursacht .. Nur warum weiß ich nicht :|
Benutze ich putch(), verabschiedet sich das Teil .. Der Compiler gibt aber weder Fehler noch Warnung in dieser Datei aus ..
Nehme ich die putch() aus dem Task raus, steht alles (Auch keine Interrupts kommen durch ..)
Irgendwer ne Ahnung?
Edit2:
Jippie! Das Multitasking funktioniert jetzt ..
allerdings kann ich überhaupt keine C-Funktionen nutzen ..
Habe es mit dem Systemlautsprecher ausprobiert, der verschiedene Töne von sich gibt. (Mittels inline asm outb)
Aber wie komme ich nun an meine ganzen C-Funktionen?
Edit3:
In QEmu funktionierts wunderbar ..
Aber VMWare 8 zeigt mir nen General Protection Fault :/
Edit4:
Ich hab mal die Register, vor und nach dem switch, ausgeben lassen (Direkt danach crasht er mit nem GPF)
(http://www0.xup.in/exec/ximg.php?fid=17833228) (http://www.xup.in/dl,17833228/vmware-qemu.png/)
Ich bin, was das angeht, noch blutiger Anfänger, aber mir ist aufgefallen, dass der wert in fs sehr hoch ist.
Ist FS kein 16 Bit Register? Warum holt sich VMWare dann einen so großen Wert? Das könnte doch schon der Fehler sein, oder?
Fällt noch wem was auf?
btw .. auf nem echten PC funktioniert es auch nicht :(
-
Es ist keine gute Idee die Segmentregister mit 0 zu Initialisieren. Möglicherweise löst das schon direkt ein #GP aus, aber spätestens der zugriff über ein eines der genullten Register tut das. Also quasi jeder zugriff auf (Globalen-)Speicher.
-
Ich bin da nach dem Wiki Eintrag vorgegangen ..
Mittlerweile bin ich etwas weiter ..
Mein ASM Code ist einfach falsch.
Der sieht aktuell so aus:
irq_common_stub:
pusha
push ds
push es
push fs
push gs
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push esp
call _irq_handler
pop ebx ; Hier bin ich mir nicht
mov esp, ebx ; sicher, ob das richtig ist
pop gs
pop fs
pop es
pop ds
popa
add esp, 8
iret
Jemand sagte mir, ich solle solle mich um den ESP kümmern und den auf den Wert einstellen, den er vor beladen
des Stacks (für die Parameter der C-Funktion) hatte ..
Aber wenn ich am Anfang den ESP auf den Stack pushe und direkt vor dem iret wieder ins esp poppe, tut sich einfach
überhaupt garnichts :/
-
so (http://forum.lowlevel.eu/index.php?topic=2854.msg33525#msg33525) war das schon richtig. Du willst ja den Stack nehmen der von irq_handler zurück kommt.
Du musst nur in deiner init_task die segment register ds, es, (fs, gs) mit (scheinbar) 16 initialisieren.
-
ein pop fehlt aber trotzdem, oder? ich pushe ja vor dem call ESP auf den Stack..
Aber was meinst du mit "ds, es, fs und gs mit 16 initialisieren"? Einfach hart kodieren?
Weil im Code im Wiki werden diese Register auch mit 0 initialisiert .. die werden im wiki überhaupt nicht initialisiert ..
Hatte gerade die Idee, dass der zu große fs Wert auch durch die fehlende Initialisierung entstehen könnte ..
Habe jetzt jede Variable des structs mit 0 initialisiert, trotzdem bekomme ich einen GPF :/
-
nein ein pop fehlt nicht, das brauchst du nur um den stack wieder herzustellen, aber du hast dir ja den stack den du wieder herstellen willst von vor dem 'push esp' gemerkt.
also du könntest zwar ein pop ebx machen, und dann esp mit eax überschreiben, aber das bringt dir nichts, weil dich der wert auf dem stack nicht interressiert und esp ja eh überschrieben wird.
im tutorial werden ds, es auch hard codiert, nur im assembler Teil(evtl. aber an der stelle auch noch gar nicht angefasst und erst später hard codiert).
Hardcodieren ist völlig inordnung wenn du FlatMemory nutzt. Für den anfang (nur KernelSpace/kein Userspace) muss da bei dir die 16 rein.
-
Ick könnt dir knutschen, et funktioniert :D
Ich danke dir und Jidder schonmal recht herzlich :)
Für alle die es Interessiert oder die selben Probleme haben sollten, hier mein letzter Stand:
Der ASM IRQ-Stub:
irq_common_stub:
pusha
push ds
push es
push fs
push gs
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push esp
call _irq_handler
mov esp, eax
pop gs
pop fs
pop es
pop ds
popa
add esp, 8
iret
Das dazugehörige Struct im init_task() :
struct regs new_state = {
.eax = 0,
.ebx = 0,
.ecx = 0,
.edx = 0,
.esi = 0,
.edi = 0,
.ebp = 0,
//.esp = unbenutzt (kein Ring-Wechsel)
.eip = (uint32_t) entry,
/* Ring-0-Segmentregister */
.cs = 0x08,
//.ss = unbenutzt (kein Ring-Wechsel)
/* IRQs einschalten (IF = 1) */
.eflags = 0x202,
.gs=16,
.fs=16,
.es=16,
.ds=16,
.esp=0,
.useresp=0,
.ss=0,
.int_no=0,
.err_code=0,
};
Danke, nochmals ;)