#include "scheduler.h" #include "libc/stdio.h" #include "gdt.h" #include "paging.h" char show_tics=0; char scheduler_on=0; PROC procs[MAX_PROC]; u16 current_id; u16 nproc; void schedule(u32 *stack){ // Note that this function is called by clock() // and clock() is called by INT_CLOCK (cf core/int.S) // which stores all the processes registers on // the stack. // No proc to schedule, just skip this function if(nproc<2) return; PROC *p=&procs[current_id]; p->regs.gs=stack[0]; // cf function specification in scheduler.h p->regs.fs=stack[1]; p->regs.es=stack[2]; p->regs.ds=stack[3]; p->regs.edi=stack[4]; p->regs.esi=stack[5]; p->regs.ebp=stack[6]; // We do not take p->regs.esp=stack[7] // since it corresponds to the kernel stack // (it was push during the interruption) p->regs.edx=stack[8]; p->regs.ecx=stack[9]; p->regs.ebx=stack[10]; p->regs.eax=stack[11]; p->regs.eip=stack[12]; p->regs.cs=stack[13]; p->regs.eflags=stack[14]; p->regs.esp=stack[15]; p->regs.ss=stack[16]; // Get the next task to run current_id++; if(current_id>=nproc) current_id=0; p=&procs[current_id]; // Have a clean stack on next interrupt TSS.esp0=(u32)stack+17; asm("mov %%ss, %0": "=m" (TSS.ss0)); // Ensure interrupts are activated and NT flag is clear p->regs.eflags|=0x200; p->regs.eflags&=0xffffbfff; // Perform task switch asm( "push %0 \n\t" "jmp task_switch \n\t" :: "a" (p) ); } void clock(){ u32* stack; asm("mov %%ebp, %0":"=r" (stack)); stack=&stack[2]; // Make stack pointing to gs static int tic=0; static int sec=0; tic++; if(tic>=20){ tic=0; sec++; if(show_tics) putchar('.'); } if(scheduler_on==1) schedule(stack); } void task_create(void *task, int task_size){ if(nproc<=MAX_PROC){ // Allocate at least 1 page and 1 page for the user stack int allocated=1+task_size/4096+1; int* page_dir=paging_allocate(allocated); // Compute various addresses void *entry_point=PAGING_ENTRY_POINT_VIRT; // User stack start at the end of last allocated page (since addresses are going down) void *ustack=(void*)(PAGING_ENTRY_POINT_VIRT+allocated*4096); // Load the task into memory memcpy(task,PAGING_ENTRY_POINT_PHY(page_dir), task_size); // Setup process registers PROC *p=&procs[nproc]; p->regs.cs=0x23; // Task cs which is 0x30 along with prlv which is 0x3 p->regs.eip=entry_point; p->regs.ss=0x33; // Task ss which is 0x20 along with prlv which is 0x3 p->regs.esp=ustack; // Stack is here for now... p->regs.ds=0x2B; // GDT entry 0x28 along with prlv which is 0x3 p->regs.eax=0; p->regs.ebx=0; p->regs.ecx=0; p->regs.edx=0; // Manage eflags u32 eflags; asm ( "pushfl \n\t" // Retrieve flags "popl %%eax \n\t" "orl $0x200, %%eax \n\t" // Enable interrupt for the user task "and $0xffffbfff, %%eax \n\t" // Clear the NT flags "mov %%eax, %0 \n\t" // Get flags into eflag : "=m" (eflags) ); p->regs.eflags=eflags; // Setup other attributes p->id=nproc; p->pid=nproc; p->page_dir=page_dir; nproc++; } else print("Could not create more task!"); } void scheduler_start(){ if(nproc>0){ // Disable interrupt to not be interrupted asm("cli"); // Enable scheduling scheduler_on=1; // Save kernel stack state u32 *stack; asm("mov %%ebp, %0":"=r" (stack)); // Remove ebp from the (c call convention) and return address (call) TSS.esp0=(u32)stack+2; asm("mov %%ss, %0": "=m" (TSS.ss0)); // Get first stack current_id=0; PROC *p=&procs[current_id]; // Ensure interrupts are activated and NT flag is clear p->regs.eflags|=0x200; p->regs.eflags&=0xffffbfff; // Switch to user task asm( "push %0 \n\t" "jmp task_switch" :: "a" (p) ); } }