Page 1 of 2
How to enter user mode?
Posted: Thu May 16, 2013 4:34 pm
by gabemaiberger
How do I enter ring 3 (user mode)? I have heard you have to setup more than a GDT entry.
Re: How to enter user mode?
Posted: Thu May 16, 2013 4:43 pm
by feare56
Check out the brokenthorn development series it has a section on user mode and it is a good series if on windows. If you are on Linux the roll your own toy UNIX clone is good
Re: How to enter user mode?
Posted: Thu May 16, 2013 5:08 pm
by Gigasoft
You should create GDT entries for ring 3 code and data, and you also have to have a TSS. Set the SS0 field in the TSS to your kernel data segment. Before entering user mode, save ESP into the ESP0 field of the TSS. (If you have multiple threads, be sure to remember to update this field when switching threads.) To enter user mode, execute an IRET instruction with the return CS set to the user mode segment. The ESP and SS values for user mode follow the EFlags value on the stack.
Re: How to enter user mode?
Posted: Thu May 16, 2013 7:06 pm
by bigorenski
Re: How to enter user mode?
Posted: Sat May 18, 2013 3:17 pm
by gabemaiberger
Hello, I have GDT entries for ring 3 and a function to enter user mode. The GDT entries look like this:
Code: Select all
USERCODE_DESC:
dw 0xFFFF
dw 0
db 0
db 11111010b
db 11001111b
db 0
USERDATA_DESC:
dw 0xFFFF
dw 0
db 0
db 11110010b
db 11001111b
db 0
The function looks like this:
Code: Select all
usermode:
mov ebx, msgUserMode
call DisplayMessage
mov ebx, msgNWLN
call DisplayMessage
cli
mov eax, 0x23
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
push 0x23
push esp
pushf
or dword [esp], 0x200
push dword 0x1B
push umode
iret
umode:
ret
How would the TSS look in assembly(preferably NASM syntax)?
Re: How to enter user mode?
Posted: Sat May 18, 2013 4:54 pm
by bluemoon
I don't believe you can do mov ds,
eax.
But anyway, it's not recommended to reuse the kernel stack for user mode code (that
push esp)
It's also usually not possible to return from user mode to kernel by simply ret, if you enforce page protection, which is why you do user-mode.
For TSS, you fill it according to manual, where ring1/2 fields are optional.
ring0 fields should be set before entering user mode.
you don't need to initialize general purpose registers field as the CPU will store them upon task switch.
bigorenski wrote:How would the TSS look in assembly(preferably NASM syntax)?
Re: How to enter user mode?
Posted: Sat May 18, 2013 8:34 pm
by gabemaiberger
Hello again, (please correct me if i'm wrong) according to
Getting_to_Ring_3 I am supposed to put 0x23 which is my data segment for ring 3.
Re: How to enter user mode?
Posted: Sun May 19, 2013 12:23 am
by Antti
bluemoon wrote:I don't believe you can do mov ds, eax
Yes you can and you should do it. It avoids the 16-bit operand prefix (unless the assembler is smart enough to leave it out anyway).
Re: How to enter user mode?
Posted: Sun May 19, 2013 3:18 am
by bluemoon
Antti wrote:bluemoon wrote:I don't believe you can do mov ds, eax
Yes you can and you should do it. It avoids the 16-bit operand prefix (unless the assembler is smart enough to leave it out anyway).
According to intel manual, the MOV instruction support the following for segment registers:
Code: Select all
8E/r MOV Sreg,r/m16 Move r/m16 to segment register
REX.w+8E /r MOV Sreg,r/m64 Move lower 16 bits of r/m64 to segment register
Note that there is no MOV Sreg,r/m32.
It also noted that for 32-bit assembler, it can emit 16-bit operand-size prefix with this instruction.
So, no matter which assembler you use, in 32-bit you need the prefix and it actually is executing o16 mov Sreg,r16.
So, technically you do mov Sreg, r16 in 32-bit environment and let the assembler emit o16 automatically, as in other 16-bit operations.
Whether an assembler is smart enough is out of my interest.
Re: How to enter user mode?
Posted: Sun May 19, 2013 3:50 am
by Antti
@bluemoon: Please read description also. The same manual says:
Intel wrote:When operating in 32-bit mode and moving data between a segment register and a general-purpose register, the 32-bit IA-32 processors do not require the use of the 16-bit operand-size prefix (a byte with the value 66H) with this instruction, but most assemblers will insert it if the standard form of the instruction is used (for example, MOV DS, AX). The processor will execute this instruction correctly, but it will usually require an extra clock. With most assemblers, using the instruction form MOV DS, EAX will avoid this unneeded 66H prefix. When the processor executes the instruction with a 32-bit general-purpose register, it assumes that the 16 least-significant bits of the general-purpose register are the destination or source operand.
Re: How to enter user mode?
Posted: Sun May 19, 2013 5:18 am
by Love4Boobies
That advice is for MASM, JWasm, and possibly other assemblers. It's basically an assembler hack that helps you decide whether or not the assembler should generate an operand-size prefix; it's not an actual instruction. NASM and YASM, for instance, automatically pick the best encoding with "mov ds, ax."
Re: How to enter user mode?
Posted: Sun May 19, 2013 5:58 am
by AbstractYouShudNow
You don't need to have mor ethan one GDT entry, if you have paging.
Remember the interrupts ? When called, it pushes many things on the stack, including CS:EIP. To change to user mode, you must set the two low bits of CS to the ring you want (3 in that case). This shouldn't matter, since every selector is a multiple of 16 and doesn't use those bits.
So what you need to do is:
- Setup DS,ES,GS,FS to user mode selectors (if you data segment is 0x20, use 0x23 for ring 3)
- Push your user-mode SS (which is probably the same as your DS/ES/FS/GS) on the stack
- Push your user-mode ESP
- Push your user-mode EFLAGS (Careful here, you should have disabled interrupts during the switch. If you want interrupts in user-mode, you must set the Interrupt Flag in this value, since user-mode code can't execute STI)/
- Push your user-mode CS, with its lowest 2 bits set to 3 (for ring 3)
- Push your user-mode EIP
- Execute IRETD instruction. The CPU will then setup itw own state as the stack image, believing that you return from an interrupt, and you'll end up in user-mode.
For more details,
http://www.brokenthorn.com/Resources/OSDev23.html
Re: How to enter user mode?
Posted: Sun May 19, 2013 8:31 am
by Antti
I apologize this pedantic ranting but I consider "mov ds, eax" to be an
actual instruction and also perfectly valid one. Without any assembler hacks or anything. If an assembler is really pedantic and no optimizations are in use, I think it should generate the 16-bit operand-size prefix if using "mov ds, ax" and should not generate it if using "mov ds, eax". NASM is "smart enough" and knows that users probably do not want the prefix.
Intel wrote:When the processor executes the instruction with a 32-bit general-purpose register, it assumes that the 16 least-significant bits of the general-purpose register are the destination or source operand. If the register is a destination operand, the resulting value in the two high-order bytes of the register is implementation dependent.
I think this clearly states that all 32 bits are somehow "acknowledged" although the 16 least-significant bits are actually used when doing "mov ds, eax". Executing the instruction with a 32-bit general purpose register means e.g. "eax" and not "ax". Doing "mov ds, eax" in this context is correct in every way and not just as a hack.
Re: How to enter user mode?
Posted: Sun May 19, 2013 11:33 am
by Combuster
AbstractYouShudNow wrote:You don't need to have mor ethan one GDT entry, if you have paging.
"Don't"? I see three minimum for a starter kernel and six minimum for one that supports userspace.
Re: How to enter user mode?
Posted: Sun May 19, 2013 2:22 pm
by gabemaiberger
I have the code to switch to user mode ready. However, I have one problem: (I know I already asked but...) I would like to know what the TSS structure:
Code: Select all
#ifdef _MSC_VER
#pragma pack (push, 1)
#endif
struct tss_entry {
uint32_t prevTss;
uint32_t esp0;
uint32_t ss0;
uint32_t esp1;
uint32_t ss1;
uint32_t esp2;
uint32_t ss2;
uint32_t cr3;
uint32_t eip;
uint32_t eflags;
uint32_t eax;
uint32_t ecx;
uint32_t edx;
uint32_t ebx;
uint32_t esp;
uint32_t ebp;
uint32_t esi;
uint32_t edi;
uint32_t es;
uint32_t cs;
uint32_t ss;
uint32_t ds;
uint32_t fs;
uint32_t gs;
uint32_t ldt;
uint16_t trap;
uint16_t iomap;
};
#ifdef _MSC_VER
#pragma pack (pop, 1)
#endif
looks like in nasm syntax with db, dw, dd and dq preferably.