This is a good way to exit also, but again the systemcall handler doesn't pass the interrupt frame to exit, so I can't edit it from syscall_exit.
Should I build the interrupt frame myself and do an iret? (Same way with switching to user mode)
I think you are getting a little confused here...
all your exit syscall should do is:
add task to your remove_task queue
JMP to the scheduler to schedule the next task (this, of course, never returns, since the task is never re-scheduled)
the code that changes tasks here is:
the same code that changes tasks on irq0
the same code that changes tasks whenever the code calls a blocking syscall
the same code that changes tasks when you get an interrupt from the HDD
the same code that changes tasks when... any time you change tasks
therefore, there is nothing special you have to do, because it happens automatically -- the only difference between the exit syscall calling the scheduler and the irq0 handler calling the scheduler is that the task is never rescheduled after the exit syscall, and therefore the code never continues
actually, I would make a function that moves the current task to the remove queue and then calls SwitchThread() without returning... so that function (which I would consider part of the scheduler) would never return at all and all your exit function has to do to terminate the task is call SchedulerTerminateTask()
Thanks. But won't that kill kernel stack after a while?
Syscall is an interrupt, so processor pushes ss, esp, eflags, cs and eip, and my interrupt handler pushes all registers with segment registers.
Then I switch another task in half of interrupt handler, without clearing them.
It should kill the kernel stack as these values never gets popped off.
Also my task switching mechanism is a bit different.
When IRQ0 fires, it calls the task handler.
Task handler saves current register state to task's register state, then restores next task's state.
After iret, all register state of next task gets restored.
void task_handler(registers_t* regs)
memcpy(current_task->regs, regs, sizeof(registers_t));
if (!current_task->next || remove_queue) current_task = first_task;
else current_task = current_task->next;
rq_task_t* rq_task = remove_queue;
remove_queue = remove_queue->next;
task_t* current = first_task;
while (current->next != rq_task->task) current = current->next;
current->next = rq_task->task->next;
memcpy(regs, current_task->regs, sizeof(registers_t));
// asm volatile ("xchgw %bx, %bx"); (Bochs' magic break)
That is my task switch handler. CR3 (Page directory) changes in another part of code.
That is how I initialize tasking and switch to first task:
first_task = create_task(&kernel_idle, 0);
first_task->pid = 0;
first_task->name = "KERNEL_IDLE";
first_task->priority = PRIORITY_MID;
current_task = first_task;
set_kernel_stack((uintptr_t)memalign(0x10, 0x1000) + 0x1000);
// asm volatile ("xchgw %bx, %bx"); (Bochs debugger breakpoint)
asm volatile (" \
movl %%bx, %%ax; \
movl %%ax, %%ds; \
movl %%ax, %%es; \
movl %%ax, %%fs; \
movl %%ax, %%gs; \
" : : "b"(first_task->regs->ds));
asm volatile (" \
pushl %%eax; \
pushl %%ebx; \
pushl %%ecx; \
pushl %%edx; \
pushl %%edi; \
" : : "a"(first_task->regs->ss), "b"(first_task->regs->esp), "c"(first_task->regs->eflags), "d"(first_task->regs->cs), "D"(first_task->regs->eip));