#include "process.h" #include "common.h" #include "alloc.h" #include "vmm.h" #include "descriptortables.h" #include "elf.h" #include "screen.h" #include "debugprint.h" #include "isr.h" #include "timer.h" #include "message.h" #define MESSAGE_QUEUE_SIZE 64 Process* gKernelProcess = NULL; Thread* gFirstThread = NULL; Thread* gCurrentThread = NULL; Thread* gDestroyedThread = NULL; uint32 gProcessIdGenerator = 0; uint32 gThreadIdGenerator = 0; uint32 gSystemContextSwitchCount = 0; uint32 gLastUptimeSeconds = 0; extern Tss gTss; uint32 generateProcessId() { return gProcessIdGenerator++; } uint32 generateThreadId() { return gThreadIdGenerator++; } uint32 getSystemContextSwitchCount() { return gSystemContextSwitchCount; } void initializeTasking() { Process* process = (Process*)kmalloc(sizeof(Process)); memset((uint8*)process, 0, sizeof(Process)); strcpy(process->name, "[kernel]"); process->pid = generateProcessId(); process->pd = (uint32*) KERN_PAGE_DIRECTORY; process->workingDirectory = getFileSystemRootNode(); gKernelProcess = process; Thread* thread = (Thread*)kmalloc(sizeof(Thread)); memset((uint8*)thread, 0, sizeof(Thread)); thread->owner = gKernelProcess; thread->threadId = generateThreadId(); thread->userMode = 0; thread->state = TS_RUN; thread->messageQueue = FifoBuffer_create(sizeof(SosoMessage) * MESSAGE_QUEUE_SIZE); Spinlock_Init(&(thread->messageQueueLock)); thread->regs.cr3 = (uint32) process->pd; uint32 selector = 0x10; thread->regs.ss = selector; thread->regs.eflags = 0x0; thread->regs.cs = 0x08; thread->regs.eip = NULL; thread->regs.ds = selector; thread->regs.es = selector; thread->regs.fs = selector; thread->regs.gs = selector; thread->regs.esp = 0; //no need because this is already main kernel thread. ESP will written to this in first schedule. thread->kstack.ss0 = 0x10; thread->kstack.esp0 = 0;//For kernel threads, this is not required gFirstThread = thread; gCurrentThread = thread; } static int getStringArrayItemCount(char *const array[]) { if (NULL == array) { return 0; } int i = 0; const char* a = array[0]; while (NULL != a) { a = array[++i]; } return i; } static char** cloneStringArray(char *const array[]) { int itemCount = getStringArrayItemCount(array); char** newArray = kmalloc(sizeof(char*) * (itemCount + 1)); for (int i = 0; i < itemCount; ++i) { const char* str = array[i]; int len = strlen(str); char* newStr = kmalloc(len + 1); strcpy(newStr, str); newArray[i] = newStr; } newArray[itemCount] = NULL; return newArray; } static void destroyStringArray(char** array) { char* a = array[0]; int i = 0; while (NULL != a) { kfree(a); a = array[++i]; } kfree(array); } //This function must be called within the correct page directory for target process static void copyArgvEnvToProcess(char *const argv[], char *const envp[]) { char** destination = (char**)USER_ARGV_ENV_LOC; int destinationIndex = 0; //printkf("ARGVENV: destination:%x\n", destination); int argvCount = getStringArrayItemCount(argv); int envpCount = getStringArrayItemCount(envp); //printkf("ARGVENV: argvCount:%d envpCount:%d\n", argvCount, envpCount); char* stringTable = (char*)USER_ARGV_ENV_LOC + sizeof(char*) * (argvCount + envpCount + 2); //printkf("ARGVENV: stringTable:%x\n", stringTable); for (int i = 0; i < argvCount; ++i) { strcpy(stringTable, argv[i]); destination[destinationIndex] = stringTable; stringTable += strlen(argv[i]) + 2; destinationIndex++; } destination[destinationIndex++] = NULL; for (int i = 0; i < envpCount; ++i) { strcpy(stringTable, envp[i]); destination[destinationIndex] = stringTable; stringTable += strlen(envp[i]) + 2; destinationIndex++; } destination[destinationIndex++] = NULL; } Process* createUserProcessFromElfData(const char* name, uint8* elfData, char *const argv[], char *const envp[], Process* parent, FileSystemNode* tty) { return createUserProcessEx(name, generateProcessId(), generateThreadId(), NULL, elfData, argv, envp, parent, tty); } Process* createUserProcessEx(const char* name, uint32 processId, uint32 threadId, Function0 func, uint8* elfData, char *const argv[], char *const envp[], Process* parent, FileSystemNode* tty) { printkf("createUserProcessEx: %s %d %d\n", name, processId, threadId); if (0 == processId) { processId = generateProcessId(); } if (0 == threadId) { threadId = generateThreadId(); } Process* process = (Process*)kmalloc(sizeof(Process)); memset((uint8*)process, 0, sizeof(Process)); strcpy(process->name, name); process->pid = processId; process->pd = createPd();//our page directories are identity mapped so this is also a physical address. process->workingDirectory = getFileSystemRootNode(); Thread* thread = (Thread*)kmalloc(sizeof(Thread)); memset((uint8*)thread, 0, sizeof(Thread)); thread->owner = process; thread->threadId = threadId; thread->userMode = 1; thread->state = TS_RUN; thread->messageQueue = FifoBuffer_create(sizeof(SosoMessage) * MESSAGE_QUEUE_SIZE); Spinlock_Init(&(thread->messageQueueLock)); thread->regs.cr3 = (uint32) process->pd; //Since stack grows backwards, we must allocate previous page. So lets substract a small amount. uint32 stackp = USER_STACK-4; if (parent) { process->parent = parent; process->workingDirectory = parent->workingDirectory; process->tty = parent->tty; } if (tty) { process->tty = tty; } char** newArgv = cloneStringArray(argv); char** newEnvp = cloneStringArray(envp); //Change memory view (page directory) asm("mov %0, %%eax; mov %%eax, %%cr3"::"m"(process->pd)); initializeProcessHeap(process); initializeProcessMmap(process); copyArgvEnvToProcess(newArgv, newEnvp); destroyStringArray(newArgv); destroyStringArray(newEnvp); uint32 selector = 0x23; thread->regs.ss = selector; thread->regs.eflags = 0x0; thread->regs.cs = 0x1B; thread->regs.eip = (uint32)func; thread->regs.ds = selector; thread->regs.es = selector; thread->regs.fs = selector; thread->regs.gs = selector; thread->regs.esp = stackp; char* p_addr = getPageFrame4M(); char* v_addr = (char *) (USER_STACK - PAGESIZE_4M); addPageToPd(process->pd, v_addr, p_addr, PG_USER); thread->kstack.ss0 = 0x10; uint8* stack = (uint8*)kmalloc(KERN_STACK_SIZE); thread->kstack.esp0 = (uint32)(stack + KERN_STACK_SIZE - 4); thread->kstack.stackStart = (uint32)stack; Thread* p = gCurrentThread; while (p->next != NULL) { p = p->next; } p->next = thread; if (elfData) { printkf("about to load ELF data\n"); uint32 startLocation = loadElf((char*)elfData); if (startLocation > 0) { thread->regs.eip = startLocation; } } //Restore memory view (page directory) asm("mov %0, %%eax ;mov %%eax, %%cr3":: "m"(gCurrentThread->regs.cr3)); open_fs_forProcess(thread, process->tty, 0);//0: standard input open_fs_forProcess(thread, process->tty, 0);//1: standard output open_fs_forProcess(thread, process->tty, 0);//2: standard error printkf("running process %d\n", process->pid); return process; } //This function should be called in interrupts disabled state void destroyThread(Thread* thread) { Spinlock_Lock(&(thread->messageQueueLock)); //TODO: signal the process somehow Thread* previousThread = getPreviousThread(thread); if (NULL != previousThread) { previousThread->next = thread->next; kfree((void*)thread->kstack.stackStart); FifoBuffer_destroy(thread->messageQueue); Debug_PrintF("destroying thread %d\n", thread->threadId); kfree(thread); if (thread == gCurrentThread) { gCurrentThread = NULL; } } else { printkf("Could not find previous thread for thread %d\n", thread->threadId); PANIC("This should not be happened!\n"); } } //This function should be called in interrupts disabled state void destroyProcess(Process* process) { Thread* thread = gFirstThread; Thread* previous = NULL; while (thread) { if (process == thread->owner) { if (NULL != previous) { previous->next = thread->next; kfree((void*)thread->kstack.stackStart); Spinlock_Lock(&(thread->messageQueueLock)); FifoBuffer_destroy(thread->messageQueue); Debug_PrintF("destroying thread id:%d (owner process %d)\n", thread->threadId, process->pid); kfree(thread); if (thread == gCurrentThread) { gCurrentThread = NULL; } thread = previous->next; continue; } } previous = thread; thread = thread->next; } //Cleanup opened files for (int i = 0; i < MAX_OPENED_FILES; ++i) { if (process->fd[i] != NULL) { close_fs(process->fd[i]); } } if (process->parent) { thread = gFirstThread; while (thread) { if (process->parent == thread->owner) { if (thread->state == TS_WAITCHILD) { thread->state = TS_RUN; } } thread = thread->next; } } Debug_PrintF("destroying process %d\n", process->pid); destroyPd(process->pd); kfree(process); } void threadStateToString(ThreadState state, uint8* buffer, uint32 bufferSize) { if (bufferSize < 1) { return; } buffer[0] = '\0'; if (bufferSize < 10) { return; } switch (state) { case TS_RUN: strcpy((char*)buffer, "run"); break; case TS_SLEEP: strcpy((char*)buffer, "sleep"); break; case TS_SUSPEND: strcpy((char*)buffer, "suspend"); break; case TS_WAITCHILD: strcpy((char*)buffer, "waitchild"); break; case TS_WAITIO: strcpy((char*)buffer, "waitio"); break; case TS_YIELD: strcpy((char*)buffer, "yield"); break; default: break; } } void waitForSchedule() { //printkf("Waiting for a schedule()\n"); enableInterrupts(); while (TRUE) { halt(); } disableInterrupts(); PANIC("waitForSchedule(): Should not be reached here!!!\n"); } void yield(uint32 count) { gCurrentThread->yield = count; gCurrentThread->state = TS_YIELD; enableInterrupts(); while (gCurrentThread->yield > 0) { halt(); } disableInterrupts(); } int32 getEmptyFd(Process* process) { int32 result = -1; beginCriticalSection(); for (int i = 0; i < MAX_OPENED_FILES; ++i) { if (process->fd[i] == NULL) { result = i; break; } } endCriticalSection(); return result; } int32 addFileToProcess(Process* process, File* file) { int32 result = -1; beginCriticalSection(); //printkf("addFileToProcess: pid:%d\n", process->pid); for (int i = 0; i < MAX_OPENED_FILES; ++i) { //printkf("addFileToProcess: i:%d fd[%d]:%x\n", i, i, process->fd[i]); if (process->fd[i] == NULL) { result = i; file->fd = i; process->fd[i] = file; break; } } endCriticalSection(); return result; } int32 removeFileFromProcess(Process* process, File* file) { int32 result = -1; beginCriticalSection(); for (int i = 0; i < MAX_OPENED_FILES; ++i) { if (process->fd[i] == file) { result = i; process->fd[i] = NULL; break; } } endCriticalSection(); return result; } Thread* getThreadById(uint32 threadId) { Thread* p = gFirstThread; while (p != NULL) { if (p->threadId == threadId) { return p; } p = p->next; } return NULL; } Thread* getPreviousThread(Thread* thread) { Thread* t = gFirstThread; while (t->next != NULL) { if (t->next == thread) { return t; } t = t->next; } return NULL; } Thread* getMainKernelThread() { return gFirstThread; } Thread* getCurrentThread() { return gCurrentThread; } BOOL isThreadValid(Thread* thread) { Thread* p = gFirstThread; while (p != NULL) { if (p == thread) { return TRUE; } p = p->next; } return FALSE; } BOOL isProcessValid(Process* process) { Thread* p = gFirstThread; while (p != NULL) { if (p->owner == process) { return TRUE; } p = p->next; } return FALSE; } static void switchToTask(Thread* current); static void updateMetrics(Thread* thread) { uint32 seconds = getUptimeSeconds(); if (seconds > gLastUptimeSeconds) { gLastUptimeSeconds = seconds; Thread* t = gFirstThread; while (t != NULL) { t->contextSwitchCount = t->totalContextSwitchCount - t->totalContextSwitchCountPrevious; t->totalContextSwitchCountPrevious = t->totalContextSwitchCount; t = t->next; } } ++gSystemContextSwitchCount; ++thread->totalContextSwitchCount; } void schedule(TimerInt_Registers* registers) { Thread* current = gCurrentThread; if (NULL != current) { if (current->next == NULL && current == gFirstThread) { //We are the only process, no need to schedule return; } current->regs.eflags = registers->eflags; current->regs.cs = registers->cs; current->regs.eip = registers->eip; current->regs.eax = registers->eax; current->regs.ecx = registers->ecx; current->regs.edx = registers->edx; current->regs.ebx = registers->ebx; current->regs.ebp = registers->ebp; current->regs.esi = registers->esi; current->regs.edi = registers->edi; current->regs.ds = registers->ds; current->regs.es = registers->es; current->regs.fs = registers->fs; current->regs.gs = registers->gs; if (current->regs.cs != 0x08) { //Debug_PrintF("schedule() - 2.1\n"); current->regs.esp = registers->esp_if_privilege_change; current->regs.ss = registers->ss_if_privilege_change; } else { //Debug_PrintF("schedule() - 2.2\n"); current->regs.esp = registers->esp + 12; current->regs.ss = gTss.ss0; } //Save the TSS from the old process current->kstack.ss0 = gTss.ss0; current->kstack.esp0 = gTss.esp0; current = current->next; while (NULL != current) { if (current->state == TS_YIELD) { if (current->yield > 0) { --current->yield; } if (current->yield == 0) { current->state = TS_RUN; } } if (current->state == TS_SLEEP) { uint32 uptime = getUptimeMilliseconds(); uint32 target = (uint32)current->state_privateData; if (uptime >= target) { current->state = TS_RUN; current->state_privateData = NULL; } } if (current->state == TS_RUN) { break; } current = current->next; } if (current == NULL) { //reached last process returning to first current = gFirstThread; } } else { //current is NULL. This means thread is destroyed, so start from the begining current = gFirstThread; } gCurrentThread = current;//Now gCurrentThread is the thread we are about to schedule to /* if (gCurrentThread->threadId == 5) { printkf("I am scheduling to %d and its EIP is %x\n", gCurrentThread->threadId, gCurrentThread->regs.eip); } */ updateMetrics(current); switchToTask(current); } static void switchToTask(Thread* current) { uint32 kesp, eflags; uint16 kss, ss, cs; //Set TSS values gTss.ss0 = current->kstack.ss0; gTss.esp0 = current->kstack.esp0; ss = current->regs.ss; cs = current->regs.cs; eflags = (current->regs.eflags | 0x200) & 0xFFFFBFFF; int oldMode; if (cs != 0x08) { oldMode = USERMODE; kss = current->kstack.ss0; kesp = current->kstack.esp0; } else { oldMode = KERNELMODE; kss = current->regs.ss; kesp = current->regs.esp; } //switchTask is in task.asm asm(" mov %0, %%ss; \ mov %1, %%esp; \ cmpl %[KMODE], %[mode]; \ je nextt; \ push %2; \ push %3; \ nextt: \ push %4; \ push %5; \ push %6; \ push %7; \ ljmp $0x08, $switchTask" :: \ "m"(kss), \ "m"(kesp), \ "m"(ss), \ "m"(current->regs.esp), \ "m"(eflags), \ "m"(cs), \ "m"(current->regs.eip), \ "m"(current), \ [KMODE] "i"(KERNELMODE), \ [mode] "g"(oldMode) ); }