From 349262a6550b91cf89e0d835fc3d4bdbc6e6945a Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Fri, 13 Sep 2013 01:05:38 +0200 Subject: [PATCH] hw: handle pagefaults via IPC request/reply ref #589 --- base-hw/include/base/ipc_pager.h | 18 ++++-- base-hw/src/base/pager.cc | 58 ++++++++++++------- base-hw/src/core/cpu/arm.h | 8 +-- base-hw/src/core/kernel.cc | 18 ------ base-hw/src/core/kernel/thread.h | 95 ++++++++++++++++++++++---------- 5 files changed, 120 insertions(+), 77 deletions(-) diff --git a/base-hw/include/base/ipc_pager.h b/base-hw/include/base/ipc_pager.h index b3a08cab5f..640ba6d825 100644 --- a/base-hw/include/base/ipc_pager.h +++ b/base-hw/include/base/ipc_pager.h @@ -73,14 +73,19 @@ 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 */ + 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 */ diff --git a/base-hw/src/base/pager.cc b/base-hw/src/base/pager.cc index 687a9f3c95..69d1f2de31 100644 --- a/base-hw/src/base/pager.cc +++ b/base-hw/src/base/pager.cc @@ -31,26 +31,30 @@ void Pager_activation_base::entry() /* receive and handle faults */ bool mapping_pending = 0; + pager.wait_for_first_fault(); while (1) { + /* protect bottom half of loop against pager-object guard */ + { + /* lookup pager object for current faulter */ + Object_pool::Guard + o(_ep ? _ep->lookup_and_lock(pager.badge()) : 0); + + if (!o) { + PERR("invalid pager object"); + mapping_pending = 0; + } else { + /* try to find an appropriate mapping */ + 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(); - } - /* lookup pager object for current faulter */ - Object_pool::Guard o(_ep ? _ep->lookup_and_lock(pager.badge()) : 0); - if (!o) { - PERR("invalid pager object"); - mapping_pending = 0; - } else { - /* try to find an appropriate mapping */ - mapping_pending = !o->pager(pager); - } + } 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; } } } } - diff --git a/base-hw/src/core/cpu/arm.h b/base-hw/src/core/cpu/arm.h index 00f34b93f9..128262a8f5 100644 --- a/base-hw/src/core/cpu/arm.h +++ b/base-hw/src/core/cpu/arm.h @@ -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) { diff --git a/base-hw/src/core/kernel.cc b/base-hw/src/core/kernel.cc index 9b4b607b8a..9a3a10c66b 100644 --- a/base-hw/src/core/kernel.cc +++ b/base-hw/src/core/kernel.cc @@ -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 */ diff --git a/base-hw/src/core/kernel/thread.h b/base-hw/src/core/kernel/thread.h index d58926005e..8f4e73295b 100644 --- a/base-hw/src/core/kernel/thread.h +++ b/base-hw/src/core/kernel/thread.h @@ -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) { - user_arg_0(s); - if (_state != SCHEDULED) { _schedule(); } + switch (_state) { + case AWAIT_IPC: + _schedule(); + case SCHEDULED: + user_arg_0(s); + 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() { - cpu_scheduler()->remove(this); - _state = AWAIT_IPC; + 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();