hw: handle pagefaults via IPC request/reply

ref #589
This commit is contained in:
Martin Stein 2013-09-13 01:05:38 +02:00 committed by Norman Feske
parent 6d03292a1e
commit 349262a655
5 changed files with 120 additions and 77 deletions

View File

@ -73,15 +73,20 @@ namespace Genode
};
/**
* Special paging server class
* Paging-server backend
*/
class Ipc_pager : public Native_capability
{
enum { VERBOSE = 1 };
private:
Pagefault _pagefault; /* data of lastly received pagefault */
Mapping _mapping; /* mapping to resolve last pagefault */
/**
* Backend for wait_for_fault and wait_for_first_fault
*/
void _wait_for_fault(size_t s);
public:
/**
@ -99,6 +104,11 @@ namespace Genode
}
}
/**
* Wait for the first pagefault request
*/
void wait_for_first_fault();
/**
* Wait for the next pagefault request
*/

View File

@ -31,19 +31,15 @@ void Pager_activation_base::entry()
/* receive and handle faults */
bool mapping_pending = 0;
pager.wait_for_first_fault();
while (1)
{
if (mapping_pending) {
/* apply mapping and await next fault */
if (pager.resolve_and_wait_for_fault()) {
PERR("failed to resolve page fault");
pager.wait_for_fault();
}
} else {
pager.wait_for_fault();
}
/* protect bottom half of loop against pager-object guard */
{
/* lookup pager object for current faulter */
Object_pool<Pager_object>::Guard o(_ep ? _ep->lookup_and_lock(pager.badge()) : 0);
Object_pool<Pager_object>::Guard
o(_ep ? _ep->lookup_and_lock(pager.badge()) : 0);
if (!o) {
PERR("invalid pager object");
mapping_pending = 0;
@ -52,6 +48,14 @@ void Pager_activation_base::entry()
mapping_pending = !o->pager(pager);
}
}
if (mapping_pending) {
/* apply mapping and await next fault */
if (pager.resolve_and_wait_for_fault()) {
PERR("failed to resolve page fault");
pager.wait_for_fault();
}
} else { pager.wait_for_fault(); }
}
}
@ -59,8 +63,8 @@ void Pager_activation_base::entry()
** Pager_entrypoint **
**********************/
Pager_entrypoint::Pager_entrypoint(Cap_session *,
Pager_activation_base * const a)
Pager_entrypoint::
Pager_entrypoint(Cap_session *, Pager_activation_base * const a)
: _activation(a) { _activation->ep(this); }
@ -89,18 +93,31 @@ Pager_capability Pager_entrypoint::manage(Pager_object * const o)
** Ipc_pager **
***************/
void Ipc_pager::wait_for_first_fault()
{
/* receive message */
size_t const s = Kernel::wait_for_request();
_wait_for_fault(s);
}
void Ipc_pager::wait_for_fault()
{
/* receive first message */
size_t s = Kernel::wait_for_request();
while (1) {
size_t const s = Kernel::reply(0, 1);
_wait_for_fault(s);
}
void Ipc_pager::_wait_for_fault(size_t s)
{
while (1)
{
/*
* FIXME: the message size is a weak indicator for the message type
*/
switch (s) {
switch (s)
{
case sizeof(Pagefault): {
/* message is a pagefault */
@ -114,7 +131,7 @@ void Ipc_pager::wait_for_fault()
}
/* pagefault is invalid so get the next message */
else {
PERR("%s:%d: Invalid pagefault", __FILE__, __LINE__);
PERR("invalid pagefault");
continue;
}
continue; }
@ -132,9 +149,8 @@ void Ipc_pager::wait_for_fault()
default: {
PERR("%s:%d: Invalid message format", __FILE__, __LINE__);
PERR("invalid message format");
continue; }
}
}
}

View File

@ -619,12 +619,12 @@ namespace Arm
}
/**
* Check if a pagefault has occured due to a translation miss
* Return if the context is in a pagefault due to a translation miss
*
* \param va holds the virtual fault-address if this returns 1
* \param w wether it is a write fault if this returns 1
* \param va holds the virtual fault-address if call returns 1
* \param w holds wether it's a write fault if call returns 1
*/
bool translation_miss(addr_t & va, bool & w) const
bool pagefault(addr_t & va, bool & w) const
{
/* determine fault type */
switch (cpu_exception) {

View File

@ -226,24 +226,6 @@ namespace Kernel
}
/**
* Handle an usermode pagefault
*
* \param user thread that has caused the pagefault
*/
void handle_pagefault(Thread * const user)
{
/* check out cause and attributes of abort */
addr_t virt_addr = 0;
bool write = 0;
bool is_transl_miss = user->translation_miss(virt_addr, write);
assert(is_transl_miss);
/* the user might be able to resolve the pagefault */
user->pagefault(virt_addr, write);
}
/**
* Handle request of an unknown signal type
*/

View File

@ -37,7 +37,6 @@ namespace Kernel
typedef Genode::Native_utcb Native_utcb;
unsigned core_id();
void handle_pagefault(Thread * const);
void handle_syscall(Thread * const);
void handle_interrupt(void);
void handle_invalid_excpt(void);
@ -90,14 +89,16 @@ class Kernel::Thread
enum State
{
SCHEDULED,
AWAIT_START,
AWAIT_IPC,
AWAIT_RESUMPTION,
AWAIT_IRQ,
AWAIT_SIGNAL,
AWAIT_SIGNAL_CONTEXT_KILL,
CRASHED,
SCHEDULED = 1,
AWAIT_START = 2,
AWAIT_IPC = 3,
AWAIT_RESUME = 4,
AWAIT_PAGER = 5,
AWAIT_PAGER_IPC = 6,
AWAIT_IRQ = 7,
AWAIT_SIGNAL = 8,
AWAIT_SIGNAL_CONTEXT_KILL = 9,
CRASHED = 10,
};
Platform_thread * const _platform_thread;
@ -173,14 +174,39 @@ class Kernel::Thread
void _has_received(size_t const s)
{
switch (_state) {
case AWAIT_IPC:
_schedule();
case SCHEDULED:
user_arg_0(s);
if (_state != SCHEDULED) { _schedule(); }
return;
case AWAIT_PAGER_IPC:
_schedule();
return;
case AWAIT_PAGER:
/* pager replied before pagefault has been resolved */
_state = AWAIT_RESUME;
return;
default:
PERR("wrong thread state to receive IPC");
crash();
return;
}
}
void _awaits_receipt()
{
switch (_state) {
case SCHEDULED:
cpu_scheduler()->remove(this);
_state = AWAIT_IPC;
case AWAIT_PAGER:
return;
default:
PERR("wrong thread state to await IPC");
crash();
return;
}
}
@ -293,9 +319,9 @@ class Kernel::Thread
*/
void pause()
{
assert(_state == AWAIT_RESUMPTION || _state == SCHEDULED);
assert(_state == AWAIT_RESUME || _state == SCHEDULED);
cpu_scheduler()->remove(this);
_state = AWAIT_RESUMPTION;
_state = AWAIT_RESUME;
}
/**
@ -313,9 +339,13 @@ class Kernel::Thread
int resume()
{
switch (_state) {
case AWAIT_RESUMPTION:
case AWAIT_RESUME:
_schedule();
return 0;
case AWAIT_PAGER:
/* pagefault has been resolved before pager replied */
_state = AWAIT_PAGER_IPC;
return 0;
case SCHEDULED:
return 1;
case AWAIT_IPC:
@ -339,7 +369,8 @@ class Kernel::Thread
return 0;
case AWAIT_START:
default:
PERR("unresumable state");
PERR("wrong state to resume thread");
crash();
return -1;
}
}
@ -376,22 +407,26 @@ class Kernel::Thread
}
/**
* Handle a pagefault that originates from this thread
*
* \param va virtual fault address
* \param w if fault was caused by a write access
* Handle an exception thrown by the MMU
*/
void pagefault(addr_t const va, bool const w)
void handle_mmu_exception()
{
assert(_state == SCHEDULED && _pager);
/* pause faulter */
/* pause thread */
cpu_scheduler()->remove(this);
_state = AWAIT_RESUMPTION;
_state = AWAIT_PAGER;
/* inform pager through IPC */
/* check out cause and attributes */
addr_t va = 0;
bool w = 0;
if (!pagefault(va, w)) {
PERR("unknown MMU exception");
return;
}
/* inform pager */
_pagefault = Pagefault(id(), (Tlb *)tlb(), ip, va, w);
Ipc_node::send_note(_pager, &_pagefault, sizeof(_pagefault));
void * const base = &_pagefault;
size_t const size = sizeof(_pagefault);
Ipc_node::send_request_await_reply(_pager, base, size, base, size);
}
/**
@ -411,10 +446,10 @@ class Kernel::Thread
handle_syscall(this);
return;
case PREFETCH_ABORT:
handle_pagefault(this);
handle_mmu_exception();
return;
case DATA_ABORT:
handle_pagefault(this);
handle_mmu_exception();
return;
case INTERRUPT_REQUEST:
handle_interrupt();