From 3070af91949e679eaf49cb731feadc54347adba1 Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Mon, 9 Sep 2013 15:20:30 +0200 Subject: [PATCH] hw: thread in extra header with asserts reviewed ref #528 --- base-hw/src/core/kernel.cc | 271 ++--------------- base-hw/src/core/kernel/thread.h | 508 +++++++++++++++++++++---------- 2 files changed, 370 insertions(+), 409 deletions(-) diff --git a/base-hw/src/core/kernel.cc b/base-hw/src/core/kernel.cc index 0cde1168c..4f3d928db 100644 --- a/base-hw/src/core/kernel.cc +++ b/base-hw/src/core/kernel.cc @@ -28,9 +28,9 @@ /* core includes */ #include -#include #include #include +#include /* base-hw includes */ #include @@ -47,8 +47,7 @@ namespace Kernel /* import Genode types */ typedef Genode::Thread_state Thread_state; typedef Genode::umword_t umword_t; - - class Schedule_context; + typedef Genode::Core_tlb Core_tlb; } namespace Kernel @@ -70,11 +69,6 @@ namespace Kernel timer()->start_one_shot(timer()->ms_to_tics(USER_LAP_TIME_MS)); } - /** - * Access to the static CPU scheduler - */ - static Cpu_scheduler * cpu_scheduler(); - /** * Static kernel PD that describes core @@ -92,119 +86,6 @@ namespace Kernel * Get core attributes */ unsigned core_id() { return core()->id(); } - - - class Thread; - - void handle_pagefault(Thread * const); - void handle_syscall(Thread * const); - void handle_interrupt(void); - void handle_invalid_excpt(void); -} - - -void Kernel::Thread::_schedule() -{ - cpu_scheduler()->insert(this); - _state = SCHEDULED; -} - - -void Kernel::Thread::pause() -{ - assert(_state == AWAIT_RESUMPTION || _state == SCHEDULED); - cpu_scheduler()->remove(this); - _state = AWAIT_RESUMPTION; -} - - -void Kernel::Thread::stop() -{ - cpu_scheduler()->remove(this); - _state = AWAIT_START; -} - - -void Kernel::Thread::request_and_wait(Thread * const dest, size_t const size) { - Ipc_node::send_request_await_reply(dest, phys_utcb()->base(), - size, phys_utcb()->base(), - phys_utcb()->size()); } - - -void Kernel::Thread::wait_for_request() { - Ipc_node::await_request(phys_utcb()->base(), - phys_utcb()->size()); } - - -void Kernel::Thread::reply(size_t const size, bool const await_request) -{ - Ipc_node::send_reply(phys_utcb()->base(), size); - if (await_request) - Ipc_node::await_request(phys_utcb()->base(), - phys_utcb()->size()); - else user_arg_0(0); -} - - -void Kernel::Thread::await_signal(Kernel::Signal_receiver * receiver) -{ - cpu_scheduler()->remove(this); - _state = AWAIT_SIGNAL; - _signal_receiver = receiver; -} - - -void Kernel::Thread::_received_irq() -{ - assert(_state == AWAIT_IRQ); - _schedule(); -} - - -void Kernel::Thread::handle_exception() -{ - switch(cpu_exception) { - case SUPERVISOR_CALL: - handle_syscall(this); - return; - case PREFETCH_ABORT: - case DATA_ABORT: - handle_pagefault(this); - return; - case INTERRUPT_REQUEST: - case FAST_INTERRUPT_REQUEST: - handle_interrupt(); - return; - default: - handle_invalid_excpt(); - } -} - - -void Kernel::Thread::proceed() -{ - mtc()->continue_user(static_cast(this)); -} - - -void Kernel::Thread::_has_received(size_t const s) -{ - user_arg_0(s); - if (_state != SCHEDULED) _schedule(); -} - - -void Kernel::Thread::_awaits_receipt() -{ - cpu_scheduler()->remove(this); - _state = AWAIT_IPC; -} - - -void Kernel::Thread::_awaits_irq() -{ - cpu_scheduler()->remove(this); - _state = AWAIT_IRQ; } @@ -218,7 +99,7 @@ namespace Kernel } class Vm : public Object, - public Schedule_context + public Execution_context { private: @@ -245,9 +126,9 @@ namespace Kernel void pause() { cpu_scheduler()->remove(this); } - /********************** - ** Schedule_context ** - **********************/ + /*********************** + ** Execution_context ** + ***********************/ void handle_exception() { @@ -279,17 +160,18 @@ namespace Kernel if (initial) { /* initialize idle thread */ - void * sp; - sp = (void *)&idle_stack[sizeof(idle_stack)/sizeof(idle_stack[0])]; + enum { STACK_SIZE = sizeof(idle_stack)/sizeof(idle_stack[0]) }; + void * const ip = (void *)&idle_main; + void * const sp = (void *)&idle_stack[STACK_SIZE]; /* * Idle doesn't use its UTCB pointer, thus * utcb_phys = utcb_virt = 0 is save. * Base-hw doesn't support multiple cores, thus * cpu_no = 0 is ok. We don't use 'start' to avoid - * recursive call of'cpu_scheduler'. + * recursive call of'cpu_scheduler()'. */ - idle.prepare_to_start((void *)&idle_main, sp, 0, core_id(), 0, 0); + idle.prepare_to_start(ip, sp, 0, core_id(), 0, 0, 0); initial = 0; } /* create scheduler with a permanent idle thread */ @@ -444,14 +326,17 @@ namespace Kernel Platform_thread * pt = (Platform_thread *)user->user_arg_1(); void * const ip = (void *)user->user_arg_2(); void * const sp = (void *)user->user_arg_3(); - unsigned const cpu = (unsigned)user->user_arg_4(); + unsigned const cpu_id = (unsigned)user->user_arg_4(); /* get targeted thread */ Thread * const t = Thread::pool()->object(pt->id()); assert(t); /* start thread */ - t->start(ip, sp, cpu, pt->pd_id(), pt->phys_utcb(), pt->virt_utcb()); + unsigned const pd_id = pt->pd_id(); + Native_utcb * const utcb_p = pt->phys_utcb(); + Native_utcb * const utcb_v = pt->virt_utcb(); + t->start(ip, sp, cpu_id, pd_id, utcb_p, utcb_v, pt->main_thread()); /* return software TLB that the thread is assigned to */ Pd::Pool * const pp = Pd::pool(); @@ -979,7 +864,7 @@ extern "C" void kernel() enum { CM_STACK_SIZE = sizeof(cm_stack)/sizeof(cm_stack[0]) + 1 }; core_main.start((void *)CORE_MAIN, (void *)&cm_stack[CM_STACK_SIZE - 1], - 0, core_id(), &cm_utcb, &cm_utcb); + 0, core_id(), &cm_utcb, &cm_utcb, 1); /* kernel initialization finished */ reset_lap_time(); @@ -989,128 +874,6 @@ extern "C" void kernel() cpu_scheduler()->head()->proceed(); } - -/******************** - ** Kernel::Thread ** - ********************/ - -void Kernel::Thread::crash() -{ - cpu_scheduler()->remove(this); - _state = CRASHED; -} - -int Kernel::Thread::resume() -{ - switch (_state) { - case AWAIT_RESUMPTION: - _schedule(); - return 0; - case SCHEDULED: - return 1; - case AWAIT_IPC: - PDBG("cancel IPC receipt"); - Ipc_node::cancel_waiting(); - _schedule(); - return 0; - case AWAIT_IRQ: - PDBG("cancel IRQ receipt"); - Irq_receiver::cancel_waiting(); - _schedule(); - return 0; - case AWAIT_SIGNAL: - PDBG("cancel signal receipt"); - _signal_receiver->remove_handler(signal_handler()); - _schedule(); - return 0; - case AWAIT_SIGNAL_CONTEXT_DESTRUCT: - PDBG("cancel signal context destruction"); - _schedule(); - return 0; - case AWAIT_START: - default: - PERR("unresumable state"); - return -1; - } -} - - -void Thread::prepare_to_start(void * const ip, - void * const sp, - unsigned const cpu_id, - unsigned const pd_id, - Native_utcb * const utcb_phys, - Native_utcb * const utcb_virt) -{ - /* check state and arguments */ - assert(_state == AWAIT_START) - assert(!cpu_id); - - /* store thread parameters */ - _phys_utcb = utcb_phys; - _virt_utcb = utcb_virt; - _pd_id = pd_id; - - /* join a protection domain */ - Pd * const pd = Pd::pool()->object(_pd_id); - assert(pd) - addr_t const tlb = pd->tlb()->base(); - - /* initialize CPU context */ - if (!_platform_thread) - /* this is the main thread of core */ - User_context::init_core_main_thread(ip, sp, tlb, pd_id); - else if (!_platform_thread->main_thread()) - /* this is not a main thread */ - User_context::init_thread(ip, sp, tlb, pd_id); - else - /* this is the main thread of a program other than core */ - User_context::init_main_thread(ip, _virt_utcb, tlb, pd_id); -} - - -void Thread::start(void * const ip, - void * const sp, - unsigned const cpu_id, - unsigned const pd_id, - Native_utcb * const utcb_phys, - Native_utcb * const utcb_virt) -{ - prepare_to_start(ip, sp, cpu_id, pd_id, utcb_phys, utcb_virt); - _schedule(); -} - - -void Thread::pagefault(addr_t const va, bool const w) -{ - /* pause faulter */ - cpu_scheduler()->remove(this); - _state = AWAIT_RESUMPTION; - - /* inform pager through IPC */ - assert(_pager); - _pagefault = Pagefault(id(), (Tlb *)tlb(), ip, va, w); - Ipc_node::send_note(_pager, &_pagefault, sizeof(_pagefault)); -} - - -void Thread::kill_signal_context_blocks() -{ - cpu_scheduler()->remove(this); - _state = AWAIT_SIGNAL_CONTEXT_DESTRUCT; -} - - -void Thread::kill_signal_context_done() -{ - if (_state != AWAIT_SIGNAL_CONTEXT_DESTRUCT) { - PDBG("ignore unexpected signal-context destruction"); - return; - } - user_arg_0(1); - _schedule(); -} - static Kernel::Mode_transition_control * Kernel::mtc() { /* compose CPU context for kernel entry */ diff --git a/base-hw/src/core/kernel/thread.h b/base-hw/src/core/kernel/thread.h index b79024c84..19207ed42 100644 --- a/base-hw/src/core/kernel/thread.h +++ b/base-hw/src/core/kernel/thread.h @@ -1,5 +1,5 @@ /* - * \brief Kernel representation of a user thread + * \brief Kernel backend for userland execution-contexts * \author Martin Stein * \date 2012-11-30 */ @@ -15,15 +15,13 @@ #define _CORE__KERNEL__THREAD_H_ /* core includes */ -#include -#include #include #include -#include +#include +#include #include +#include #include -#include -#include namespace Genode { @@ -32,34 +30,61 @@ namespace Genode namespace Kernel { - typedef Genode::Cpu Cpu; - typedef Genode::Core_tlb Core_tlb; - typedef Genode::Pagefault Pagefault; - typedef Genode::Native_utcb Native_utcb; + class Thread; - class Schedule_context; - typedef Scheduler Cpu_scheduler; + typedef Genode::Cpu Cpu; + typedef Genode::Pagefault Pagefault; + 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); /** * Kernel object that can be scheduled for the CPU */ - class Schedule_context : public Cpu_scheduler::Item - { - public: + class Execution_context; - virtual void handle_exception() = 0; - virtual void proceed() = 0; - }; + typedef Scheduler Cpu_scheduler; /** - * Kernel representation of a user thread + * Return the systems CPU scheduler */ - class Thread : public Cpu::User_context, - public Object, - public Schedule_context, - public Ipc_node, - public Irq_receiver - { + static Cpu_scheduler * cpu_scheduler(); + + /** + * Kernel backend for userland execution-contexts + */ + class Thread; +} + +class Kernel::Execution_context : public Cpu_scheduler::Item +{ + public: + + /** + * Handle an exception that occured during execution + */ + virtual void handle_exception() = 0; + + /** + * Continue execution + */ + virtual void proceed() = 0; +}; + +class Kernel::Thread +: + public Cpu::User_context, + public Object, + public Execution_context, + public Ipc_node, + public Irq_receiver +{ + private: + enum State { SCHEDULED, @@ -72,179 +97,352 @@ namespace Kernel CRASHED, }; - Platform_thread * const _platform_thread; /* userland object wich - * addresses this thread */ - State _state; /* thread state, description given at the beginning */ - Pagefault _pagefault; /* last pagefault triggered by this thread */ - Thread * _pager; /* gets informed if thread throws a pagefault */ - unsigned _pd_id; /* ID of the PD this thread runs on */ - Native_utcb * _phys_utcb; /* physical UTCB base */ - Native_utcb * _virt_utcb; /* virtual UTCB base */ - Signal_receiver * _signal_receiver; - Signal_handler _signal_handler; + Platform_thread * const _platform_thread; + State _state; + Pagefault _pagefault; + Thread * _pager; + unsigned _pd_id; + Native_utcb * _phys_utcb; + Native_utcb * _virt_utcb; + Signal_receiver * _signal_receiver; + Signal_handler _signal_handler; /** * Resume execution */ - void _schedule(); + void _schedule() + { + cpu_scheduler()->insert(this); + _state = SCHEDULED; + } /************** ** Ipc_node ** **************/ - void _has_received(size_t const s); + void _has_received(size_t const s) + { + user_arg_0(s); + if (_state != SCHEDULED) { _schedule(); } + } - void _awaits_receipt(); + void _awaits_receipt() + { + cpu_scheduler()->remove(this); + _state = AWAIT_IPC; + } /*************** ** Irq_owner ** ***************/ - void _received_irq(); + void _received_irq() + { + assert(_state == AWAIT_IRQ); + _schedule(); + } - void _awaits_irq(); + void _awaits_irq() + { + cpu_scheduler()->remove(this); + _state = AWAIT_IRQ; + } - public: + public: - void receive_signal(void * const base, size_t const size) - { - assert(_state == AWAIT_SIGNAL); - assert(size <= phys_utcb()->size()) - Genode::memcpy(phys_utcb()->base(), base, size); + /** + * Constructor + * + * \param platform_thread userland backend of execution context + */ + Thread(Platform_thread * const platform_thread) + : + _platform_thread(platform_thread), _state(AWAIT_START), + _pager(0), _pd_id(0), _phys_utcb(0), _virt_utcb(0), + _signal_receiver(0), _signal_handler((unsigned)this) + { } + + /** + * Suspend the thread due to unrecoverable misbehavior + */ + void crash() + { + cpu_scheduler()->remove(this); + _state = CRASHED; + } + + /** + * Prepare thread to get scheduled the first time + * + * \param ip initial instruction pointer + * \param sp initial stack pointer + * \param cpu_id target cpu + * \param pd_id target protection-domain + * \param utcb_phys physical UTCB pointer + * \param utcb_virt virtual UTCB pointer + * \param main wether the thread is the first one in its PD + */ + void prepare_to_start(void * const ip, + void * const sp, + unsigned const cpu_id, + unsigned const pd_id, + Native_utcb * const utcb_phys, + Native_utcb * const utcb_virt, + bool const main) + { + assert(_state == AWAIT_START) + + /* FIXME: support SMP */ + if (cpu_id) { PERR("multicore processing not supported"); } + + /* store thread parameters */ + _phys_utcb = utcb_phys; + _virt_utcb = utcb_virt; + _pd_id = pd_id; + + /* join a protection domain */ + Pd * const pd = Pd::pool()->object(_pd_id); + assert(pd) + addr_t const tlb = pd->tlb()->base(); + + /* initialize CPU context */ + User_context * const c = static_cast(this); + bool const core = (_pd_id == core_id()); + if (!main) { c->init_thread(ip, sp, tlb, pd_id); } + else if (!core) { c->init_main_thread(ip, utcb_virt, tlb, pd_id); } + else { c->init_core_main_thread(ip, sp, tlb, pd_id); } + } + + /** + * Start this thread + * + * \param ip initial instruction pointer + * \param sp initial stack pointer + * \param cpu_id target cpu + * \param pd_id target protection-domain + * \param utcb_p physical UTCB pointer + * \param utcb_v virtual UTCB pointer + * \param main wether the thread is the first one in its PD + */ + void start(void * const ip, + void * const sp, + unsigned const cpu_id, + unsigned const pd_id, + Native_utcb * const utcb_p, + Native_utcb * const utcb_v, + bool const main) + { + prepare_to_start(ip, sp, cpu_id, pd_id, utcb_p, utcb_v, main); + _schedule(); + } + + /** + * Pause this thread + */ + void pause() + { + assert(_state == AWAIT_RESUMPTION || _state == SCHEDULED); + cpu_scheduler()->remove(this); + _state = AWAIT_RESUMPTION; + } + + /** + * Stop this thread + */ + void stop() + { + cpu_scheduler()->remove(this); + _state = AWAIT_START; + } + + /** + * Resume this thread + */ + int resume() + { + switch (_state) { + case AWAIT_RESUMPTION: _schedule(); + return 0; + case SCHEDULED: + return 1; + case AWAIT_IPC: + PDBG("cancel IPC receipt"); + Ipc_node::cancel_waiting(); + _schedule(); + return 0; + case AWAIT_IRQ: + PDBG("cancel IRQ receipt"); + Irq_receiver::cancel_waiting(); + _schedule(); + return 0; + case AWAIT_SIGNAL: + PDBG("cancel signal receipt"); + _signal_receiver->remove_handler(signal_handler()); + _schedule(); + return 0; + case AWAIT_SIGNAL_CONTEXT_DESTRUCT: + PDBG("cancel signal context destruction"); + _schedule(); + return 0; + case AWAIT_START: + default: + PERR("unresumable state"); + return -1; } + } - void * operator new (size_t, void * p) { return p; } + /** + * Send a request and await the reply + */ + void request_and_wait(Thread * const dest, size_t const size) + { + Ipc_node::send_request_await_reply( + dest, phys_utcb()->base(), size, phys_utcb()->base(), + phys_utcb()->size()); + } - /** - * Constructor - */ - Thread(Platform_thread * const platform_thread) - : - _platform_thread(platform_thread), _state(AWAIT_START), - _pager(0), _pd_id(0), _phys_utcb(0), _virt_utcb(0), - _signal_receiver(0), _signal_handler((unsigned)this) - { } + /** + * Wait for any request + */ + void wait_for_request() + { + Ipc_node::await_request(phys_utcb()->base(), phys_utcb()->size()); + } - /** - * Suspend the thread due to unrecoverable misbehavior - */ - void crash(); + /** + * Reply to the last request + */ + void reply(size_t const size, bool const await_request) + { + Ipc_node::send_reply(phys_utcb()->base(), size); + if (await_request) { + Ipc_node * const ipc = static_cast(this); + ipc->await_request(phys_utcb()->base(), phys_utcb()->size()); + } + else { user_arg_0(0); } + } - /** - * Prepare thread to get scheduled the first time - * - * \param ip initial instruction pointer - * \param sp initial stack pointer - * \param cpu_id target cpu - * \param pd_id target protection-domain - * \param utcb_phys physical UTCB pointer - * \param utcb_virt virtual UTCB pointer - */ - void prepare_to_start(void * const ip, - void * const sp, - unsigned const cpu_id, - unsigned const pd_id, - Native_utcb * const utcb_phys, - Native_utcb * const utcb_virt); + /** + * Handle a pagefault that originates from this thread + * + * \param va virtual fault address + * \param w if fault was caused by a write access + */ + void pagefault(addr_t const va, bool const w) + { + assert(_state == SCHEDULED && _pager); - /** - * Start this thread - * - * \param ip initial instruction pointer - * \param sp initial stack pointer - * \param cpu_id target cpu - * \param pd_id target protection-domain - * \param utcb_phys physical UTCB pointer - * \param utcb_virt virtual UTCB pointer - */ - void start(void * const ip, - void * const sp, - unsigned const cpu_id, - unsigned const pd_id, - Native_utcb * const utcb_phys, - Native_utcb * const utcb_virt); + /* pause faulter */ + cpu_scheduler()->remove(this); + _state = AWAIT_RESUMPTION; - /** - * Pause this thread - */ - void pause(); + /* inform pager through IPC */ + _pagefault = Pagefault(id(), (Tlb *)tlb(), ip, va, w); + Ipc_node::send_note(_pager, &_pagefault, sizeof(_pagefault)); + } - /** - * Stop this thread - */ - void stop(); + /** + * Get unique thread ID, avoid method ambiguousness + */ + unsigned id() const { return Object::id(); } - /** - * Resume this thread - */ - int resume(); + /** + * Let the thread block for signal receipt + * + * \param receiver the signal pool that the thread blocks for + */ + void await_signal(Signal_receiver * receiver) + { + cpu_scheduler()->remove(this); + _state = AWAIT_SIGNAL; + _signal_receiver = receiver; + } - /** - * Send a request and await the reply - */ - void request_and_wait(Thread * const dest, size_t const size); + /** + * Let the thread receive signal data + * + * \param base signal-data base + * \param size signal-data size + */ + void receive_signal(void * const base, size_t const size) + { + assert(_state == AWAIT_SIGNAL && size <= phys_utcb()->size()); + Genode::memcpy(phys_utcb()->base(), base, size); + _schedule(); + } - /** - * Wait for any request - */ - void wait_for_request(); + /** + * Destructing a signal context blocks the thread for now + */ + void kill_signal_context_blocks() + { + cpu_scheduler()->remove(this); + _state = AWAIT_SIGNAL_CONTEXT_DESTRUCT; + } - /** - * Reply to the last request - */ - void reply(size_t const size, bool const await_request); + /** + * A signal-context destruction that blocked the thread is done + */ + void kill_signal_context_done() + { + if (_state != AWAIT_SIGNAL_CONTEXT_DESTRUCT) { + PDBG("ignore unexpected signal-context destruction"); + return; + } + user_arg_0(1); + _schedule(); + } - /** - * Handle a pagefault that originates from this thread - * - * \param va virtual fault address - * \param w if fault was caused by a write access - */ - void pagefault(addr_t const va, bool const w); - /** - * Get unique thread ID, avoid method ambiguousness - */ - unsigned id() const { return Object::id(); } + /*********************** + ** Execution_context ** + ***********************/ - /** - * Gets called when we await a signal at 'receiver' - */ - void await_signal(Kernel::Signal_receiver * receiver); + void handle_exception() + { + switch(cpu_exception) { + case SUPERVISOR_CALL: + handle_syscall(this); + return; + case PREFETCH_ABORT: + handle_pagefault(this); + return; + case DATA_ABORT: + handle_pagefault(this); + return; + case INTERRUPT_REQUEST: + handle_interrupt(); + return; + case FAST_INTERRUPT_REQUEST: + handle_interrupt(); + return; + default: + handle_invalid_excpt(); + } + } - /** - * Handle the exception that currently blocks this thread - */ - void handle_exception(); + void proceed() + { + mtc()->continue_user(static_cast(this)); + } - /** - * Continue executing this thread in userland - */ - void proceed(); - void kill_signal_context_blocks(); + /*************** + ** Accessors ** + ***************/ - void kill_signal_context_done(); + Platform_thread * platform_thread() const { return _platform_thread; } - /*************** - ** Accessors ** - ***************/ + void pager(Thread * const p) { _pager = p; } - Platform_thread * platform_thread() const { - return _platform_thread; } + unsigned pd_id() const { return _pd_id; } - void pager(Thread * const p) { _pager = p; } + Native_utcb * phys_utcb() const { return _phys_utcb; } - unsigned pd_id() const { return _pd_id; } - - Native_utcb * phys_utcb() const { return _phys_utcb; } - - Signal_handler * signal_handler() { return &_signal_handler; } - }; -} + Signal_handler * signal_handler() { return &_signal_handler; } +}; #endif /* _CORE__KERNEL__THREAD_H_ */ -