Help: Multitasking problem.

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
orloff

Help: Multitasking problem.

Post by orloff »

i'm trying to have a context switch mechanism to switch tasks in my microkernel, but i'm having trouble in returning the eip to the next instruction to be executed. i will provide the information i think is important to someone can at least understand what i'm trying to do.

first i boot my kernel.elf via grub, and call main. inside main i setup the gdt and idt, install interrupts, including a time interrupt (it's what i use to switch context). inside main i also setup two tasks, with the following code:

Code: Select all

void setupTask (int id,void (*task)()) {
   unsigned int *s = (unsigned int*)t[id].esp;
   *--s = 0x0202; //eflags
   *--s = 0x08; //cs
   *--s = (unsigned int)task; //eip

   *--s = 0;
   *--s = 0;
   *--s = 0;
   *--s = 0; //registers pushed by pusha
   *--s = 0;
   *--s = 0;
   *--s = 0;
   *--s = 0;
   
   *--s = 0x10;
   *--s = 0x10;
   *--s = 0x10; //segment registers
   *--s = 0x10;

   t[id].esp = (unsigned int)s; //new esp

}//end setup task

void task1 () {
   print(0x04,"task1\n\0");
}

void task2 () {
   print(0x03,"task2\n\0");
}
when the timer interrupt fires and it has passed one second i call the task switch function. the function simply checks if there's a task running if not gives the esp of task0 else switch between task 0 or task 1.

the code that handles the interrupt is the following:

Code: Select all

extern irq_handler

common_irq_handler:
   pusha   ;save registers
   push ds
   push es
   push fs
   push gs ;save segment registers

   ;save esp in stack, via ebx (don't even know why i did this, i    was trying to get my switching function to return the old esp and it wasn't working and this worked).

   mov ebx,esp
   push ebx

   mov eax,0x10   ;change segment registers
   mov ds,eax
   mov es,eax
   mov fs,eax
   mov gs,eax

   call irq_handler ;call handler function (this function returns    the esp to the task that should be resumed)
   pop ebx ;because of the push ebx above.

   mov esp,eax   ;moves the returned eax to the esp (new esp)

   pop gs
   pop fs      ;pops the segment registers of the new task
   pop es
   pop ds
   popa

   add esp,0x08   ;ignore data that was pushed as parameter for    the interruption
   sti   ;enable interrupts
   iret   ;return from interruption
before i use the mov ebx,esp push ebx i wasn't able to get the returned new esp. i was getting an invalid value returned, so i use ebx to store the old value in case i don't switch tasks it only resumes execution.

the problem now is that, when a task switch occurs, all things go ok but the eip value. i checked the cpu state and the returned new esp is ok, it has the task.esp value but when the iret instruction is executed the value 0x000035 is placed in eip and the machine locks and qemu aborts. what i think might be happening is that in some point of my assembly code i pop something, altering the value of esp and thus poping the wrong thing when iret executes. the structure that i use to hold the registers pushed before the code calls irq_handler is:

Code: Select all

struct registers {
    unsigned int ds, es, fs, gs;      /* segment registers */
    unsigned int edi, esi, ebp, esp, ebx, edx, ecx, eax;  /* pushed by 'pusha' */
    unsigned int int_no, err_code;    /* pushed by the interruption */
    unsigned int eip, cs, eflags, useresp, ss;   /* saved automatically */
};//end registers
and finally the qemu dump:

Code: Select all

qemu: fatal: invalid tss type
EAX=00e05bea EBX=00000000 ECX=00000000 EDX=00000000
ESI=00000000 EDI=00000000 EBP=00000000 ESP=85048b08
EIP=000167e0 EFL=000a0002 [-------]    CPL=3 II=0 A20=1
ES =4589 00045890 0000ffff 00000000
CS =8955 00089550 0000ffff 00000000
SS =2120 00021200 0000ffff 00000000
DS =fc45 000fc450 0000ffff 00000000
FS =8b04 0008b040 0000ffff 00000000
GS =00c7 00000c70 0000ffff 00000000
LDT=0000 00000000 0000ffff 00008000
TR =0000 00000000 0000ffff 00008000
GDT=     00102132 00000047
IDT=     00102180 000007ff
CR0=60000011 CR2=00000000 CR3=00000000 CR4=00000000
CCS=00000000 CCD=00000000 CCO=ADDB
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000
i think the problem is in the values poped because if the task simply resumes execution nothing happens, but if i change the esp the eip value is invalid. i tryed to find the error but i'm not used to assembly programming. i don't know what else to do or where to look.
Maslan

Re:Help: Multitasking problem.

Post by Maslan »

hi
ur tasks shouldn't return
add inifinite loop while(1) or for(;;)
orloff

Re:Help: Multitasking problem.

Post by orloff »

thanks for the hint, qemu still dumps the same message though.
Cjmovie

Re:Help: Multitasking problem.

Post by Cjmovie »

Hmm, can't see anything wrong directly, but a few possible errors/optimizations I can see....
First of all, you are initializing the stack to t[id].esp, then 'pushing' the values onto it. The way you push etc. all seem fine.

But did you take into account that you need to start at the _end_ of the data pointed at by t[id].esp? For instance, if you have 4K of memory for the stack, you'd add 4096 to it to get to the end of the memory, because the stack grows down.

Another thing, why are you messing with your IRQ common stub to do this? You should make a new function to handle task switches, you don't want to break the other IRQ's you have -> You don't want eg. your keyboard IRQ to mess with the same code as the task switcher.
That also means you should write it as one piece of code, and you won't need the two arguments (what the "add esp, 0x08" cleans up for at the end) thus you will save a few precious cycles, and it will make a lot more sense :).

Also, try simply returning the esp for task0 all the time or simply returning the current stack (eg. it never really does anything) and if it goes wacky that means you've got a stack mis-pair or something somewhere.
AR

Re:Help: Multitasking problem.

Post by AR »

A few additional fixes:

Code: Select all

void task1 () {
   print(0x04,"task1\n\0");
}

void task2 () {
   print(0x03,"task2\n\0");
}
You do not need explicit null terminator, " " will automatically null terminate the string.

Code: Select all

   add esp,0x08   ;ignore data that was pushed as parameter for    the interruption
   sti   ;enable interrupts
   iret   ;return from interruption
The STI is unnecessary, EFLAGS is automatically pushed by the CPU regardless of what privilege level it was at and will be automatically popped by IRET [Thereby restoring the IF to whatever it was before].

For the actual problem itself, I can't see what's wrong so I can only suggest you use the Bochs Debugger to find the point of failure then work backwards verifying the contents of the stack and variables as you go.
Post Reply