#include <kernel/kernel.h>
#include <kernel/proc.h>
#include <kernel/memory.h>
#include <kernel/handle.h>
#include <kernel/thread.h>
#include <kernel/vmm.h>
#include <kernel/fs.h>
#include <malloc.h>
#include <wchar.h>
#include <errno.h>
#include <string.h>
extern dword kernel_pagedir[];
extern byte start[];
extern process_t proc_idle;
unsigned last_process_id;
bool procPageFault(process_t* proc, addr_t virt)
{
addr_t phys, page;
vm_area_t* area;
if (virt >= proc->stack_end &&
virt < 0x80000000)
{
phys = memAlloc();
page = virt & -PAGE_SIZE;
memMap(proc->page_dir, (addr_t) page, phys, 1,
PRIV_USER | PRIV_WR | PRIV_PRES);
invalidate_page((void*) page);
return true;
}
else
{
virt &= -PAGE_SIZE;
for (area = proc->first_vm_area; area; area = area->next)
{
if (virt >= (addr_t) area->start &&
virt < (addr_t) area->start + area->pages * PAGE_SIZE)
{
//if (!area->is_committed)
return vmmCommit(area, virt, 1);
/*else
{
wprintf(L"Area at %p already committed\n", area->start);
errno = EBUFFER;
return false;
}*/
}
}
errno = ENOTFOUND;
return false;
}
}
size_t mslen(const wchar_t* str)
{
size_t size = 0, len;
while (*str)
{
len = wcslen(str);
str += len + 1;
size += len;
}
return size;
}
const wchar_t* msncpy(wchar_t *dest, const wchar_t *src, size_t max)
{
size_t len;
wchar_t *ret = dest;
while (*src)
{
wcsncpy(dest, src, max);
len = wcslen(src);
max -= len + 1;
if (max <= 0)
return ret;
dest += len + 1;
src += len + 1;
}
*dest = '\0';
return ret;
}
//! Creates a new process, based on an image file on disk
/*!
* The process's fields are set up and the specified module is loaded, along
* with kernelu.dll. A new thread is created, at the same level as the
* process, at the entry point of the module. The new thread is not
* scheduled yet, but it is unsuspended.
*
* \param level Level of the new process. Zero (kernel mode) and 3 (user
* mode) are supported.
* \param file Image file from which to load the process
* \param cmdline Command line to pass to the process
* \return A new process_t structure representing the new process.
*/
process_t* procLoad(byte level, const wchar_t* file, const wchar_t* cmdline,
unsigned priority, file_t* input, file_t* output)
{
process_t* proc;
thread_t* thr;
module_t *mod, *kmod, *nmod;
//dword kernel_start = (dword) start >> 20;
process_info_t* info;
vm_area_t* area;
proc = hndAlloc(sizeof(process_t), NULL);
if (!proc)
return NULL;
memset(proc, 0, sizeof(process_t));
proc->page_dir = (dword*) memAlloc();
assert(proc->page_dir != NULL);
proc->stack_end = 0x80000000;
proc->vmm_end = 0;
memcpy(proc->page_dir, kernel_pagedir, PAGE_SIZE);
proc->level = level;
proc->last_vm_area = proc->first_vm_area = NULL;
proc->marshal_map = NULL;
proc->last_marshal = 0;
proc->id = ++last_process_id;
semInit(&proc->sem_vmm);
proc->mod_first = proc->mod_last = NULL;
mod = peLoad(proc, file, 0);
if (mod == NULL)
{
procDelete(proc);
return NULL;
}
peLoad(proc, L"kernelu.dll", 0);
for (kmod = proc_idle.mod_first; kmod; kmod = kmod->next)
{
nmod = hndAlloc(sizeof(module_t), proc);
*nmod = *kmod;
nmod->name = wcsdup(kmod->name);
if (proc->mod_last)
proc->mod_last->next = nmod;
if (proc->mod_first == NULL)
proc->mod_first = nmod;
nmod->prev = proc->mod_last;
nmod->next = NULL;
proc->mod_last = nmod;
}
vmmAlloc(proc, _sysinfo.memory_top / PAGE_SIZE, 0, 0);
/* Allocate process memory for the user-mode process structure */
proc->info = vmmAlloc(proc, 1, NULL, MEM_USER | MEM_COMMIT | MEM_READ | MEM_WRITE);
assert(proc->info != NULL);
//wprintf(L"%s: PCB at %x\n", proc->mod_first->name, proc->info);
area = vmmArea(proc, proc->info);
assert(area != NULL);
if (area)
{
int pages;
addr_t phys;
process_info_t *cur = current->process->info;
phys = memTranslate(proc->page_dir, area->start) & -PAGE_SIZE;
/* Initialize the fields */
info = (process_info_t*) phys;
info->id = proc->id;
info->root = cur->root;
wcscpy(info->cwd, cur->cwd);
info->base = mod->base;
#if 0
if (cur->environment)
{
pages = (mslen(cur->environment) / sizeof(wchar_t) + PAGE_SIZE) / PAGE_SIZE;
if (pages < 1)
pages = 1;
}
else
pages = 1;
info->environment = vmmAlloc(proc, pages, NULL,
MEM_USER | MEM_COMMIT | MEM_READ | MEM_WRITE);
if (cur->environment && info->environment)
{
vm_area_t *area;
area = vmmArea(proc, info->environment);
assert(area != NULL);
if (area)
{
phys = memTranslate(current->process->page_dir, area->start) & -PAGE_SIZE;
wprintf(L"procLoad: copying environment from %p to %x\n",
cur->environment, phys);
msncpy((wchar_t*) phys, cur->environment, pages * PAGE_SIZE);
}
}
#endif
if (input == NULL)
input = (file_t*) cur->input;
if (output == NULL)
output = (file_t*) cur->output;
info->input = (addr_t) input;
info->output = (addr_t) output;
if (cmdline == NULL)
cmdline = L"";
/* Allocate process memory for the command line */
if (wcslen(cmdline) + 1 <= PAGE_SIZE + sizeof(process_info_t))
{
//_cputws(L"Putting command line alongside process info...");
info->cmdline = (wchar_t*) (proc->info + 1);
wcscpy((wchar_t*) (info + 1), cmdline);
//_cputws(L"done\n");
}
else
{
_cputws(L"Giving command line its own block\n");
pages = (wcslen(cmdline) + PAGE_SIZE) & -PAGE_SIZE;
info->cmdline = vmmAlloc(proc, pages, NULL, MEM_USER | MEM_COMMIT |
MEM_READ | MEM_WRITE | MEM_ZERO);
area = vmmArea(proc, proc->info->cmdline);
assert(area != NULL);
if (area)
{
phys = memTranslate(current->process->page_dir, area->start) & -PAGE_SIZE;
wcscpy((wchar_t*) phys, cmdline);
}
}
}
thr = thrCreate(level, proc, (void*) mod->entry, priority);
thrSuspend(thr, false);
return proc;
}
//! Closes a process handle. If the handle is freed then the process is terminated.
/*!
* Since a process object is a handle, freeing a process results in
* decrementing its reference count (see hndFree()).
*
* This function signals the process handle, terminates its threads and calls
* hndFree(). If the handle was freed (and its reference count became
* zero) then all resources associated with the process are freed also.
*
* \param proc Process to be freed
*/
void procDelete(process_t* proc)
{
thread_t *thr, *next;
vm_area_t *area, *anext;
module_t *mod, *mnext;
if (proc)
{
hndSignal(proc, true);
/*if (proc->info)
{
if ((addr_t) proc->info->cmdline < (addr_t) proc->info + PAGE_SIZE)
vmmFree(vmmArea(proc, proc->info->cmdline));
if (proc->info->environment)
vmmFree(vmmArea(proc, proc->info->environment));
}*/
for (thr = thr_first; thr; thr = next)
{
next = thr->next;
if (thr->process == proc)
thrDelete(thr);
}
if (hndHandle(proc)->refs == 0)
{
mod = proc->mod_first;
while (mod)
{
mnext = mod->next;
peUnload(proc, mod);
mod = mnext;
}
for (area = proc->first_vm_area; area; area = anext)
{
anext = area->next;
vmmFree(area);
}
memFree((addr_t) proc->page_dir);
}
hndEnum(proc);
hndFree(proc);
}
}
//! Terminates a process.
/*!
* Whereas procDelete() may only decrement a process's reference count,
* procTerminate() will always cause a process to stop executing and
* release all its resources.
*
* \param proc Process to be terminated
*/
void procTerminate(process_t* proc)
{
procDelete(proc);
}
process_t* procCurrent()
{
return current->process;
}