Hardware Task swiching
Hardware Task swiching
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?
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?
Re:Hardware Task swiching
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.
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.
Re:Hardware Task swiching
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.
I you have a suggestion about how to port my existing hardware switching. look to my tss_struct.
Re:Hardware Task swiching
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.
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.
Re:Hardware Task swiching
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 ?
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 ?
Re:Hardware Task swiching
why dont you show some bochs output when crashing occurs? I mean the core dump part.
Re:Hardware Task swiching
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
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
Re:Hardware Task swiching
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.
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.
Re:Hardware Task swiching
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?
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?
Re:Hardware Task swiching
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
here is the assembly for the function writetest:
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)
);
}
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
Re:Hardware Task swiching
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?
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?
Re:Hardware Task swiching
Hi,
Just a guess..
Cheers,
Brendan
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.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?
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.
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:Hardware Task swiching
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" ...
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" ...
Re:Hardware Task swiching
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.
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.
Re:Hardware Task swiching
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.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.