Help: Multitasking problem.
Posted: Tue Nov 08, 2005 6:26 pm
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:
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:
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:
and finally the qemu dump:
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.
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");
}
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
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
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