Page 1 of 1

Unable to call a C-Function after task switch

Posted: Wed Oct 05, 2011 4:16 pm
by eltomato
Hi,

I just implemented Multitasking in my os and it works .. But there is a big problem left ..

I can't use any C-Functions .. If I do that, it crashs with a general protection fault :/
inline asm works fine .. now I have a operating system, which beeps all the time (with outb) but
can't do anything else

Any Idea?

Re: Unable to call a C-Function after task switch

Posted: Wed Oct 05, 2011 4:38 pm
by NickJohnson
Is your stack in a reasonable state after the task switch? Inline assembly doesn't always use the stack, but C functions do.

Re: Unable to call a C-Function after task switch

Posted: Wed Oct 05, 2011 4:49 pm
by eltomato
hmm.. it's working in QEmu .. But VMWare 8 shows a panic :/

My Task Switch looks like this:

Code: Select all

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
	
    push esp

	call _irq_handler
	mov esp, eax
	
    pop gs
    pop fs
    pop es
    pop ds
    popa
    add esp, 8
    iret
this returns the cpu registers of the next task ..

Code: Select all

struct regs* irq_handler(struct regs *r)
{
    struct regs* new_cpu = r;
    /* ... */
    if (r->int_no == 0x20) {
        new_cpu = schedule(r);
    }

    /*... */
    return new_cpu;	
} 
And struct regs looks like this:

Code: Select all

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;    
};
Any Idea?

Edit:
I have printed the registers before and after the switch ..
Image

As I am a beginner, here is what i have noticed.
Isn't fs a 16 bit register? why is fs so high in vmware? And why does it work fine in Qemu?

What could be the problem?

btw .. it doesn't work in vmware neither on a real pc :(

Re: Unable to call a C-Function after task switch

Posted: Wed Oct 05, 2011 9:37 pm
by thepowersgang
As I noted in your other thread, you didn't quite get the idea of cdecl. Parameters are passed on the stack in reverse order (going upwards - return address, param1, param2)
Parameters also need to be removed by the caller, not the callee. You don't do this after calling the irq handler, so the your state restore fails.

Re: Unable to call a C-Function after task switch

Posted: Wed Oct 05, 2011 9:54 pm
by eltomato
hmm .. ok .. I'm still learning ;)

Parameters are passed on the stack in reverse order (going upwards - return address, param1, param2)
Am I right that I forgot to pop the esp again?

correct code would be:

Code: Select all

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
   
    push esp

   call _irq_handler
   pop esp
   mov esp, eax
   
    pop gs
    pop fs
    pop es
    pop ds
    popa
    add esp, 8
    iret
this would fix the first problem, right?

2.
Parameters also need to be removed by the caller, not the callee. You don't do this after calling the irq handler, so the your state restore fails.
Hmm.. so I need to pop the stack items pushed by the c-function? (I mean the return struct)
for

Code: Select all

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;    
};
i need to pop 19 times, right?

Could you tell me if I'm getting closer or completely wrong? :D

thx :P

Re: Unable to call a C-Function after task switch

Posted: Wed Oct 05, 2011 11:56 pm
by thepowersgang
Your first part was right (but only on a technicality, because doing "pop esp" is in most cases a bad idea) However, you do not need to care about what the C function does with the stack, only that it will return to the stack pointer to the same value as when it was called.

Re: Unable to call a C-Function after task switch

Posted: Thu Oct 06, 2011 7:31 am
by eltomato
OK, what should I pop in then? ebx?

What I don't really understand is, what happens to the stack pointer?
What do I have to do with it after the call ?

source would look like this atm:

Code: Select all

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
   
    push esp

   call _irq_handler
   pop ebx
   mov esp, eax
   
    pop gs
    pop fs
    pop es
    pop ds
    popa
    add esp, 8
    iret
hmpf :/

Edit:
OK, i found this: http://www.cs.umbc.edu/~chang/cs313.s02/stack.shtml
So, do I need to pop the struct from the esp by doing "add esp, [num of bytes]"?
With the struct of 19 ints I would do a "add esp, 76" ?

I'm a little clueless right now :/

Re: Unable to call a C-Function after task switch

Posted: Thu Oct 06, 2011 7:55 am
by Solar
You don't have to pop the values from stack individually because you no longer need them. You simply reset the stack pointer to the value it had before you started pushing values to it for the function call.

Re: Unable to call a C-Function after task switch

Posted: Thu Oct 06, 2011 8:23 am
by eltomato
Hell yeah, it works :D
I just forgot to init variables in the struct #-o

Thx guys for helping me ;)

If somebody is interested in my solution, here are some pieces ..

irq stub inside my asm file:

Code: Select all

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
	
    push esp
    call _irq_handler
    mov esp, eax
	
    pop gs
    pop fs
    pop es
    pop ds
    popa
    add esp, 8
	
    iret
The timer handler (fired by pic) calls a simple scheduling function

Code: Select all

struct regs* irq_handler(struct regs *r) {
    /* ... */
    struct regs* new_cpu = r;
    new_cpu = schedule(r);
    /* ... */
    return new_cpu;
}
and returns the registers of the next task (in a struct):

The Struct looks like this:

Code: Select all

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;    
};
thx anyone :)