Page 1 of 1

multitasking and ring0/ring3

Posted: Fri Nov 16, 2007 11:33 am
by sigler
Hello,

I've got my kernel with software multitasking, up to now all the tasks are running in ring0. So I tried to setup a task to run in ring3. I made two new entries in the gdt, one for code and one for data in ring3.
my gdt now looks like this

code ring0 // 8
data ring0 // 16
code ring3 // 24
data ring3 // 32
tss

Then I setup the task like this (the only difference I've done from ring0 tasks is change the cs and ds)

Code: Select all


        // ring0
         //       int cs = 8;
         //       int ds = 16;

           // ring3
                int cs = 24;
                int ds = 32;

	// iret pops these
	*(--esp) = EFLAGS | EFLAGS_IF;	// eflags
	*(--esp) = cs;	// cs
	*(--esp) = (dword)entrypoint; // eip

               // the timer interrupt pops/pushes these
	*(--esp) = 0;		// ebp
	*(--esp) = 0;		// esp
	*(--esp) = 0;		// edi
	*(--esp) = 0;		// esi
	*(--esp) = 0;		// edx
	*(--esp) = 0;		// ecx
	*(--esp) = 0;		// ebx
	*(--esp) = 0;		// eax
	*(--esp) = ds;		// ds
	*(--esp) = ds;		// es
	*(--esp) = ds;		// fs
	*(--esp) = ds;		// gs
If I correctly understand what's happening, I get a general protection fault when this task is selected in the timer interrupt and iret is performed.
(again the only change I've made to what's working, is change the cs/ds in the tasks stack)

What am I missing?

Must I introduce ldt ? (I haven't yet understood what ldt is for)

thanks,

--
Sigurd Lerstad

Posted: Fri Nov 16, 2007 11:52 am
by XCHG
I remember I had a lot of difficulty when I was implementing software context switching in my kernel. Some of the most important ones were (Perhaps you can use them as a simple checklist):

1) I wasn't using the TSS correctly (with the SS:ESP of the kernel in TSS)
2) I was not setting EFLAGS correctly for the new task (Interrupt Flag and etc were not correctly set).
3) I was not setting the GS/DS/FS/ES of the kernel upon each Timer Interrupt. So when I was addressing a memory location with DS, I could be using an arbitrary memory address.

These were just a few. I think it would help if you gave us Bochs' debug output.

Re: multitasking and ring0/ring3

Posted: Fri Nov 16, 2007 2:04 pm
by egos
sigler wrote:What am I missing?
In user mode (ring3) the values of segment registers must include in self RPL=3, e.g. cs=24+3=27.
Must I introduce ldt?
No, but it is possible.

Posted: Fri Nov 16, 2007 3:04 pm
by AJ
Hi,

Something else to check:

If you are using paging, ensure that all code and data for your ring 3 task has the u/s bit of the PDE and PTE *set*. If not, you will get a GPF. This includes the stack, all variables, bss and all code.

Cheers,
Adam

Posted: Fri Nov 16, 2007 4:23 pm
by Combuster
since you IRET to a lower privilege, you must add an additional SS and ESP to the stack frame to give it all the data it needs

Posted: Sat Nov 17, 2007 2:41 pm
by sigler
Combuster wrote:since you IRET to a lower privilege, you must add an additional SS and ESP to the stack frame to give it all the data it needs
Okay, I now setup task like this:

Code: Select all

	unsigned int cs = 24+3;
	unsigned int ds = 32+3;

	// iret pops these

	*(--esp) = ds;	// ss
	*(--esp) = ustacktop;	// esp

	*(--esp) = EFLAGS | EFLAGS_IF;	// eflags
	*(--esp) = cs;	// cs
	*(--esp) = (dword)entrypoint; // eip
The bochs error message is:

00003694910p[CPU0 ] >>PANIC<< get_SS_ESP_from_TSS: TR.cache invalid

So is there something wrong in my tss? not setup correctly etc.

I have added a tss to the gdt. Do I have to do more to make it visible? (I think there's a task register in hardware multitasking, but in software multitasking ?)

Before calling the iret that crashes, I set the global tss.esp0 to the task's esp, and tss.ss0 to 32+3 (the task's stack segment), all other fields in tss are zero.

I'm not setting the NT flag in EFLAGS ever, should I ?

Anything else?

thanks,

--
Sigurd Lerstad

Posted: Sat Nov 17, 2007 6:00 pm
by pcmattman
When you do a privilege level switch from a lesser privilege to a higher one, the CPU reads SS0:ESP0 from the TSS and sets that as the ring0 ss:esp pair. You'll need to make sure that the TSS is loaded into the TR (even for software multitasking) and that a valid SS0:ESP0 pair is stored in it.

Posted: Sun Nov 18, 2007 8:22 am
by sigler
Thanks everyone,

It works now.

One more question:

When an interrupt occurs, to know the ring that the current task was running in, is the correct way to store a ring field in the task structure?

*edit*

I just thought of:
I could look at the lower two bits of cs segment that is on the stack?

--
Sigurd Lerstad

Posted: Mon Nov 19, 2007 3:10 am
by AJ
Yes - that's how to do it :)

I originally had a flags field in my task structure and stored it there - the only problem with this was that if you preempted a task when it had called a system service, you are in ring 0 even though it is a ring 3 task. As you say - look at the lower bits of CS.

Cheers,
Adam