OSDev.org

The Place to Start for Operating System Developers
It is currently Mon Apr 29, 2024 1:16 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 13 posts ] 
Author Message
 Post subject: Having an odd problem with mulitasking
PostPosted: Sun Apr 13, 2008 1:27 am 
Offline
Member
Member

Joined: Sun Apr 29, 2007 1:13 am
Posts: 234
Multitasking in my kernel work, except that it GPFs after a while. Here's what will happen, my kernel will say
Quote:
switching from 0 to 1
switching from 1 to 2
Switching from 2 to 0
GPF

I've also had it do this
Quote:
Switching from 0 to 2
Switching from 2 to 0
Switching from 0 to 2
GPF

Here's my task creation code
Code:
void create_task(void (*task)(),char *name,unsigned char access)
{
   unsigned int *stack;
   unsigned short c;
   for(c=0;c<MAX_TASKS;c++)
   {
      if(tasks[c].status==0) break;
   }

   stack=kmalloc(4096)+4096;
   *stack--=0x202;
   *stack--=0x08;
   *stack--=(unsigned int)task;
   *stack--=0;
   *stack--=0;
   *stack--=0;
   *stack--=0;
   *stack--=0;
   *stack--=0;
   *stack--=0;
   *stack--=0;
   *stack--=0x10;
   *stack--=0x10;
   *stack--=0x10;
   *stack--=0x10;
   tasks[c].esp=(unsigned int)stack;
   tasks[c].status=TASK_ACTIVE;
   strcpy(tasks[c].name,name);
}

my task switching code
Code:
unsigned int task_switcher(unsigned int addr)
{
   unsigned short buffer=current_task;
   unsigned short c;

   buffer++;
   if(tasks[buffer].status!=0)
   {
      Printf("switching from %d to %d\n",current_task,buffer);
      if(just_init)
      {
         just_init=0;
         buffer=current_task;
         current_task++;
         return tasks[buffer].esp;
      }
      else
      {
         tasks[current_task].esp=addr;
         current_task=buffer;
         return tasks[current_task].esp;
      }
   }
   else
   {
      c=current_task;
      c++;
      while(tasks[c].status==0)
      {
         if(c>=MAX_TASKS) break;
         c++;
      }
      if(c>=MAX_TASKS) c=0;
      Printf("Switching from %d ",current_task);
      current_task=c;
      Printf("to %d\n",current_task);
      return tasks[current_task].esp;
   }   
}   

my IRQ 0 code
Code:
pusha
mov %ds, %eax
push %eax
mov %es, %eax
push %eax
mov %fs, %eax
push %eax
mov %gs, %eax
push %eax
mov %esp, %eax
push %eax
call task_switcher

mov %eax, %esp
mov $0x20, %al
out %al, $0x20
pop %eax

pop %eax
pop %eax
pop %eax
pop %eax
popa
sti
iret

the code for my tasks
Code:
void idle_task(void) //0
{
   for(;;);
}

void task1(void) //1
{
   for(;;);
}

void task2(void) //2
{
   for(;;);
}


Has anybody had a similar problem? or any ideas as to why this is happening?


Top
 Profile  
 
 Post subject:
PostPosted: Sun Apr 13, 2008 10:03 am 
Offline
Member
Member

Joined: Sat Nov 18, 2006 9:11 am
Posts: 571
Try this for your irq0

Code:
cli
pusha
push %ds
push %es
push %fs
push %gs
push %esp
call task_switcher
mov %eax, %esp
pop %gs
pop %fs
pop %es
pop %ds
mov $0x20, %al
out %al, $0x20
popa
sti
iret


This is pretty much how mine works, except that I also set ds,es,fs,gs to my kernel data section and use my kernel stack (this is for usermode- >kernel switches). Also, since when each task is FIRST run, it doesn't have a valid value to pop off the stack for the pop %esp call, so you must also update your create_task function with one more value, and since it's discarded, the value can just be 0 :). I chose to just remove the pop %eax, it makes things a bit simpler, since you already stored the ESP, and you wipe this out on the next call after the pop, there is no reason for it.

Unless I missed something, this should work alright, I rearranged some things and tried to make it simpler to read. I don't typically use AT&T syntax (read, almost never), so if there is a syntax problem, sorry :).


Top
 Profile  
 
 Post subject:
PostPosted: Sun Apr 13, 2008 1:00 pm 
Offline
Member
Member
User avatar

Joined: Sun Oct 22, 2006 7:01 am
Posts: 2646
Location: Devon, UK
Hi,

In my experience, multitasking crashing after a certain interval tends to be due to "stack creep". Ensure that when your IRQ happens, the value of ESP isn't gradually creeping down in memory due to dodgy updating of TSS or similar.

Cheers,
Adam


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 14, 2008 4:56 pm 
Offline
Member
Member

Joined: Thu Nov 09, 2006 6:23 pm
Posts: 269
Code:
mov %esp, %eax
push %eax
call task_switcher


and then

Code:
         tasks[current_task].esp=addr;


At the end of each entry into kernel mode, you should set the current kernel stack pointer to the base of the stack. What is happening is that the task starts at the original stack(lets say at 0x1000). Then the irq is called and you push values and now the stack is (0xFEE, these are just examples.) Now when the task is schedule again, the kernel stack base is 0xFEE. Then on the next task switch you again push everything and you save the current stack pointer so your stack goes down again(maybe 0xFCC). As you can see your stack gets lower and lower. So sooner or later it will reach an unmapped address or something like that.

Hope that helps.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 14, 2008 8:58 pm 
Offline
Member
Member

Joined: Sat Nov 18, 2006 9:11 am
Posts: 571
That entire task_switcher routine looks somewhat fishy though. Like, why should it being just initialized mean that you need to do somethin special? The ESP value should be correct on start, and if you pushed the registers of the current task on the current stack (whatever stack your actual kernel is using), it won't screw with any of your tasks anyways, you could easily make that much simpler to read. Disregard what the guy above said, he obviously didn't read your code or missed something sinc you are obviously poping values back off the stack after storing ESP, i have no clue where he got that from (pulled from his a$$?) Like I mentioned you have the extra pop in there, fix that and let us know, also I will post a bit later on how to fix your task_switch so it doesn't look so clustered and waste time with those checks every time after it's started.

--- Edit ---
Ok, here is how mine works (mine uses pointers so it's a bit simpler, but i'll show you how with an array like you're using).

Code:
#define MAX_TASKS 32
struct Task_S TaskList[MAX_TASKS];
long CurrentTask=0;

unsigned int task_switcher(unsigned int addr)
{
  TaskList[CurrentTask].esp = addr;
  do{
    CurrentTask= (CurrentTask+1)%MAX_TASKS; //Automagically circle
  }  while (TaskList[CurrentTask].status == 0);
  return TaskList[CurrentTask].esp;
}

void InitMulttasking(void)
{
TaskList[0].status = TASK_ACTIVE;
strcpy(TaskList[0].name,"Idle Thread");

//Add whatever other tasks you want here...
  create_task(kernel_thread,"Kernel",0);

EnableInterrupt(TimerInt); //start your timer going :P

//Now, if you don't like having an idle thread, we can kill it whenever it runs again..
TaskList[0].status = 0; //Kill me off, set me as useable!
//If you have a way to switch tasks already (or directly call the interrupt),
//you can do that here instead of this for loop :).
for (;;)
    asm("hlt"); //do nothing until we get interrupted and never called again!
}


See how much simpler your task_switch is now, and since it's called a lot, it will make a difference! Please keep us updated on how it's going if you got it working yet or not :).


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 14, 2008 10:38 pm 
Offline
Member
Member

Joined: Sun Apr 29, 2007 1:13 am
Posts: 234
There's a good reason i have the extra pop %eax. For whatever reason when task_switcher gets called. It doesn't pop the last value off the stack, and if i remove the pop eax, it corrupts the stack and causes a GPF.

--Edit--
Okay, i figured out how to create a proper bootable image using grub, so now i can run my OS in bochs :) here's what it tells me

The actual screen
Quote:
Created task PID: 0, stack: 0x001241D4
Created task PID: 1, stack: 0x001251D4
Created task PID: 2, stack: 0x001261D4
Created task PID: 3, stack: 0x001271D4
Switching from 0 To 1
Switching from 1 To 2
Switching from 2 To 3
Switching from 3 To 0
PyroS has encountered an error
General Protection Fault Exception, interrupt 13, error 512
Registers:
eax: 0x00103B6F ebx: 0x00000000 ecx: 0x00000000
edx: 0x00000000 esp: 0x001241F4 ebp: 0x00124204
esi: 0x00124210 edi: 0x00000000 eip: 0x00103CE4
gs: 0x00000010 cs: 0x00000008 ds: 0x00000010
es: 0x00000010 fs: 0x00000010
eflags: 0x00010206
Task that caused this exception
PID: 0
Name: idle task
Access: Kernel

what the log says
Quote:
00246160795e[CPU0 ] fetch_raw_descriptor: GDT: index (207)40 > limit (17)
00246187558i[CPU0 ] WARNING: HLT instruction with IF=0!
13572406000p[WGUI ] >>PANIC<< POWER button turned off.
13572406000i[CPU0 ] CPU is in protected mode (halted)
13572406000i[CPU0 ] CS.d_b = 32 bit
13572406000i[CPU0 ] SS.d_b = 32 bit
13572406000i[CPU0 ] EFER = 0x00000000
13572406000i[CPU0 ] | RAX=0000000000000000 RBX=0000000000109640
13572406000i[CPU0 ] | RCX=0000000000000000 RDX=00000000000b076c
13572406000i[CPU0 ] | RSP=0000000000124190 RBP=00000000001241b8
13572406000i[CPU0 ] | RSI=0000000000124210 RDI=0000000000000000
13572406000i[CPU0 ] | R8=0000000000000000 R9=0000000000000000
13572406000i[CPU0 ] | R10=0000000000000000 R11=0000000000000000
13572406000i[CPU0 ] | R12=0000000000000000 R13=0000000000000000
13572406000i[CPU0 ] | R14=0000000000000000 R15=0000000000000000
13572406000i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf ZF af PF cf
13572406000i[CPU0 ] | SEG selector base limit G D
13572406000i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
13572406000i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 000fffff 1 1
13572406000i[CPU0 ] | DS:0010( 0002| 0| 0) 00000000 000fffff 1 1
13572406000i[CPU0 ] | SS:0010( 0002| 0| 0) 00000000 000fffff 1 1
13572406000i[CPU0 ] | ES:0010( 0002| 0| 0) 00000000 000fffff 1 1
13572406000i[CPU0 ] | FS:0010( 0002| 0| 0) 00000000 000fffff 1 1
13572406000i[CPU0 ] | GS:0010( 0002| 0| 0) 00000000 000fffff 1 1
13572406000i[CPU0 ] | MSR_FS_BASE:0000000000000000
13572406000i[CPU0 ] | MSR_GS_BASE:0000000000000000
13572406000i[CPU0 ] | RIP=0000000000102afb (0000000000102afb)
13572406000i[CPU0 ] | CR0=0x00000011 CR1=0x0 CR2=0x0000000000000000
13572406000i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
13572406000i[CPU0 ] >> add esp, 0x00000024 : 83C424


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 15, 2008 12:01 pm 
Offline
Member
Member

Joined: Sat Nov 18, 2006 9:11 am
Posts: 571
Well, then something is wrong somewhere, here is a walkthrough of your code:

esp = 0x1000 (for simplicity)

pusha ;Pushes 8 dwords, 32 bytes
;esp = 0xFE0
mov %ds, %eax
push %eax ;4 more bytes
;esp = 0xFDC
mov %es, %eax
push %eax ;4 more bytes
;esp = 0xFD8
mov %fs, %eax
push %eax ; and 4 more
;esp = 0xFD4
mov %gs, %eax
push %eax ;and 4 more...
;esp = 0xFD0

mov %esp, %eax ; Here we are pushing ESP, so EAX = 0xFD0
push %eax
;esp = 0xFCC (but remember, we pushed FD0, not FCC!)

call task_switcher ;Returns the STORED ESP value

mov %eax, %esp ;restore ESP to 0xFD0

mov $0x20, %al ;eoi for pic
out %al, $0x20
pop %eax
;esp = 0xFD4

pop %eax
;esp = 0xFD8
pop %eax
;esp = 0xFDC
pop %eax
;esp = 0xFE0
pop %eax
;esp = 0xFE4
popa
;esp = 0x1004 ;Uh-oh, we blew past our stack start, this is a problem!
;Crap, now if we iret, we will jump to the incorrect EIP, and GPF occurs


Please tell me what i'm missing, because you have more pop's than pushes as demonstrated, it will destroy your stack, and the wrong values are ending up in the wrong registers. if you remove that pop, and it's still crashing, then start looking in your other functions and let us know. This is how the stack works, you cannot fool the stack, if you aren't pushing the correct # of values to the stack on task creation (i push more than you do, so possible problem), then it will work on the first run through (poping a bogus value from the stack for it to work and put the EIP in the correct location for the first iret, then never work again when the task is called, which looks to be your problem). Every time you switch BACK to a task, it will crash, if you had 15 tasks, each one would run ONCE then GPF when the first task is called again. So, again, please remove that pop and let me know what happens, and we can try to find the ACTUAL problem. I know how a stack works, i'm not trying to point you in a wrong direction i promise. Also, for me, I had to put an additional *stack--=0x10 to compensate for the pushed SS value.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 15, 2008 12:27 pm 
Offline
Member
Member

Joined: Sun Apr 29, 2007 1:13 am
Posts: 234
Quote:
please remove that pop and let me know what happens,

This is what happens when i remove the pop
It doesn't even go through the list once

Quote:
00035167062p[CPU0 ] >>PANIC<< fetch_raw_descriptor: LDTR.valid=0
00035167062i[CPU0 ] CPU is in protected mode (active)
00035167062i[CPU0 ] CS.d_b = 32 bit
00035167062i[CPU0 ] SS.d_b = 32 bit
00035167062i[CPU0 ] EFER = 0x00000000
00035167062i[CPU0 ] | RAX=0000000000000000 RBX=0000000000000000
00035167062i[CPU0 ] | RCX=0000000000000000 RDX=0000000000000000
00035167062i[CPU0 ] | RSP=0000000000124204 RBP=0000000000000000
00035167062i[CPU0 ] | RSI=0000000000000000 RDI=0000000000000010
00035167062i[CPU0 ] | R8=0000000000000000 R9=0000000000000000
00035167062i[CPU0 ] | R10=0000000000000000 R11=0000000000000000
00035167062i[CPU0 ] | R12=0000000000000000 R13=0000000000000000
00035167062i[CPU0 ] | R14=0000000000000000 R15=0000000000000000
00035167062i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df IF tf sf zf af pf cf
00035167062i[CPU0 ] | SEG selector base limit G D
00035167062i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00035167062i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 000fffff 1 1
00035167062i[CPU0 ] | DS:0010( 0002| 0| 0) 00000000 000fffff 1 1
00035167062i[CPU0 ] | SS:0010( 0002| 0| 0) 00000000 000fffff 1 1
00035167062i[CPU0 ] | ES:0010( 0002| 0| 0) 00000000 000fffff 1 1
00035167062i[CPU0 ] | FS:0010( 0002| 0| 0) 00000000 000fffff 1 1
00035167062i[CPU0 ] | GS:0010( 0002| 0| 0) 00000000 000fffff 1 1
00035167062i[CPU0 ] | MSR_FS_BASE:0000000000000000
00035167062i[CPU0 ] | MSR_GS_BASE:0000000000000000
00035167062i[CPU0 ] | RIP=0000000000103ce2 (0000000000103ce1)
00035167062i[CPU0 ] | CR0=0x00000011 CR1=0x0 CR2=0x0000000000000000
00035167062i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00035167062i[CPU0 ] >> iretd : CF


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 15, 2008 12:45 pm 
Offline
Member
Member

Joined: Sat Nov 18, 2006 9:11 am
Posts: 571
Ok, so now that you are there, try a few extra things... firstly, try putting known values into eax, ebx, ecx, etc in your create task function, then before your interrupt can return (right before the sti I would) hlt the cpu or put an infinite loop, then exit out and check if the correct values are put into the correct registers. If they differ, then you can see which values ended up where and have an idea on what is going on. Once you figure this out, you can proceed to the next trouble shooting step. this is why bochs is great, it allows you to stop the CPU and see what is going on inside at any point. I would use something closer to how my code is (actually push/pop es,ds,gs, and fs) that way, if you ever want to do other code/dats segments, you are already popping the correct values into their perspective registers (rather than just incrementing esp by poping the values into EAX). This will also help determine if they are popping the correct values as you are expecting. also, in your create task function, trying pushing another value for SS at the start (as the first value put on the stack).


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 15, 2008 1:09 pm 
Offline
Member
Member

Joined: Sun Apr 29, 2007 1:13 am
Posts: 234
Getting closer to having this working :)
The value that's in ebp should be in ecx
which means the stack is off my 16 bytes :?
Quote:
Created task PID: 0, stack: 0x001241D4
Created task PID: 1, stack: 0x001251D4
Created task PID: 2, stack: 0x001261D4
Created task PID: 3, stack: 0x001271D4
PyroS has encountered an error
General Protection Fault Exception, interrupt 13, error 15212
Registers:
eax: 0x00000000 ebx: 0x00000000 ecx: 0x00000000
edx: 0x00000000 esp: 0x001241F0 ebp: 0x1BADB00B
esi: 0x00000000 edi: 0x00000010 eip: 0x00103CE1
gs: 0x00000010 cs: 0x00000008 ds: 0x00000010
es: 0x00000010 fs: 0x00000010
eflags: 0x00010202
Task that caused this exception
PID: 0
Name: idle task
Access: Kernel


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 15, 2008 7:09 pm 
Offline
Member
Member

Joined: Sat Nov 18, 2006 9:11 am
Posts: 571
So, this is how bug tracing typically goes with Bochs. So, you see your stack is off by 16-bytes, which is 4 dwords. So, how are you first starting your task switching? Are you just letting the interrupt fire? calling a function or sorts, etc? Any clue where those 16 bytes could come from? Also, are you sure that you are reading them properly, remember, that the stack starts at the top, and moves downwards, so it should push in this order:

ss
eflags
cs
eip
eax
ecx
edx
ebx
esp
ebp
esi
edi
ds
es
fs
gs

Now, also, look at your ESP value... in your print out, it says the stack is at 0x001241D4... then in your printout, it shows it at 0x001241F0... what value are you printing (before the stack is moved or after?) These things can give you hints on the problem. You appear to be pushing the correct # of things onto the stack (although I would add one more *stack-- = 0x10 at the beginning to reserve it for ss). Now, one BIG thing that I did notice, is that you're post decrementing..., you need to pre-decrement...
*--stack = xxx;. The reason for this is, the stack grows downwards right.. lets assume 0x1000 for ease of use again:

esp = 0x1000
*stack-- = 0x10;

Lets break this down:
*stack = 0x10;
stack--;

So, you are setting 0x1000 = 0x10, when indeed you are trying to set 0xFFC = 0x10; So you must predecrement:

*--stack = 0x10, etc, etc. This was your reason for off by one, hence why you had to add the extra pop in there to get it to even fire each task once (which doesn't work once the tasks is reentered due to the off by one problem). So, I hope this concludes this lessons learned, and you should have a functioning multi-tasker once you fix the post/pre decrement, and have that extra pop instruction removed. I hope you learned a bit about bug-hunting and can learn from this and move forward. If you're still having issues, post your updated code so I can see what you're currently working with and can assist further. Like I said, I wasn't trying to lead you astray telling you you had an extra pop in there, it WAS a problem masking the real problem (post decrement rather than pre), so putting it back allowed us to find the actual problem.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 15, 2008 9:28 pm 
Offline
Member
Member

Joined: Sun Apr 29, 2007 1:13 am
Posts: 234
I guess that pre-decrementing was the answer. It works perfectly now. Thanks for all your help.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 15, 2008 10:33 pm 
Offline
Member
Member

Joined: Sat Nov 18, 2006 9:11 am
Posts: 571
Glad it's working, hope you got some stuff out of troubleshooting a bit. Now if I could only figure out the bugs in my own kernel i'd be set :). Anyways, let us know if you run into more issues and don't be affraid to put endless loops, etc to check outputs in bochs to see what is going on with the registers, and think it through logically (like I did when I described what was happening with ESP when it is calling your function to prove why you didn't want that extra pop in there, to figure out the problem was else where).


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 13 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: No registered users and 14 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group