/*
* Copyright (C) 2015 Niek Linnenbank
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include "ProcessEvent.h"
#warning merge with Process please
ProcessShares::ProcessShares(ProcessID pid)
{
m_pid = pid;
m_memory = ZERO;
m_kernelChannel = new MemoryChannel;
}
ProcessShares::~ProcessShares()
{
ProcessManager *procs = Kernel::instance->getProcessManager();
List pids;
// Cleanup members
delete m_kernelChannel;
// Make a list of unique process IDs which
// have a share with this Process
Size size = m_shares.size();
for (Size i = 0; i < size; i++)
{
MemoryShare *sh = (MemoryShare *) m_shares.get(i);
if (sh)
{
if (!pids.contains(sh->pid))
pids.append(sh->pid);
releaseShare(sh, i);
}
}
// Raise process terminated events
for (ListIterator i(pids); i.hasCurrent(); i++)
{
ProcessID pid = i.current();
Process *proc = procs->get(pid);
if (proc)
{
ProcessEvent event;
event.type = ProcessTerminated;
event.number = m_pid;
proc->raiseEvent(&event);
}
}
}
const ProcessID ProcessShares::getProcessID() const
{
return m_pid;
}
MemoryContext * ProcessShares::getMemoryContext()
{
return m_memory;
}
ProcessShares::Result ProcessShares::setMemoryContext(MemoryContext *ctx)
{
m_memory = ctx;
return Success;
}
ProcessShares::Result ProcessShares::createShare(ProcessID pid,
Size coreId,
Size tagId,
Address virt,
Size size)
{
MemoryShare *share = ZERO;
if (size == 0 || size % PAGESIZE)
return InvalidArgument;
// Allocate MemoryShare objects
share = new MemoryShare;
if (!share)
return OutOfMemory;
// Fill the share object
share->pid = pid;
share->coreId = coreId;
share->tagId = tagId;
share->range.virt = virt;
share->range.size = size;
m_memory->lookup(share->range.virt, &share->range.phys);
m_memory->access(share->range.virt, &share->range.access);
// insert into shares list
m_shares.insert(*share);
return Success;
}
ProcessShares::Result ProcessShares::createShare(ProcessShares & instance,
ProcessShares::MemoryShare *share)
{
MemoryShare *localShare = ZERO;
MemoryShare *remoteShare = ZERO;
MemoryContext *localMem = m_memory;
MemoryContext *remoteMem = instance.getMemoryContext();
Address paddr, vaddr;
Arch::Cache cache;
if (share->range.size == 0)
return InvalidArgument;
// Check if the share already exists
if (readShare(share) == Success)
return AlreadyExists;
// Allocate local
localShare = new MemoryShare;
if (!localShare)
return OutOfMemory;
// Allocate remote
remoteShare = new MemoryShare;
if (!remoteShare)
{
delete localShare;
return OutOfMemory;
}
// Allocate actual pages
if (Kernel::instance->getAllocator()->allocateLow(share->range.size, &paddr) != Allocator::Success)
return OutOfMemory;
// Zero out the pages
vaddr = (Address) Kernel::instance->getAllocator()->toVirtual(paddr);
MemoryBlock::set((void *)vaddr, 0, share->range.size);
for (Size i = 0; i < share->range.size; i+=PAGESIZE)
cache.cleanData(vaddr + i);
// Fill the local share object
localShare->pid = instance.getProcessID();
localShare->coreId = Kernel::instance->getCoreInfo()->coreId;
localShare->tagId = share->tagId;
localShare->range.phys = paddr;
localShare->range.size = share->range.size;
localShare->range.access = Memory::User | share->range.access;
localShare->attached = true;
// Map in the local process
if (localMem->findFree(localShare->range.size, MemoryMap::UserShare, &localShare->range.virt) != MemoryContext::Success ||
localMem->mapRange(&localShare->range) != MemoryContext::Success)
{
delete localShare;
delete remoteShare;
return OutOfMemory;
}
// Fill the remote share object
remoteShare->pid = m_pid;
remoteShare->coreId = localShare->coreId;
remoteShare->tagId = localShare->tagId;
remoteShare->range.phys = localShare->range.phys;
remoteShare->range.size = localShare->range.size;
remoteShare->range.access = localShare->range.access;
remoteShare->attached = true;
// Map in the remote process
if (remoteMem->findFree(remoteShare->range.size, MemoryMap::UserShare, &remoteShare->range.virt) != MemoryContext::Success ||
remoteMem->mapRange(&remoteShare->range) != MemoryContext::Success)
{
delete localShare;
delete remoteShare;
return OutOfMemory;
}
// TODO: for MemoryChannel: update access permissions, ro/rw for data/feedback
// insert into shares list
m_shares.insert(*localShare);
instance.m_shares.insert(*remoteShare);
// raise event on the remote process
Process *proc = Kernel::instance->getProcessManager()->get(instance.getProcessID());
ProcessEvent event;
event.type = ShareCreated;
event.number = m_pid;
MemoryBlock::copy(&event.share, remoteShare, sizeof(*remoteShare));
proc->raiseEvent(&event);
// Update parameter outputs
MemoryBlock::copy(share, localShare, sizeof(*share));
return Success;
}
ProcessShares::Result ProcessShares::removeShares(ProcessID pid)
{
// TODO: remove all shares for the given pid
// TODO: in case the share exists in the remote share AND in our share,
// just remove it here, mark it detached in the remote share.
// TODO: in case the share exists only in our side (detached), unmap and release all memory
// TODO: call this function from VMShare() and from IPCServer (when a ProcessTerminated message comes in)
Size size = m_shares.size();
MemoryShare *s = 0;
for (Size i = 0; i < size; i++)
{
if ((s = (MemoryShare *) m_shares.get(i)) != ZERO)
{
if (s->pid != pid)
continue;
releaseShare(s, i);
}
}
return Success;
}
ProcessShares::Result ProcessShares::releaseShare(MemoryShare *s, Size idx)
{
// Only release physical memory if both processes have detached.
// Note that in case all memory shares for a certain ProcessID have
// been detached but not yet released, and due to a very unlikely race
// condition (ProcessID reuse) a new memory share was just created (before old ones were released)
// the new memory share would also be detached here, resulting in a memory
// share with is detached in this process but attached and useless in the
// other process.
if (s->attached)
{
Process *proc = Kernel::instance->getProcessManager()->get(s->pid);
if (proc)
{
ProcessShares & shares = proc->getShares();
Size size = shares.m_shares.size();
// Mark all process shares detached in the other process
for (Size i = 0; i < size; i++)
{
MemoryShare *otherShare = (MemoryShare *) shares.m_shares.get(i);
if (otherShare && otherShare->pid == m_pid
&& otherShare->coreId == s->coreId)
{
otherShare->attached = false;
}
}
}
}
else
{
// Only release physical memory pages if the other
// process already detached earlier
Allocator *alloc = Kernel::instance->getAllocator();
for (Size i = 0; i < s->range.size; i += PAGESIZE)
alloc->release(s->range.phys + i);
}
// Unmap the share
m_memory->unmapRange(&s->range);
// Remove share from our administration
m_shares.remove(idx);
delete s;
return Success;
}
ProcessShares::Result ProcessShares::readShare(MemoryShare *share)
{
Size size = m_shares.size();
const MemoryShare *s = 0;
for (Size i = 0; i < size; i++)
{
if ((s = m_shares.get(i)) != ZERO)
{
if (s->pid == share->pid &&
s->coreId == share->coreId &&
s->tagId == share->tagId)
{
MemoryBlock::copy(share, s, sizeof(MemoryShare));
return Success;
}
}
}
return NotFound;
}