Page 1 of 3

more hardware multitasking problems

Posted: Sun May 20, 2007 11:54 am
by xyjamepa
Hi...
I'm still working on my hardware multitasking it consists of three
tasks:main(),task1() and task2(),every thing working perfect and
I can switch from task1() to task2() but when I try to switch back
form task2() to task()1 without paging I'm getting Invalid opcode,
but with paging I'm getting page fault,please note that I gave
all the tasks the same page directory when paging is enabled.
Thanx.

Posted: Mon May 21, 2007 1:39 am
by AJ
Are the tasks different priorities - perhaps you are trying to access supervisor pages from a user mode task?

Cheers,
Adam

Posted: Mon May 21, 2007 5:45 am
by xyjamepa
No they are all PL0.

Posted: Mon May 21, 2007 6:06 am
by xyjamepa
Now before the far jump to the selectors I reloaded the eip field
of each tss and this is what happining:
switching from main() to task1() -> task2() -> task1() ->task2()
when doing the 4th switching from task2() to task1() Bochs restart.
All tasks are PL0.
and here is the code.

Code: Select all

void init_task()
{
 disable();
 unsigned int i=0;
 for(i=0;i<max_tasks;i++)
   {
     tss[i].trace=0;
     tss[i].io_map_addr=sizeof(TSS);
     tss[i].ldtr=0;
     tss[i].cr3=pbdr;
     tss[i].fs=tss[i].gs=0;
     tss[i].ds=tss[i].es=tss[i].ss=0x10;
     tss[i].cs=0x8;
     tss[i].eflags=0x202L;
     tss[i].esp=(dword)&task_stack[i];
   }
  tss[1].eip=(dword)&task;
  tss[2].eip=(dword)&task2;
 ltr(0x18);
 enable();
}

void ltr(unsigned short selector)
{
  asm ("ltr %0": :"r" (selector));
}

void task()
{
 
 printf("Hello form task()\n");
 printf("press any key to continue\n");
 getch();
 tss[2].eip=(dword)&task2;
 far_jmp(0x28);
}

void task2()
{
 printf("Hello from task2()\n");
 printf("press any key to continue\n");
 getch();
 tss[1].eip=(dword)&task;
 far_jmp(0x20);
 
}

void far_jmp(unsigned int selector) 
{ 
   __asm__ __volatile__ ("ljmp *(%0)\n" : :"r"((void *)&selector -4)  : "memory"); 
}

Posted: Mon May 21, 2007 6:48 am
by urxae
You should just put infinite loops inside your task functions if you want them to repeat the same code over and over again. When you switch to another task, the eip in the current task structure is set to the next instruction and the registers are also saved to it. So after a task is switched to again it should resume where it left off.

The invalid opcodes you were seeing before the "restore eip" hack were likely caused by invalid return addresses being popped off the stack when a task resumed. The page fault if paging was enabled could be caused by returning to unmapped memory, but also by trying to pop a return address off an empty stack (if the page after it was not mapped).

The way you're doing it now (with the hack), the eip gets set back to the beginning of the function, but the other registers aren't set back to what they should be.
This is likely especially important for esp, since the esp is set to the stack pointer in the far_jmp function (if it isn't inlined). So you're leaking stack memory, which could be the cause of the crash you're seeing if it causes the stack to overflow; kernel stack errors are nasty.

Posted: Mon May 21, 2007 2:12 pm
by xyjamepa
Once again It's working and I added the scheduler and every thing
is working fine.
Now my hardware multitasking consistes of three tasks:
main() which is PL0,task() which is PL3 and task2() also PL3
and I can switch from main() to task() or task2()but when I do
any more other switch from task() I get general protection fault,
so I can only switch from main() to task() or task2()
but I can't go back or switch between task() and task2().
To be more specific I can only switch from PL0 to PL3
but I can't do the opposite.
Also please not that paging isn't enabled right now.

here is the code:

Code: Select all

#define ACS_PRESENT	0X80		/* present segment */
#define ACS_CSEG	0X18		/* code segment */
#define ACS_DSEG	0X10		/* data segment */
#define ACS_CONIFORM	0X04
#define ACS_READ	0X02
#define ACS_WRITE	0X02
#define ACS_TSS_GATE	0X09
#define ACS_TSS		(ACS_PRESENT | ACS_TSS_GATE)

/* ready-made values */

#define ACS_CODE	(ACS_PRESENT | ACS_CSEG | ACS_READ)
#define ACS_DATA	(ACS_PRESENT | ACS_DSEG | ACS_WRITE) 
#define ACS_STACK       (ACS_PRESENT | ACS_DSEG | ACS_WRITE)	

#define ACS_DPL_0       0x00            /* descriptor privilege level #0 */
#define ACS_DPL_1       0x20            /* descriptor privilege level #1 */
#define ACS_DPL_2       0x40            /* descriptor privilege level #2 */
#define ACS_DPL_3       0x60            /* descriptor privilege level #3 */

void gdt_install()
{
 printf("Installing GDT...");
    /* Setup the GDT pointer and limit */
 unsigned long tmp;
 gp.limit = (sizeof(struct gdt_entry) * max_gdt_entrys) - 1;
 gp.base = &gdt;

    /* Our NULL descriptor 0x00 */
 gdt_set_gate(0, 0, 0, 0, 0);

 /* 0x8 code segment*/
 gdt_set_gate(1, 0, 0xFFFF, ACS_CODE , 0xCF);

    /*  0x10 data segment*/
  gdt_set_gate(2, 0,0xFFFF, ACS_DATA , 0xCF);
  
  /* 0x18 code segment descriptor PL3*/
  gdt_set_gate(3,0,0xFFFF,ACS_CODE | ACS_DPL_3 ,0xCF);
   
  /* 0x20 data segment descriptor PL3*/
  gdt_set_gate(4,0,0xFFFF,ACS_DATA | ACS_DPL_3,0xCF);

  /* -- 0x28 TSS for main() */
   
   gdt_set_gate(5,(dword)&tss[0],sizeof(TSS),ACS_TSS,0xCF);
  
  /* -- 0x30 TSS for task() */
   
   gdt_set_gate(6,(dword)&tss[1],sizeof(TSS),ACS_TSS,0xCF);
  
  /* --0x38 TSS for task2() */
   gdt_set_gate(7,(dword)&tss[2],sizeof(TSS),ACS_TSS,0xCF);

    /* Flush out the old GDT and install the new changes! */
  gdt_flush();
  printf("[Done]\n");
}

#define stack_size 1024

dword task_stack[max_tasks-1][stack_size];
dword pl0_stack[max_tasks-1][stack_size];

void init_task()
{
 disable();
 unsigned int i=0;
 for(i=0;i<max_tasks;i++)
   {
     tss[i].trace=0;
     tss[i].io_map_addr=sizeof(TSS);
     tss[i].ldtr=0;
     tss[i].fs=tss[i].gs=0;
     tss[i].ds=tss[i].es=tss[i].ss=0x20 | 3;
     tss[i].cs=0x18 | 3;
     tss[i].eflags=0x3202L;
     tss[i].esp=(dword)&task_stack[i];
     
     tss[i].ss0=0x10 ;
     tss[i].esp0=(dword)&pl0_stack[i];
   }
  tss[1].eip=(dword)&task;
  tss[2].eip=(dword)&task2;
 ltr(0x28);		//main() selector
 enable();
}

void task()
{
 printf("Hello from task()\n");
 getch();
 far_jmp(0x38);			//go to task2()
}

void task2()
{
 printf("Hello from task2()\n");
 getch();
 far_jmp(0x28);			//back to main() 
 
}
Thanx.

Posted: Tue May 22, 2007 1:07 am
by os64dev
This is cooperative scheduling thus it can hardly be called multitasking. :? Use a timer (PIT or APIC) to switch between tasks and then it will be true multitasking. And you'll see new problems arising. Ahh... the beauty and fun in os development. :wink:

Posted: Tue May 22, 2007 4:14 am
by xyjamepa
This is cooperative scheduling thus it can hardly be called multitasking. Use a timer (PIT or APIC) to switch between tasks and then it will be true multitasking
I know that and this is not my problem ,I can switch from PL0 to PL3
but I can't go back so I can't switch from PL3 to PL0 if I did
I get general protection fault.
Thanx.

Posted: Tue May 22, 2007 4:19 am
by AJ
If you are doing a software interrupt to switch tasks, does the interrupt descriptor have the correct privilege level?

Posted: Tue May 22, 2007 4:44 am
by os64dev
abuashraf wrote:I know that and this is not my problem ,I can switch from PL0 to PL3
but I can't go back so I can't switch from PL3 to PL0 if I did
I get general protection fault.
Thanx.
I think this is to be expected because from PL0 to PL3 is going to a lesser protected level which is allowed by a far jump and from PL3 to PL0 is going to a more protected level which is NOT allowed by a far jump.

Hence the suggestion to use a timer interrupt. On the interrupts the kernel switches to PL0 and you can do kernel/driver stuff and when finished it can switch to a task at PL3 en resume. You could test this by instead of using a far jump try to simulate an interrupt by using iret.

Posted: Tue May 22, 2007 8:47 am
by xyjamepa
I think this is to be expected because from PL0 to PL3 is going to a lesser protected level which is allowed by a far jump and from PL3 to PL0 is going to a more protected level which is NOT allowed by a far jump.
so.... how am I suppose to go back to PL0?
I need some suggestion please.

Thanx.

Posted: Tue May 22, 2007 8:51 am
by AJ
Timer interrupt.

Posted: Tue May 22, 2007 2:02 pm
by xyjamepa
come on guys I need some more detailed explain....
so could any one here give me this explain.
because I'm really confused about that.

Thanx

Posted: Tue May 22, 2007 3:31 pm
by jnc100
Try:

old PL3 task->timer interrupt->IRQ 0->PL 0 interrupt handler->task switch to PL0->rest of PL0 interrupt handler (keep eip within handler, but switch esp/cr3 to new task)->iret->new PL3 task

iret allows you to return to PL3, just look at the intel docs (2a) for iret to see what it expects on the stack.

Regards,
John.

Posted: Tue May 22, 2007 4:16 pm
by xyjamepa
humm...
What I understand till now is that I'm not allowed to do a far jump
in PL3 so I have to use the Timer interrupt IRQ0.
so...am I right?
@jnc100:I think you didn't get me right,I want to switch from
PL3 to PL0 not the opposite.
Also if I have to use the Timer interrupt to achieve switching
from PL3 to PL0 how can I do that?I'm really confused about
using the Timer interrupt to do the switching
could any one
enlighten me???

Thanx.