Page 1 of 1

Help with context switching.

Posted: Mon Dec 10, 2007 5:44 pm
by Shalted
Ok, from what I understand, this is what happens during a (software) context/task switch.
  • - Timer Pulses, CPU enters ISR in ring 0 (According to my IDT anyway). And registers/context info are stored in a task structure.

    - (Additional task scheduling code).

    - EFLAGS, EIP, CS are loaded from the new task's data struct, and are written over the existing values that currently reside within the stack.

    - The rest of the new task's data is loaded from the struct, and placed in their according registers.

    - (Send the interrupt clear flag to the timer PIC, so it can pulse again).

    - iret occurs, popping EIP, EFLAGS, CS off the stack, and the program executes from EIP, at the PL described within EFLAGS until the timer pulses
Now, the thing I don't understand, is that when an interrupt occurs at the the same privilege as the code that's currently executing, it pushes EFLAGS, CS and EIP onto the stack before executing the call, but when the code is executing at a higher level than the interrupt's, it pushes EFLAGS, SS, ESP, CS and EIP onto the stack. (Taken from the Intel Manual)

I can see this working fine for same PL context switches, but what if I want to switch back to a kernel task from a task executing at ring 3? Is there some way to detect that, or am I missing something?

(Of course, the above modal is a very basic scheduler, and doesn't deal with task priority, threads, or anything more complex than just switching tasks when the timer pulses, but it's for demonstration's sake.)

EDIT: I realized that each task/thread has it's own stack, so when returning using an iret, you need to push the EFLAGS, EIP, CS on the new task's stack.

Posted: Tue Dec 11, 2007 3:08 am
by JamesM
Hi,

The usual way to do it is:

1. Timer pulses. Code jumps to ring0 interrupt handler, pushing extra SS, ESP if in ring3.
2. Scheduling code, decide what to run next.
3. Save ESP, EBP (Kernel stack) in a task structure.
4. Switch page directories. This assumes that the task you're switching to got switched out in the same function: Just change ESP, EBP - EIP should stay the same and the stack content will have changed.
5. Just exit the scheduler function. The code will pass back up the stack until it hits the IRET - because this is the new task its stack will already be set up to return to the code it was originally running. (be it ring0 or ring3, you don't care!)

Et voila. No need for stack fiddling - just one stack switch. The advantage of this also is that normally the first thing your IRQ/ISR handler does is save all general purpose registers. These then get unsaved when the new task "returns" from it's interrupt handler so you don't have to save them manually in a task structure.

Hope this made sense. There'll be a tutorial on my website about it soon (ish)!

Cheers,

James

Posted: Tue Dec 11, 2007 1:47 pm
by Shalted
Thanks a lot, so the scheme I'm thinking of now, is somewhat like this:
  • -Clear interrupt flag, so nothing can interrupt the task switch. (Can another hardware interrupt occur within an ISR?)

    - Push all GP registers, push Segment Registers, push Control Registers (Paging directory)

    - Decrease and check timeslice, if not zero, pop all registers back from the stack.

    - Call scheduling code, which switches the stack to a previously saved stack, with all the needed context info on it.

    - Reset the IRQ

    - Pop Control registers, segment registers, and GP registers from the new stack.

    - Set the interrupt flag.

    - Return from service routine, popping the needed new EIP, EFLAGS ect...
    values off the stack.
A "CreateTask" function would need to create a new Page Directory based off the kernel's and allow user execution of code under the 0x100000 mark, and set up a stack, with all registers set as NULL, ready to be popped by the scheduling function when it's allowed its timeslice.

Posted: Wed Dec 12, 2007 2:40 am
by JamesM
Yep, that sounds good to me. And yes, even with the interrupt-enable bit clear some interrupts can still occur. NMI (non maskable interrupts), and other processor faults like page faults, general protection faults etc will fire. IRQs will not however.

Posted: Wed Dec 12, 2007 10:43 pm
by Shalted
Of course, but NMIs are necessary for proper system execution. (Catching Exceptions and such.)

Thank a lot JamesM, for clarifying everything. :D