From 65409d2312e702a48d3cf5b32479d25266bda3c3 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Wed, 1 Jan 2020 18:22:19 -0800 Subject: 5858 Move script to create a Soso boot image into a sub-directory. I'm trying to streamline newcomer attention to just a couple of use cases. --- tools/iso/kernel.soso/process.c | 713 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 713 insertions(+) create mode 100644 tools/iso/kernel.soso/process.c (limited to 'tools/iso/kernel.soso/process.c') diff --git a/tools/iso/kernel.soso/process.c b/tools/iso/kernel.soso/process.c new file mode 100644 index 00000000..7964467b --- /dev/null +++ b/tools/iso/kernel.soso/process.c @@ -0,0 +1,713 @@ +#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) + ); +} -- cgit 1.4.1-2-gfad0