about summary refs log blame commit diff stats
path: root/kernel.soso/process.c
blob: 7964467b8aca22c851db517390bc0e6216bcbcf6 (plain) (tree)




























                                     
                            


                                 
                           


                                
                                      


                                     
                          











































                                                                                                                           

                                                         




                             
                       





                       
                                                     



                                                               
                                         













                                        
                                              



                       
                       








                                                                                   
                                                                          


                                                   
                                                        



                                                  
                                                                            


                                                                                               
                                                        
 
                                         










                                                    
                                         











                                                    
                                                                                                                                                       


                                                                                                                      
                                                                                                                                                                                                 
                                                                          
                         


                                        
                        




























                                                                                                           
                 






                                                             
              









































                                                                
                             




                     
                  


                                                       
                                















                                                                           
                                    



                                                       
                                 









                                                                 
                                       


                                  
          





                                                                                    
                                       

                                  


                                       










                                                                                                             
                                               












                                          

                                                



                                     
                          
                              


                                                    













                                                          

                                                                               




                     
                          


               
                    






















                                           
                        
                                            

                       
                  





                                                                
                          


                                     
                                       




                        
                                    



                           

                                                









                         
                                                      



                           
                                                          
 
                                                
                                                                              
                                     











                                  
                                                           



                           

                                                










                                  
                                        

                             

                                      







                     
                                           

                             

                                







                     
                               


                        
                            


                          
                                    

                             

                          







                        
                                       

                             

                                  







                        
                                          
 
                                           

                                        
                                       



                                     
                           











                                                                                                    
                                              

                                     

                                                               


















                                                          
                                       



                                                                   
              









                                                    


                                             


                                     
                                          



                                            
                                             


                                                                   
                                       




                                                      
                                           




                                    
                              



                                                     
          







                                                                                           
                                        
                                                                                                                 



                           
                          

 
                                           










                                                         


                           


                                    
          
                             



























                                  
                           

          
#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)
        );
}