Code: Select all
#define CONTEXT_SWITCH_ENTRY(exn) \
Process *exception_##exn##_handler(CoreDump cd); \
extern void exception_##exn##_entry(); \
asm ( \
".globl exception_" #exn "_entry\n" \
"exception_" #exn "_entry: \n" \
" pushl $0x0\n" \
" pusha\n" \
" pushw %ds\n" \
" pushw %es\n" \
" pushw %fs\n" \
" pushw %gs\n" \
" pushw %ss\n" \
" pushw %ss\n" \
" pushw %ss\n" \
" pushw %ss\n" \
" pushw %ss\n" \
" popw %ds\n" \
" popw %es\n" \
" popw %fs\n" \
" popw %gs\n" \
" mov %esp, %eax\n" \
" call exception_" #exn "_handler\n" \
" test %eax, %eax\n" \
" jz no_switch\n" \
" int $0x3\n"\ // this is where we need to switch to the new process. what the heck do I do?
" iret\n"\
"no_switch:\n" \
" popw %gs\n" \
" popw %gs\n" \
" popw %fs\n" \
" popw %es\n" \
" popw %ds\n" \
" popa\n" \
" add $0x4, %esp\n" \
" iret\n");
Code: Select all
CONTEXT_SWITCH_ENTRY(0x20)
Process *exception_0x20_handler(CoreDump cd)
{
pic_sendeoi(0);
Process *current = sched_get_current_process();
Process *switchto = 0;
if (current) {
sched_tick();
readyqueue_add(current);
Process *n = readyqueue_pop();
if (current != n) { // i.e. a different task now has the earliest deadline, so we need to change tasks
switchto = n;
// save all registers.
// current->context.tss doesn't actually point to a TSS that is registered with the machine. I'm just
// using the same type of struct to hold context data.
current->context->tss.gs = cd.gs;
current->context->tss.fs = cd.fs;
current->context->tss.es = cd.es;
current->context->tss.ds = cd.ds;
current->context->tss.edi = cd.edi;
current->context->tss.esi = cd.esi;
current->context->tss.ebp = cd.ebp;
current->context->tss.ebx = cd.ebx;
current->context->tss.edx = cd.edx;
current->context->tss.ecx = cd.ecx;
current->context->tss.eax = cd.eax;
current->context->tss.eip = cd.eip;
current->context->tss.cs = cd.cs;
current->context->tss.eflags = cd.eflags;
// + 16 for CS, EIP, EFLAGS, exception code
current->context->tss.esp = cd.esp + 16;
current->context->tss.ss = cd.ss;
sched_set_current_process(switchto); // just sets an internal variable, doesn't do anything else
}
}
return switchto; // if going back to the same task, switchto is null. if switching tasks, points to the Process structure of the new task.
}
In the C function, we check to see if we're even in ring 3 yet (sched_get_current_process will return null if we haven't actually gotten into ring 3 yet). Next, it re-adds the currently running process to the priority queue. It then pops the highest-priority task from from the queue. I have verified that the priority queue is working correctly. If the popped task is the same one we were just running, we don't set switchto, just returning null and doing nothing else.
If the popped task is different, then we need to switch to the new task. I set switchto to point to the new process' Process struct. Then, I save the info in cd to the current task's process structure (effectively saving the current process' context), then set the scheduler's internal "current process" variable to the new process.
The return value of the C function allows the assembly to see if we're just returning to the previous process, or if a context switch needs to occur. The problem is, I have no idea how to make the actual context switch happen. I tried just copying the context data for the new process into 'cd' in the C function, but this just caused a GP fault at the iret. What actually needs to happen for the CPU to iret into a different ring 3 process?
