Hardware Task swiching

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.
amirsadig

Hardware Task swiching

Post by amirsadig »

I am trying now to complate my task switching. with one task I have no problem ;D, but when I added task 2 and when the scheduler try to switch. then the cpu generate Page Fault execption. the eip register point realy to outside my memory.

here is my tss structure:

struct tss_struct {
   unsigned short   back_link,__blh;
   unsigned long   esp0;
   unsigned short   ss0,__ss0h;
   unsigned long   esp1;
   unsigned short   ss1,__ss1h;
   unsigned long   esp2;
   unsigned short   ss2,__ss2h;
   unsigned long   cr3;
   unsigned long   eip;
   unsigned long   eflags;
   unsigned long   eax,ecx,edx,ebx;
   unsigned long   esp;
   unsigned long   ebp;
   unsigned long   esi;
   unsigned long   edi;
   unsigned short   es, __esh;
   unsigned short   cs, __csh;
   unsigned short   ss, __ssh;
   unsigned short   ds, __dsh;
   unsigned short   fs, __fsh;
   unsigned short   gs, __gsh;
   unsigned short   ldt, __ldth;
   unsigned short   trace, bitmap;
   unsigned long   io_bitmap[IO_BITMAP_SIZE+1];
   unsigned long   tr;
   unsigned long   cr2, trap_no, error_code;
//union i387_union i387;
};

when a task initialized all this value and also tr (Task register) has been initialized correctly.
here my jump to task function:
void jump_to_task()
{
__asm__(      
    " ljmp %0 \n"
    ::"m"(*(((char *)?t_task->tss.tr)-4))
    );
}

I think I have take this code from old linux (1.0).

could someone help me?

in the last year (as I remember) my task switching has worked. now I don't if I have done something wrong with it or not. note that I have not worked on my OS for along time.

a nother question:
is it make a difference when using gcc 2.95.x or gcc 3.3.x?
quaak

Re:Hardware Task swiching

Post by quaak »

Mmmh, why you are using hardware task switching?
I think software task switching with one per system TSS for all
task is more flexible, allows more tasks and should be equal in speed
or faster.
Task switching is simply done by switching kernel stacks.
You need only a 1-2 extra TSS for some special exception handlers
like double fault and so on.
amirsadig

Re:Hardware Task swiching

Post by amirsadig »

I have tried setjmp and longjmp but this has not worked for me. another reason I want to implement the two way (for learning by doing what is better).
I you have a suggestion about how to port my existing hardware switching. look to my tss_struct.
amirsadig

Re:Hardware Task swiching

Post by amirsadig »

is there no help :'( !!
I have tried the whole time (2 days) to solve this problem, so that I can begin realy with multitasking.

what is the correct way to let CPU switch to TSS?. as you see in my code (over here) I save the TSS in tss_struct and I give every Task a (Task describtor) in gdt.

I read the values of the register when I receive the GPF and I see that EIP register point to the ljmp instruction.

I wait for suggestion.
jeremyi

Re:Hardware Task swiching

Post by jeremyi »

so when one task gives birth to another, dies, leaving its offspring behind to continue, you have no problem ?

the scheduler could try switching tasks, even with only one present, copying all the registers, for debugging purposes

when you copy things, they usually don't overflow, so the value of eip might be an old one, not relevant to the present

how many times does eip change its value during the switch ?
Ozguxxx

Re:Hardware Task swiching

Post by Ozguxxx »

why dont you show some bochs output when crashing occurs? I mean the core dump part.
amirsadig

Re:Hardware Task swiching

Post by amirsadig »

here is a snapshot from bochs:

initialze FDC
NEC765 controller found
FAT Type is 12
blocksize 2: 512
blocksize : 512 512
root dir : \
init Super Block
found fat filesystem, root dir : \
* Type some text
* Press F1, F2, etc. to change virtual console
* Press Ctrl+Alt+Del to reboot
Task 0 initialized
Task EIP : 1070224
Task 1 initialized
Task EIP : 1070288

next eip: 1070288
Exception #14 (page fault)
EDI=0010B80E ESI=00102722 EBP=0010CD20 ESP=0010CCFC
EBX=00019F38 EDX=000000A0 ECX=001091C5 EAX=10008018
DS=00000010 ES=00000010 FS=00000010 GS=00000010
int=0000000E err=00000002 EIP=001038D8 CS=00000018
uSP=0010CD40 uSS=001037CF
CR2 = 0x0 : CR3: 0x200000 , CR4: 0x0
Goodbye (system halted, use reset button to end)

---
the real EIP of my two task function (kernel function is). this from symbol file.
00105490 T init (Task 0)
001054d0 T task2 (Task 1)

here is a part of my code:
_task->tss.eip = (unsigned long) init;
if have also tried this
_task->tss.eip = (unsigned long) &init;

I think here is the problem:
_stack = (unsigned long *)kmalloc(STACK_SIZE);

_task->tss.esp = (unsigned long) (_stack) + STACK_SIZE;
_task->tss.eip = (unsigned long) init;
_task->tss.cr3 = (unsigned long)k_page_dir;

I think the compiler give a wrong address. I use gcc 3.3.1 SuSE 9.0
Tim

Re:Hardware Task swiching

Post by Tim »

The EIP looks correct -- you're printing it as decimal in one place and hex in another. 1070288 decimal = 1054D0 hex = task 2's function.

You're getting a page fault exception, and apparently your CR2 is 0. So it looks like the CPU is able to access the instruction at 1054D0 OK, but it's trying to dereference a null pointer.
amirsadig

Re:Hardware Task swiching

Post by amirsadig »

I have noticed that I give the number in decimal. thank you for notice.

here is the task 2 function:

int task2()
{

const char *msg = "Please Enter your Name : ";
char *text;

while(1)
{
text = (char *)kmalloc(50);
writetest(msg, strlen(msg));
readline(text);
writetest(text, strlen(text));
kfree(text);

}
return 0;
}
--------
when I run only this task no null pointer there. only when the cpu trying to switch it generate a page faults.

I think the problem in the jump function:
void jump_to_task()
{
__asm__(
" ljmp %0 \n"
::"m"(*(((char *)?t_task->tss.tr)-4))
);
}

this (*(((char *)&tcurrent_task->tss.tr)-4)) always NULL.

current_task->tss.tr save the Task Describtor Selector for example 56.
if this long jump is wrong, how I let the CPU to jump to TSS?
amirsadig

Re:Hardware Task swiching

Post by amirsadig »

I have fix the problem.

I have not initialized the TR with the first task, therefor CPU force GPF when it try to save the current TSS.

now I can switch between two kernel thread. also I have tried to write a small program which print my name.

this is the code

Code: Select all

int main()
{
  char *msg = (char *)0x40001010;
  
  msg[0] = 'A';
  msg[1] = 'M';
  msg[2] = 'I';
  msg[3] = 'R';
  msg[4] = '\0';
  //msg = "Please Enter your Name : ";
 
  while(1)
    {
      writetest(msg, 5);
    }
  return 0;
}

/* call the sys_read */
void writetest(const char *str, unsigned len)
{
  
  __asm__ __volatile__(
   "pushl %%ebx;
         pushl %%ecx;
         pushl %%eax;
         mov %0, %%ebx;
         mov %1, %%ecx;
         mov $0, %%eax;
         int $0x80 ;
         popl %%eax;
         popl %%ecx;
         popl %%ebx;"
   ::"r"(str), "r"(len)
);

}
here is the assembly for the function writetest:

Code: Select all

40000030 <writetest>:
40000030:   55                      push   %ebp
40000031:   89 e5                   mov    %esp,%ebp
40000033:   8b 55 08                mov    0x8(%ebp),%edx
40000036:   8b 45 0c                mov    0xc(%ebp),%eax
40000039:   53                      push   %ebx
4000003a:   51                      push   %ecx
4000003b:   50                      push   %eax
4000003c:   89 d3                   mov    %edx,%ebx
4000003e:   89 c1                   mov    %eax,%ecx
40000040:   b8 00 00 00 00      mov    $0x0,%eax
40000045:   cd 80                   int    $0x80
40000047:   58                      pop    %eax
40000048:   59                      pop    %ecx
40000049:   5b                      pop    %ebx
4000004a:   c9                      leave  
4000004b:   c3                      ret  
amirsadig

Re:Hardware Task swiching

Post by amirsadig »

the task switch to the user process work but CPU generate a GPF :

Exception #14 (page fault)
EDI=00000000 ESI=00000000 EBP=00000000 ESP=1000BFE0
EBX=00000000 EDX=00000000 ECX=00000000 EAX=00000000
DS=0000002B ES=0000002B FS=0000002B GS=0000002B
int=0000000E err=00000006 EIP=40000040 CS=00000033
uSP=40002000 uSS=0000002B
CR2 = 0x0 : CR3: 0x200000 , CR4: 0x0

as you see the instruction "40000040: b8 00 00 00 00 mov $0x0,%eax" it case this GPF and it only put 0 on %eax.

why it generate GPF?
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:Hardware Task swiching

Post by Brendan »

Hi,
amirsadig wrote: the task switch to the user process work but CPU generate a GPF :

Exception #14 (page fault)
EDI=00000000 ESI=00000000 EBP=00000000 ESP=1000BFE0
EBX=00000000 EDX=00000000 ECX=00000000 EAX=00000000
DS=0000002B ES=0000002B FS=0000002B GS=0000002B
int=0000000E err=00000006 EIP=40000040 CS=00000033
uSP=40002000 uSS=0000002B
CR2 = 0x0 : CR3: 0x200000 , CR4: 0x0

as you see the instruction "40000040: b8 00 00 00 00 mov $0x0,%eax" it case this GPF and it only put 0 on %eax.

why it generate GPF?
This is a page fault caused by trying to write to a not-present page at 0x00000000. Is it possible that your code has been overwritten? For example, if something filled memory with zeros then you'd have "add [eax],al" or "addb %al,(%eax)" instead which would cause this problem because eax = 0.

Just a guess..

Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Hardware Task swiching

Post by Pype.Clicker »

iirc, "mov $0x0, %eax" is the AT'T way to say "mov eax,0", $0x0 being a litteral constant, not a memory reference ...

i would have said the code was pointing to a bad location if there wasn't "CR2=0" ... thus you're following some NULL pointer somewhere...

i'd therefore be tempted to second brendan's suggestion, as a "normal" execution of your code shouldn't lead to "ebx==ecx==0" ...
quaak

Re:Hardware Task swiching

Post by quaak »

Freak, to software task switching.

Idea is kinda simple
like
__asm__ __vollatile (
pushl %%eax;
pushl %%ecx;
pushl %%edx;
pushl %%ebp;
// other regs are callee safed, with framepointer you
// can ommit pushl %%ebp
pushl 1f;
movl %0, %esp,
call switch_context; // c func, i.e. maybe reload pagedir register, do
// some other stuff before switching the task
// or do this in assembly too!
ret; // do the switch
1: // back to old task again
popl %%ebp;
popl %%edx;
popl %%ecx;
popl %%eax;"
:"esi" new_task->kernel_esp;
);

Because your are switching between kernel code,
every newly created task needs some primitive kernel iret_to_user code
to return to userland and a artifically created kernel_stack,
so that switching to this new task will also work.
After that your are only switching between kernel code.

And i think, a optimized version of this code will beat the
hardware task switch and more important, your are not limited in tasks.
The descriptor table will impose a maximum task limit, if you using hardware tasks.
GT

Re:Hardware Task swiching

Post by GT »

quaak wrote:And i think, a optimized version of this code will beat the hardware task switch and more important, your are not limited in tasks. The descriptor table will impose a maximum task limit, if you using hardware tasks.
There certainly are advantages to managing your own task switches, but for the record, it should be noted that the above statement is false. The descriptor table imposes a limit on the number of active descriptors you can have, but there is no limit on the number of tasks you can support, just how many can currently have descriptors. There's a significant speed advantage to keeping descriptors for all your running tasks, but it's not strictly necessary, and keeping around descriptors for sleeping tasks is just wasting space (although it does simplify waking them up a bit). In short, it's easier to keep a descriptor around for every task, but not at all necessary. You can use hardware tasks and still support an unlimited number of tasks.
Post Reply