↑ Back to main site.

EPOS

Experimental Protected-mode Operating System

process.c
/*
EPOS
http://www.atanaslaskov.com/epos/

FILE:        process.h
DESCRIPTION: EPOS Process Manger

The prefixes "tm" stand for "task manager",
the old name of this part of the kernel.
 
Copyright (c) 2006, Atanas Laskov
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
   1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
   2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ATANAS LASKOV BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


#include "..\access\access.h"
#include "..\init\init.h"
#include "..\service\service.h"
#include "..\gdt\gdt.h"
#include "..\memory\memory.h"

#include "process.h"

#include "..\service\process\process.h"
#include "..\service\ipc\ipc.h"
#include "..\service\ports\ports.h"
#include "..\service\mem\mem.h"
#include "..\paging\paging.h"
#include "..\debug\dwnd.h"

void tmSwitchToProcess(PID h);
void tmNextProcess();
void tmSaveProcessState(unsigned long nt);

// States of all created processes
//
struct Process g_arProcess[MAX_PROCESS_CNT];
unsigned long g_nProcessCnt=0;

// ID of the current process
PID g_dwCrProcess=0;

// Misc data
//
unsigned char g_bFirst=1;
unsigned long g_dwCharge=0;
unsigned char g_bDontSaveState = 0;
extern unsigned long g_dwTick;

// Handler for the hardware timer interrupt (IRQ 0)
//
// This function implements Round-Robin strategy for time-sharing
// between active processes.
//
void tmirq(unsigned long sel)
{
    PID pid;
    static char a='*';
    unsigned long i = g_dwCrProcess+1;
   
    // Update timers
    //
    g_dwCharge=1;
    g_dwTick++;

    // If  the timer interrupt has occured in kernel mode exit now.
    // NOTE: The timer should be disabled when entering in the kernel
    //
    if( !(sel&0x1) ) return;

    // Examine all processes
    //
    pid = g_dwCrProcess;

    while(1)
    {
        // If we've reached the end of the list; move to PID 0
        if(i>=MAX_PROCESS_CNT) i=0;

        // If we've teached the same process again...
        if(i==g_dwCrProcess) break; // ...return and wait.

        // Select a process that is alive and not sleeping
        //
        if( (g_arProcess[i].bState&T_STATE__ALIVE) &&
            (!(g_arProcess[i].bState&T_STATE__ASLEEP)) ) {
           
            // Ok, this guy gets some processor time.
            //
            g_dwCrProcess = i;
            break;
        }
       
        // Next one
        i++;
    }

    // So do we need to switch the context ?
    //
    if( pid != g_dwCrProcess )
    {
        // Save the environment of the old process
        //
        pageDirectorySwitch(PAGING_DIR_KERNEL);
        tmSaveProcessState(pid);
        k_portClearMaskOf(pid);
        //k_irqOnSwitch();

        // Restore the environment of the new process
        //
        k_portSetMaskFor(g_dwCrProcess);
        pageDirectorySwitch(PAGING_DIR_PROCESS0 + tmGetCrProcessID());
       
        // Jump to the new process
        // Note that the EPOS kernel will stop executing after this call
        //
        tmIRetToProcess(
            g_dwCrProcess,
            &(g_arProcess[g_dwCrProcess].tsState
        ));
    }
   
    /*else {
    //If we are at the same process this means there is only 1 process.
    k_portClearMaskOf(pid);
    k_portSetMaskFor(g_dwCrProcess);
    }*/

}

// Halt in case of errors.
//
void tmhalt(char* erstr) {
    DEBUGREGISTER('t');
    DEBUGSET('t');
    DEBUGS('t', "PROCESS MANGER ERROR\n\n");
    DEBUGF('t', erstr);
    DEBUGF('t', "\n\nSystem halted.");
    DRAWDEBUG;
    while(1);
}

// Intialize process manager
//
void tmInit() {
    //Make all processes unselected
    unsigned long i;
    for(i=0; i<MAX_PROCESS_CNT; i++)
        g_arProcess[i].bState = 0;
}

// Create a new process
//
TRET tmCreateProcess(PID *ph, unsigned long dwBytes, char* strModule)
{
    struct IPCOpenLine ln;
    struct GDTDescriptor* pDesc;
    PID i,j;
    TRET  r;
   
    // Find free process state entry
    //
    for(i=0; i<MAX_PROCESS_CNT; i++)
    if(!(g_arProcess[i].bState&T_STATE__SELECTED))
    {
        // Set parent
        g_arProcess[i].dwPID_Parent = tmGetCrProcessID();
        strcpy(g_arProcess[i].strModule, strModule);

        // Allocate memory
        pageDirectorySwitch(PAGING_DIR_KERNEL);

        r = memAlloc(
            &(g_arProcess[i].hMemory),
            dwBytes + 8*1024    //8k for stack
        );
           
        if(r!=MEMRET_OK) { return T_RET_OUTOFMEMORY; }
        pDesc = gdtGetDescriptorLDT(i, 0);

        // Configure process segments
        //
        gdtSetBase(pDesc,  0x100000);
        gdtSetLimit(pDesc, tmGetProcessMemSize(i));
        pDesc++;
        gdtSetBase(pDesc,  0x100000);
        gdtSetLimit(pDesc, tmGetProcessMemSize(i));

        // Configure process pages
        //
        pageDirectorySize(
            PAGING_DIR_PROCESS0+i,
            0x100000 + dwBytes + 8*1024
        );
        //initFormatDirectory(PAGING_DIR_PROCESS0+i);
       
        pageMapPages(
            PAGING_DIR_PROCESS0+i, 0, 0,
            0x100000 + dwBytes + 8*1024
        );
       
        pageMapPages(
            PAGING_DIR_PROCESS0+i, 0x100000,
            tmGetProcessMembase(i),  dwBytes + 8*1024
        );

        // Mark the process state as "selected". This means it is
        // possible for the parent process to write into
        // the cihld's memory
        //
        *ph = i;
        g_arProcess[i].bState   = T_STATE__SELECTED;

        // Initial register state
        //
        g_arProcess[i].tsState.eax = 0;
        g_arProcess[i].tsState.ebx = 0;
        g_arProcess[i].tsState.ecx = 0;
        g_arProcess[i].tsState.edx = 0;
        g_arProcess[i].tsState.eflags = 0x200; //IOPL3
        g_arProcess[i].tsState.ebp = 0;
        g_arProcess[i].tsState.edi = 0;
        g_arProcess[i].tsState.esi = 0;
        g_arProcess[i].tsState.esp = dwBytes + 8*1024 - 4;
        g_arProcess[i].tsState.eip = 0;

        // Open IPC line to the kernel
        //
        ln.padAddress.bType = ADDRESS_PID;
        ln.padAddress.dwAddress = i;
        k_ipcOpenLine(&ln);
        g_arProcess[i].hKernelLine = ln.hLine;

        g_nProcessCnt++;

        return T_RET_OK;
    }
   
    return T_RET_TOOMANYPROCESSES;
}

// Kill process
//
TRET tmKillProcess(PID h)
{
    // Unselect and stop the process
    //
    if(h<MAX_PROCESS_CNT && (g_arProcess[h].bState&T_STATE__SELECTED))
    {
        struct IPCHLine l;

        // Discard process state, kernel IPC line, and free memory
        //
        g_arProcess[h].bState = 0;
        l.hLine = g_arProcess[h].hKernelLine;
       
        ipcCloseLine(&l);
        memFree(g_arProcess[h].hMemory);
       
        // Free process pages
        //
        pageDirectoryFree(PAGING_DIR_PROCESS0+h);

        // If (for some strange reasons) this was the last process
        // in existance, print out a warning and halt.
        //
        if( !(--g_nProcessCnt) )
            tmhalt("All processes are dead. Just halting now...");
           
    }
    else return T_RET_INVALIDPID;
   
    return T_RET_OK;
}

// Mark the process as "lviving". This will include it for
// consideration in the scheduler
//
void tmLiveProcess(PID h)
{
    if(h<MAX_PROCESS_CNT && (g_arProcess[h].bState&T_STATE__SELECTED))
    {
        g_arProcess[h].bState |= T_STATE__ALIVE;
        k_memProcessIsLive(h);
    }
    //else tmhalt("Process in not selected");
}

// Switch to a different process
//
void tmSwitchToProcess(PID h)
{
    if(h<MAX_PROCESS_CNT && (g_arProcess[h].bState&T_STATE__ALIVE))
        g_dwCrProcess = h;
    //else tmhalt("Process is not alive");
}

// Get current PID
//
unsigned long tmGetCrProcessID() {
    return g_dwCrProcess;
}

// Use the stack to extract register state of the currently executed
// process interupted by the timer. This function must be called in tmirq() to
// save the process state before witching to the new one.
//
// NOTE: Any changes of stack managment in idt\idtd.asm can ivalidate this code
//
#define PSBASE 0x0A0000

void tmSaveProcessState(unsigned long nt)
{
    if(g_bDontSaveState) {
        g_bDontSaveState = 0;
        return;
    }
   
    g_arProcess[nt].tsState.esp    = gdt_linear_stack_getdw(PSBASE-4-1-1*4);
    g_arProcess[nt].tsState.eflags = gdt_linear_stack_getdw(PSBASE-4-1-2*4);
    g_arProcess[nt].tsState.eip    = gdt_linear_stack_getdw(PSBASE-4-1-4*4);
    g_arProcess[nt].tsState.edx    = gdt_linear_stack_getdw(PSBASE-4-1-5*4);
    g_arProcess[nt].tsState.eax    = gdt_linear_stack_getdw(PSBASE-4-1-6*4);
    g_arProcess[nt].tsState.ebx    = gdt_linear_stack_getdw(PSBASE-4-1-7*4);
    g_arProcess[nt].tsState.ecx    = gdt_linear_stack_getdw(PSBASE-4-1-8*4);
    g_arProcess[nt].tsState.esi    = gdt_linear_stack_getdw(PSBASE-4-1-9*4);
    g_arProcess[nt].tsState.edi    = gdt_linear_stack_getdw(PSBASE-4-1-10*4);
    g_arProcess[nt].tsState.ebp    = gdt_linear_stack_getdw(PSBASE-4-1-11*4);
}

// Execute first process ( called immediately after kernel initialization )
//
void tmFirstProcess() {
    tmSwitchToProcess(0);
    tmIRetToProcess(g_dwCrProcess, &(g_arProcess[g_dwCrProcess].tsState));
}

// Temporaly suspend a process
//
unsigned char tmSleep(PID h)
{
    if(h>=MAX_PROCESS_CNT) return T_RET_INVALIDPID;
    if(!(g_arProcess[h].bState&T_STATE__ALIVE)) return T_RET_INVALIDPID;
   
    g_arProcess[h].bState |= T_STATE__ASLEEP;

    return T_RET_OK;
}

// Resume execuing the process
//
unsigned char tmWakeUp(PID h)
{
    //Clear the SLEEP flag
    if(h>=MAX_PROCESS_CNT)return T_RET_INVALIDPID;
    if(!(g_arProcess[h].bState&T_STATE__ALIVE))return T_RET_INVALIDPID;
    g_arProcess[h].bState &= (~T_STATE__ASLEEP);
    return T_RET_OK;
}

// Get various things
//
unsigned short tmGetProcessState(PID h) {
    return g_arProcess[h].bState;
}

HMEMORY tmGetProcessMemory(PID h) {
    return g_arProcess[h].hMemory;
}

unsigned long  tmGetProcessMembase(PID h) {
    struct MemBlock m;
    memBlockInfo(g_arProcess[h].hMemory, &m);
    return m.dwStart;
}

unsigned long  tmGetProcessMemSize(PID h) {
    struct MemBlock m;
    memBlockInfo(g_arProcess[h].hMemory, &m);
    return m.dwEnd-m.dwStart;
}

unsigned long  tmGetProcessLine(PID h) {
    return g_arProcess[h].hKernelLine;
}

PID            tmGetProcessParent(PID h) {
    if(h>=MAX_PROCESS_CNT)return MAX_PROCESS_CNT;
    return g_arProcess[h].dwPID_Parent;
}

void  tmSetCrProcesID_NoSwitch(unsigned long dwPID) {
    g_dwCrProcess = dwPID;
}

unsigned long  tmGetCharge() {
    return g_dwCharge;
}

void  tmAddCharge() {
    g_dwCharge++;
}

char* tmGetProcessModule(PID h) {
    return g_arProcess[h].strModule;
}

// Call a function contained in the specifed process
//
void tmCallLocalFunction(PID h, unsigned long dwEntryPoint)
{
    unsigned long ptr;
   
    if( h==tmGetCrProcessID() ) {
        g_bDontSaveState = 0;
        tmSaveProcessState(h);
        g_bDontSaveState = 1;
    }
   
    // First, push the current eip (instruction pointer) on the stack
    // so the process can return to it
    //
    g_arProcess[h].tsState.esp-=4;
    ptr = tmGetProcessMembase(h)+g_arProcess[h].tsState.esp-0x600;
    *((unsigned long*)(ptr)) = g_arProcess[h].tsState.eip;

    // Next, change eip to the address of the function we want to call
    // Next time the process is activated it will jump there
    //
    g_arProcess[h].tsState.eip = dwEntryPoint;
   
    /*setpos(5,5);
    puts("ESP>>");putd(g_arProcess[h].tsState.esp); puts("   ");*/

}

unsigned char tmNext() {
    //Imediatelly switch to the next process
    //tmirq(7);
    return T_RET_OK;
}