↑ Back to main site.

EPOS

Experimental Protected-mode Operating System

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

FILE:        ipc.c
DESCRIPTION: Inter Process-Communication IPC as message passing
 
BSD LICENSE
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 "..\..\memory\memory.h"
#include "..\..\process\process.h"
#include "..\..\gdt\gdt.h"
#include "..\process\process.h"
#include "..\..\paging\paging.h"

#include "ipc.h"

// Globals
//
struct IPCMemTransfer{
    unsigned char bMemoryTransfer;
    void* pMemory;
    unsigned long dwTransferSize;
};

struct IPCLine_internal{
    struct IPCInbox inbox[2];
    unsigned char bState;
    unsigned char bUndeath;

    struct IPCMemTransfer LastMessageOut[2];
};

static HMEMORY                  g_hMem;
static struct IPCLine_internal *g_arLines;

// Initialize IPC
//
void k_ipcInit()
{
    //Allocate memory
    unsigned char r;
    HLINE i;
    r = memAlloc(&g_hMem, (IPC_MAX_LINE_CNT*sizeof(struct IPCLine_internal)));
    if(r!=MEMRET_OK)
    {
        puts("\nKRNL: Out of memory");
        khalt();
    }
    g_arLines = memFormPointer(g_hMem);

    //Invalidate lines
    for(i=0; i<IPC_MAX_LINE_CNT; i++)
    {
        g_arLines[i].bState = LINE_CLOSED;
        g_arLines[i].bUndeath = 0;
    }
}

// The kernel indicates a process is dead;
// Invalidate all IPC lines for this process.
//
void k_ipcProcessDeath(PID dwPID)
{
    unsigned long i;

    for(i=0; i<IPC_MAX_LINE_CNT; i++)
    {
        if(g_arLines[i].bState==LINE_OPEN)
            if( (g_arLines[i].inbox[0].dwOwnerPID==dwPID) ||
                (g_arLines[i].inbox[1].dwOwnerPID==dwPID) ) {
               
                g_arLines[i].bUndeath = 1;
            }
    }
}

// Open a new IPC line between the kernel and a user process
//
void k_ipcOpenLine(struct IPCOpenLine *p)
{
    // Put the line in "creating" state
    ipcOpenLine(p);

    // Initialize state
    g_arLines[p->hLine].inbox[0].dwOwnerPID = PID_KERNEL;
    g_arLines[p->hLine].inbox[0].bFlags = 0;
    g_arLines[p->hLine].inbox[1].bFlags = 0;
    g_arLines[p->hLine].bState = LINE_OPEN;
    g_arLines[p->hLine].bUndeath = 0;
}

// Send message from the kernel
//
void k_ipcSend(struct IPCTransfer *p)
{
    struct IPCMessage* pProcessMsg;
    struct IPCInbox  * pInbox;
    unsigned long i;
    unsigned char nThis, nDest;
   
    // Validate request
    //
    if( (p->hLine>=IPC_MAX_LINE_CNT) ||
        (g_arLines[p->hLine].bState!=LINE_OPEN))
    {
        p->st = IPC_ST_INVLINE;
        return;
    }

    nThis = 1;
    nDest = 0;

    // Inbox full?
    //
    pInbox = g_arLines[p->hLine].inbox+nThis;
   
    if(pInbox->dwMsgCnt>=IPC_INBOX_SIZE) {
        p->st = IPC_INBOXFULL;
        return;
    }

    pProcessMsg = pInbox->arPMsg[pInbox->dwMsgCnt];

    // Write message to the destination inbox
    //
    pProcessMsg->dwMessage       = p->msg.dwMessage;
    pProcessMsg->dwSize          = p->msg.dwSize;
    pProcessMsg->bMemoryTransfer = 0;
   
    if( pProcessMsg->dwSize>IPC_MSG_BUF_SIZE )
        pProcessMsg->dwSize=IPC_MSG_BUF_SIZE;
   
    for(i=0; i<pProcessMsg->dwSize; i++) {
        // NOTE: This is probably not the best way
        // to copy the message
        pProcessMsg->arData[i] = p->msg.arData[i];
    }
   
    pInbox->dwMsgCnt++;

    // If the target process is waiting
    // for a message, unblock it.
    //
    if( pInbox->bFlags & IPC_INBOX_WAITING )
    {
        unsigned long dwPID;

        // Send the messege
        //
        if( !(pInbox->bFlags&IPC_INBOX_DONT_RECIVE) )
        {
            dwPID = tmGetCrProcessID();
            tmSetCrProcesID_NoSwitch(pInbox->dwOwnerPID);
            ipcRecive(pInbox->pUnblockingMsg);
            tmSetCrProcesID_NoSwitch(dwPID);
        }

        // Unblock the process
        //
        pInbox->bFlags  = 0;
        tmWakeUp(pInbox->dwOwnerPID);
    }

    // All is well :-).
    p->st = IPC_ST_OK;
}

// User process requests to open a line to another
// user process
//
void ipcOpenLine(struct IPCOpenLine *p)
{
    // Validate PID
    //
    unsigned char r;
    unsigned long j;
    HLINE i;
    PID dwOtherSidePID;
    struct IPCTransfer ipctTransfer;

    r = k_pGetPID(&(p->padAddress), &dwOtherSidePID);
   
    if(r!=P_S_OK) {
        p->st = IPC_INVPROCESS;
        return;
    }
   
    if( !(tmGetProcessState(dwOtherSidePID)&T_STATE__SELECTED) ) {
        p->st = IPC_INVPROCESS;
        return;
    }

    // Find a free line descriptor
    //
    for(i=0; i<IPC_MAX_LINE_CNT; i++)
        if(g_arLines[i].bState==LINE_CLOSED)
        {
            // Mark line state as "creating"
            //
            g_arLines[i].bState=LINE_CREATING;
            g_arLines[i].bUndeath = 0;
            g_arLines[i].inbox[0].dwOwnerPID = tmGetCrProcessID();
            g_arLines[i].inbox[1].dwOwnerPID = dwOtherSidePID;
            g_arLines[i].inbox[0].bFlags = 0;
            g_arLines[i].inbox[1].bFlags = 0;
            p->hLine = i;

            // Format both inboxes
            //
            g_arLines[i].inbox[0].dwMsgCnt   = 0;
            g_arLines[i].inbox[1].dwMsgCnt   = 0;
            for(j=0; j<IPC_INBOX_SIZE; j++)
            {
                g_arLines[i].inbox[0].arPMsg[j] =
                    g_arLines[i].inbox[0].arMsg+j;
                   
                g_arLines[i].inbox[1].arPMsg[j] =
                    g_arLines[i].inbox[1].arMsg+j;
            }
            p->st = IPC_ST_OK;

            // Inform recipient process
            //
            ipctTransfer.msg.dwMessage = MSG_KRN_WANT_LINE;
            ipctTransfer.msg.dwSize    = sizeof(HLINE);
            *((HLINE*)ipctTransfer.msg.arData) = i;
            ipctTransfer.hLine = tmGetProcessLine(dwOtherSidePID);
            k_ipcSend(&ipctTransfer);
            p->st = ipctTransfer.st;
            if(tmGetCrProcessID()==2){ putd(p->st);}
            return;
        }
    p->st = IPC_ST_OUTOFLINES;
}

// Get detailed information about an IPC line
//
void ipcGetLineInfo(struct IPCPID *p)
{
    HLINE i;
   
    for(i=0; i<IPC_MAX_LINE_CNT; i++)
        if( (g_arLines[i].bState==LINE_CREATING) &&
            (g_arLines[i].inbox[1].dwOwnerPID==tmGetCrProcessID()))
        {
            p->hLine = i;
            p->dwRequestorPID = g_arLines[i].inbox[0].dwOwnerPID;
            p->st = IPC_ST_OK;
            return;
        }
       
    p->st = IPC_ST_INVLINE;
}

// An user process accepts the request of another process
// for opening an IPC line
//
void ipcAcceptOpen(struct IPCHLine *p)
{
    // Validate request
    //
    struct IPCTransfer ipctTransfer;

    if( (p->hLine>=IPC_MAX_LINE_CNT) ||
        (g_arLines[p->hLine].bState!=LINE_CREATING) ||
        (g_arLines[p->hLine].inbox[1].dwOwnerPID!=tmGetCrProcessID()) )
    {
        p->st = IPC_ST_INVLINE;
        return;
    }

    // Establish connection
    g_arLines[p->hLine].bState = LINE_OPEN;

    // Inform line requester
    //
    ipctTransfer.msg.dwMessage = MSG_KRN_LINE_OPENED;
    ipctTransfer.msg.dwSize    = sizeof(HLINE);
   
    *((HLINE*)ipctTransfer.msg.arData) = p->hLine;
   
    ipctTransfer.hLine = tmGetProcessLine(
        g_arLines[p->hLine].inbox[0].dwOwnerPID
    );
   
    k_ipcSend(&ipctTransfer);
   
    p->st = ipctTransfer.st;
}

// An user process denies the request of another process
// for opening an IPC line
//
void ipcDenyOpen(struct IPCHLine *p)
{
    // Validate request
    //
    struct IPCTransfer ipctTransfer;

    if( (p->hLine>=IPC_MAX_LINE_CNT) ||
        (g_arLines[p->hLine].bState!=LINE_CREATING) ||
        (g_arLines[p->hLine].inbox[1].dwOwnerPID!=tmGetCrProcessID()) )
    {
        p->st = IPC_ST_INVLINE;
        return;
    }

    // Establish connection
    g_arLines[p->hLine].bState = LINE_CLOSED;

    // Inform line requester
    //
    ipctTransfer.msg.dwMessage = MSG_KRN_LINE_CLOSED;
    ipctTransfer.msg.dwSize    = sizeof(HLINE);
    *((HLINE*)ipctTransfer.msg.arData) = p->hLine;
   
    ipctTransfer.hLine = tmGetProcessLine(
        g_arLines[p->hLine].inbox[0].dwOwnerPID
    );
   
    k_ipcSend(&ipctTransfer);
    p->st = ipctTransfer.st;
}

// An user process wants to close an IPC line
//
void ipcCloseLine(struct IPCHLine *p)
{
    // Validate request
    //
    struct IPCTransfer ipctTransfer;

    if( (p->hLine>=IPC_MAX_LINE_CNT) ||
        (g_arLines[p->hLine].bState!=LINE_OPEN))
    {
        p->st = IPC_ST_INVLINE;
        return;
    }

    // Close the line
    //
    if(g_arLines[p->hLine].inbox[0].dwOwnerPID==tmGetCrProcessID())
    {
        g_arLines[p->hLine].bState = LINE_CLOSED;

        // Informate the other side (1)
        //
        ipctTransfer.msg.dwMessage = MSG_KRN_LINE_CLOSED;
        ipctTransfer.msg.dwSize    = sizeof(HLINE);
        *((HLINE*)ipctTransfer.msg.arData) = p->hLine;
       
        ipctTransfer.hLine = tmGetProcessLine(
            g_arLines[p->hLine].inbox[1].dwOwnerPID
        );
       
        k_ipcSend(&ipctTransfer);
        p->st = ipctTransfer.st;
    }
    else if(g_arLines[p->hLine].inbox[1].dwOwnerPID==tmGetCrProcessID())
    {
        g_arLines[p->hLine].bState = LINE_CLOSED;

        // Inform the other side (0)
        //
        ipctTransfer.msg.dwMessage = MSG_KRN_LINE_CLOSED;
        ipctTransfer.msg.dwSize    = sizeof(HLINE);
        *((HLINE*)ipctTransfer.msg.arData) = p->hLine;
       
        ipctTransfer.hLine = tmGetProcessLine(
            g_arLines[p->hLine].inbox[0].dwOwnerPID
        );
       
    }else p->st = IPC_ST_INVLINE;
}

// Send a message
//
void ipcSend(struct IPCTransfer *p)
{
    // Validate request
    //
    struct IPCMessage* pProcessMsg;
    struct IPCInbox  * pInbox;
    unsigned long i;
    unsigned char nThis, nDest;
    if( (p->hLine>=IPC_MAX_LINE_CNT) ||
        (g_arLines[p->hLine].bState!=LINE_OPEN))
    {
        p->st = IPC_ST_INVLINE;
        return;
    }

    // Determine direction
    //
    if(g_arLines[p->hLine].inbox[0].dwOwnerPID==tmGetCrProcessID())
    {
        nThis = 0;
        nDest = 1;
    }
    else if(g_arLines[p->hLine].inbox[1].dwOwnerPID==tmGetCrProcessID())
    {
        nThis = 1;
        nDest = 0;
    }
    else {
        p->st = IPC_ST_INVLINE;
        return;
    }

    // Inbox is full ?
    //
    pInbox = g_arLines[p->hLine].inbox+nDest;
    if(pInbox->dwMsgCnt>=IPC_INBOX_SIZE) {
        p->st = IPC_INBOXFULL;
        return;
    }

    // Find message index
    pProcessMsg = pInbox->arPMsg[pInbox->dwMsgCnt];

    // Write message to the infox
    //
    pProcessMsg->dwMessage   = p->msg.dwMessage;
    pProcessMsg->dwSize      = p->msg.dwSize;
    pProcessMsg->bMemoryTransfer = p->msg.bMemoryTransfer;
    pProcessMsg->pMemory         = p->msg.pMemory;
    pProcessMsg->dwTransferSize  = p->msg.dwTransferSize;
   
    if(pProcessMsg->dwSize>IPC_MSG_BUF_SIZE)
        pProcessMsg->dwSize=IPC_MSG_BUF_SIZE;
   
    for(i=0; i<pProcessMsg->dwSize; i++) {
        // NOTE: This is probably not the best way
        // to copy the message
        pProcessMsg->arData[i] = p->msg.arData[i];
    }
   
    pInbox->dwMsgCnt++;

    // If target process is waiting for a message, unblock it
    //
    if(pInbox->bFlags & IPC_INBOX_WAITING)
    {
        unsigned long dwPID;

        // Send the messege
        //
        if( !(pInbox->bFlags&IPC_INBOX_DONT_RECIVE) )
        {
            dwPID = tmGetCrProcessID();
            tmSetCrProcesID_NoSwitch(pInbox->dwOwnerPID);
            ipcRecive(pInbox->pUnblockingMsg);
            tmSetCrProcesID_NoSwitch(dwPID);
        }

        // Unblock the process
        //
        pInbox->bFlags  = 0;
        tmWakeUp(pInbox->dwOwnerPID);
    }

    // All is well :-)
    p->st = IPC_ST_OK;
}

// Extended send
//
inline void ipcReciveEx(struct IPCTransfer *p, char bBlock)
{
    // Validate request
    //
    struct IPCMessage* pProcessMsg;
    struct IPCInbox  * pInbox;
    unsigned long i;
    unsigned char nThis, nDest;

    if( (p->hLine>=IPC_MAX_LINE_CNT) ||
        (g_arLines[p->hLine].bState!=LINE_OPEN))
    {
        p->st = IPC_ST_INVLINE;
        return;
    }

    // Determine direction
    //
    if(g_arLines[p->hLine].inbox[0].dwOwnerPID==tmGetCrProcessID())
    {
        nThis = 0;
        nDest = 1;
    }else
    if(g_arLines[p->hLine].inbox[1].dwOwnerPID==tmGetCrProcessID())
    {
        nThis = 1;
        nDest = 0;
    }else
    {
        p->st = IPC_ST_INVLINE;
        return;
    }

    // Inbox empty ?
    //
    pInbox = g_arLines[p->hLine].inbox+nThis;
    if(pInbox->dwMsgCnt==0)
    {
        // Inactive line ?
        //
        if( (g_arLines[p->hLine].bUndeath)
            && (g_arLines[p->hLine].inbox[nDest].dwMsgCnt==0))
        {
            struct IPCTransfer ipctTransfer;

            // Kill it
            g_arLines[p->hLine].bState = LINE_CLOSED;

            // Inform the other side this line is no longer valid
            //
            ipctTransfer.msg.dwMessage = MSG_KRN_LINE_CLOSED;
            ipctTransfer.msg.dwSize    = sizeof(HLINE);
           
            *((HLINE*)ipctTransfer.msg.arData) = p->hLine;
           
            ipctTransfer.hLine = tmGetProcessLine(
                g_arLines[p->hLine].inbox[nThis].dwOwnerPID
            );
           
            k_ipcSend(&ipctTransfer);
        }
       
        //Blocking primitive ?
        //
        else if(bBlock)
        {
            g_arLines[p->hLine].inbox[nThis].bFlags
                |= IPC_INBOX_WAITING;
               
            g_arLines[p->hLine].inbox[nThis].pUnblockingMsg = p;
           
            p->st = IPC_ST_OK;
            tmSleep(tmGetCrProcessID());
            tmAddCharge();
           
            return;
        }

        p->st = IPC_INBOXEMPTY;
        return;
    }

    // Extract first message
    //
    pProcessMsg             = pInbox->arPMsg[0];
    p->msg.dwMessage        = pProcessMsg->dwMessage;
    p->msg.dwSize           = pProcessMsg->dwSize;
    p->msg.bMemoryTransfer  = pProcessMsg->bMemoryTransfer;
    p->msg.pMemory          = pProcessMsg->pMemory;
    p->msg.dwTransferSize   = pProcessMsg->dwTransferSize;
   
    for(i=0; i<p->msg.dwSize; i++)
        p->msg.arData[i] = pProcessMsg->arData[i];

    // Save memory attachment pointer for later extraction
    //
    struct IPCMemTransfer* pMTransf;

    pMTransf = g_arLines[p->hLine].LastMessageOut+nThis;

    pMTransf->bMemoryTransfer = pProcessMsg->bMemoryTransfer;
    pMTransf->pMemory         = pProcessMsg->pMemory;
    pMTransf->dwTransferSize  = pProcessMsg->dwTransferSize;

    // Shift all messages by one
    //
    pInbox->dwMsgCnt--;
    for(i=0; i<IPC_INBOX_SIZE-1; i++)
        pInbox->arPMsg[i] = pInbox->arPMsg[i+1];
       
    pInbox->arPMsg[i] = pProcessMsg;
   
    p->st = IPC_ST_OK;
}

void ipcRecive(struct IPCTransfer *p) {
    ipcReciveEx(p, 0);
}

void ipcReciveB(struct IPCTransfer *p) {
    ipcReciveEx(p, 1);
}

// Direct memory transfer between user processes
//
void ipcReciveMem(struct IPCTransfer *p)
{
    // Validate request
    //
    struct IPCMemTransfer* pProcessMsg;
    unsigned long i;
    unsigned char nThis, nDest;

    if( (p->hLine>=IPC_MAX_LINE_CNT) ||
        (g_arLines[p->hLine].bState!=LINE_OPEN)){
       
        p->st = IPC_ST_INVLINE;
        return;
    }

    // Determine direction
    //
    if(g_arLines[p->hLine].inbox[0].dwOwnerPID==tmGetCrProcessID())
    {
        nThis = 0;
        nDest = 1;
    }
    else if(g_arLines[p->hLine].inbox[1].dwOwnerPID==tmGetCrProcessID())
    {
        nThis = 1;
        nDest = 0;
    }
    else {
        p->st = IPC_ST_INVLINE;
        return;
    }

    // Message pointer
    //
    pProcessMsg = g_arLines[p->hLine].LastMessageOut+nThis;

    // Extract attached memory
    //
    if(pProcessMsg->bMemoryTransfer)
    {
        PID dwSenderPID, dwThisPID;
        void *pSenderMem;
        void *pThisMem;
        unsigned long exact1, exact2; // exact 4k page boundary

        // Determine transfer size
        //
        unsigned long dwSize = p->msg.dwTransferSize;
       
        if(pProcessMsg->dwTransferSize<dwSize)
            dwSize = pProcessMsg->dwTransferSize;

        // Get PID
        dwSenderPID = g_arLines[p->hLine].inbox[nDest].dwOwnerPID;
        dwThisPID   = g_arLines[p->hLine].inbox[nThis].dwOwnerPID;

        // Form sender and destination pointers
        //
        exact1 = (unsigned long)(pProcessMsg->pMemory)/(4*1024);
        exact1 = (unsigned long)(pProcessMsg->pMemory - exact1*4*1024);
        exact2 = (unsigned long)(p->msg.pMemory)/(4*1024);
        exact2 = (unsigned long)(p->msg.pMemory - exact2*4*1024);
       
        //if( (exact1!=0) || (exact2!=0) )
        //{
        //  p->st = ...; return;
        //}

        if(pageGetPhysicalAddress(PAGING_DIR_PROCESS0+dwSenderPID,
            0x100000+(unsigned long)pProcessMsg->pMemory,
            (unsigned long*)&pSenderMem)!=PAGE_OK)
        {
            p->st = IPC_ST_BAD_MEM;
            return;
        }
        if(pageGetPhysicalAddress(PAGING_DIR_PROCESS0+dwThisPID,
            0x100000+(unsigned long)p->msg.pMemory,
            (unsigned long*)&pThisMem)!=PAGE_OK)
        {
            p->st = IPC_ST_BAD_MEM;
            return;
        }

        pSenderMem = (char*)((unsigned long)pSenderMem-0x600)+exact1;
        pThisMem   = (char*)((unsigned long)pThisMem-0x600)+exact2;

        // Perform transfer
        //
        kMemCpy(pThisMem, pSenderMem, dwSize);
    }
    p->st = IPC_ST_OK;
}

// Utility function for memory transfers
//
void kMemCpy(unsigned char* pDst, unsigned char* pSrc, unsigned long uSize)
{
    // Implementing this in assembly is a good idea
    unsigned long i=0;
    for(i=0; i<uSize; i++){pDst[i]=pSrc[i];}
}

// User process is blocked until it receives a message
//
void ipcWaitMessage()
{
    struct IPCInbox  * pInbox;
    unsigned long i;
    char bFound = 0;

    for(i=0; i<IPC_MAX_LINE_CNT; i++)
    if(g_arLines[i].bState==LINE_OPEN)
    {
        if(g_arLines[i].inbox[0].dwOwnerPID == tmGetCrProcessID()) {
            pInbox = g_arLines[i].inbox + 0;
        }
        else if(g_arLines[i].inbox[1].dwOwnerPID == tmGetCrProcessID()) {
            pInbox = g_arLines[i].inbox + 1;
        }
        else continue;

        bFound = 1;
        if(pInbox->dwMsgCnt!=0) return;
        pInbox->bFlags |= IPC_INBOX_WAITING|IPC_INBOX_DONT_RECIVE;
    }

    if(bFound)
    {
        tmSleep(tmGetCrProcessID());
        tmAddCharge();
        //tmirq(7);
        return;
    }
}