Lowlevel

Lowlevel => Lowlevel-Coding => Thema gestartet von: üäpöol am 14. February 2013, 01:02

Titel: Taskswitch
Beitrag 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?
Titel: Re: Taskswitch
Beitrag von: kevin am 15. February 2013, 16:54
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.
Titel: Re: Taskswitch
Beitrag von: üäpöol am 16. February 2013, 00:34
Nicht wirklich, weil .eip doch auch nicht am Ende der Struktur ist, sondern irgendwo in der Mitte. Warum sollte iret zu .eip springen?
Titel: Re: Taskswitch
Beitrag von: Jidder am 16. February 2013, 01:17
Hilft dir vielleicht diese Erklärung (http://www.lowlevel.eu/wiki/ISR)?
Titel: Re: Taskswitch
Beitrag von: üäpöol am 16. February 2013, 01:26
Ja, das hilft. Ich glaube, ich hab's verstanden.
Vielen Dank, Jidder!
Titel: Re: Taskswitch
Beitrag von: üäpöol am 16. February 2013, 13:15
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. :)
Titel: Re: Taskswitch
Beitrag von: Martin Erhardt am 16. February 2013, 13:33
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?
Titel: Re: Taskswitch
Beitrag von: üäpöol am 16. February 2013, 13:46
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?
Titel: Re: Taskswitch
Beitrag von: Martin Erhardt am 16. February 2013, 13:50
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.
Titel: Re: Taskswitch
Beitrag von: üäpöol am 16. February 2013, 13:58
Hm... Ich hab mir mal die Stackadresse ausgeben lassen.
multitasking_scheduler() gibt 0x1FFF74 zurück. Weiß jemand, ob das so stimmen kann?
Titel: Re: Taskswitch
Beitrag von: Martin Erhardt am 16. February 2013, 14:09
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.
Titel: Re: Taskswitch
Beitrag von: Martin Erhardt am 16. February 2013, 14:16
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
Titel: Re: Taskswitch
Beitrag von: üäpöol am 16. February 2013, 14:18
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).
Titel: Re: Taskswitch
Beitrag von: üäpöol am 16. February 2013, 15:30
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.
Titel: Re: Taskswitch
Beitrag von: Martin Erhardt am 16. February 2013, 15:40
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)
Titel: Re: Taskswitch
Beitrag von: Jidder am 16. February 2013, 16:23
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.
Titel: Re: Taskswitch
Beitrag von: üäpöol am 16. February 2013, 16:51
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.
Titel: Re: Taskswitch
Beitrag von: Jidder am 16. February 2013, 17:03
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-)
Titel: Re: Taskswitch
Beitrag von: üäpöol am 16. February 2013, 17:51
Ja. Und natürlich den Stack wechseln statt add esp, 4.
Was meinst du mit Stack wechseln? Es gibt doch nur ein esp.
Titel: Re: Taskswitch
Beitrag von: Jidder am 16. February 2013, 17:52
Damit meine ich mov esp, eax. Da wird der Wert in ESP verändert. Es wird also der Stack gewechselt.
Titel: Re: Taskswitch
Beitrag von: üäpöol am 16. February 2013, 18:41
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?
Titel: Re: Taskswitch
Beitrag von: üäpöol am 16. February 2013, 19:54
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.
Titel: Re: Taskswitch
Beitrag von: Jidder am 16. February 2013, 20:05
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.)
Titel: Re: Taskswitch
Beitrag von: üäpöol am 16. February 2013, 20:40
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?
Titel: Re: Taskswitch
Beitrag von: Jidder am 16. February 2013, 20:47
Ich traue deiner itoa-Funktion nicht, weil die eine 16 als Parameter hat, aber nur dezimale Ziffern ausgibt.
Titel: Re: Taskswitch
Beitrag von: üäpöol am 17. February 2013, 00:08
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.
Titel: Re: Taskswitch
Beitrag von: üäpöol am 17. February 2013, 00:55
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.
Titel: Re: Taskswitch
Beitrag von: üäpöol am 17. February 2013, 02:02
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.
Titel: Re: Taskswitch
Beitrag von: Jidder am 17. February 2013, 02:24
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.
Titel: Re: Taskswitch
Beitrag von: üäpöol am 17. February 2013, 02:45
Wow! Jidder, du bist ein (und der einzige :D) Gott!
Vielen Dank! :)
Titel: Re: Taskswitch
Beitrag von: üäpöol am 17. February 2013, 21:00
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?
Titel: Re: Taskswitch
Beitrag von: kevin am 18. February 2013, 13:14
Fehlt dir das Userspace-Datensegment in der GDT? Ist das Limit der GDT hoch genug?
Titel: Re: Taskswitch
Beitrag von: üäpöol am 18. February 2013, 14:37
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?
Titel: Re: Taskswitch
Beitrag von: üäpöol am 22. February 2013, 15:57
Ich weiß leider immer noch nicht, was das Problem ist. Hat noch jemand eine Idee?