I'm reimplementing task-switching in my OS, using the discussion in this post as a guide: viewtopic.php?f=1&t=30601.
The way it works is, `context_switch` is periodically called from the PIT interrupt. The common IRQ handler saves and restores registers, so as long as `context_switch` leaves the machine with a valid mid-interrupt kernel stack, things should work fine.
The `context_switch` function looks like so:
Code: Select all
context_switch:
; EBX, ESI, EDI and EBP are callee-saved and can be used
mov ebx, [esp+4] ; get new task's esp from arg to ebx
push ecx
push edx
push eax
call read_eip ; the jmp call will return to here
_context_entry:
cmp esi, 0xdeadbeef ; if esi contains this magic value, then the task switch has completed and we should return to caller
je _context_switch_ret
push esp
mov esp, ebx ; load new process's esp
pop ebx ; pop eip into ebx
pop eax ; pop eax into eax
pop edx ; pop edx into edx
pop ecx ; pop ecx into ecx
mov esi, 0xdeadbeef ; magic value to detect when the new task has began executing
jmp ebx
_context_switch_ret:
ret
* load esp, pop address of _context_entry into eip, and zero into eax, edx, and ecx.
* ret gets executed, which will pop the stack frame.
This means that I must setup the initial stack so after the ret, control must return to the function which called context_switch(), which will return, and finally control will return to the interrupt handler, which will pop machine state and iret.
According to this, it seems the task construction must set up several stack frames, and must also know the address of the line after the context_switch() call. My intuition tells me this can't be the correct approach.
Could someone provide some insight on the correct way to setup a new task so it will return normally from the interrupt handler the first time it runs?
This is the current relevant code for constructing a new task:
Code: Select all
uint32_t stack_size = 0x2000;
char* stack = kmalloc(stack_size);
uint32_t* stack_top = (uint32_t*)(stack + stack_size - 0x4); // point to top of malloc'd stack
*(stack_top--) = 0xdeadd00d; //ecx
*(stack_top--) = 0xcafed00d; //edx
*(stack_top--) = 0xcafebabe; //eax
*(stack_top) = & _context_entry; //eip
initial_register_state.esp = (uint32_t)stack_top;
initial_register_state.ebp = (uint32_t)stack_top;
new_task->register_state = initial_register_state;