diff --git a/repos/base-hw/src/core/kernel/core_interface.h b/repos/base-hw/src/core/kernel/core_interface.h index 7b22ebf168..a57c0acf6e 100644 --- a/repos/base-hw/src/core/kernel/core_interface.h +++ b/repos/base-hw/src/core/kernel/core_interface.h @@ -66,6 +66,7 @@ namespace Kernel { constexpr Call_arg call_id_set_cpu_state() { return 125; } constexpr Call_arg call_id_exception_state() { return 126; } constexpr Call_arg call_id_single_step() { return 127; } + constexpr Call_arg call_id_ack_pager_signal() { return 128; } /** * Invalidate TLB entries for the `pd` in region `addr`, `sz` @@ -147,13 +148,16 @@ namespace Kernel { /** * Set or unset the handler of an event that can be triggered by a thread * - * \param thread pointer to thread kernel object + * \param thread reference to thread kernel object + * \param pager reference to pager kernel object * \param signal_context_id capability id of the page-fault handler */ - inline void thread_pager(Thread & thread, + inline void thread_pager(Thread &thread, + Thread &pager, capid_t const signal_context_id) { - call(call_id_thread_pager(), (Call_arg)&thread, signal_context_id); + call(call_id_thread_pager(), (Call_arg)&thread, (Call_arg)&pager, + signal_context_id); } @@ -202,6 +206,18 @@ namespace Kernel { { call(call_id_single_step(), (Call_arg)&thread, (Call_arg)&on); } + + /** + * Acknowledge a signal transmitted to a pager + * + * \param context signal context to acknowledge + * \param thread reference to faulting thread kernel object + * \param resolved whether fault got resolved + */ + inline void ack_pager_signal(capid_t const context, Thread &thread, bool resolved) + { + call(call_id_ack_pager_signal(), context, (Call_arg)&thread, resolved); + } } #endif /* _CORE__KERNEL__CORE_INTERFACE_H_ */ diff --git a/repos/base-hw/src/core/kernel/thread.cc b/repos/base-hw/src/core/kernel/thread.cc index 6a2c95310a..688afe1eb7 100644 --- a/repos/base-hw/src/core/kernel/thread.cc +++ b/repos/base-hw/src/core/kernel/thread.cc @@ -385,6 +385,7 @@ void Thread::_call_restart_thread() _die(); return; } + user_arg_0(thread._restart()); } @@ -392,7 +393,10 @@ void Thread::_call_restart_thread() bool Thread::_restart() { assert(_state == ACTIVE || _state == AWAITS_RESTART); - if (_state != AWAITS_RESTART) { return false; } + + if (_state == ACTIVE && _exception_state == NO_EXCEPTION) + return false; + _exception_state = NO_EXCEPTION; _become_active(); return true; @@ -572,7 +576,9 @@ void Thread::_call_pager() { /* override event route */ Thread &thread = *(Thread *)user_arg_1(); - thread._pager = pd().cap_tree().find((Kernel::capid_t)user_arg_2()); + Thread &pager = *(Thread *)user_arg_2(); + Signal_context &sc = *pd().cap_tree().find((Kernel::capid_t)user_arg_3()); + thread._fault_context.construct(pager, sc); } @@ -824,6 +830,25 @@ void Thread::_call_single_step() { } +void Thread::_call_ack_pager_signal() +{ + Signal_context * const c = pd().cap_tree().find((Kernel::capid_t)user_arg_1()); + if (!c) + Genode::raw(*this, ": cannot ack unknown signal context"); + else + c->ack(); + + Thread &thread = *(Thread*)user_arg_2(); + thread.helping_finished(); + + bool resolved = user_arg_3() || + thread._exception_state == NO_EXCEPTION; + if (resolved) thread._restart(); + else thread._become_inactive(AWAITS_RESTART); +} + + + void Thread::_call() { /* switch over unrestricted kernel calls */ @@ -906,6 +931,7 @@ void Thread::_call() case call_id_set_cpu_state(): _call_set_cpu_state(); return; case call_id_exception_state(): _call_exception_state(); return; case call_id_single_step(): _call_single_step(); return; + case call_id_ack_pager_signal(): _call_ack_pager_signal(); return; default: Genode::raw(*this, ": unknown kernel call"); _die(); @@ -914,18 +940,37 @@ void Thread::_call() } +void Thread::_signal_to_pager() +{ + if (!_fault_context.constructed()) { + Genode::warning(*this, " could not send signal to pager"); + _die(); + return; + } + + /* first signal to pager to wake it up */ + _fault_context->sc.submit(1); + + /* only help pager thread if runnable and scheduler allows it */ + bool const help = Cpu_context::_helping_possible(_fault_context->pager) + && (_fault_context->pager._state == ACTIVE); + if (help) Cpu_context::_help(_fault_context->pager); + else _become_inactive(AWAITS_RESTART); +} + + void Thread::_mmu_exception() { using namespace Genode; using Genode::log; - _become_inactive(AWAITS_RESTART); _exception_state = MMU_FAULT; Cpu::mmu_fault(*regs, _fault); _fault.ip = regs->ip; if (_fault.type == Thread_fault::UNKNOWN) { Genode::warning(*this, " raised unhandled MMU fault ", _fault); + _die(); return; } @@ -940,17 +985,16 @@ void Thread::_mmu_exception() Hw::Mm::core_stack_area().size }; regs->for_each_return_address(stack, [&] (void **p) { log(*p); }); + _die(); + return; } - if (_pager && _pager->can_submit(1)) { - _pager->submit(1); - } + _signal_to_pager(); } void Thread::_exception() { - _become_inactive(AWAITS_RESTART); _exception_state = EXCEPTION; if (_type != USER) { @@ -958,12 +1002,7 @@ void Thread::_exception() _die(); } - if (_pager && _pager->can_submit(1)) { - _pager->submit(1); - } else { - Genode::raw(*this, " could not send signal to pager on exception"); - _die(); - } + _signal_to_pager(); } diff --git a/repos/base-hw/src/core/kernel/thread.h b/repos/base-hw/src/core/kernel/thread.h index 479d98306d..b7b5abfcba 100644 --- a/repos/base-hw/src/core/kernel/thread.h +++ b/repos/base-hw/src/core/kernel/thread.h @@ -173,7 +173,15 @@ class Kernel::Thread : private Kernel::Object, public Cpu_context, private Timeo size_t _ipc_rcv_caps { 0 }; Genode::Native_utcb *_utcb { nullptr }; Pd *_pd { nullptr }; - Signal_context *_pager { nullptr }; + + struct Fault_context + { + Thread &pager; + Signal_context ≻ + }; + + Genode::Constructible _fault_context {}; + Thread_fault _fault { }; State _state; Signal_handler _signal_handler { *this }; @@ -221,6 +229,11 @@ class Kernel::Thread : private Kernel::Object, public Cpu_context, private Timeo */ void _die(); + /** + * In case of fault, signal to pager, and help or block + */ + void _signal_to_pager(); + /** * Handle an exception thrown by the memory management unit */ @@ -296,6 +309,7 @@ class Kernel::Thread : private Kernel::Object, public Cpu_context, private Timeo void _call_set_cpu_state(); void _call_exception_state(); void _call_single_step(); + void _call_ack_pager_signal(); template void _call_new(auto &&... args) diff --git a/repos/base-hw/src/core/pager.cc b/repos/base-hw/src/core/pager.cc index 276bfcb32f..dcb0f9c4fe 100644 --- a/repos/base-hw/src/core/pager.cc +++ b/repos/base-hw/src/core/pager.cc @@ -19,6 +19,7 @@ /* base-internal includes */ #include +#include using namespace Core; @@ -71,13 +72,15 @@ void Pager_object::wake_up() } -void Pager_object::start_paging(Kernel_object & receiver) +void Pager_object::start_paging(Kernel_object &receiver, + Platform_thread &pager_thread) { using Object = Kernel_object; using Entry = Object_pool::Entry; create(*receiver, (unsigned long)this); Entry::cap(Object::_cap); + _pager_thread = &pager_thread; } @@ -110,24 +113,23 @@ Pager_object::Pager_object(Cpu_session_capability cpu_session_cap, void Pager_entrypoint::Thread::entry() { - Untyped_capability cap; - while (1) { - if (cap.valid()) Kernel::ack_signal(Capability_space::capid(cap)); - /* receive fault */ - if (Kernel::await_signal(Capability_space::capid(_kobj.cap()))) continue; + if (Kernel::await_signal(Capability_space::capid(_kobj.cap()))) + continue; Pager_object *po = *(Pager_object**)Thread::myself()->utcb()->data(); - cap = po->cap(); + if (!po) + continue; - if (!po) continue; + Untyped_capability cap = po->cap(); /* fetch fault data */ Platform_thread * const pt = (Platform_thread *)po->badge(); if (!pt) { warning("failed to get platform thread of faulter"); + Kernel::ack_signal(Capability_space::capid(cap)); continue; } @@ -138,19 +140,25 @@ void Pager_entrypoint::Thread::entry() "pd='", pt->pd().label(), "', " "thread='", pt->label(), "', " "ip=", Hex(pt->state().cpu.ip)); + pt->fault_resolved(cap, false); continue; } _fault = pt->fault_info(); /* try to resolve fault directly via local region managers */ - if (po->pager(*this) == Pager_object::Pager_result::STOP) + if (po->pager(*this) == Pager_object::Pager_result::STOP) { + pt->fault_resolved(cap, false); continue; + } /* apply mapping that was determined by the local region managers */ { Locked_ptr locked_ptr(pt->address_space()); - if (!locked_ptr.valid()) continue; + if (!locked_ptr.valid()) { + pt->fault_resolved(cap, false); + continue; + } Hw::Address_space * as = static_cast(&*locked_ptr); @@ -173,8 +181,7 @@ void Pager_entrypoint::Thread::entry() 1UL << _mapping.size_log2, flags); } - /* let pager object go back to no-fault state */ - po->wake_up(); + pt->fault_resolved(cap, true); } } @@ -206,7 +213,8 @@ Pager_capability Pager_entrypoint::manage(Pager_object &o) if (cpu >= _cpus) { error("Invalid location of pager object ", cpu); } else { - o.start_paging(_threads[cpu]._kobj); + o.start_paging(_threads[cpu]._kobj, + *_threads[cpu].native_thread().platform_thread); _threads[cpu].insert(&o); } diff --git a/repos/base-hw/src/core/pager.h b/repos/base-hw/src/core/pager.h index 6ff4a7d6d4..93f2e1ad86 100644 --- a/repos/base-hw/src/core/pager.h +++ b/repos/base-hw/src/core/pager.h @@ -31,6 +31,7 @@ namespace Core { class Platform; + class Platform_thread; /** * Interface used by generic region_map code @@ -111,6 +112,7 @@ class Core::Pager_object : private Object_pool::Entry, Affinity::Location _location; Cpu_session_capability _cpu_session_cap; Thread_capability _thread_cap; + Platform_thread *_pager_thread { nullptr }; /** * User-level signal handler registered for this pager object via @@ -118,6 +120,12 @@ class Core::Pager_object : private Object_pool::Entry, */ Signal_context_capability _exception_sigh { }; + /* + * Noncopyable + */ + Pager_object(const Pager_object&) = delete; + Pager_object& operator=(const Pager_object&) = delete; + public: /** @@ -167,7 +175,8 @@ class Core::Pager_object : private Object_pool::Entry, * * \param receiver signal receiver that receives the page faults */ - void start_paging(Kernel_object & receiver); + void start_paging(Kernel_object &receiver, + Platform_thread &pager_thread); /** * Called when a page-fault finally could not be resolved @@ -176,6 +185,11 @@ class Core::Pager_object : private Object_pool::Entry, void print(Output &out) const; + void with_pager(auto const &fn) + { + if (_pager_thread) fn(*_pager_thread); + } + /****************** ** Pure virtual ** diff --git a/repos/base-hw/src/core/platform_thread.cc b/repos/base-hw/src/core/platform_thread.cc index 5133d80b86..c538306fa9 100644 --- a/repos/base-hw/src/core/platform_thread.cc +++ b/repos/base-hw/src/core/platform_thread.cc @@ -187,12 +187,14 @@ void Platform_thread::start(void * const ip, void * const sp) } -void Platform_thread::pager(Pager_object &pager) +void Platform_thread::pager(Pager_object &po) { using namespace Kernel; - thread_pager(*_kobj, Capability_space::capid(pager.cap())); - _pager = &pager; + po.with_pager([&] (Platform_thread &pt) { + thread_pager(*_kobj, *pt._kobj, + Capability_space::capid(po.cap())); }); + _pager = &po; } @@ -238,3 +240,9 @@ void Platform_thread::restart() { Kernel::restart_thread(Capability_space::capid(_kobj.cap())); } + + +void Platform_thread::fault_resolved(Untyped_capability cap, bool resolved) +{ + Kernel::ack_pager_signal(Capability_space::capid(cap), *_kobj, resolved); +} diff --git a/repos/base-hw/src/core/platform_thread.h b/repos/base-hw/src/core/platform_thread.h index 83fe4269b1..85ddf9dd15 100644 --- a/repos/base-hw/src/core/platform_thread.h +++ b/repos/base-hw/src/core/platform_thread.h @@ -216,6 +216,8 @@ class Core::Platform_thread : Noncopyable void restart(); + void fault_resolved(Untyped_capability, bool); + /** * Pause this thread */