genode/base-hw/src/core/kernel/thread.cc
Martin Stein f128a52e8b hw: get rid of struct Msg and Msg::Type
Struct Msg was introduced due to the handling of pagefaults
and interrupts via synchronous IPC. Its only purpose was to provide
the message type in front of the typed message. Now pagefaults and
interrupts are handled via signals and struct Msg is not necessary
anymore.

ref #958
2013-12-03 08:33:23 +01:00

902 lines
19 KiB
C++

/*
* \brief Kernel backend for execution contexts in userland
* \author Martin Stein
* \date 2013-09-15
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/thread_state.h>
/* base-hw includes */
#include <placement_new.h>
/* core includes */
#include <kernel/kernel.h>
#include <kernel/thread.h>
#include <kernel/vm.h>
#include <platform_pd.h>
using namespace Kernel;
typedef Genode::Thread_state Thread_state;
unsigned Thread::pd_id() const { return _pd ? _pd->id() : 0; }
bool Thread::_core() const { return pd_id() == core_id(); }
void Thread::_signal_context_kill_pending()
{
assert(_state == SCHEDULED);
_state = AWAITS_SIGNAL_CONTEXT_KILL;
cpu_scheduler()->remove(this);
}
void Thread::_signal_context_kill_done()
{
assert(_state == AWAITS_SIGNAL_CONTEXT_KILL);
user_arg_0(0);
_schedule();
}
void Thread::_signal_receiver_kill_pending()
{
assert(_state == SCHEDULED);
_state = AWAITS_SIGNAL_RECEIVER_KILL;
cpu_scheduler()->remove(this);
}
void Thread::_signal_receiver_kill_done()
{
assert(_state == AWAITS_SIGNAL_RECEIVER_KILL);
user_arg_0(0);
_schedule();
}
void Thread::_await_signal(Signal_receiver * const receiver)
{
cpu_scheduler()->remove(this);
_state = AWAITS_SIGNAL;
_signal_receiver = receiver;
}
void Thread::_receive_signal(void * const base, size_t const size)
{
assert(_state == AWAITS_SIGNAL && size <= _utcb_phys->size());
Genode::memcpy(_utcb_phys->base(), base, size);
_schedule();
}
void Thread::_received_ipc_request(size_t const s)
{
switch (_state) {
case SCHEDULED:
user_arg_0(0);
return;
default:
PERR("wrong thread state to receive IPC");
_stop();
return;
}
}
void Thread::_await_ipc()
{
switch (_state) {
case SCHEDULED:
cpu_scheduler()->remove(this);
_state = AWAITS_IPC;
return;
default:
PERR("wrong thread state to await IPC");
_stop();
return;
}
}
void Thread::_await_ipc_succeeded(size_t const s)
{
switch (_state) {
case AWAITS_IPC:
user_arg_0(0);
_schedule();
return;
default:
PERR("wrong thread state to receive IPC");
_stop();
return;
}
}
void Thread::_await_ipc_failed()
{
switch (_state) {
case AWAITS_IPC:
user_arg_0(-1);
_schedule();
return;
case SCHEDULED:
PERR("failed to receive IPC");
_stop();
return;
default:
PERR("wrong thread state to cancel IPC");
_stop();
return;
}
}
int Thread::_resume()
{
switch (_state) {
case AWAITS_RESUME:
_schedule();
return 0;
case SCHEDULED:
return 1;
case AWAITS_IPC:
Ipc_node::cancel_waiting();
return 0;
case AWAITS_SIGNAL:
Signal_handler::cancel_waiting();
return 0;
case AWAITS_SIGNAL_CONTEXT_KILL:
Signal_context_killer::cancel_waiting();
return 0;
case AWAITS_SIGNAL_RECEIVER_KILL:
Signal_receiver_killer::cancel_waiting();
return 0;
case AWAITS_START:
case STOPPED:;
}
PERR("failed to resume thread");
return -1;
}
void Thread::_pause()
{
assert(_state == AWAITS_RESUME || _state == SCHEDULED);
cpu_scheduler()->remove(this);
_state = AWAITS_RESUME;
}
void Thread::_schedule()
{
cpu_scheduler()->insert(this);
_state = SCHEDULED;
}
Thread::Thread(unsigned const priority, char const * const label)
:
Execution_context(priority),
Thread_cpu_support(this),
_state(AWAITS_START),
_pd(0),
_utcb_phys(0),
_signal_receiver(0),
_label(label)
{ }
void
Thread::init(unsigned const cpu_id, unsigned const pd_id_arg,
Native_utcb * const utcb_phys, bool const start)
{
assert(_state == AWAITS_START)
/* FIXME: support SMP */
if (cpu_id) { PERR("multicore processing not supported"); }
/* store thread parameters */
_utcb_phys = utcb_phys;
/* join protection domain */
_pd = Pd::pool()->object(pd_id_arg);
assert(_pd);
addr_t const tlb = _pd->tlb()->base();
User_context::init_thread(tlb, pd_id());
/* print log message */
if (START_VERBOSE) {
PINF("in program %u '%s' start thread %u '%s'",
pd_id(), pd_label(), id(), label());
}
/* start execution */
if (start) { _schedule(); }
}
void Thread::_stop()
{
if (_state == SCHEDULED) { cpu_scheduler()->remove(this); }
_state = STOPPED;
}
void Thread::handle_exception()
{
switch (cpu_exception) {
case SUPERVISOR_CALL:
_call();
return;
case PREFETCH_ABORT:
_mmu_exception();
return;
case DATA_ABORT:
_mmu_exception();
return;
case INTERRUPT_REQUEST:
handle_interrupt();
return;
case FAST_INTERRUPT_REQUEST:
handle_interrupt();
return;
default:
PERR("unknown exception");
_stop();
reset_lap_time();
}
}
void Thread::_receive_yielded_cpu()
{
if (_state == AWAITS_RESUME) { _schedule(); }
else { PERR("failed to receive yielded CPU"); }
}
void Thread::proceed()
{
mtc()->continue_user(static_cast<Cpu::Context *>(this));
}
char const * Kernel::Thread::pd_label() const
{
if (_core()) { return "core"; }
if (!_pd) { return "?"; }
return _pd->platform_pd()->label();
}
void Thread::_call_new_pd()
{
/* check permissions */
if (!_core()) {
PERR("not entitled to create protection domain");
user_arg_0(0);
return;
}
/* create translation lookaside buffer and protection domain */
void * p = (void *)user_arg_1();
Tlb * const tlb = new (p) Tlb();
p = (void *)((addr_t)p + sizeof(Tlb));
Pd * const pd = new (p) Pd(tlb, (Platform_pd *)user_arg_2());
user_arg_0(pd->id());
}
void Thread::_call_kill_pd()
{
/* check permissions */
if (!_core()) {
PERR("not entitled to destruct protection domain");
user_arg_0(-1);
return;
}
/* lookup protection domain */
unsigned id = user_arg_1();
Pd * const pd = Pd::pool()->object(id);
if (!pd) {
PERR("unknown protection domain");
user_arg_0(-1);
return;
}
/* destruct translation lookaside buffer and protection domain */
Tlb * const tlb = pd->tlb();
pd->~Pd();
tlb->~Tlb();
/* clean up buffers of memory management */
Cpu::flush_tlb_by_pid(pd->id());
user_arg_0(0);
}
void Thread::_call_new_thread()
{
/* check permissions */
if (!_core()) {
PERR("not entitled to create thread");
user_arg_0(0);
return;
}
/* create new thread */
void * const p = (void *)user_arg_1();
unsigned const priority = user_arg_2();
char const * const label = (char *)user_arg_3();
Thread * const t = new (p) Thread(priority, label);
user_arg_0(t->id());
}
void Thread::_call_kill_thread()
{
/* check permissions */
assert(_core());
/* get targeted thread */
unsigned thread_id = (unsigned)user_arg_1();
Thread * const thread = Thread::pool()->object(thread_id);
assert(thread);
/* destroy thread */
thread->~Thread();
}
void Thread::_call_start_thread()
{
/* check permissions */
if (!_core()) {
PERR("not entitled to start thread");
user_arg_0(0);
return;
}
/* dispatch arguments */
unsigned const thread_id = user_arg_1();
unsigned const cpu_id = user_arg_2();
unsigned const pd_id = user_arg_3();
Native_utcb * const utcb = (Native_utcb *)user_arg_4();
/* lookup targeted thread */
Thread * const t = Thread::pool()->object(thread_id);
if (!t) {
PERR("unknown thread");
user_arg_0(0);
return;
}
/* start thread */
t->init(cpu_id, pd_id, utcb, 1);
user_arg_0((Call_ret)t->_pd->tlb());
}
void Thread::_call_pause_thread()
{
unsigned const tid = user_arg_1();
/* shortcut for a thread to pause itself */
if (!tid) {
_pause();
user_arg_0(0);
return;
}
/* get targeted thread and check permissions */
Thread * const t = Thread::pool()->object(tid);
assert(t && (_core() || this == t));
/* pause targeted thread */
t->_pause();
user_arg_0(0);
}
void Thread::_call_resume_thread()
{
/* lookup thread */
Thread * const t = Thread::pool()->object(user_arg_1());
if (!t) {
PERR("unknown thread");
user_arg_0(-1);
return;
}
/* check permissions */
if (!_core() && pd_id() != t->pd_id()) {
PERR("not entitled to resume thread");
user_arg_0(-1);
return;
}
/* resume targeted thread */
user_arg_0(t->_resume());
}
void Thread_event::_signal_acknowledged()
{
Cpu::tlb_insertions();
_thread->_resume();
}
Thread_event::Thread_event(Thread * const t)
:
_thread(t), _signal_context(0)
{ }
void Thread_event::submit()
{
if (_signal_context && !_signal_context->submit(1)) { return; }
PERR("failed to communicate thread event");
}
void Thread::_call_yield_thread()
{
Thread * const t = Thread::pool()->object(user_arg_1());
if (t) { t->_receive_yielded_cpu(); }
cpu_scheduler()->yield();
}
void Thread::_call_await_request_msg()
{
void * buf_base;
size_t buf_size;
_utcb_phys->call_await_request_msg(buf_base, buf_size);
Ipc_node::await_request(buf_base, buf_size);
}
void Thread::_call_send_request_msg()
{
Thread * const dst = Thread::pool()->object(user_arg_1());
if (!dst) {
PERR("unknown recipient");
_await_ipc();
return;
}
void * msg_base;
size_t msg_size;
void * buf_base;
size_t buf_size;
_utcb_phys->call_send_request_msg(msg_base, msg_size,
buf_base, buf_size);
Ipc_node::send_request_await_reply(dst, msg_base, msg_size,
buf_base, buf_size);
}
void Thread::_call_send_reply_msg()
{
void * msg_base;
size_t msg_size;
_utcb_phys->call_send_reply_msg(msg_base, msg_size);
Ipc_node::send_reply(msg_base, msg_size);
bool const await_request_msg = user_arg_1();
if (await_request_msg) { _call_await_request_msg(); }
else { user_arg_0(0); }
}
void Thread::_call_route_thread_event()
{
/* check permissions */
if (!_core()) {
PERR("not entitled to route thread event");
user_arg_0(-1);
return;
}
/* get targeted thread */
unsigned const thread_id = user_arg_1();
Thread * const t = Thread::pool()->object(thread_id);
if (!t) {
PERR("unknown thread");
user_arg_0(-1);
return;
}
/* override event route */
unsigned const event_id = user_arg_2();
unsigned const signal_context_id = user_arg_3();
if (t->_route_event(event_id, signal_context_id)) { user_arg_0(-1); }
else { user_arg_0(0); }
return;
}
int Thread::_route_event(unsigned const event_id,
unsigned const signal_context_id)
{
/* lookup signal context */
Signal_context * c;
if (signal_context_id) {
c = Signal_context::pool()->object(signal_context_id);
if (!c) {
PERR("unknown signal context");
return -1;
}
} else { c = 0; }
/* lookup event and assign signal context */
Thread_event Thread::* e = _event(event_id);
if (!e) { return -1; }
(this->*e).signal_context(c);
return 0;
}
void Thread_event::signal_context(Signal_context * const c)
{
_signal_context = c;
if (_signal_context) { _signal_context->ack_handler(this); }
}
unsigned Thread_event::signal_context_id() const
{
if (_signal_context) { return _signal_context->id(); }
return 0;
}
void Thread::_call_access_thread_regs()
{
/* check permissions */
if (!_core()) {
PERR("not entitled to access thread regs");
user_arg_0(-1);
return;
}
/* get targeted thread */
unsigned const thread_id = user_arg_1();
Thread * const t = Thread::pool()->object(thread_id);
if (!t) {
PERR("unknown thread");
user_arg_0(-1);
return;
}
/* execute read operations */
unsigned const reads = user_arg_2();
unsigned const writes = user_arg_3();
addr_t * const utcb = (addr_t *)_utcb_phys->base();
addr_t * const read_ids = &utcb[0];
addr_t * const read_values = (addr_t *)user_arg_4();
for (unsigned i = 0; i < reads; i++) {
if (t->_read_reg(read_ids[i], read_values[i])) {
user_arg_0(reads + writes - i);
return;
}
}
/* execute write operations */
addr_t * const write_ids = &utcb[reads];
addr_t * const write_values = (addr_t *)user_arg_5();
for (unsigned i = 0; i < writes; i++) {
if (t->_write_reg(write_ids[i], write_values[i])) {
user_arg_0(writes - i);
return;
}
}
user_arg_0(0);
return;
}
void Thread::_call_update_pd()
{
assert(_core());
Cpu::flush_tlb_by_pid(user_arg_1());
}
void Thread::_call_update_region()
{
assert(_core());
/* FIXME we don't handle instruction caches by now */
Cpu::flush_data_cache_by_virt_region((addr_t)user_arg_1(),
(size_t)user_arg_2());
}
void Thread::_call_print_char()
{
Genode::printf("%c", (char)user_arg_1());
}
void Thread::_call_new_signal_receiver()
{
/* check permissions */
if (!_core()) {
PERR("not entitled to create signal receiver");
user_arg_0(0);
return;
}
/* create receiver */
void * const p = (void *)user_arg_1();
Signal_receiver * const r = new (p) Signal_receiver();
user_arg_0(r->id());
}
void Thread::_call_new_signal_context()
{
/* check permissions */
if (!_core()) {
PERR("not entitled to create signal context");
user_arg_0(0);
return;
}
/* lookup receiver */
unsigned const id = user_arg_2();
Signal_receiver * const r = Signal_receiver::pool()->object(id);
if (!r) {
PERR("unknown signal receiver");
user_arg_0(0);
return;
}
/* create and assign context*/
void * const p = (void *)user_arg_1();
unsigned const imprint = user_arg_3();
try {
Signal_context * const c = new (p) Signal_context(r, imprint);
user_arg_0(c->id());
} catch (Signal_context::Assign_to_receiver_failed) {
PERR("failed to assign context to receiver");
user_arg_0(0);
}
}
void Thread::_call_await_signal()
{
/* check wether to acknowledge a context */
unsigned const context_id = user_arg_2();
if (context_id) {
Signal_context * const c = Signal_context::pool()->object(context_id);
if (c) { c->ack(); }
else { PERR("failed to acknowledge signal context"); }
}
/* lookup receiver */
unsigned const receiver_id = user_arg_1();
Signal_receiver * const r = Signal_receiver::pool()->object(receiver_id);
if (!r) {
PERR("unknown signal receiver");
user_arg_0(-1);
return;
}
/* register handler at the receiver */
if (r->add_handler(this)) {
PERR("failed to register handler at signal receiver");
user_arg_0(-1);
return;
}
user_arg_0(0);
}
void Thread::_call_signal_pending()
{
/* lookup signal receiver */
unsigned const id = user_arg_1();
Signal_receiver * const r = Signal_receiver::pool()->object(id);
if (!r) {
PERR("unknown signal receiver");
user_arg_0(0);
return;
}
/* get pending state */
user_arg_0(r->deliverable());
}
void Thread::_call_submit_signal()
{
/* lookup signal context */
unsigned const id = user_arg_1();
Signal_context * const c = Signal_context::pool()->object(id);
if(!c) {
PERR("unknown signal context");
user_arg_0(-1);
return;
}
/* trigger signal context */
if (c->submit(user_arg_2())) {
PERR("failed to submit signal context");
user_arg_0(-1);
return;
}
user_arg_0(0);
}
void Thread::_call_ack_signal()
{
/* lookup signal context */
unsigned const id = user_arg_1();
Signal_context * const c = Signal_context::pool()->object(id);
if (!c) {
PERR("unknown signal context");
return;
}
/* acknowledge */
c->ack();
}
void Thread::_call_kill_signal_context()
{
/* check permissions */
if (!_core()) {
PERR("not entitled to kill signal context");
user_arg_0(-1);
return;
}
/* lookup signal context */
unsigned const id = user_arg_1();
Signal_context * const c = Signal_context::pool()->object(id);
if (!c) {
PERR("unknown signal context");
user_arg_0(0);
return;
}
/* kill signal context */
if (c->kill(this)) {
PERR("failed to kill signal context");
user_arg_0(-1);
return;
}
user_arg_0(0);
}
void Thread::_call_kill_signal_receiver()
{
/* check permissions */
if (!_core()) {
PERR("not entitled to kill signal receiver");
user_arg_0(-1);
return;
}
/* lookup signal receiver */
unsigned const id = user_arg_1();
Signal_receiver * const r = Signal_receiver::pool()->object(id);
if (!r) {
PERR("unknown signal receiver");
user_arg_0(0);
return;
}
/* kill signal receiver */
if (r->kill(this)) {
PERR("unknown signal receiver");
user_arg_0(-1);
return;
}
user_arg_0(0);
}
void Thread::_call_new_vm()
{
/* check permissions */
assert(_core());
/* dispatch arguments */
void * const allocator = (void * const)user_arg_1();
Genode::Cpu_state_modes * const state =
(Genode::Cpu_state_modes * const)user_arg_2();
Signal_context * const context =
Signal_context::pool()->object(user_arg_3());
assert(context);
/* create vm */
Vm * const vm = new (allocator) Vm(state, context);
/* return vm id */
user_arg_0((Call_ret)vm->id());
}
void Thread::_call_run_vm()
{
/* check permissions */
assert(_core());
/* get targeted vm via its id */
Vm * const vm = Vm::pool()->object(user_arg_1());
assert(vm);
/* run targeted vm */
vm->run();
}
void Thread::_call_pause_vm()
{
/* check permissions */
assert(_core());
/* get targeted vm via its id */
Vm * const vm = Vm::pool()->object(user_arg_1());
assert(vm);
/* pause targeted vm */
vm->pause();
}
int Thread::_read_reg(addr_t const id, addr_t & value) const
{
addr_t Thread::* const reg = _reg(id);
if (reg) {
value = this->*reg;
return 0;
}
PERR("unknown thread register");
return -1;
}
int Thread::_write_reg(addr_t const id, addr_t const value)
{
addr_t Thread::* const reg = _reg(id);
if (reg) {
this->*reg = value;
return 0;
}
PERR("unknown thread register");
return -1;
}
void Thread::_call()
{
switch (user_arg_0()) {
case Call_id::NEW_THREAD: _call_new_thread(); return;
case Call_id::KILL_THREAD: _call_kill_thread(); return;
case Call_id::START_THREAD: _call_start_thread(); return;
case Call_id::PAUSE_THREAD: _call_pause_thread(); return;
case Call_id::RESUME_THREAD: _call_resume_thread(); return;
case Call_id::YIELD_THREAD: _call_yield_thread(); return;
case Call_id::SEND_REQUEST_MSG: _call_send_request_msg(); return;
case Call_id::SEND_REPLY_MSG: _call_send_reply_msg(); return;
case Call_id::AWAIT_REQUEST_MSG: _call_await_request_msg(); return;
case Call_id::UPDATE_PD: _call_update_pd(); return;
case Call_id::UPDATE_REGION: _call_update_region(); return;
case Call_id::NEW_PD: _call_new_pd(); return;
case Call_id::PRINT_CHAR: _call_print_char(); return;
case Call_id::NEW_SIGNAL_RECEIVER: _call_new_signal_receiver(); return;
case Call_id::NEW_SIGNAL_CONTEXT: _call_new_signal_context(); return;
case Call_id::KILL_SIGNAL_CONTEXT: _call_kill_signal_context(); return;
case Call_id::KILL_SIGNAL_RECEIVER: _call_kill_signal_receiver(); return;
case Call_id::AWAIT_SIGNAL: _call_await_signal(); return;
case Call_id::SUBMIT_SIGNAL: _call_submit_signal(); return;
case Call_id::SIGNAL_PENDING: _call_signal_pending(); return;
case Call_id::ACK_SIGNAL: _call_ack_signal(); return;
case Call_id::NEW_VM: _call_new_vm(); return;
case Call_id::RUN_VM: _call_run_vm(); return;
case Call_id::PAUSE_VM: _call_pause_vm(); return;
case Call_id::KILL_PD: _call_kill_pd(); return;
case Call_id::ACCESS_THREAD_REGS: _call_access_thread_regs(); return;
case Call_id::ROUTE_THREAD_EVENT: _call_route_thread_event(); return;
default:
PERR("unknown kernel call");
_stop();
reset_lap_time();
}
}