Longmode Ring0->Ring3 Issues

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.
Post Reply
iLewis
Posts: 22
Joined: Mon Nov 01, 2010 5:46 pm
Location: Ballarat, Victoria, Australia
Contact:

Longmode Ring0->Ring3 Issues

Post by iLewis »

I have got basic multitasking working now on my OS, and i can create/delete threads from the taskmanager, this works 100% in long mode ring0! However, now i am attempting to get to userland, and am having a somewhat frustrating issue.

here is how i am trying to do the task switch to ring3

Set up the TSS and load the TSS into the 64bit TSS Descriptor in the GDT (with the DPL in the descriptor as 3)
Load the TSS with the LTR instruction, which does not throw any exceptions

(the TSS is empty except for the RSP0 field which does have a correct address)

then i push SS = 0
push RSP = allocated and user priv read/write
push EFLAGS = 0x200 (enable ints)
push CS = 0x23 (yes that is the correct GDT entry for DPL = 3 long mode)
push RIP = this does have the correct address
iretq

Now what happens is no matter what value is put in SS it throws a GPF 0xD with the selector index as the error code, 0x3 = 0, 0 = 0, 0x23 throws an error code 0x20 and so on. I know its definitely an issue with the value in SS because if i try and make CS 0x22, it will except with CS = 0x20 as the error code. (and i was under the impression SS had to be null which according to the AMD manual forces a CPL switch to the CS RPL as if it had interrupted from ring3 in the first place? Or, I read wrong)

SO! What should SS really be? or am i doing something wrong elsewhere?
Some ideas could be handy.

Ok so here is what i have found myself:
In IA-32e mode, IRET is allowed to load a NULL SS under certain conditions. If the
target mode is 64-bit mode and the target CPL <> 3, IRET allows SS to be loaded
with a NULL selector.
Ok... so this means if i am switching to ring3 i must load SS with a non null value...
When stacks are switched as
part of a 64-bit mode privilege-level change (resulting from an interrupt), a new SS
descriptor is not loaded. IA-32e mode loads only an inner-level RSP from the TSS.
The new SS selector is forced to NULL and the SS selector’s RPL field is set to the new
CPL. The new SS is set to NULL in order to handle nested far transfers (CALLF, INT,
interrupts and exceptions). The old SS and RSP are saved on the new stack
(Figure 6-8). On the subsequent IRET, the old SS is popped from the stack and
loaded into the SS register.
Um.. So wait.. current SS before i execute the IRET should be null with a RPL = 0 for CPL = ring0, and, SS on the iret stack should be... ??? when returning to a higher CPL? *head explodes*

What i fail to understand, is, there is no data segments in long mode, so what the heck am i putting in SS before executing iret? an index to a long mode segment with rpl = 0 or rpl = 3???

Cheers,
iLewis
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Longmode Ring0->Ring3 Issues

Post by gerryg400 »

Ok... so this means if i am switching to ring3 i must load SS with a non null value...
The manuals are very confusing. But to switch to ring 3 in long mode you must have a valid ring 3 data segment for your stack. So you do need a valid ring 64bit data selector in the GDT.
If a trainstation is where trains stop, what is a workstation ?
iLewis
Posts: 22
Joined: Mon Nov 01, 2010 5:46 pm
Location: Ballarat, Victoria, Australia
Contact:

Re: Longmode Ring0->Ring3 Issues

Post by iLewis »

gerryg400 wrote:The manuals are very confusing. But to switch to ring 3 in long mode you must have a valid ring 3 data segment. So you do need a valid ring 64bit data selector in the GDT.
Incidentally... in long mode data and code selectors are the same thing am i right?
In Figure 4-21, gray shading indicates the fields that are ignored
in 64-bit mode when the descriptor is used during a memory
reference. However, the fields are loaded whenever the
segment register is loaded in 64-bit mode.
Where everything is grayed out except the "P" field...

So ^ does the "the fields are loaded whenever the segment register is loaded" apply to the SS on the iretq stack?

Well here is my GDT

Code: Select all

GDT_ENTRY GDT_TABLE[] =
{
	0,0,0,0,0,0,							// 0
	0xffff,0,0,0x92,0xcf,0,		// CM ring 0 ds    // 8
	0xffff,0,0,0x9e,0xcf,0,		// CM ring 0 cs	// 10
	0,0,0,0x98,0x20,0,		// LM ring 0 cs	// 18
	0,0,0,0xF8,0x20,0,		// LM ring 3 cs	// 20
	// TSS's are a 64bit descriptor
	{},					// tss0-1		// 28
	{}					// tss0-2		// 30
};
TSS is filled in automatically by the scheduler before it swaps tasks

And regardless if i set both CS and SS to 0x23 it still throws a GPF with error code 0x20 for SS... I just dont get it. And yes the Manuals (i have read both AMD and INTEL) are indeed confusing. They dont show real case examples. Which would ideally make life easier
Last edited by iLewis on Fri Dec 03, 2010 7:55 pm, edited 1 time in total.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Longmode Ring0->Ring3 Issues

Post by gerryg400 »

Where are your data segments. You can't use an execute-only code segment for a stack.
If a trainstation is where trains stop, what is a workstation ?
iLewis
Posts: 22
Joined: Mon Nov 01, 2010 5:46 pm
Location: Ballarat, Victoria, Australia
Contact:

Re: Longmode Ring0->Ring3 Issues

Post by iLewis »

Ok so... i need to create a long mode data segment with a DPL 3... Let me try this quickly and ill let you know how i go. I am trying to load SS with my long mode ring 3 cs as i was under the impression they are both the same thing
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Longmode Ring0->Ring3 Issues

Post by gerryg400 »

In Figure 4-21, gray shading indicates the fields that are ignored
in 64-bit mode when the descriptor is used during a memory
reference. However, the fields are loaded whenever the
segment register is loaded in 64-bit mode.
This quote is a little tricky to understand. The fields are ignored during memory reference, BUT they are read when the segment registers are loaded. So, your stack segment MUST be writable, data, long mode etc.

Because there are no runtime checks, you don't ever need to load ds once in long mode. But if you do load it, it must be a valid data segment.
If a trainstation is where trains stop, what is a workstation ?
iLewis
Posts: 22
Joined: Mon Nov 01, 2010 5:46 pm
Location: Ballarat, Victoria, Australia
Contact:

Re: Longmode Ring0->Ring3 Issues

Post by iLewis »

gerryg400 wrote:
In Figure 4-21, gray shading indicates the fields that are ignored
in 64-bit mode when the descriptor is used during a memory
reference. However, the fields are loaded whenever the
segment register is loaded in 64-bit mode.
This quote is a little tricky to understand. The fields are ignored during memory reference, BUT they are read when the segment registers are loaded. So, your stack segment MUST be writable, data, long mode etc.

Dear sir, you are officially my hero! :)

I threw a new entry in my GDT for a long mode ring3 Data Descriptor and referenced SS = 0x28 | 3;

Well guess what, I just switched to ring3, except that i do not know for sure that it worked since it triple faulted my machine. Which i take to assume my TSS is now wrong (or something is nonconforming). BUT it didnt throw an exception on the iretq.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Longmode Ring0->Ring3 Issues

Post by gerryg400 »

Try going to ring 3 with interrupts disabled first.
If a trainstation is where trains stop, what is a workstation ?
iLewis
Posts: 22
Joined: Mon Nov 01, 2010 5:46 pm
Location: Ballarat, Victoria, Australia
Contact:

Re: Longmode Ring0->Ring3 Issues

Post by iLewis »

gerryg400 wrote:Try going to ring 3 with interrupts disabled first.
Well i have tried it with rflags = 0 (so no ints after it enters ring3) however regardless VMWare chucks me and error saying "Kernel stack fault" which really means any number of things.

It seems that execution never gets to ring3, it goes from the iretq directly to a triple fault which i cant understand, i just debugged my TSS and sure enough that seems ok, in long mode does the DPL level matter in the TSS?

It must have something to do with the TSS tho, since it *should* fire an exception handler on an error which it appears it is not doing. OR my stacks are wrong. all my paging is User/Write/Present so that shouldnt be the issue. My RSP0 stack is allocated then the length of the allocation added minus about 0x10 so its not the extreme end of the stack. when loading the Task Register with LTR should i be or-ing that by 3 aswell? because i am not. and the TSS descriptor, should it have a DPL = 3?

This blows my mind ha!
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Longmode Ring0->Ring3 Issues

Post by gerryg400 »

iLewis wrote:Well i have tried it with rflags = 0 (so no ints after it enters ring3) however regardless VMWare chucks me and error saying "Kernel stack fault" which really means any number of things.
Yes it's not a helpful message though I've noticed it occurs when I break the page-tables or the ring 0 stack.
iLewis wrote:It seems that execution never gets to ring3, it goes from the iretq directly to a triple fault which i cant understand, i just debugged my TSS and sure enough that seems ok, in long mode does the DPL level matter in the TSS?
The DPL should be 0.
iLewis wrote:It must have something to do with the TSS tho, since it *should* fire an exception handler on an error which it appears it is not doing. OR my stacks are wrong. all my paging is User/Write/Present so that shouldnt be the issue. My RSP0 stack is allocated then the length of the allocation added minus about 0x10 so its not the extreme end of the stack. when loading the Task Register with LTR should i be or-ing that by 3 aswell? because i am not. and the TSS descriptor, should it have a DPL = 3?
The stacks in the tss MUST be 16 byte aligned. If they are not, the processor will enforce that by clearing (I think) the least significant bits of rsp. This can be annoying!!!

I recommend that when you iret to ring 3 you target a piece of code that does this

Code: Select all

   loop:
      jmp loop
That might allow you to get to ring 3. If it does, then your ring 0 stack might be the problem. Take the smallest steps possible.
If a trainstation is where trains stop, what is a workstation ?
iLewis
Posts: 22
Joined: Mon Nov 01, 2010 5:46 pm
Location: Ballarat, Victoria, Australia
Contact:

Re: Longmode Ring0->Ring3 Issues

Post by iLewis »

Thankyou for your help, i had to duck out this arv and I'm going out tonight, so ill have a play tomorrow and see where i can get. Cheers mate. Also my stacks are page aligned - 0x10 which should still be a 16bit alignment.

Ill let you know how i fair
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Longmode Ring0->Ring3 Issues

Post by gerryg400 »

iLewis wrote:i had to duck out this arv
Are you an Aussie?
If a trainstation is where trains stop, what is a workstation ?
iLewis
Posts: 22
Joined: Mon Nov 01, 2010 5:46 pm
Location: Ballarat, Victoria, Australia
Contact:

Re: Longmode Ring0->Ring3 Issues

Post by iLewis »

gerryg400 wrote:
iLewis wrote:i had to duck out this arv
Are you an Aussie?
Sure am mate!

Hmm... So... If i am in ring0, and i execute LTR to load the new entry to the TSS, at which point does the processor check its validity? Because if i change my TSS descriptor to some other non TSS descriptor (just for debugging sake), it still allows the execution of LTR without an invalid tss exception, and then proceeds to iretq... Which is still triple faulting regardless. Perhaps i am loading the TSS incorrectly? But would you still expect it to at least execute SOMETHING in ring3 even with no TSS? Since it only needs to reference the TSS on an interrupt or CPL change
LTR Instruction: Loads the source operand into the segment selector field of the task register. The source operand (a general-purpose register or a memory location) contains a segment selector that points to a task state segment (TSS). After the segment selector is loaded in the task register, the processor uses the segment selector to locate the segment descriptor for the TSS in the global descriptor table (GDT). It then loads the segment limit and base address for the TSS from the segment descriptor into the task register. The task pointed to by the task register is marked busy, but a switch to the task does not occur.
But this would infer that if the tss descriptor WAS incorrect it will except...
Disregard that, it is excepting if the descriptor is incorrect.

Ok inevitably, i moved across to bochs, and here is what it says
00051097884i[CPU0 ] CPU is in long mode (active)
00051097884i[CPU0 ] CS.d_b = 16 bit
00051097884i[CPU0 ] SS.d_b = 16 bit
00051097884i[CPU0 ] EFER = 0x00000500
00051097884i[CPU0 ] | RAX=0000000000000000 RBX=0000000000000001
00051097884i[CPU0 ] | RCX=0000000000000000 RDX=0000000000000000
00051097884i[CPU0 ] | RSP=00007f000006f878 RBP=0000000000000000
00051097884i[CPU0 ] | RSI=0000000000000000 RDI=0000000000000000
00051097884i[CPU0 ] | R8=0000000000000000 R9=0000000000000000
00051097884i[CPU0 ] | R10=0000000000000000 R11=0000000000000000
00051097884i[CPU0 ] | R12=0000000000000000 R13=0000000000000000
00051097884i[CPU0 ] | R14=0000000000000000 R15=0000000000000000
00051097884i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af pf cf
00051097884i[CPU0 ] | SEG selector base limit G D
00051097884i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00051097884i[CPU0 ] | CS:0023( 0004| 0| 3) 00000000 00000000 0 0
00051097884i[CPU0 ] | DS:0000( 0001| 0| 0) 00000000 ffffffff 1 1
00051097884i[CPU0 ] | SS:002b( 0005| 0| 3) 00000000 00000000 0 0
00051097884i[CPU0 ] | ES:0000( 0001| 0| 0) 00000000 ffffffff 1 1
00051097884i[CPU0 ] | FS:0000( 0005| 0| 0) 000113d0 0000ffff 0 0
00051097884i[CPU0 ] | GS:0000( 0005| 0| 0) 00000d10 0000ffff 0 0
00051097884i[CPU0 ] | MSR_FS_BASE:00000000000113d0
00051097884i[CPU0 ] | MSR_GS_BASE:0000000000000d10
00051097884i[CPU0 ] | RIP=00007f8000005fb0 (00007f8000005fb0)
00051097884i[CPU0 ] | CR0=0xe0000013 CR2=0x00007f000014ffe8
00051097884i[CPU0 ] | CR3=0x00220000 CR4=0x000006a0
00051097884i[CPU0 ] 0x00007f8000005fb0>> nop : 6690
00051097884e[CPU0 ] exception(): 3rd (14) exception with no resolution, shutdown status is 00h, resetting
00051097884i[SYS ] bx_pc_system_c::Reset(HARDWARE) called
00051097884i[CPU0 ] cpu hardware reset
Well... it got to ring3! but it cant execute a NOP instruction? :S
00051097858d[CPU0 ] 64 bit TSS base = 0x00007f00000045b8
00051097866d[PIC ] IO write to 0020 = 20
00051097883d[CPU0 ] LONG MODE IRET
00051097884d[CPU0 ] page walk for address 0x00007f8000005fb0
00051097884d[CPU0 ] page fault for address 00007f8000005fb0 @ 00007f8000005fb0
00051097884d[CPU0 ] exception(0x0e): error_code=0005
00051097884d[CPU0 ] interrupt(): vector = 0e, TYPE = 3, EXT = 1
00051097884d[CPU0 ] interrupt(long mode): INTERRUPT TO INNER PRIVILEGE
00051097884d[CPU0 ] page walk for address 0x00007f000014ffe8
00051097884d[CPU0 ] PTE: entry not present
00051097884d[CPU0 ] page fault for address 00007f000014ffe8 @ 00007f8000005fb0
00051097884d[CPU0 ] exception(0x0e): error_code=0002
00051097884d[CPU0 ] exception(0x08): error_code=0000
00051097884d[CPU0 ] interrupt(): vector = 08, TYPE = 3, EXT = 1
00051097884d[CPU0 ] interrupt(long mode): INTERRUPT TO INNER PRIVILEGE
00051097884d[CPU0 ] page walk for address 0x00007f000014ffe8
00051097884d[CPU0 ] PTE: entry not present
00051097884d[CPU0 ] page fault for address 00007f000014ffe8 @ 00007f8000005fb0
00051097884d[CPU0 ] exception(0x0e): error_code=0002
00051097884i[CPU0 ] CPU is in long mode (active)
And the actual lead up and triple fault errors
exception(0x0e): error_code=0005
Righhhht... So apparently usermode does not have permission to access this page, and the exception was caused in user mode... despite all of my paging being OR'd by 3 to set usermode/write privs...

-----------------
Did i mention i was an idiot? 3 != 111b 3 = 11b ... 7 = 111b to set user/w/present. I may have just solved my own problems.
-----------------

Usermode now runs fine and i can print to the screen in VESA as a test...
Thankyou very much for your help gerryg400, Very much appreciated!
Post Reply