Switching to User-mode using iret

Alex Nichol picture Alex Nichol · Jul 31, 2011 · Viewed 16.1k times · Source

I am writing a small OS that will execute some code in user mode (privilege level 3). From that user level code, I want to call an interrupt back to the OS that prints a message. Right now I don't really care how my interrupt handler takes arguments or anything like that, I really just want an interrupt handler to inform me (the user) that the code has executed.

My question is: how do I run code in user mode? I have a function that sets up a Local Descriptor Table with a code segment and data segment (both with user mode privileges). What I dont understand is how I am supposed to load these segments into cs, ss, and ds. I successfully load the my LDT, but I do not know how to actually use it. I have heard that I should use iret, but I don't understand exactly how.

Another question that I have is how my interrupt handler should work. Let's say I install an interrupt handler for vector number 0x40, which I want to print "hello, user mode!". I know how to setup an interrupt handler, but I don't exactly understand how the context will be switched when entering a kernel interrupt handler from user mode. I know that the cs register must change, since my routine will be running from the code segment specified in my IDT entry. I also understand that the stack selector probably changes as well, but I cannot be certain of this.

Could someone please explain to me what context changes are made when an interrupt gate is called?

Answer

ughoavgfhw picture ughoavgfhw · Aug 1, 2011

Getting to ring 3 can be done using iret because the way it works has been documented. When you receive an interrupt, the processor pushes:

  1. The stack segment and pointer (ss:esp), as 4 words
  2. EFLAGS
  3. The return code segment and instruction pointer (cs:eip), as 4 words
  4. An error code, if required.

iret works by undoing steps 1-3 (The ISR is responsible for undoing step 4 if necessary). We can use this fact to get to ring 3 by pushing the required information to the stack and issuing an iret instruction. Make sure you have the proper CPL in your code and stack segments (the low two bits should be set in each). However, iret doesn't change any of the data segments, so you will need to change them manually. You use the mov instruction to do this, but you will not be able to read data outside the stack between doing this and switching rings.

cli
mov   ax, Ring3_DS
mov   ds, eax
push  dword Ring3_SS
push  dword Ring3_ESP
pushfd
or    dword [esp], 0x200 // Set IF in EFLAGS so that interrupts will be reenabled in user mode
push  dword Ring3_CS
push  dword Ring3_EIP
iret

If you want a complete, working example, see this tutorial.


When an interrupt is issued, the processor reads your IDT to get the proper code segment and instruction pointer for the ISR. It then looks at your TSS to find the new stack segment and pointer. It changes ss and esp appropriately, and then pushes the old values to the new stack. It does not change any of the data segment registers. You must do this manually if you need to access memory in your ISR.