more hardware multitasking problems

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.
User avatar
os64dev
Member
Member
Posts: 553
Joined: Sat Jan 27, 2007 3:21 pm
Location: Best, Netherlands

Post by os64dev »

Ok, i'll try to be a little more explicit. In a multitasking environment you have a bunch of applications they all should run on PL3. Next to those applications you have some drivers or kernel processes They should run on PL < 3 (PL0, PL1, PL2) for portability issues its better to only use PL0.

Using a Timer interrupt gives you a mechanism to switch beteen these process on different PLs. For instance like : PL0 - # - PL3 - # - PL3 - # - PL0 - # - PL3 where the hash is the timer interrupt occuring.

Each process has it's own stack and memory where in case of context switching (PL0->PL3 or PL3->PL0 or PL3->PL3 (other application)) the timer interrupt handler switches to the stack of the new application and thus gives the cpu to the new application.

As fas as i can gather from your code it seems you use TSS alot so it is hardware based contect switching. You might consider software based context switching since in x86-64 hardware based context switching is not supported anymore. And software based switching is faster anyways.

I think there is good documentation on this subject in the wiki otherwise google for it. I cannot give details about x86 in protected mode as i am only sufficient with 64 bit long mode. But as mentioned in another reply read the intel or amd manuals for information about how the stack behaves during interrupts. I know there is a difference between interrupts on different PLs and interrupts on the same PL.

In a nutshell it should work like this (very abstract):

1) process 1 is running on PL0
2) # (timer interrupt) occurs, which stores all registers on the stack of process1, including the segement registers which contain the PL.
3) find the next process to run which is process 2 and switch to its stack
4) get all registers from the stack and segments.
5) do the interrupt return (iret). (effectively changes PL).
Author of COBOS
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

Software multitasking is much simpler to implement and to modify as well. If you want, PM me and I can give you some code (and an explanation, so you aren't blindly copying-and-pasting) so you can get an understanding of how stack-switching works. I didn't understand it until I got help from 'frank' (here on this forum) and it works really well.

With the hardware multitasking, I'd suggest you shy away from it as you have a limit on the number of TSS's you can have (it's something thousand... but who wants to be limited?) because of the GDT.

My 2c.
User avatar
Kevin McGuire
Member
Member
Posts: 843
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Post by Kevin McGuire »

I do not think the AMD64 or IA64 support hardware task switching.

The microcode is supposed to be very old in the IA32 modern processors that supports the hardware task switching, IIRC. So someone correct me if I am wrong which I could be.

Not really sure if it makes a performance impact as I do not a engineer for processor chips, but I figure they stopped for a reason. As well, Window and Linux both use software task switching.
Software multitasking is much simpler to implement and to modify as well.
I like it better.
User avatar
xyjamepa
Member
Member
Posts: 397
Joined: Fri Sep 29, 2006 8:59 am

Post by xyjamepa »

humm...
First of all thank you guys... so you're advicing me to go for
software multitasking but the problem here I don't know any thing
about software multitasking and I mean no thing....
If you want, PM me and I can give you some code (and an explanation, so you aren't blindly copying-and-pasting) so you can get an understanding of how stack-switching works.
yes,would you please ?

Thanx.
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

Yes, but not right now. Within the next day. I have work to do ATM :(

If you want to take a look at code, look at my CVS repositry on sourceforge. I've comment pretty regularly so you shouldn't get lost looking there. Specifically, you'll want to look at CPP_Kernel/tasking/mt.cc, CPP_Kernel/irq.inc and CPP_Kernel/syscore/gdt.cc (for tss).
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

User avatar
xyjamepa
Member
Member
Posts: 397
Joined: Fri Sep 29, 2006 8:59 am

Post by xyjamepa »

Ok...till that time could any one please explain to me
how can I use the Timer interrupt from PL3 to go back to PL0?
os64dev gave me nice explain but till now I don't know how I can
go back to PL0 using IRQ0,to be more specific I don't know
the mechanism to do that,what is the connect between IRQ0
and RPL,DPL and CPL,I'm soooooo confused about that.
Please note I'm using hardware taskswitching so the IRET
will not do any thing in my case.

Thanx.
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Hi,

Assume you are running in ring 3 when a timer IRQ fires. The CPU will switch to CPL0 automatically, pushing the current SS and ESP for your ring 3 code. It loads a ring 0 stack from the TSS.

The nice thing about this is that you do not need to worry about switching back to ring 0 as the CPU does this for you.

Cheers,
Adam
User avatar
xyjamepa
Member
Member
Posts: 397
Joined: Fri Sep 29, 2006 8:59 am

Post by xyjamepa »

unfortunately it didn't work I got Invalid opcode exception
Assume you are running in ring 3 when a timer IRQ fires. The CPU will switch to CPL0 automatically, pushing the current SS and ESP for your ring 3 code. It loads a ring 0 stack from the TSS.
are you sure you're talking about hardware multitasking not software?
The nice thing about this is that you do not need to worry about switching back to ring 0 as the CPU does this for you.
I'm worry about that ...I've been hanging in PL3 and could'nt go back to PL0 for about three days,so could some one end my tragedy??????
Also would you please check my code over there.

Thanx.
User avatar
os64dev
Member
Member
Posts: 553
Joined: Sat Jan 27, 2007 3:21 pm
Location: Best, Netherlands

Post by os64dev »

for switching to PL0 from PL3 you problably need something like a callgate. They are explained nicely in the intel or amd manuals. Can't help you with those due to lack of experience (i dont use them :wink: ).

Still you should consider doing software switching. It does make your life easier.
Author of COBOS
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

abuashraf wrote:unfortunately it didn't work I got Invalid opcode exception
...
are you sure you're talking about hardware multitasking not software?

I've been hanging in PL3 and could'nt go back to PL0 for about three
OK - firstly, it doesn't matter whether I'm talking about hardware or software multitasking - a timer IRQ will still get you back in to ring 0. When the timer fires, the CPU will automatically do the following:

Code: Select all

Switch to CPL0 with a ring 0 stack loaded from your TSS values.
PUSH user mode SS    [if you are in ring 3]
PUSH user mode ESP  [if you are in ring 3]
PUSH EFLAGS             [whatever ring you were in]
PUSH CS                    [whatever ring you were in]
PUSH EIP                   [whatever ring you were in]
So all this happens automagically whether you are using software or hardware multitasking. If you are using hardware multitasking, the TSS used is the TSS for your current task. If you are using software multitasking, the TSS used is the one and only TSS you have set up and pre-loaded with the correct ESP and SS values.

NOTE: At this point, no task switch has occurred!

Then, if you are doing *hardware* multitasking, you need to do the following (pseudo-code):

Code: Select all

CALL _scheduler       ;this selects your next task (your own scheduler)
FAR jmp [tss index]  ;this saves your old task TSS and loads the new
                              ;one automatically using the CPUs built in mechanisms.
Note that you may not want to far jump - you can do clever things with far calls and irets and manually modifying task linkages (if you set the NT flag and point the backlink field of TSS to your incoming task, you just need an IRET, not a FAR JMP)...

Alternatively, if you are *software* multitasking, you need to do the following (again, pseudo-code):

Code: Select all

;all pushes are on to the outgoing task's ring 0 stack (which has been
;restored by the CPU).
PUSH ds, es, fs and gs    ;saving outgoing task's segments
PUSHA                          ;saving outgoing task's GP registers

MOV  eax, esp               ;saving current ESP
MOV [task_struct+offset], eax   ;saving esp in your own defined task structure

MOV esp, [kernel esp]   ;a stack your kernel can use for the _schedule 
                                   ;function

CALL _schedule                ;your custom scheduler function

;now we have returned, your scheduler has ensured that the *incoming*
;task structure is now pointed to by task_struct

MOV eax, [task_struct+offset]  ;restore the new tasks ring 0 stack
MOV esp, eax

MOV [_your_tss_esp0], x ;where x is the desired *base* ring 0 esp value
                                     ;for when the next task switch happens.

POPA                             ;incoming GP regs restored
POP ds, es, fs, gs            ;incoming segment regs restored
IRET                              ;POPs EIP, CS and EFLAGS if CS has a RPL of 3,
                                     ;CPU also POPs ESP3 and SS3 from stack.
So as you can see, there is much more code for you to write in software switching, but you have a fair bit of flexibility about what registers are saved / restored. Note that when you get more advanced, you can also do things like saving and restoring CR3 to switch memory space. Of course, the hardware switch does that for you.

So, don't worry about call gates and so on, set up an IRQ and get the CPU to switch for you - that's how the internal mechanisms are designed to work, after all...

HTH
Adam
User avatar
xyjamepa
Member
Member
Posts: 397
Joined: Fri Sep 29, 2006 8:59 am

Post by xyjamepa »

unfortunately it didn't work I got general protection fault :? :(

for more simplicity I just made the multitasking consists of two
tasks :main() PL0 and task() PL3,I used the back link of the tss
and loaded it with the selector of the previous task which is ox28

so now here's my code may be some one will figure out what is the wrong
with it....

Code: Select all


#define max_gdt_entrys	7

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);

    /* The second entry is our Code Segment. The base address
    *  is 0, the limit is 4GBytes, it uses 4KByte granularity,
    *  uses 32-bit opcodes, and is a Code Segment descriptor.
    *  Please check the table above in the tutorial in order
    *  to see exactly what each value means 0x08*/
  gdt_set_gate(1, 0, 0xFFFF, ACS_CODE , 0xCF);

    /* The third entry is our Data Segment. It's EXACTLY the
    *  same as our code segment, but the descriptor type in
    *  this entry's access byte says it's a Data Segment 0x10 */
  gdt_set_gate(2, 0,0xFFFF, ACS_DATA , 0xCF);
  
  /* 0x18 code segment descriptor*/
  gdt_set_gate(3,0,0xFFFF,ACS_CODE | ACS_DPL_3 ,0xCF);
   
  /* 0x20 data segment descriptor*/
  gdt_set_gate(4,0,0xFFFF,ACS_DATA | ACS_DPL_3,0xCF);

  /* -- 0x28 TSS for main() */
   tmp=(word)&tss[0];
   gdt_set_gate(5,(dword)&tss[0],sizeof(TSS),ACS_TSS,0xCF);
  
  /* -- 0x30 TSS for task() */
   tmp=(word)&tss[1];
   gdt_set_gate(6,(dword)&tss[1],sizeof(TSS),ACS_TSS,0xCF);
  
  
    /* Flush out the old GDT and install the new changes! */
  gdt_flush();
  printf("[Done]\n");
}

struct tss_t
{
 dword  link,
	esp0,
	ss0,
	esp1,
	ss1,
	esp2,
	ss2,
	cr3,
	eip,
	eflags,
	eax,
	ecx,
	edx,
	ebx,
	esp,
	ebp,
	esi,
	edi,
	es,
	cs,
	ss,
	ds,
	fs,
	gs,
	ldtr;
 word	trace,
	io_map_addr;
	
};

typedef struct tss_t TSS;

#define max_tasks	2

TSS tss[max_tasks];

#define stack_size 1024

dword task_stack[max_tasks-1][stack_size];	//kernel stack
dword pl0_stack[max_tasks-1][stack_size];	//user stack

void init_task()
{
 disable();
 unsigned int i=0;
 for(i;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];	//stack for user programes
   tss[i].ss0=0x10;
   tss[i].esp0=(dword)&pl0_stack[i];	//stack for kernel
  }
 tss[1].eip=(dword)&task;
 tss[1].link=0x28;		//main() selector
 ltr(0x28);

 enable();
}

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

void task()
{
 printf("Hello from task()\n");
 __asm__ __volatile__("iret");			//back to main()
}
Thanx.
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

1) Can we see the Bochs output?

2) Are you *sure* that your kernel is running in user mode pages (pages with the user/supervisor mode bit set)? As your PL3 task is running in ring 3 and is statically linked with the kernel, the *entire* kernel needs to be running in user pages.

3) You still seem to be using cooperative multitasking. How about trying that timer interrupt?

4) Before you iret in your task, you need to ensure that NT is set to avoid the CPU trying to pop the required regs off the stack. This is another reason why you need a timer interrupt - you can then put the task in to a for(;;); loop rather than trying to iret while you are testing the multitasking. I panic when I see iret (or any ISR associated code) in inline asm - this could just be me. :)

5) Can you post a link to your entire kernel code (in archive format of your choice :) )?
User avatar
xyjamepa
Member
Member
Posts: 397
Joined: Fri Sep 29, 2006 8:59 am

Post by xyjamepa »

humm...

here's a minimized version of my kernel you will find:
pm.c which treats with protected mode stuff like gdt idt isrs...
scr.c treats with video like printf....
str.c treats with string like strcmp...
the most important for us is task.c which treats with hardware
multitasking.
you can do what ever you want with this code,you may please try
using the Timer interrupt which exist in "pm.c"
Also you will find an IMG file,I think it may help...
Please note paging isn't enabled.
any way I'm so grateful thak you so much.
Attachments
knl.tar.gz
kernel
(14.62 KiB) Downloaded 17 times
a.tar.gz
IMG file
(46.85 KiB) Downloaded 21 times
User avatar
xyjamepa
Member
Member
Posts: 397
Joined: Fri Sep 29, 2006 8:59 am

Post by xyjamepa »

Could some body please debug my IMG using Bochs debugger
becuase I don't have a debugger in my Bochs :( :(
I'll get one as soon as I can.

Thanx
Post Reply