Page 1 of 1

Co-operative Multitasking?

Posted: Sat Feb 10, 2007 9:36 pm
by pcmattman
I'm working on a co-operative multitasking system for my kernel. My ASM kernel was able to do this without any problems. My C kernel just won't work! My system basically has a function called 'NextProc' (NASM code below) that gets the return address, pushes it onto the stack as an argument for a C function, then calls it. Problem is, it doesn't work.

Edit: I can get it to work now, but the only way is basically to call each procedure from the start - not a true multitasking system. Does anyone know how I can get the IP of the function that was running before it let the next one run (NextProc()) and use that in resched?

Code: Select all

_NextProc:

	; get the address
	push ebp
	mov ebp,esp

	mov eax,[ebp]

	pop ebp

	push eax
	call _resched

	ret
resched:

Code: Select all

// reschedules
void resched( unsigned int ip )
{
	// put the data into the process list so that we can return to it again
	ptable[currpid++].base = ip;

	// check for validity
	if( ptable[currpid].state == PFREE )
	{
		// back -> 0
		currpid = 0;
	}

	// run the code (this is not really what you would call a multitasking system)
	MTProc* p = (MTProc*) ptable[currpid].base;
	p();
}
ctxsw code:

Code: Select all

_ctxsw:

	push ebp
	mov ebp,esp

	; get the address
	mov eax,[ebp+8]

	; IMPORTANT: because if you don't do this
	; some serious errors will come up
	pop ebp

	; performs the context switch to the address given
	; in the first argument

	; eax holds the address - return to the next process
	push eax
	retn
MTProc is defined as:

Code: Select all

typedef void (MTProc) ();
Any help would be really nice!

Posted: Sun Feb 11, 2007 3:11 am
by Otter
mmh you use a i386+ so why don't you use the hardware mt support ? Create a tss for each process and the processor does the rest for you. It's much easier and faster. If you setup your tss correctly, you simply need a far jmp to the tss you want to activate and that's all. If you want to manipulate single registers ( for example to emit a return code to the scheduler or something like that ) you can easily access them via the tss.

Posted: Sun Feb 11, 2007 3:34 am
by pcmattman
How do I do that? Could you possibly post a tutorial or link?

Posted: Sun Feb 11, 2007 6:54 am
by Otter
I have no tutorial for that but I could give you a headline:
You should search for the tss structure, there a billions on the internet.

A TSS is a structure ( at least 104 bytes in size ) which contains the state of the processor. If you switch a task, the processor will write everything in the current TSS. Every tss has a descriptor in the gdt. To activate a tss, there are several ways. You want cooperative mt, so the simplest you can use is a far call to the selector of the tss you use. So lets say your tss-selector is 0x30 ( 7th entry in gdt), you need a "jmp far 0x30:0" to activate it.

If you do so, the processor stores everything ( the registers, cr3 if you use paging and so on ) in the actual tss ( the processor register TR contains the selector of the current active tss ) and loads everything from the new tss. After that, it changes the TR register to the new value. The offset of your far jmp is ignored, the eip is encoded in the tss.

So, if you start, you have to do:
- Create at least two tss: one for the actual active kernel process, you do not need to fill in any data. Just allocate memory for it ( 104 bytes for beginning ) and create a descriptor in gdt which refers to it. Than, you need to create one for the process you want to activate. You have to fill all necessary data, at least cs,ds,esp and eip.
- Load the kernel process tss selctor, the one without data, with the LTR instruction. That means nothing more than the processor tr register is set to a new value. This is necessary, because if you want a task switch the processor needs to know where to store the actual processor state.
- Now you can do a far jmp to the selector of the process tss. The processor fills in the data for the kernel process tss.

If you want to return to your kernel you simply need a far jmp to your kernel tss. The processor does the rest ... and so on

Hope this helps

[edit]
Another thing:

Code: Select all

   push ebp
   mov ebp,esp

   mov eax,[ebp]

   pop ebp
I really think that is not want you want it to be. If I reformulate it, it becomes:

Code: Select all

mov eax,ebp
That's all ;) If you push ebp, ebp is in [esp]. 'Cause you mov esp to ebp, it's also in [ebp], so "mov eax,[ebp]" is nothing else than that you mov the old ebp register to eax. I think you want simply to get the return adress, so you could use:

Code: Select all

getEIP:
    mov eax,[esp]
    ret
[/edit]

Posted: Sun Feb 11, 2007 2:13 pm
by Combuster
Otter wrote:mmh you use a i386+ so why don't you use the hardware mt support ? Create a tss for each process and the processor does the rest for you. It's much easier and faster.
Errr, software task switching is faster than hardware task switching.

Posted: Mon Feb 12, 2007 1:31 am
by pcmattman
I think he means faster as in 'faster to code'... Thanks for all the input guys, there's just one problem... how do I implement this? I understand it, I just don't know how to code it. I would prefer to use software task switching, but if needs be I can use hardware.

Posted: Mon Feb 12, 2007 1:44 am
by Solar
You do have a copy of the Intel manuals, do you?

Posted: Mon Feb 12, 2007 2:20 am
by pcmattman
Yes I do, I'm just looking for an easy way out :D . I guess I'll just have to do it myself then...