about summary refs log tree commit diff stats
path: root/tools/iso/kernel.soso/process.c
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2020-01-01 18:22:19 -0800
committerKartik Agaram <vc@akkartik.com>2020-01-01 18:42:48 -0800
commit65409d2312e702a48d3cf5b32479d25266bda3c3 (patch)
tree62a7262fce61f2302109246da4536ce6f8e9ef80 /tools/iso/kernel.soso/process.c
parenta6da50ad30d2e1825575ffef497ab450a8f26e94 (diff)
downloadmu-65409d2312e702a48d3cf5b32479d25266bda3c3.tar.gz
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.
Diffstat (limited to 'tools/iso/kernel.soso/process.c')
-rw-r--r--tools/iso/kernel.soso/process.c713
1 files changed, 713 insertions, 0 deletions
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)
+        );
+}