#include "pager-server.h"
#include <l4/kdebug.h>
IDL4_INLINE void pager_pagefault_implementation(CORBA_Object _caller, const CORBA_long address, const CORBA_long ip, const CORBA_long privileges, idl4_fpage_t *page, idl4_server_environment *_env)
{
pdbg1("pager: page fault on 0x%x ip 0x%x by thread 0x%lx\n",
address, ip, _caller.raw);
struct TaskEntry *te = tasklist.find(_caller);
if (!te) {
panic("pager: page fault on unmanaged task.\n");
}
PageMappingEntry *pme = te->mapping.findAddress(address);
if (!pme)
{
int vmac = vmaddressclass.get(address);
if (vmac == VMA_PROGRAM)
{
vmac = ((L4_Word_t)address <= te->heaplimit) ? VMA_ANONYMOUS : VMA_SEGFAULT;
vmac = VMA_ANONYMOUS;
}
if (vmac == VMA_SEGFAULT)
{
pdbg0("pager: segfault of thread 0x%lx at ip 0x%x accessing 0x%x\n",
_caller.raw, ip, address);
panic("pager: segmentation fault");
}
else if (vmac == VMA_ANONYMOUS)
{
pdbg1("pager: allocating anonymous memory for page fault\n");
L4_Fpage_t backing = buddysystem.allocate(4096);
L4_Fpage_t userpage = L4_FpageLog2(address & ~0x0fff, 12);
zeroFpage(backing);
pme = te->mapping.insert(userpage, backing);
}
else {
panic("Invalid virtual memory class");
}
}
assert((L4_Word_t)address >= L4_Address(pme->userpage));
assert((L4_Word_t)address < L4_Address(pme->userpage) + L4_Size(pme->userpage));
pdbg1("pager: sending backing 0x%lx size 0x%lx -> 0x%lx\n",
L4_Address(pme->backing), L4_Size(pme->backing),
L4_Address(pme->userpage));
idl4_fpage_set_mode(page, IDL4_MODE_MAP);
idl4_fpage_set_page(page, pme->backing);
idl4_fpage_set_base(page, L4_Address(pme->userpage));
idl4_fpage_set_permissions(page, IDL4_PERM_FULL);
}
IDL4_PUBLISH_PAGER_PAGEFAULT(pager_pagefault_implementation);
IDL4_INLINE void pager_GetPageGrant_implementation(CORBA_Object _caller, const L4_Word_t size, idl4_fpage_t *page, idl4_server_environment *_env)
{
pdbg2("pager: GetPageGrant(%lu) called by thread 0x%lx\n", size, _caller.raw);
L4_Fpage_t fp = buddysystem.allocate(size);
if (L4_IsNilFpage(fp)) {
pdbg0("pager: out of memory in GetPageGrant\n");
CORBA_exception_set(_env, ex_IF_MEMORY_out_of_memory, NULL);
}
else {
zeroFpage(fp);
}
idl4_fpage_set_page(page, fp);
idl4_fpage_set_mode(page, IDL4_MODE_GRANT);
}
IDL4_PUBLISH_PAGER_GETPAGEGRANT(pager_GetPageGrant_implementation);
IDL4_INLINE void pager_GetSpecialPhysicalPage_implementation(CORBA_Object _caller, const L4_Fpage_t *phyaddr, idl4_fpage_t *page, idl4_server_environment *_env)
{
panic("Not implemented.");
}
IDL4_PUBLISH_PAGER_GETSPECIALPHYSICALPAGE(pager_GetSpecialPhysicalPage_implementation);
IDL4_INLINE void pager_CreateTask_implementation(CORBA_Object _caller, L4_ThreadId_t *thread, idl4_server_environment *_env)
{
pdbg2("pager: CreateTask() called by thread 0x%lx\n",
_caller.raw);
L4_ThreadId_t newthread = *thread;
if (L4_IsNilThread(newthread))
{
static int pick_newthreadid = 100;
newthread = L4_GlobalId(pick_newthreadid++, 1);
if (tasklist.find(newthread) != NULL)
{
pdbg0("pager: CreateTask called for already managed task.\n");
CORBA_exception_set(_env, ex_IF_MEMORY_generic, 0);
return;
}
}
else
{
if (tasklist.find(newthread) != NULL)
{
pdbg0("pager: CreateTask called for already managed task.\n");
CORBA_exception_set(_env, ex_IF_MEMORY_generic, 0);
return;
}
}
CORBA_Environment env (idl4_default_environment);
pdbg1("pager: calling Sigma1ThreadControl\n");
L4_ThreadId_t scheduler = L4_Myself();
L4_ThreadId_t mynilthread = L4_nilthread;
IF_SYSCALL_Sigma1ThreadControl(syscallServerId,
&newthread,
&newthread,
&scheduler,
&mynilthread,
-1UL,
&env);
if (env._major != CORBA_NO_EXCEPTION)
{
pdbg0("pager: call to Sigma1ThreadControl failed, code: %d", CORBA_exception_id(&env));
CORBA_exception_set(_env, ex_IF_MEMORY_generic, 0);
return;
}
assert(L4_UtcbAreaSizeLog2(KIP) <= 14);
L4_Fpage_t kiparea = L4_FpageLog2(0x02000000, L4_KipAreaSizeLog2(KIP));
L4_Fpage_t utcbarea = L4_FpageLog2(0x02010000, 14);
IF_SYSCALL_Sigma1SpaceControl(syscallServerId,
&newthread,
0,
&kiparea,
&utcbarea,
&mynilthread,
&env);
if (env._major != CORBA_NO_EXCEPTION)
{
pdbg0("pager: call to Sigma1SpaceControl failed, code: %d", CORBA_exception_id(&env));
CORBA_exception_set(_env, ex_IF_MEMORY_generic, 0);
return;
}
TaskEntry *te = tasklist.allocate();
te->thread = newthread;
te->status = TS_NEWBORN;
te->heaplimit = 0x90000000;
te->mapping.setSlabAllocator(pagemappingslaballoc);
te->waitfor = L4_nilthread;
tasklist.insert(te);
*thread = newthread;
}
IDL4_PUBLISH_PAGER_CREATETASK(pager_CreateTask_implementation);
IDL4_INLINE void pager_GetSharedRegion_implementation(CORBA_Object _caller, const L4_ThreadId_t *destthread, const L4_Word_t destaddress, const L4_Word_t pagesize, idl4_fpage_t *page, idl4_server_environment *_env)
{
pdbg2("pager: GetSharedRegion(0x%lx, 0x%lx, 0x%lx) called by thread 0x%lx\n",
destthread->raw, destaddress, pagesize, _caller.raw);
struct TaskEntry *te = tasklist.find(*destthread);
if (!te) {
pdbg0("pager: GetSharedRegion received for unmanaged thread!\n");
idl4_fpage_set_page(page, L4_Nilpage);
return;
}
if (te->status != TS_NEWBORN) {
pdbg0("pager: GetSharedRegion received for running thread!\n");
idl4_fpage_set_page(page, L4_Nilpage);
return;
}
L4_Fpage_t newfp;
PageMappingEntry *pme = te->mapping.findAddress(destaddress);
if (pme)
{
newfp = pme->backing;
if (L4_Size(newfp) < pagesize) {
panic("pager: shared fpage is re-requested as larger area.");
}
}
else
{
newfp = buddysystem.allocate(pagesize);
if (L4_IsNilFpage(newfp)) {
panic("pager: out of memory in GetSharedRegion\n");
}
zeroFpage(newfp);
newfp = L4_FpageAddRights(newfp, L4_FullyAccessible);
te->mapping.insert(L4_Fpage(destaddress, pagesize), newfp);
}
pdbg1("pager: sharing 0x%lx - %lu - 0x%lx - raw 0x%lx\n", L4_Address(newfp), L4_Size(newfp), L4_Rights(newfp), newfp.raw);
struct TaskEntry *callte = tasklist.find(_caller);
if (callte)
{
L4_Word_t pick = callte->mapping.findSpace(0xA0000000, pagesize);
assert(pick >= 0xA0000000 && pick <= 0xB8000000);
callte->mapping.insert(L4_Fpage(pick, pagesize), newfp);
idl4_fpage_set_page(page, newfp);
idl4_fpage_set_base(page, pick);
idl4_fpage_set_permissions(page, L4_FullyAccessible);
idl4_fpage_set_mode(page, IDL4_MODE_MAP);
}
else
{
idl4_fpage_set_page(page, newfp);
idl4_fpage_set_base(page, 0x0);
idl4_fpage_set_permissions(page, L4_FullyAccessible);
idl4_fpage_set_mode(page, IDL4_MODE_MAP);
}
pdbg1("pager: sharing done\n");
}
IDL4_PUBLISH_PAGER_GETSHAREDREGION(pager_GetSharedRegion_implementation);
IDL4_INLINE void pager_FreeSharedRegion_implementation(CORBA_Object _caller, const L4_ThreadId_t *destthread, const L4_Word_t destaddress, L4_Fpage_t *page, idl4_server_environment *_env)
{
pdbg2("pager: FreeSharedRegion(0x%lx, 0x%lx, 0x%lx) called by thread 0x%lx\n",
destthread->raw, destaddress, L4_Address(*page), _caller.raw);
struct TaskEntry *te = tasklist.find(*destthread);
if (!te) {
pdbg0("pager: FreeSharedRegion received for unmanaged thread!\n");
return;
}
if (te->status != TS_NEWBORN) {
pdbg0("pager: FreeSharedRegion received for running thread!\n");
return;
}
PageMappingEntry *pme = te->mapping.findAddress(destaddress);
if (!pme) {
pdbg0("pager: FreeSharedRegion received illegal fpage to unmap!\n");
return;
}
L4_Fpage_t ufp = pme->backing + L4_FullyAccessible;
pdbg2("pager: unmapping fpage 0x%lx - size %ld - raw 0x%lx\n", L4_Address(ufp), L4_Size(ufp), ufp.raw);
L4_UnmapFpage(ufp);
struct TaskEntry *callte = tasklist.find(_caller);
if (callte)
{
pdbg1("pager: removing shared page from managed address space\n");
PageMappingEntry *callerpme = callte->mapping.findAddress(L4_Address(*page));
if (!callerpme) {
pdbg0("pager: FreeSharedRegion tried to unmap different fpage on caller\n");
}
else
{
callte->mapping.remove(callerpme);
}
}
}
IDL4_PUBLISH_PAGER_FREESHAREDREGION(pager_FreeSharedRegion_implementation);
IDL4_INLINE void pager_StartTask_implementation(CORBA_Object _caller, const L4_ThreadId_t *thread, const L4_Word_t ip, const L4_Word_t sp, idl4_server_environment *_env)
{
pdbg2("pager: StartTask(0x%lx,0x%lx,0x%lx) called by thread 0x%lx\n",
thread->raw, ip, sp, _caller.raw);
struct TaskEntry *te = tasklist.find(*thread);
if (!te || te->status != TS_NEWBORN) {
pdbg0("pager: StartTask received for unmanaged thread!\n");
CORBA_exception_set(_env, ex_IF_MEMORY_generic, 0);
return;
}
const PageMappingEntry *pme = te->mapping.begin();
while(1)
{
L4_Fpage_t ufp = pme->backing;
pdbg1("pager: unmapping fpage 0x%lx - size %ld\n", L4_Address(ufp), L4_Size(ufp));
ufp = L4_FpageAddRights(ufp, L4_FullyAccessible);
L4_UnmapFpage(ufp);
if (pme == pme->next) break;
pme = pme->next;
}
CORBA_Environment env (idl4_default_environment);
L4_ThreadId_t mynilthread = L4_nilthread;
L4_ThreadId_t mypagerid = L4_Myself();
IF_SYSCALL_Sigma1ThreadControl(syscallServerId,
thread,
thread,
&mynilthread,
&mypagerid,
0x02010000,
&env);
if (env._major != CORBA_NO_EXCEPTION)
{
pdbg0("pager: call to Sigma1ThreadControl failed, code: %d", CORBA_exception_id(&env));
CORBA_exception_set(_env, ex_IF_MEMORY_generic, 0);
return;
}
te->status = TS_RUNNING;
L4_Msg_t msg;
L4_Clear(&msg);
L4_Append(&msg, ip);
L4_Append(&msg, sp);
L4_Load(&msg);
L4_Send(*thread);
}
IDL4_PUBLISH_PAGER_STARTTASK(pager_StartTask_implementation);
static bool pager_NotifyWaiters(L4_ThreadId_t termtask, L4_Word_t retcode)
{
unsigned int waiters = 0;
struct TaskEntry* te = tasklist.begin();
while(1)
{
if (te->waitfor == termtask)
{
pdbg0("pager: NotifyWaiters notifying thread 0x%lx that 0x%lx terminated.\n",
te->thread.raw, termtask.raw);
IF_MEMORY_WaitFor_reply(te->thread, &retcode, true);
te->waitfor = L4_nilthread;
waiters++;
}
if (te == te->next) break;
te = te->next;
}
return (waiters > 0);
}
IDL4_INLINE void pager_KillTask_implementation(CORBA_Object _caller, const L4_ThreadId_t *thread, idl4_server_environment *_env)
{
pdbg2("pager: KillTask(0x%lx) called by thread 0x%lx\n",
thread->raw, _caller.raw);
struct TaskEntry *te = tasklist.find(*thread);
if (!te) {
pdbg0("pager: KillTask received for unmanaged thread!\n");
CORBA_exception_set(_env, ex_IF_MEMORY_unknown_task, NULL);
return;
}
const PageMappingEntry *pme = te->mapping.begin();
while(1)
{
L4_Fpage_t ufp = pme->backing;
pdbg1("pager: unmapping fpage 0x%lx - size %ld\n", L4_Address(ufp), L4_Size(ufp));
ufp = L4_FpageAddRights(ufp, L4_FullyAccessible);
L4_UnmapFpage(ufp);
buddysystem.insert(pme->backing);
if (pme == pme->next) break;
pme = pme->next;
}
te->status = TS_ZOMBIE;
te->retcode = -1LU;
if (pager_NotifyWaiters(te->thread, -1LU)) {
tasklist.remove(te);
}
}
IDL4_PUBLISH_PAGER_KILLTASK(pager_KillTask_implementation);
IDL4_INLINE void IF_pager_KillMe_implementation(CORBA_Object _caller, const L4_Word_t retcode, idl4_server_environment *_env)
{
pdbg2("pager: KillMe() called by thread 0x%lx\n",
_caller.raw);
struct TaskEntry *te = tasklist.find(_caller);
if (!te) {
pdbg0("pager: KillMe received for unmanaged thread!\n");
CORBA_exception_set(_env, ex_IF_MEMORY_unknown_task, NULL);
return;
}
CORBA_Environment env (idl4_default_environment);
L4_ThreadId_t mynilthread = L4_nilthread;
L4_ThreadId_t mypagerid = L4_Myself();
IF_SYSCALL_Sigma1ThreadControl(syscallServerId,
&_caller,
&mynilthread,
&mynilthread,
&mynilthread,
-1LU,
&env);
if (env._major != CORBA_NO_EXCEPTION)
{
pdbg0("pager: call to Sigma1ThreadControl failed, code: %d", CORBA_exception_id(&env));
CORBA_exception_set(_env, ex_IF_MEMORY_generic, 0);
return;
}
const PageMappingEntry *pme = te->mapping.begin();
while(1)
{
L4_Fpage_t ufp = pme->backing;
pdbg1("pager: unmapping fpage 0x%lx - size %ld\n", L4_Address(ufp), L4_Size(ufp));
L4_UnmapFpage(ufp + L4_FullyAccessible);
buddysystem.insert(pme->backing);
if (pme == pme->next) break;
pme = pme->next;
}
te->mapping.clear();
te->status = TS_ZOMBIE;
te->retcode = retcode;
pdbg0("pager: Thread 0x%lx killed. return code %ld\n", _caller.raw, retcode);
if (pager_NotifyWaiters(te->thread, retcode)) {
tasklist.remove(te);
}
idl4_set_no_response(_env);
}
IDL4_PUBLISH_PAGER_KILLME(IF_pager_KillMe_implementation);
IDL4_INLINE CORBA_boolean pager_WaitFor_implementation(CORBA_Object _caller, const L4_ThreadId_t *thread, const L4_Word_t flags, L4_Word_t *retcode, idl4_server_environment *_env)
{
pdbg2("pager: WaitFor(0x%lx, %lu) called by thread 0x%lx\n",
thread->raw, flags, _caller.raw);
struct TaskEntry *te = tasklist.find(*thread);
if (!te) {
pdbg0("pager: WaitFor received for unmanaged thread!\n");
CORBA_exception_set(_env, ex_IF_MEMORY_unknown_task, NULL);
return false;
}
if (te->status == TS_ZOMBIE)
{
*retcode = te->retcode;
tasklist.remove(te);
return true;
}
if (flags & IF_MEMORY_WAIT_FOREVER)
{
struct TaskEntry *cte = tasklist.find(_caller);
if (!cte) {
pdbg0("pager: WaitFor must be called by a thread managed by the pager!\n");
CORBA_exception_set(_env, ex_IF_MEMORY_unknown_task, NULL);
return false;
}
assert(L4_IsNilThread(cte->waitfor));
cte->waitfor = *thread;
idl4_set_no_response(_env);
return false;
}
else
{
return false;
}
}
IDL4_PUBLISH_PAGER_WAITFOR(pager_WaitFor_implementation);
IDL4_INLINE void pager_GetPageAny_implementation(CORBA_Object _caller, const L4_Word_t size, L4_Fpage_t *page, idl4_server_environment *_env)
{
panic("pager: GetPageAny not implemented.");
}
IDL4_PUBLISH_PAGER_GETPAGEANY(pager_GetPageAny_implementation);
IDL4_INLINE void pager_brk_implementation(CORBA_Object _caller, const L4_Word_t heapend, idl4_server_environment *_env)
{
pdbg1("pager: brk(0x%lx) called by thread 0x%lx\n",
heapend, _caller.raw);
struct TaskEntry *te = tasklist.find(_caller);
if (!te) {
pdbg0("pager: brk received for unmanaged thread!\n");
CORBA_exception_set(_env, ex_IF_MEMORY_unknown_task, NULL);
return;
}
if (te->heaplimit > heapend && te->heaplimit != 0x90000000) {
pdbg1("pager: brk is reducing the heap size!\n");
}
te->heaplimit = heapend;
if (te->heaplimit >= 0x90000000) {
pdbg0("pager: brk is trying to set the heap limit beyond the valid heap range.\n");
te->heaplimit = 0x8fffffff;
}
if (te->heaplimit < 0x08408000) {
pdbg0("pager: brk trying to set the heap limit lower than the .text start address.\n");
te->heaplimit = 0x08408000;
}
}
IDL4_PUBLISH_PAGER_BRK(pager_brk_implementation);
#include "pager-files.h"
void *pager_vtable_4[PAGER_DEFAULT_VTABLE_SIZE] = PAGER_DEFAULT_VTABLE_4;
void *pager_vtable_5[PAGER_DEFAULT_VTABLE_SIZE] = PAGER_DEFAULT_VTABLE_5;
void *pager_vtable_6[PAGER_DEFAULT_VTABLE_SIZE] = PAGER_DEFAULT_VTABLE_6;
void *pager_vtable_10[PAGER_DEFAULT_VTABLE_SIZE] = PAGER_DEFAULT_VTABLE_10;
void *pager_vtable_discard[PAGER_DEFAULT_VTABLE_SIZE] = PAGER_DEFAULT_VTABLE_DISCARD;
void **pager_itable[16] = { pager_vtable_discard, pager_vtable_discard, pager_vtable_discard, pager_vtable_discard, pager_vtable_4, pager_vtable_5, pager_vtable_6, pager_vtable_discard, pager_vtable_discard, pager_vtable_discard, pager_vtable_10, pager_vtable_discard, pager_vtable_discard, pager_vtable_discard, pager_vtable_discard, pager_vtable_discard };
void *pager_ktable[PAGER_DEFAULT_KTABLE_SIZE] = PAGER_DEFAULT_KTABLE;
void pager_server()
{
L4_ThreadId_t partner;
L4_MsgTag_t msgtag;
idl4_msgbuf_t msgbuf;
long cnt;
idl4_msgbuf_init(&msgbuf);
static char msgbuf8000[8000];
idl4_msgbuf_add_buffer(&msgbuf, msgbuf8000, 8000);
while (1)
{
partner = L4_nilthread;
msgtag.raw = 0;
cnt = 0;
while (1)
{
idl4_msgbuf_sync(&msgbuf);
idl4_reply_and_wait(&partner, &msgtag, &msgbuf, &cnt);
if (idl4_is_error(&msgtag))
break;
if (IDL4_EXPECT_FALSE(idl4_is_kernel_message(msgtag)))
idl4_process_request(&partner, &msgtag, &msgbuf, &cnt, pager_ktable[idl4_get_kernel_message_id(msgtag) & PAGER_KID_MASK]);
else idl4_process_request(&partner, &msgtag, &msgbuf, &cnt, pager_itable[idl4_get_interface_id(&msgtag) & PAGER_IID_MASK][idl4_get_function_id(&msgtag) & PAGER_FID_MASK]);
}
}
}
void pager_discard()
{
panic("pager: ipc message discarded");
}