/*
* 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
#include "Process.h"
#include "ProcessEvent.h"
Process::Process(ProcessID id, Address entry, bool privileged, const MemoryMap &map)
: m_id(id), m_map(map), m_shares(id)
{
m_state = Stopped;
m_kernelStack = 0;
m_userStack = 0;
m_pageDirectory = 0;
m_parent = 0;
m_waitId = 0;
m_wakeups = 0;
m_entry = entry;
m_privileged = privileged;
m_memoryContext = ZERO;
m_kernelChannel = new MemoryChannel;
MemoryBlock::set(&m_sleepTimer, 0, sizeof(m_sleepTimer));
}
Process::~Process()
{
delete m_kernelChannel;
if (m_memoryContext)
{
m_memoryContext->releaseRegion(MemoryMap::UserData);
m_memoryContext->releaseRegion(MemoryMap::UserHeap);
m_memoryContext->releaseRegion(MemoryMap::UserStack);
m_memoryContext->releaseRegion(MemoryMap::UserPrivate);
delete m_memoryContext;
}
}
ProcessID Process::getID() const
{
return m_id;
}
ProcessID Process::getParent() const
{
return m_parent;
}
ProcessID Process::getWait() const
{
return m_waitId;
}
Process::State Process::getState() const
{
return m_state;
}
ProcessShares & Process::getShares()
{
return m_shares;
}
const Timer::Info * Process::getSleepTimer() const
{
return &m_sleepTimer;
}
Address Process::getPageDirectory() const
{
return m_pageDirectory;
}
Address Process::getUserStack() const
{
return m_userStack;
}
Address Process::getKernelStack() const
{
return m_kernelStack;
}
MemoryContext * Process::getMemoryContext()
{
return m_memoryContext;
}
bool Process::isPrivileged() const
{
return m_privileged;
}
void Process::setState(Process::State st)
{
m_state = st;
}
void Process::setParent(ProcessID id)
{
m_parent = id;
}
void Process::setWait(ProcessID id)
{
m_waitId = id;
}
void Process::setSleepTimer(const Timer::Info *sleepTimer)
{
MemoryBlock::copy(&m_sleepTimer, sleepTimer, sizeof(m_sleepTimer));
}
void Process::setPageDirectory(Address addr)
{
m_pageDirectory = addr;
}
void Process::setUserStack(Address addr)
{
m_userStack = addr;
}
void Process::setKernelStack(Address addr)
{
m_kernelStack = addr;
}
Process::Result Process::raiseEvent(ProcessEvent *event)
{
// Write the message. Be sure to flush the caches because
// the kernel has mapped the channel pages separately in low memory.
m_kernelChannel->write(event);
m_kernelChannel->flush();
// Wakeup the Process, if needed
return wakeup();
}
Process::Result Process::initialize()
{
Memory::Range range;
Address paddr, vaddr;
Arch::Cache cache;
// Allocate two pages for the kernel event channel
if (Kernel::instance->getAllocator()->allocateLow(PAGESIZE*2, &paddr) != Allocator::Success)
return OutOfMemory;
// Translate to virtual address in kernel low memory
vaddr = (Address) Kernel::instance->getAllocator()->toVirtual(paddr);
MemoryBlock::set((void *)vaddr, 0, PAGESIZE*2);
cache.cleanData(vaddr);
cache.cleanData(vaddr + PAGESIZE);
// Map data and feedback pages in userspace
range.phys = paddr;
range.access = Memory::User | Memory::Readable;
range.size = PAGESIZE * 2;
m_memoryContext->findFree(range.size, MemoryMap::UserPrivate, &range.virt);
m_memoryContext->mapRange(&range);
// Remap the feedback page with write permissions
m_memoryContext->unmap(range.virt + PAGESIZE);
m_memoryContext->map(range.virt + PAGESIZE,
range.phys + PAGESIZE, Memory::User | Memory::Readable | Memory::Writable);
// Create shares entry
m_shares.setMemoryContext(m_memoryContext);
m_shares.createShare(KERNEL_PID, Kernel::instance->getCoreInfo()->coreId, 0, range.virt, range.size);
// Setup the kernel event channel
m_kernelChannel->setMode(Channel::Producer);
m_kernelChannel->setMessageSize(sizeof(ProcessEvent));
m_kernelChannel->setVirtual(vaddr, vaddr + PAGESIZE);
return Success;
}
Process::Result Process::wakeup()
{
m_wakeups++;
if (m_state != Running)
m_state = Ready;
MemoryBlock::set(&m_sleepTimer, 0, sizeof(m_sleepTimer));
return Success;
}
Process::Result Process::sleep(Timer::Info *timer)
{
if (!m_wakeups)
{
m_state = Sleeping;
if (timer)
MemoryBlock::copy(&m_sleepTimer, timer, sizeof(m_sleepTimer));
return Success;
}
m_wakeups = 0;
return WakeupPending;
}
bool Process::operator==(Process *proc)
{
return proc->getID() == m_id;
}