Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: üäpöol am 14. February 2013, 01:02
-
Hi,
kann mir vielleicht jemand erklären, wie genau der Taskswitch funktioniert?
Das hier ist ja der Code aus dem Tutorial:
push %esp
call handle_interrupt
mov %eax, %esp
Danach kommt ein iret und es wird mit den richtigen Registern und dem richtigen Stack an die Adresse gesprungen, die ganz oben auf dem Stack liegt, oder? Das sollte doch .eip in der Struktur des Tasks sein... Aber .eip ist doch nicht am Anfang der Struktur, oder habe ich was falsch verstanden?
-
Hilft es dir, wenn ich dir sage, dass der Stack nach unten wächst? Jedes pop geht also in der struct einen Eintrag weiter nach hinten, nicht nach vorne.
-
Nicht wirklich, weil .eip doch auch nicht am Ende der Struktur ist, sondern irgendwo in der Mitte. Warum sollte iret zu .eip springen?
-
Hilft dir vielleicht diese Erklärung (http://www.lowlevel.eu/wiki/ISR)?
-
Ja, das hilft. Ich glaube, ich hab's verstanden.
Vielen Dank, Jidder!
-
Ich hab's scheinbar immer noch nicht verstanden. Es wird bei mir weder task_a() noch task_b() aufgerufen.
Hier mal mein Code:
main.c
void task_a(){
print_char('a', -1, 0xF);
while(1);
}
void task_b(){
print_char('b', -1, 0xF);
while(1);
}
int main(){
install_start();
CreateTask(task_a);
CreateTask(task_b);
__asm__ __volatile__ ("int $0x20");
while(1);
return 0;
}
multitasking.c
/** GLOBAL VARIABLES **/
struct struct_Task Tasks[MAX_TASK_NUM+1];
int task_number = 0;
int currentPID = 1;
/** GLOBAL FUNCTIONS **/
void InitMultitasking();
unsigned int multitasking_scheduler(unsigned int stack);
void CreateTask(void* start_addr);
void InitMultitasking(){
IRQ_install_handler(0, SwitchTask);
}
unsigned int multitasking_scheduler(unsigned int stack){
Tasks[currentPID].stack = stack;
while(!Tasks[currentPID].status == 1){
currentPID++;
if(currentPID > MAX_TASK_NUM) currentPID = 1;
}
print(itoa((unsigned int)Tasks[currentPID].stack), 80, 0xF); // 0x1FFF74
return Tasks[currentPID].stack;
}
void CreateTask(void* start_addr){
unsigned int* stack;
__asm__ __volatile__ ("cli");
stack = (void*)(pmm_malloc()+1024);
*stack-- = 0x202;
*stack-- = 0x8;
*stack-- = (unsigned int)start_addr;
*stack-- = 0x0;
*stack-- = 0x0;
*stack-- = 0x0;
*stack-- = 0x0;
*stack-- = 0x0;
*stack-- = 0x0;
*stack-- = 0x0;
*stack-- = 0x0;
*stack-- = 0x10;
*stack-- = 0x10;
*stack-- = 0x10;
*stack-- = 0x10;
Tasks[task_number+1].status = 1;
Tasks[task_number+1].stack = (unsigned int) stack;
Tasks[task_number+1].PID = task_number+1;
task_number++;
__asm__ __volatile__ ("sti");
}
multitasking.asm
[BITS 32]
[GLOBAL SwitchTask]
[EXTERN multitasking_scheduler]
SwitchTask:
push esp
call multitasking_scheduler
mov esp, eax
ret
In diesem (http://www.lowlevel.eu/wiki/Teil_6_-_Multitasking) und
in diesem (http://www.lowlevel.eu/wiki/Software_Multitasking_Tutorial) Tutorial ist der Assembler Code zwar anders, aber so wie ich das verstanden habe, wird das alles um meinen Code von meiner ISR erledigt.
Ich hab das mal debugged: Es taucht nirgends etwas mit task_a oder task_b auf. Deshalb meine Frage: Was fehlt in meinem Code, was dafür sorgt, dass nicht zurück gesprungen wird, sondern in den Task.
Hier noch das, was beim Debuggen raus gekommen ist:
=> 0x1000e8 <main+47>: int $0x20
(gdb)
0x00101111 in IRQ0 ()
1: x/i $pc
=> 0x101111 <IRQ0+1>: push $0x0
(gdb)
0x00101113 in IRQ0 ()
1: x/i $pc
=> 0x101113 <IRQ0+3>: push $0x20
(gdb)
0x00101115 in IRQ0 ()
1: x/i $pc
=> 0x101115 <IRQ0+5>: jmp 0x101180 <IRQ_common_stub>
(gdb)
0x00101180 in IRQ_common_stub ()
1: x/i $pc
=> 0x101180 <IRQ_common_stub>: pusha
(gdb)
0x00101181 in IRQ_common_stub ()
1: x/i $pc
=> 0x101181 <IRQ_common_stub+1>: push %ds
(gdb)
0x00101182 in IRQ_common_stub ()
1: x/i $pc
=> 0x101182 <IRQ_common_stub+2>: push %es
(gdb)
0x00101183 in IRQ_common_stub ()
1: x/i $pc
=> 0x101183 <IRQ_common_stub+3>: push %fs
(gdb)
0x00101185 in IRQ_common_stub ()
1: x/i $pc
=> 0x101185 <IRQ_common_stub+5>: push %gs
(gdb)
0x00101187 in IRQ_common_stub ()
1: x/i $pc
=> 0x101187 <IRQ_common_stub+7>: mov $0x10,%ax
(gdb)
0x0010118b in IRQ_common_stub ()
1: x/i $pc
=> 0x10118b <IRQ_common_stub+11>: mov %eax,%ds
(gdb)
0x0010118d in IRQ_common_stub ()
1: x/i $pc
=> 0x10118d <IRQ_common_stub+13>: mov %eax,%es
(gdb)
0x0010118f in IRQ_common_stub ()
1: x/i $pc
=> 0x10118f <IRQ_common_stub+15>: mov %eax,%fs
(gdb)
0x00101191 in IRQ_common_stub ()
1: x/i $pc
=> 0x101191 <IRQ_common_stub+17>: mov %eax,%gs
(gdb)
0x00101193 in IRQ_common_stub ()
1: x/i $pc
=> 0x101193 <IRQ_common_stub+19>: mov %esp,%eax
(gdb)
0x00101195 in IRQ_common_stub ()
1: x/i $pc
=> 0x101195 <IRQ_common_stub+21>: push %eax
(gdb)0x00101196 in IRQ_common_stub ()
1: x/i $pc
=> 0x101196 <IRQ_common_stub+22>: mov $0x1007ca,%eax
(gdb)
0x0010119b in IRQ_common_stub ()
1: x/i $pc
=> 0x10119b <IRQ_common_stub+27>: call *%eax
(gdb)
0x001007ca in IRQ_handler ()
1: x/i $pc
=> 0x1007ca <IRQ_handler>: push %esi
(gdb)
0x001007cb in IRQ_handler ()
1: x/i $pc
=> 0x1007cb <IRQ_handler+1>: push %ebx
(gdb)
0x001007cc in IRQ_handler ()
1: x/i $pc
=> 0x1007cc <IRQ_handler+2>: sub $0x4,%esp
(gdb)
0x001007cf in IRQ_handler ()
1: x/i $pc
=> 0x1007cf <IRQ_handler+5>: mov 0x10(%esp),%ebx
(gdb)
0x001007d3 in IRQ_handler ()
1: x/i $pc
=> 0x1007d3 <IRQ_handler+9>: mov 0x30(%ebx),%eax
(gdb)
0x001007d6 in IRQ_handler ()
1: x/i $pc
=> 0x1007d6 <IRQ_handler+12>: mov 0x1241c0(,%eax,4),%esi
(gdb)
0x001007dd in IRQ_handler ()
1: x/i $pc
=> 0x1007dd <IRQ_handler+19>: test %esi,%esi
(gdb)
0x001007df in IRQ_handler ()
1: x/i $pc
=> 0x1007df <IRQ_handler+21>: je 0x1007ea <IRQ_handler+32>
(gdb)
0x001007e1 in IRQ_handler ()
1: x/i $pc
=> 0x1007e1 <IRQ_handler+23>: sub $0xc,%esp
(gdb)
0x001007e4 in IRQ_handler ()
1: x/i $pc
=> 0x1007e4 <IRQ_handler+26>: push %ebx
(gdb)
0x001007e5 in IRQ_handler ()
1: x/i $pc
=> 0x1007e5 <IRQ_handler+27>: call *%esi
(gdb)
Breakpoint 1, 0x001011b0 in SwitchTask ()
1: x/i $pc
=> 0x1011b0 <SwitchTask>: push %esp
(gdb)
0x001011b1 in SwitchTask ()
1: x/i $pc
=> 0x1011b1 <SwitchTask+1>: call 0x100cb4 <multitasking_scheduler>
(gdb)
0x00100cb4 in multitasking_scheduler ()
1: x/i $pc
=> 0x100cb4 <multitasking_scheduler>: push %ebx
(gdb)
0x00100cb5 in multitasking_scheduler ()
1: x/i $pc
=> 0x100cb5 <multitasking_scheduler+1>: mov 0x1021c0,%eax
(gdb)
0x00100cba in multitasking_scheduler ()
1: x/i $pc
=> 0x100cba <multitasking_scheduler+6>: lea (%eax,%eax,2),%edx
(gdb)
0x00100cbd in multitasking_scheduler ()
1: x/i $pc
=> 0x100cbd <multitasking_scheduler+9>: mov 0x8(%esp),%ecx
(gdb)
0x00100cc1 in multitasking_scheduler ()
1: x/i $pc
=> 0x100cc1 <multitasking_scheduler+13>: mov %ecx,0x122ac4(,%edx,4)
(gdb)
0x00100cc8 in multitasking_scheduler ()
1: x/i $pc
=> 0x100cc8 <multitasking_scheduler+20>: mov $0x1,%ebx
(gdb)
0x00100ccd in multitasking_scheduler ()
1: x/i $pc
=> 0x100ccd <multitasking_scheduler+25>:
jmp 0x100cdc <multitasking_scheduler+40>
(gdb)
0x00100cdc in multitasking_scheduler ()
1: x/i $pc
=> 0x100cdc <multitasking_scheduler+40>: lea (%eax,%eax,2),%edx
(gdb)
0x00100cdf in multitasking_scheduler ()
1: x/i $pc
=> 0x100cdf <multitasking_scheduler+43>: lea 0x122ac8(,%edx,4),%edx
(gdb)
0x00100ce6 in multitasking_scheduler ()
1: x/i $pc
=> 0x100ce6 <multitasking_scheduler+50>: cmpb $0x0,(%edx)
(gdb)
0x00100ce9 in multitasking_scheduler ()
1: x/i $pc
=> 0x100ce9 <multitasking_scheduler+53>:
je 0x100ccf <multitasking_scheduler+27>
(gdb)
0x00100ceb in multitasking_scheduler ()
1: x/i $pc
=> 0x100ceb <multitasking_scheduler+55>: mov %eax,0x1021c0
(gdb)
0x00100cf0 in multitasking_scheduler ()
1: x/i $pc
=> 0x100cf0 <multitasking_scheduler+60>: lea (%eax,%eax,2),%eax
(gdb)
0x00100cf3 in multitasking_scheduler ()
1: x/i $pc
=> 0x100cf3 <multitasking_scheduler+63>: mov 0x122ac4(,%eax,4),%eax
(gdb)
0x00100cfa in multitasking_scheduler ()
1: x/i $pc
=> 0x100cfa <multitasking_scheduler+70>: pop %ebx
(gdb)
0x00100cfb in multitasking_scheduler ()
1: x/i $pc
=> 0x100cfb <multitasking_scheduler+71>: ret
(gdb)
0x001011b6 in SwitchTask ()
1: x/i $pc
=> 0x1011b6 <SwitchTask+6>: mov %eax,%esp
(gdb)
0x001011b8 in SwitchTask ()
1: x/i $pc
=> 0x1011b8 <SwitchTask+8>: ret
(gdb)
0x001007e7 in IRQ_handler ()
1: x/i $pc
=> 0x1007e7 <IRQ_handler+29>: add $0x10,%esp
(gdb)
0x001007ea in IRQ_handler ()
1: x/i $pc
=> 0x1007ea <IRQ_handler+32>: cmpl $0x27,0x30(%ebx)
(gdb)
0x001007ee in IRQ_handler ()
1: x/i $pc
=> 0x1007ee <IRQ_handler+36>: jbe 0x100802 <IRQ_handler+56>
(gdb)
0x00100802 in IRQ_handler ()
1: x/i $pc
=> 0x100802 <IRQ_handler+56>: sub $0x8,%esp
(gdb)
0x00100805 in IRQ_handler ()
1: x/i $pc
=> 0x100805 <IRQ_handler+59>: push $0x20
(gdb)
0x00100807 in IRQ_handler ()
1: x/i $pc
=> 0x100807 <IRQ_handler+61>: push $0x20
(gdb)
0x00100809 in IRQ_handler ()
1: x/i $pc
=> 0x100809 <IRQ_handler+63>: call 0x100fac <outb>
(gdb)
0x00100fac in outb ()
1: x/i $pc
=> 0x100fac <outb>: push %ebp
(gdb)
0x00100fad in outb ()
1: x/i $pc
=> 0x100fad <outb+1>: mov %esp,%ebp
(gdb)
0x00100faf in outb ()
1: x/i $pc
=> 0x100faf <outb+3>: mov 0x8(%ebp),%dx
(gdb)
0x00100fb3 in outb ()
1: x/i $pc
=> 0x100fb3 <outb+7>: mov 0xc(%ebp),%al
(gdb)
0x00100fb6 in outb ()
1: x/i $pc
=> 0x100fb6 <outb+10>: out %al,(%dx)
(gdb)
0x00100fb7 in outb ()
1: x/i $pc
=> 0x100fb7 <outb+11>: mov %ebp,%esp
(gdb)
0x00100fb9 in outb ()
1: x/i $pc
=> 0x100fb9 <outb+13>: pop %ebp
(gdb)
0x00100fba in outb ()
1: x/i $pc
=> 0x100fba <outb+14>: ret
(gdb)
0x0010080e in IRQ_handler ()
1: x/i $pc
=> 0x10080e <IRQ_handler+68>: add $0x14,%esp
(gdb)
0x00100811 in IRQ_handler ()
1: x/i $pc
=> 0x100811 <IRQ_handler+71>: pop %ebx
(gdb)
0x00100812 in IRQ_handler ()
1: x/i $pc
=> 0x100812 <IRQ_handler+72>: pop %esi
(gdb)
0x00100813 in IRQ_handler ()
1: x/i $pc
=> 0x100813 <IRQ_handler+73>: ret
(gdb)
0x0010119d in IRQ_common_stub ()
1: x/i $pc
=> 0x10119d <IRQ_common_stub+29>: pop %eax
(gdb)
0x0010119e in IRQ_common_stub ()
1: x/i $pc
=> 0x10119e <IRQ_common_stub+30>: pop %gs
(gdb)
0x0010119f in IRQ_common_stub ()
1: x/i $pc
=> 0x10119f <IRQ_common_stub+32>: pop %fs
(gdb)
0x001011a2 in IRQ_common_stub ()
1: x/i $pc
=> 0x1011a2 <IRQ_common_stub+34>: pop %es
(gdb)
0x001011a3 in IRQ_common_stub ()
1: x/i $pc
=> 0x1011a3 <IRQ_common_stub+35>: pop %ds
(gdb)
0x001011a4 in IRQ_common_stub ()
1: x/i $pc
=> 0x1011a4 <IRQ_common_stub+36>: popa
(gdb)
0x001011a5 in IRQ_common_stub ()
1: x/i $pc
=> 0x1011a5 <IRQ_common_stub+37>: add $0x8,%esp
(gdb)
0x001011a8 in IRQ_common_stub ()
1: x/i $pc
=> 0x1011a8 <IRQ_common_stub+40>: iret
(gdb)
0x001000ea in main ()
1: x/i $pc
=> 0x1000ea <main+49>: add $0x10,%esp
(gdb)
0x001000ed in main ()
1: x/i $pc
=> 0x1000ed <main+52>: jmp 0x1000ed <main+52>
(gdb)
0x001000ed in main ()
1: x/i $pc
=> 0x1000ed <main+52>: jmp 0x1000ed <main+52>
(gdb)
0x001000ed in main ()
1: x/i $pc
=> 0x1000ed <main+52>: jmp 0x1000ed <main+52>
(gdb)
0x001000ed in main ()
1: x/i $pc
=> 0x1000ed <main+52>: jmp 0x1000ed <main+52>
(gdb)
0x001000ed in main ()
1: x/i $pc
=> 0x1000ed <main+52>: jmp 0x1000ed <main+52>
(gdb)
0x001000ed in main ()
1: x/i $pc
=> 0x1000ed <main+52>: jmp 0x1000ed <main+52>
(gdb)
Danke schon mal. :)
-
CreateTask kommt mir zunächst komisch vor.
"*(void*)((unsigned int)(stack)--)"ist eigentlich(,falls das gleich "*stack--" ist), immer die address vom stack minus ein byte.
dh du schreibst immer an die gleiche stelle 0x0.
Wieso verwendest du dazu keine schöne Struktur, dann müsste man sich hier gar nicht mit pointerarithmetik auseinandersetzen
Und wieso verbietest du während deserstellen des Tasks Interrupts
void CreateTask(void* start_addr){
unsigned int* stack;
__asm__ __volatile__ ("cli");
stack = (void*)(pmm_malloc()+1024);
*stack-- = 0x202;
*stack-- = 0x8;
*stack-- = (unsigned int)start_addr;
*stack-- = 0x0;
*stack-- = 0x0;
*stack-- = 0x0;
*stack-- = 0x0;
*stack-- = 0x0;
*stack-- = 0x0;
*stack-- = 0x0;
*stack-- = 0x0;
*stack-- = 0x10;
*stack-- = 0x10;
*stack-- = 0x10;
*stack-- = 0x10;
Tasks[task_number+1].status = 1;
Tasks[task_number+1].stack = (unsigned int) stack;
Tasks[task_number+1].PID = task_number+1;
task_number++;
__asm__ __volatile__ ("sti");
}
Wieso legst du eigtl einen leeren Cpu status auf den Userstack?
-
Ich hab mich bei meinem Code sehr stark an dieses (http://www.lowlevel.eu/wiki/Software_Multitasking_Tutorial) Tutorial gehalten. Was kann es denn für Probleme geben, die auftreten, wenn ich Interrupts verbiete? Ich habe es beim Debuggen überprüft, beim stack wird nicht immer an dieselbe Adresse geschrieben. Ich werd's aber trotzdem umschreiben. Woran kann's denn noch liegen?
-
Ich hab mich bei meinem Code sehr stark an dieses (http://www.lowlevel.eu/wiki/Software_Multitasking_Tutorial) Tutorial gehalten. Was kann es denn für Probleme geben, die auftreten, wenn ich Interrupts verbiete? Ich habe es beim Debuggen überprüft, beim stack wird nicht immer an dieselbe Adresse geschrieben. Ich werd's aber trotzdem umschreiben. Woran kann's denn noch liegen?
Eig gar keine ^^ soviel ich weiß, ich wollte nur wissen wieso du das machst.
-
Hm... Ich hab mir mal die Stackadresse ausgeben lassen.
multitasking_scheduler() gibt 0x1FFF74 zurück. Weiß jemand, ob das so stimmen kann?
-
sry die Stelle mit *stack-- ist schon richtig ich (Noob!) hab nicht gewusst das bei -- nicht nur ein dekrementierter wert zurückgegeben wird sondern auch dem Operand ein dekrementierter Wert zugewiesen wird. und weil stack vom typ uint ist stimmt auch die Pointerarithmetik.
-
Ich versteh den Code aber immer noch nicht nach dem Aufruf von multitasking_scheduler verschiebst du den aktuellen wert von esp nach eax und die addresse die die Funktion zurückgegeben hat geht verloren. vllt wolltest du mov eax esp
-
Kein Problem.
Ich verschiebe nicht den Wert von esp nach eax, sondern genau andersrum. :)
Ich hab ne andere Syntax als hier (http://www.lowlevel.eu/wiki/Teil_2_-_Assembler_101).
-
Ich hab meinen Code jetzt mal geändert so sieht er jetzt aus:
void CreateTask(void* start_addr){
__asm__ __volatile__ ("cli");
struct cpu_state* task_state = pmm_malloc();
task_state->eax = 0;
task_state->ebx = 0;
task_state->ecx = 0;
task_state->edx = 0;
task_state->esi = 0;
task_state->edi = 0;
task_state->ebp = 0;
task_state->eip = (unsigned int) start_addr;
task_state->cs = 0x08;
task_state->eflags = 0x202;
Tasks[task_number+1].status = 1;
Tasks[task_number+1].stack = (unsigned int) task_state;
Tasks[task_number+1].PID = task_number+1;
task_number++;
__asm__ __volatile__ ("sti");
}
Das hat aber leider nichts geändert.
-
Ich hab meinen Code jetzt mal geändert so sieht er jetzt aus:
void CreateTask(void* start_addr){
__asm__ __volatile__ ("cli");
struct cpu_state* task_state = pmm_malloc();
task_state->eax = 0;
task_state->ebx = 0;
task_state->ecx = 0;
task_state->edx = 0;
task_state->esi = 0;
task_state->edi = 0;
task_state->ebp = 0;
task_state->eip = (unsigned int) start_addr;
task_state->cs = 0x08;
task_state->eflags = 0x202;
Tasks[task_number+1].status = 1;
Tasks[task_number+1].stack = (unsigned int) task_state;
Tasks[task_number+1].PID = task_number+1;
task_number++;
__asm__ __volatile__ ("sti");
}
Das hat aber leider nichts geändert.
Nö aber es ist ne schönere Lösung(finde ich zumindest :))
Ich zumindest kann jezt nichts wirklich finden vllt wird nach dem interrupt der cpu Zustand nicht richtig hergestellt mit pop %eax
pop %ebx
pop ...
oder wie auch immer das in Intel Syntax aussieht. oder deine Tasks wollen iwann returnen aber das geht nicht so wie das bei mir mal war: http://forum.lowlevel.eu/index.php?topic=3153.msg36532#msg36532 (http://forum.lowlevel.eu/index.php?topic=3153.msg36532#msg36532)
-
Ein Problem ist auf jeden Fall, wie du den IRQ-Handler einrichtest. Das passt nicht zu dem Stack.
Der Interrupthandler muss auf IRET enden, nicht RET. Wenn SwitchTask der komplette Interrupthandler ist, dann fehlt das IRET. Wenn hingegen IRQ_install_handler SwitchTask so einträgt, dass er indirekt von einem Interrupthandler aufgerufen wird, dann passt die struct cpu_state nicht dazu. Ich glaube alle Tutorials im Wiki (inkl. unseres empfohlenen Tutorials (http://www.lowlevel.eu/wiki/Teil_6_-_Multitasking)) wechseln die Tasks in den Interrupt-Stubs, nicht indirekt über eine zusätzliche Methode. Ein Vorteil davon ist, dass die struct cpu_state dann einfacher ist.
Falls SwitchTask tatsächlich indirekt aufgerufen wird: Du könntest zwar die Idee den Taskwechsel in einer ausgelagerten Methode durchzuführen weiterverfolgen, aber dann musst du mindestens die struct cpu_state anpassen. Unter anderem muss dann CS raus, und du musst EFLAGS in SwitchTask manuell wechseln, ebenso wie die General Purpose Register, die in der C-Konvention als Callee-Save deklariert sind. Die übrigen Register (Caller-Save inkl. Clobber-Register wie EAX) musst du im Interruptstub wechseln. Klingt kompliziert und ist es auch. Ich weiß nicht, ob das wirklich dein Plan ist, weil du nicht den kompletten Interruptcode gezeigt hast, aber wenn ja, dann rate ich davon ab. Ich empfehle, dass du dich an die Tutorials hältst, bis du weißt, was da genau passiert.
Das mit dem Interrupts deaktivieren und wieder aktivieren halte ich auch für problematisch. (Denk mal über den Fall nach, wenn beim CLI die Interrupts bereits aus irgendeinem guten Grund deaktiviert waren. Dann aktiviert STI sie wieder, obwohl das gar nicht so sein soll.) In unseren Tutorials werden die Interrupts übrigens immer so eingerichtet, dass die Interrupts für die dauer der Behandlung deaktiviert sind, und nach Rückkehr wieder aktiviert werden. Das heißt Kernel-Code läuft immer mit deaktiviertem Interrupts und User-Code mit aktiviertem Interrupts. Das vermeidet viele Probleme.
-
Ja, so etwas war meine Idee.
Ich habe die Struktur cpu_state auch schon geändert. Sie sieht jetzt so aus:
struct cpu_state {
unsigned int eax;
unsigned int ebx;
unsigned int ecx;
unsigned int edx;
unsigned int esi;
unsigned int edi;
unsigned int ebp;
unsigned int int_no;
unsigned int err_code;
unsigned int eip;
unsigned int cs;
unsigned int eflags;
unsigned int esp;
unsigned int ss;
};
Ich denke, ich werde es trotzdem sein lassen, vor allem, weil ich nicht genau weiß, wie ich eflags setzen muss und was das General Purpose Register ist. :D
Habe ich es richtig verstanden, dass die Interruptbehandlung zumindest für IRQ 0 so ändern muss, dass direkt SwitchTask aufgerufen wird und in SwitchTask auch das gemacht wird, was ansonsten meine Interruptbehandlung automatisch macht?
Hier ist noch meine Interruptbehandlung.
IRQ.c
#include "system.h"
/** GLOBAL VARIABLES **/
void *IRQ_routines[16] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
/** GLOBAL FUNCTIONS **/
void IRQ_install_handler(int irq, void (*handler)(struct cpu_state *r));
void IRQ_uninstall_handler(int irq);
void IRQ_remap();
void IRQ_handler(struct cpu_state *r);
void IRQ_install();
extern void IRQ0();
extern void IRQ1();
extern void IRQ2();
extern void IRQ3();
extern void IRQ4();
extern void IRQ5();
extern void IRQ6();
extern void IRQ7();
extern void IRQ8();
extern void IRQ9();
extern void IRQ10();
extern void IRQ11();
extern void IRQ12();
extern void IRQ13();
extern void IRQ14();
extern void IRQ15();
void IRQ_install_handler(int irq, void (*handler)(struct cpu_state *r)){
IRQ_routines[irq] = handler;
}
void IRQ_uninstall_handler(int irq){
IRQ_routines[irq] = 0;
}
void IRQ_remap(){
outb(0x20, 0x11);
outb(0xA0, 0x11);
outb(0x21, 0x20);
outb(0xA1, 0x28);
outb(0x21, 0x04);
outb(0xA1, 0x02);
outb(0x21, 0x01);
outb(0xA1, 0x01);
outb(0x21, 0x0);
outb(0xA1, 0x0);
}
void IRQ_handler(struct cpu_state *r){
void (*handler)(struct cpu_state *r);
handler = IRQ_routines[r->int_no - 32];
if(handler) handler(r);
if(r->int_no >= 40){
outb(0xA0, 0x20);
}
outb(0x20, 0x20);
}
void IRQ_install(){
IRQ_remap();
CreateIDTGate(32, (unsigned)IRQ0, 0x08, 0x8E);
CreateIDTGate(33, (unsigned)IRQ1, 0x08, 0x8E);
CreateIDTGate(34, (unsigned)IRQ2, 0x08, 0x8E);
CreateIDTGate(35, (unsigned)IRQ3, 0x08, 0x8E);
CreateIDTGate(36, (unsigned)IRQ4, 0x08, 0x8E);
CreateIDTGate(37, (unsigned)IRQ5, 0x08, 0x8E);
CreateIDTGate(38, (unsigned)IRQ6, 0x08, 0x8E);
CreateIDTGate(39, (unsigned)IRQ7, 0x08, 0x8E);
CreateIDTGate(40, (unsigned)IRQ8, 0x08, 0x8E);
CreateIDTGate(41, (unsigned)IRQ9, 0x08, 0x8E);
CreateIDTGate(42, (unsigned)IRQ10, 0x08, 0x8E);
CreateIDTGate(43, (unsigned)IRQ11, 0x08, 0x8E);
CreateIDTGate(44, (unsigned)IRQ12, 0x08, 0x8E);
CreateIDTGate(45, (unsigned)IRQ13, 0x08, 0x8E);
CreateIDTGate(46, (unsigned)IRQ14, 0x08, 0x8E);
CreateIDTGate(47, (unsigned)IRQ15, 0x08, 0x8E);
}
void fault_handler(struct cpu_state *r){
if(r->int_no < 32){
print(exception_messages[r->int_no], 160+44, 0x07);
kernelerror();
restart_pc();
}
}
IRQ.asm
; ...
[EXTERN] IRQ_handler
IRQ_common_stub:
push ebp
push edi
push esi
push edx
push ecx
push ebx
push eax
push esp
call IRQ_handler
add esp, 4
pop eax
pop ebx
pop ecx
pop edx
pop esi
pop edi
pop ebp
add esp, 0x8
iret
Im Moment ist bei mir halt der IRQ_handler SwitchTask, aber man sieht ja, dass das nicht funktioniert.
-
Habe ich es richtig verstanden, dass die Interruptbehandlung zumindest für IRQ 0 so ändern muss, dass direkt SwitchTask aufgerufen wird und in SwitchTask auch das gemacht wird, was ansonsten meine Interruptbehandlung automatisch macht?
Ja. Und natürlich den Stack wechseln statt add esp, 4.
Ein mögliches Problem, dass ich mit so einer Umsetzung sehe, ist, dass die Stacks bei verschiedenen Interrupts nun unterschiedlich aussehen. Wenn jetzt zum Beispiel ein Systemaufruf wie Sleep einen Taskwechsel verursacht, muss der ja zu einem Task wechseln können, der von einem Timer-IRQ unterbrochen wurde. Und irgendwann muss wieder zurückgewechselt werden. Kann sein, dass das kein Problem ist. Ich hab da nicht länger drüber nachgedacht, als ich an diesem Absatz geschrieben habe. Und der ist deswegen unnötig lang. 8-)
-
Ja. Und natürlich den Stack wechseln statt add esp, 4.
Was meinst du mit Stack wechseln? Es gibt doch nur ein esp.
-
Damit meine ich mov esp, eax. Da wird der Wert in ESP verändert. Es wird also der Stack gewechselt.
-
SwitchTask:
push ebp
push edi
push esi
push edx
push ecx
push ebx
push eax
push esp
call multitasking_scheduler
mov eax, esp
pop eax
pop ebx
pop ecx
pop edx
pop esi
pop edi
pop ebp
add esp, 0x8
iret
So sieht meine IRQ0 Behandlung jetzt aus. Ich bekomme aber einen General Protection Fault nach dem iret. Hab ich was vergessen?
-
Ich hab jetzt einfach mal versucht, mir den Stack vor dem iret ausgeben zu lassen:
multitasking.asm
SwitchTask:
push ebp
push edi
push esi
push edx
push ecx
push ebx
push eax
push esp
call multitasking_scheduler
mov eax, esp
mov al, 0x20
out 0x20, al
pop eax
pop ebx
pop ecx
pop edx
pop esi
pop edi
pop ebp
add esp, 0x8
push esp
call check_stack
add esp, 0x8
iret
main.c
void check_stack(unsigned int* stack){
for(int i = 0; i < 25; i++){
print(itoa(*(stack-i*4), 16), i*80, 0xF);
}
while(1);
}
Ein Screenshot von der unsinnigen Ausgabe ist angehängt.
-
Wenn du ESP auf den Wert von EAX setzen willst, solltest du mov esp, eax schreiben. Das sollte auch die Werte auf dem Stack erklären.
(Bei dem add esp, 0x8 nach check_stack sollte das eigentlich eine 0x4 sein, aber du machst es richtig und kehrst von check_stack gar nicht erst zurück.)
-
Hm... Das war natürlich ein blöder Fehler, am General Protection Fault ändert das aber trotzdem nichts. Die Adresse auf dem Stack ist einfach totaler Unsinn. Hast du noch eine Idee, wo der Fehler liegt?
-
Ich traue deiner itoa-Funktion nicht, weil die eine 16 als Parameter hat, aber nur dezimale Ziffern ausgibt.
-
Ich hab's mir nochmal mit der Basis 10 angeschaut und ausgerechnet. Die Werte stimmen.
Hier die Ausgabe mit mov esp, eax.
Ich häng auch meinen schönen Bluescreen an. :D
Besonders komisch ist, dass esp 0x202 sein soll.
Was ist nur mit diesem Stack los?
An der Adresse 0x10152d ist übrigens das iret.
-
Ich habe in der Funktion SwitchTask mal das add esp, 0x8 weggelassen und jetzt wird zumindest schon mal in die main Funktion gesprungen. Das heißt, kein (!) General Protection Fault. :D
Warum nicht in die Funktion task_a gesprungen wird, weiß ich aber immer noch nicht.
-
Okay, ich hab noch ein paar Fehler im Code gefunden.
Das sieht jetzt schon besser aus, aber ganz stimmt der Stack noch nicht.
0x10010B ist die richtige Adresse, an die gesprungen werden soll.
Im Moment bekomme ich natürlich wieder einen General Protection Fault, aber man kann die richtige Adresse schon sehen. :)
Ich hab versucht das pop eax etc. wegzulassen und am Stack mit add esp, 0x4 und sogar sub esp, 0x4 zu arbeiten, aber bisher hat das nicht funktioniert.
Aktueller Screenshot ist angehängt.
Ach ja.
Hier ist noch der dazugehörige Code:
SwitchTask:
push ebp
push edi
push esi
push edx
push ecx
push ebx
push eax
push esp
call multitasking_scheduler
mov esp, eax
mov al, 0x20
out 0x20, al
pop eax
pop ebx
pop ecx
pop edx
pop esi
pop edi
pop ebp
push esp
call show_stack
add esp, 0x4
hlt
iret
Ich glaube, ich komme der Lösung langsam näher.
-
Die Beschriftung an deiner Ausgabe macht keinen Sinn. Die General Purpose Register (das sind eax, ebx, ecx, edx, esp, ebp, esi, edi) sind ja gar nicht mehr auf dem Stack. Du hast sie direkt vor der Ausgabe vom Stack genommen. Du kannst sie also nicht mehr ausgeben.
Das Problem ist, dass du in deiner struct cpu_state noch die Werte int_no und err_code hast. Du musst die entweder entfernen (und dann passt das struct nicht mehr zu den anderen Interrupts), oder du musst diese beiden Zeilen einfügen:
SwitchTask:
sub esp, 8 ; <------------------------------------
push ebp
push edi
push esi
push edx
push ecx
push ebx
push eax
push esp
call multitasking_scheduler
mov esp, eax
mov al, 0x20
out 0x20, al
pop eax
pop ebx
pop ecx
pop edx
pop esi
pop edi
pop ebp
add esp, 8 ; <-----------------------------------
push esp
call show_stack
add esp, 0x4
hlt
iret
Dann ist EIP, CS, EFLAGS ganz oben auf dem Stack und IRET sollte funktionieren.
-
Wow! Jidder, du bist ein (und der einzige :D) Gott!
Vielen Dank! :)
-
Ich hab mich jetzt mal an den Usermode versucht, was natürlich zu einem General Protection Fault führt. Diesmal aber nicht nach einem return, sondern ganz schlicht nach mov ax, 0x23 und mov ds, ax
Woran kann das schon wieder liegen?
-
Fehlt dir das Userspace-Datensegment in der GDT? Ist das Limit der GDT hoch genug?
-
struct GDTstruct GDT[6];
// ...
CreateGDT(0, 0, 0, 0, 0);
CreateGDT(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
CreateGDT(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
CreateGDT(3, 0, 0xFFFFFFFF, 0x9A | 0x60, 0xCF);
CreateGDT(4, 0, 0xFFFFFFFF, 0x92 | 0x60, 0xCF);
CreateGDT(5, (unsigned int) tss, sizeof(tss), 0x09 | 0x80 | 0x60, 0x0);
Das müsste so passen, oder?
-
Ich weiß leider immer noch nicht, was das Problem ist. Hat noch jemand eine Idee?