Multitasking Woes

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
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Multitasking Woes

Post by AJ »

Hello,

After setting up a basic MMU I am happy with for the present, I have started trying to implement software task switching. Nothing complex - just trying to run a kernel-level task with the same segment selectors as the kernel itself. The code for setting up the new task is:

Code: Select all

void task_add(void *IP)
{
	//fill a new task structure
   	unsigned long *s = kmalloc(0x1000);
   	s+=0x3FF;

   	s--;			//(unnecessary?)buffer
   	*s--=0x0202;		//eflags
   	*s--=0x08;		//cs
   	*s--=(size_t)IP;	//task entry point
   	*s--=0x10;		//ds
   	*s--=0x10;		//es
   	*s--=0x10;		//fs
   	*s--=0x10;		//gs
   	s-=6;			//eax, ebx, ecx, edx, esi, edi, ebp

	ltasks++;
   	stacks[ltasks].ss = 0x10;
   	stacks[ltasks].esp = (unsigned long)s;
}
I don't think my asm task switching code is relevant for this query. The problem is, when I try to switch to this task, *all* registers are read of this stack as zero. If I printf() the value for s[12], I would expect to see IP, but all I get is 0x0. CS also seems to be 0x0.

Needless to say, I get an exception when I try doing an iret.

This is getting really annoying as I'm sure I am putting the values on the stack but must have missed a really obvious error. Can anyone help please?

Cheers,
Adam
User avatar
kataklinger
Member
Member
Posts: 381
Joined: Fri Nov 04, 2005 12:00 am
Location: Serbia

Post by kataklinger »

First of all your stack is not aligned to 4-bytes boundery, you should do that to speed up you code.

Second - what is stacks array?

Third - it would be easier for us if we can see your task switching code!
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

OK - sorry :oops:

I have made the change to 4-byte align the stack as suggested. The switching code is (adapted) from a tutorial found on bonafide, I believe. 'Stacks' simply refers to a structure containing esp and ss for all the task stacks (just 2 records long at the moment - one for the kernel at index 0 and one for the task at index 1). Here's the switching code:

Code: Select all

void schedule()
{
	//very simple no-priority round-robin 'scheduler'
	next_task = current_task+1;
	if(next_task>ltasks) next_task=0;
}
and the asm part (handles int 0x30):

Code: Select all

[extern _current_task]
[extern _next_task]
[extern _stacks]
[global _tswitch]
_tswitch:				;change system state (task switch) int 0x30 (48)
        PUSH    DS
        PUSH    ES
        PUSH    FS
        PUSH    GS
        PUSH    EAX
        PUSH    EBX
        PUSH    ECX
        PUSH    EDX
        PUSH    ESI
        PUSH    EDI
        PUSH    EBP                     ; SAVE TASK REGISTERS ON THE STACK

        MOV     EAX, 10h                 ; SYSTEM DATA SEGMENT SELECTOR
        MOV     DS, EAX
        MOV     ES, EAX

	MOV	EBX,	[_current_task]
	SHL	EBX,	3		;*8
	LEA	ESI,	[_stacks]
	MOV	[DS:EBX+ESI],	ESP
	MOV	[DS:EBX+ESI+4],	SS

	MOV	EBX,	[_next_task]
	MOV	[_current_task],	EBX
	SHL	EBX,	3		;*8

	MOV	ESP,	[DS:EBX+ESI]
	MOV	SS,	[DS:EBX+ESI+4]

        POP     EBP
        POP     EDI
        POP     ESI
        POP     EDX
        POP     ECX
        POP     EBX
        POP     EAX
        POP     GS
        POP     FS
        POP     ES
        POP     DS                      ; RESTORE TASK REGISTERS FROM THE STACK
iretd
So, the idea is that the ss and esp offset in stacks is calculated from _current_task and _next_task.

If I call int 0x30 prior to adding a task to the stack list, it's fine - stack[0] holds the correct values for the kernel.

If I attempt to call int 0x30 having added my own task to the stack list (via the previously posted code), I get a fault as CS, IP and all registers and selectors are set to zero - it's like I set up a blank stack for the new task.

I have checked in bochs, and the value of ESP for the new task is exactly what I would expect - the top of the memory allocated for the stack minus the stack size.

Sorry I didn't include all this detail in my first post - I obviously over-did the brevity! I was also quite embarrased about posting my code, as being a self-taught amateur no-one has ever critiqued it before! :oops:

Adam

[EDIT: Amended error in source code and removed db 66h's before pushing selectors. It was pointed out that all pushed selectors are 32 bit anyway in pmode...]
User avatar
kataklinger
Member
Member
Posts: 381
Joined: Fri Nov 04, 2005 12:00 am
Location: Serbia

Post by kataklinger »

AJ wrote:

Code: Select all

MOV	SS,	[DS:ESI+ESI+4]
Did you mean:

Code: Select all

MOV	SS,	[DS:EBX+ESI+4]
And you should remove 66h prefixes!
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Edited - thank you. I'll have a look but I think the example I copied from was also 32 bit so don't know why the db 66h's were in the original...

Unfortunately, the stack is still empty. When I try printing the value of s[12] and s[13] to the screen, I am getting 0x00 instead of the expected 0x08 and eip. I know my print functions work...
0Scoder
Member
Member
Posts: 53
Joined: Sat Nov 11, 2006 8:02 am

Post by 0Scoder »

AJ wrote:Edited - thank you. I'll have a look but I think the example I copied from was also 32 bit so don't know why the db 66h's were in the original...

Unfortunately, the stack is still empty. When I try printing the value of s[12] and s[13] to the screen, I am getting 0x00 instead of the expected 0x08 and eip. I know my print functions work...
How are you testing your OS? If you have bochs working nicely, it would be a help if you used the debugger to check how the area (hopefully) filled by the add_task() function looks right after the code that fills it. If the worst comes to the worst, you can allways do what I did and write an asm routine that fills it using actual 'push' instructions.

It looked like this:

Code: Select all

_fill_stack:
;load the stack
 mov edx, esp
 mov eax, [edx+4]	;stack to fill
 mov ebx, [edx+8]	;eip value to be loaded in
 mov ecx, esp		;save stack to be later re-loaded
 mov esp, eax		;load the new stack

;fill it with values
 mov eax, 0x202		;EFLAGS
 push eax
 mov eax, 0x08		;CS
 push eax
 push ebx		;EIP

 pusha

;get the old stack back
 mov esp, ecx
ret
I remember having problems with a certain tutorial in the stack filling department...
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Thanks for the reply,

Yes, I have bochs and also have my own basic debug routines which output to COM1 - they send a register and segment dump and a virtual memory map to the com port every time an exception occurs.

My first thoughts were that this must be something stupid in my C coding, because to me the code looks fine, but sometimes others see problems where I wouldn't!

If I go in to the assembly routine and physically move the address of the task I wish to run in to eip (via a push followed by an iret), the task switches over without any problems, but i should be able to do this via the c routine. If not, maybe something more fundamental is wrong.

It just doesn't make any sense to me as I *know* I have filled the new task's stack, and yet all segments and registers are set to zero as if my stack never existed.

Cheers,
Adam
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

OK - I've kind of fixed this, but it seems a bit of a botched job. I have some major sorting out to do :(

It seemed to be my maths - relocating that pointer. If I simply place all the values at the location kmalloc() returned, it seems to work. This is odd as kmalloc does not (yet) necessarily return a 4-byte aligned value.

Of course, as soon as the new process uses the stack it overflows, but I am in the process of sorting this now. In a way, I am quite happy that I have some form of multitasking working, even though it's just a quick fix at the moment :roll:

Adam
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

It may be simple, it may be slow, but I'm multitasking wooohoooo!

Thanks to everyone who replied! :D
Midas
Member
Member
Posts: 140
Joined: Sat Jun 24, 2006 4:40 pm
Location: Falkirk, Scotland
Contact:

Post by Midas »

AJ wrote:It seemed to be my maths - relocating that pointer. If I simply place all the values at the location kmalloc() returned, it seems to work. This is odd as kmalloc does not (yet) necessarily return a 4-byte aligned value.
<disclaimer>I haven't read the rest of this thread, and therefore may be way off bat.</disclaimer>

The return value of a function will be 32 bit, as the value is returned in a register (EAX in this case, I think) according to the C calling conventions. Also, other data stored on the stack will be padded to 4 bytes-aligned by your compiler as if the stack is not 4 bytes aligned, you get the major (~5 microseconds, OTOH, but I can't remember where I got that figure) performance hit of another IO cycle.
Regards,
Angus [Óengus] 'Midas' Lepper
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Thanks for the suggestion,

The main thing this thread has helped me to see is that what I thought was a relatively robust memory management system is useless. Although I have got masic multitasking working, I think the main problems were in my MMU which I am just starting to re-write from the ground up :cry: .

Oh well - noone said that os dev would be quick :roll:

Cheers,
Adam
Post Reply