Unable to call a C-Function after task switch

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
eltomato
Posts: 7
Joined: Wed Oct 05, 2011 9:49 am
Location: Bonn, Germany

Unable to call a C-Function after task switch

Post 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?
User avatar
NickJohnson
Member
Member
Posts: 1249
Joined: Tue Mar 24, 2009 8:11 pm
Location: Sunnyvale, California

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

Post 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.
eltomato
Posts: 7
Joined: Wed Oct 05, 2011 9:49 am
Location: Bonn, Germany

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

Post 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 :(
User avatar
thepowersgang
Member
Member
Posts: 734
Joined: Tue Dec 25, 2007 6:03 am
Libera.chat IRC: thePowersGang
Location: Perth, Western Australia
Contact:

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

Post 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.
Kernel Development, It's the brain surgery of programming.
Acess2 OS (c) | Tifflin OS (rust) | mrustc - Rust compiler
Currently Working on: mrustc
eltomato
Posts: 7
Joined: Wed Oct 05, 2011 9:49 am
Location: Bonn, Germany

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

Post 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
User avatar
thepowersgang
Member
Member
Posts: 734
Joined: Tue Dec 25, 2007 6:03 am
Libera.chat IRC: thePowersGang
Location: Perth, Western Australia
Contact:

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

Post 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.
Kernel Development, It's the brain surgery of programming.
Acess2 OS (c) | Tifflin OS (rust) | mrustc - Rust compiler
Currently Working on: mrustc
eltomato
Posts: 7
Joined: Wed Oct 05, 2011 9:49 am
Location: Bonn, Germany

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

Post 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 :/
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

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

Post 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.
Every good solution is obvious once you've found it.
eltomato
Posts: 7
Joined: Wed Oct 05, 2011 9:49 am
Location: Bonn, Germany

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

Post 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 :)
Post Reply