Page 1 of 1
Multitasking Woes
Posted: Fri Nov 17, 2006 5:26 am
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
Posted: Fri Nov 17, 2006 7:30 am
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!
Posted: Fri Nov 17, 2006 7:53 am
by AJ
OK - sorry
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!
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...]
Posted: Fri Nov 17, 2006 9:49 am
by kataklinger
Did you mean:
And you should remove 66h prefixes!
Posted: Fri Nov 17, 2006 9:56 am
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...
Posted: Sun Nov 19, 2006 10:15 am
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...
Posted: Sun Nov 19, 2006 10:22 am
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
Posted: Mon Nov 20, 2006 4:28 am
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
Adam
Posted: Mon Nov 20, 2006 8:57 am
by AJ
It may be simple, it may be slow, but I'm multitasking wooohoooo!
Thanks to everyone who replied!
Posted: Mon Nov 20, 2006 9:49 am
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.
Posted: Tue Nov 21, 2006 6:34 am
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
.
Oh well - noone said that os dev would be quick
Cheers,
Adam