Problems with task switching
Posted: Sat Jun 23, 2007 3:33 pm
I decided my old task switcher was too... well, it sucked. So I attempted to rewrite it. So far I've started a task for the kernel and gotten it to jump to it. But as soon as the timer fires again, the scheduler gets called and promptly crashes the computer with a general protection fault, error code=0xf7cc.
Here's the setup code, and the worst scheduler ever created (currently it will just keep switching to the kernel/idle task, more tasks will be added later when this works)
And the assembly part:
What am I doing wrong?
(By the way, I'd also like to be able to run all user tasks in ring 3, and services (this is a microkernel) in ring 0 or 1. Can I do that without messing with TSSes?)
Here's the setup code, and the worst scheduler ever created (currently it will just keep switching to the kernel/idle task, more tasks will be added later when this works)
Code: Select all
#define EFLAGS_INTERRUPTS (1 << 9)
#define EFLAGS_DEFAULTS (1 << 1)
namespace Tasks
{
uint_32 next_pid;
uint_32 cpid = 0;
Task tasks[NUM_TASKS];
void idle()
{
while (1)
cdebug << "Idling\n";
}
extern "C" void do_bootstrap_task_switch(uint_32 newesp);
void setup()
{
int n = 3;
for (int i = 0; i < NUM_TASKS; i++)
{
GDT::set(GDT::gdt[n], (uint_32)&tasks[i].ldt, sizeof(tasks[i].ldt) - 1, 0x20, 0);
n++;
}
// give the kernel its own task
tasks[0].name = "[kernel]";
// set up the ldt
GDT::set(tasks[0].ldt[0], 0, 0xFFFFFFFF, 0x9A, 0xCF);
GDT::set(tasks[0].ldt[1], 0, 0xFFFFFFFF, 0x92, 0xCF);
// and the stack
uint_32 *stack = new uint_32[PROCESS_STACK_SIZE / 4];
assert(stack != NULL);
tasks[0].stack = stack;
int stack_index = (PROCESS_STACK_SIZE / 4) - 1;
stack[stack_index--] = EFLAGS_DEFAULTS | EFLAGS_INTERRUPTS; // eflags
stack[stack_index--] = 0x08; // cs
stack[stack_index--] = (uint_32)(&idle); // eip
stack[stack_index--] = 0; // ebp
stack[stack_index--] = 0; // not used for anything
stack[stack_index--] = 0; // edi
stack[stack_index--] = 0; // esi
stack[stack_index--] = 0; // edx
stack[stack_index--] = 0; // ecx
stack[stack_index--] = 0; // ebx
stack[stack_index--] = 0; // eax
stack[stack_index--] = 0x10; // ds
stack[stack_index--] = 0x10; // es
stack[stack_index--] = 0x10; // fs
stack[stack_index--] = 0x10; // gs
stack[stack_index--] = 0x10; // ss
tasks[0].esp = (uint_32)&stack[stack_index + 1];
do_bootstrap_task_switch(tasks[0].esp);
}
extern "C" void do_task_switch(uint_32 *oldesp, uint_32 eip, uint_32 newesp, uint_32 newldt);
void task_switch(IDT::regs_t *regs)
{
do_task_switch(&tasks[cpid].esp, regs->eip, tasks[cpid].esp, (3 + cpid) << 3);
}
};
Code: Select all
global do_task_switch
do_task_switch:
; save old task's state
pushf
push cs
push dword [esp+8+12]
pusha
push ds
push es
push fs
push gs
push ss
mov eax, [esp+64+16]
mov [eax], esp
; and load the new one
lldt [esp+64+4]
mov esp, [esp+64+8]
pop ss
pop gs
pop fs
pop es
pop ds
popa
iret
global do_bootstrap_task_switch
do_bootstrap_task_switch:
mov esp, [esp+4]
pop ss
pop gs
pop fs
pop es
pop ds
popa
iret
(By the way, I'd also like to be able to run all user tasks in ring 3, and services (this is a microkernel) in ring 0 or 1. Can I do that without messing with TSSes?)