#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(){ // Note that this function is called by clock // clock is called by INT_CLOCK (core/int.S) // which store all the process information on // the stack. Thus, knowing the C calling conventions // and that schedule() is call by two functions with no parameters, // the first process register can be accessed by ebp+2 u32 *stack; asm("mov %%ebp, %0":"=r" (stack)); // No proc to schedule if(nproc<2) return; PROC *p=&procs[current_id]; p->regs.gs=stack[2]; // ebp+2=gs cf note above p->regs.fs=stack[3]; p->regs.es=stack[4]; p->regs.ds=stack[5]; p->regs.edi=stack[6]; p->regs.esi=stack[7]; p->regs.ebp=stack[8]; // We do not take p->regs.esp=stack[9] // since it corresponds to the kernel stack // (it was push during the interruption) p->regs.edx=stack[10]; p->regs.ecx=stack[11]; p->regs.ebx=stack[12]; p->regs.eax=stack[13]; p->regs.eip=stack[14]; p->regs.cs=stack[15]; p->regs.eflags=stack[16]; p->regs.esp=stack[17]; p->regs.ss=stack[18]; current_id++; if(current_id>=nproc) current_id=0; // Have a clean stack on next interrupt TSS.esp0=(u32)stack+19; 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( "mov %0, %%esi \n\t" "jmp task_switch \n\t" :: "a" (p) ); } void clock(){ static int tic=0; static int sec=0; tic++; if(tic>=20){ tic=0; sec++; if(show_tics) putchar('.'); } if(scheduler_on==1) schedule(); } void run_task(int *page_dir, void *task, int task_size){ // Compute various addresses int*pt_addr=(int*)PADDR(page_dir[1]); void *entry_point=(void*)(PADDR(pt_addr[3])); void *ustack=(void*)((int)entry_point+0xFF); // Load the task into memory memcpy(task,entry_point, task_size); // Load page directory asm( "mov %0, %%eax \n\t" "mov %%eax,%%cr3 \n\t" :: "b"(page_dir) ); // Setup users adresses // Switch to user task asm ( "cli \n\t" // Ensure we do not get interrupted "movl %%ss, %%eax \n\t" "movl %%eax, %0 \n\t" // Save kernel ss segment into the TSS "movl %%esp, %1 \n\t" // Save kernel esp into the TSS BEFORE setting up the stack "pushl $0x33 \n\t" // Push task ss which is 0x30 along with prlv which is 0x3 "pushl %2 \n\t" // Push task esp "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 "push %%eax \n\t" // Push task flags "push $0x23 \n\t" // Push task cs which is 0x20 along with prlv which is 0x3 "push %3 \n\t" // Push task entry point "mov $0x2B, %%eax \n\t" // GDT entry 0x28 along with prlv which is 0x3 "mov %%eax, %%ds \n\t" // Setting up user data segment "iret \n\t" // Launch user task : "=m" (TSS.ss0), "=m" (TSS.esp0) : "b" (ustack), "c" (entry_point) ); }