ASM Code:
extern _oldesp, _curresp, _ktss
[global _task_switch]
_task_switch:
pushf ; Save registers
pusha
mov eax, [_oldesp] ; Save old esp
mov [eax], esp
mov esp, [_curresp] ; Switch to the new task
popa ; Restore registers
popf
ret ; Not doing an iret here because we return to the ISR handler which iret's for us
[global _load_tss]
_load_tss:
ltr [_ktss]
ret
C code - init tasks system:
void tasks_init() {
int ret;
/* If this is called twice it's a programming error. Make sure they
* know about it */
if(enabled)
panic("Attempted to called tasks_init twice!");
/* Initialize the tasks list */
procs = list_new();
/* Initialize the TSS */
memset(&ktss, 0, sizeof(ktss));
if(!kprocsdone) {
/* Create the kernel process */
printf("tasks: creating kernel process...\n");
debug("tasks", "creating kernel process...");
ret = exec("kernel", (entry_t)kernel, PRIORITIES);
if(ret != 0) {
if(ret == -1) {
panic("Failed to initialize kernel process!");
} else {
panic("Kernel process did not get PID 0!");
}
}
/* Set the current process */
LIST_FOREACH (procs, iter) {
process_t *myproc = (process_t *)iter;
currproc = myproc;
}
}
/* Set up the currproc and the TSS for the new process */
curresp = currproc->esp;
oldesp = &kstack;
ktss.ss0 = 0x10;
ktss.esp0 = currproc->esp;
/* Switch into multitasking mode. The only chance we have of getting
* past here is if tasks_deinit is called */
kprocsdone = 1;
enabled = 1;
load_tss();
task_switch();
}
C code - switch tasks:
void schedule() {
process_t *newproc;
if(enabled) {
/* Decrease the time to run */
currproc->timetorun--;
/* Check if the process has used up it's timeslice */
if(currproc->timetorun == 0) {
/* Basically this gives a process a timeslice the size
* of it's priority. So if the priority is 8, the
* process will get 8 ticks each time it runs */
currproc->timetorun = currproc->priority;
/* Find a new process to run. If there is only one
* process, don't switch */
LIST_FOREACH (procs, iter) {
newproc = (process_t *)iter;
if(newproc->pid > currproc->pid) {
if (newproc->running)
break;
}
newproc = NULL;
}
/* Grab the first entry in the list (should be the kernel process) */
if(newproc == NULL)
newproc = (process_t *)procs->next;
if(newproc->pid == currproc->pid)
return;
/* Save the address to the old process's ESP */
oldesp = &currproc->esp;
/* Change the TSS */
ktss.esp0 = newproc->esp;
load_tss();
/* Set currproc to the new process, and switch */
currproc = newproc;
curresp = newproc->esp;
task_switch();
}
}
}
TSS struct:
typedef struct {
uint16_t backlink, __blh;
uint32_t esp0;
uint16_t ss0, __ss0h;
uint32_t esp1;
uint16_t ss1, __ss1h;
uint32_t esp2;
uint16_t ss2, __ss2h;
uint32_t cr3;
uint32_t eip;
uint32_t eflags;
uint32_t eax, ecx, edx, ebx;
uint32_t esp, ebp, esi, edi;
uint16_t es, __esh;
uint16_t cs, __csh;
uint16_t ss, __ssh;
uint16_t ds, __dsh;
uint16_t fs, __fsh;
uint16_t gs, __gsh;
uint16_t ldt, __ldth;
uint16_t trace, bitmap;
} tss_t;