genode/base-fiasco/src/core/platform_pd.cc

291 lines
5.4 KiB
C++

/*
* \brief Fiasco protection domain facility
* \author Christian Helmuth
* \date 2006-04-11
*
* On Fiasco, the pd class has several duties:
*
* - It is an allocator for L4 tasks and cares for versioning and recycling. We
* do this with "static class members".
* - L4 threads are tied to L4 tasks and there are only 128 per L4 task. So
* each pd object is an allocator for its threads.
*/
/*
* Copyright (C) 2006-2012 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/native_types.h>
/* core includes */
#include <util.h>
#include <platform_pd.h>
/* Fiasco includes */
namespace Fiasco {
#include <l4/sys/syscalls.h>
}
using namespace Fiasco;
using namespace Genode;
static const bool verbose = false;
/**************************
** Static class members **
**************************/
static bool _init = false;
void Platform_pd::init()
{
if (_init) return;
unsigned i;
Pd_alloc reserved(true, true, 0);
Pd_alloc free(false, true, 0);
/* mark reserved protection domains */
for (i = 0; i < PD_FIRST; ++i) _pds()[i] = reserved;
/* init remainder */
for ( ; i < PD_MAX; ++i) _pds()[i] = free;
_init = true;
}
/****************************
** Private object members **
****************************/
void Platform_pd::_create_pd(bool syscall)
{
l4_threadid_t l4t = l4_myself();
l4t.id.task = _pd_id;
l4t.id.lthread = 0;
l4t.id.version_low = _version;
l4_taskid_t nt;
if (syscall)
nt = l4_task_new(l4t, 0, 0, 0, l4t);
else
nt = l4t;
if (l4_is_nil_id(nt))
panic("pd creation failed");
_l4_task_id = nt;
}
void Platform_pd::_destroy_pd()
{
l4_threadid_t l4t = _l4_task_id;
/* L4 task deletion is: make inactive with myself as chief in 2nd parameter */
l4_taskid_t nt = l4_task_new(l4t, convert_native_thread_id_to_badge(l4_myself()),
0, 0, L4_NIL_ID);
if (l4_is_nil_id(nt))
panic("pd destruction failed");
_l4_task_id = L4_INVALID_ID;
}
int Platform_pd::_alloc_pd(signed pd_id)
{
if (pd_id == PD_INVALID) {
unsigned i;
for (i = PD_FIRST; i < PD_MAX; i++)
if (_pds()[i].free) break;
/* no free protection domains available */
if (i == PD_MAX) return -1;
pd_id = i;
} else {
if (!_pds()[pd_id].reserved || !_pds()[pd_id].free)
return -1;
}
_pds()[pd_id].free = 0;
_pd_id = pd_id;
_version = _pds()[pd_id].version;
return pd_id;
}
void Platform_pd::_free_pd()
{
unsigned t = _pd_id;
/* XXX check and log double-free? */
if (_pds()[t].free) return;
/* maximum reuse count reached leave non-free */
if (_pds()[t].version == PD_VERSION_MAX) return;
_pds()[t].free = 1;
++_pds()[t].version;
}
void Platform_pd::_init_threads()
{
unsigned i;
for (i = 0; i < THREAD_MAX; ++i)
_threads[i] = 0;
}
Platform_thread* Platform_pd::_next_thread()
{
unsigned i;
/* look for bound thread */
for (i = 0; i < THREAD_MAX; ++i)
if (_threads[i]) break;
/* no bound threads */
if (i == THREAD_MAX) return 0;
return _threads[i];
}
int Platform_pd::_alloc_thread(int thread_id, Platform_thread *thread)
{
int i = thread_id;
/* look for free thread */
if (thread_id == Platform_thread::THREAD_INVALID) {
for (i = 0; i < THREAD_MAX; ++i)
if (!_threads[i]) break;
/* no free threads available */
if (i == THREAD_MAX) return -1;
} else {
if (_threads[i]) return -2;
}
_threads[i] = thread;
return i;
}
void Platform_pd::_free_thread(int thread_id)
{
if (!_threads[thread_id])
PWRN("double-free of thread %x.%x detected", _pd_id, thread_id);
_threads[thread_id] = 0;
}
/***************************
** Public object members **
***************************/
int Platform_pd::bind_thread(Platform_thread *thread)
{
/* thread_id is THREAD_INVALID by default - only core is the special case */
int thread_id = thread->thread_id();
l4_threadid_t l4_thread_id;
int t = _alloc_thread(thread_id, thread);
if (t < 0) {
PERR("Thread alloc failed");
return -1;
}
thread_id = t;
l4_thread_id = _l4_task_id;
l4_thread_id.id.lthread = thread_id;
/* finally inform thread about binding */
thread->bind(thread_id, l4_thread_id, this);
if (verbose) _debug_log_threads();
return 0;
}
void Platform_pd::unbind_thread(Platform_thread *thread)
{
int thread_id = thread->thread_id();
/* unbind thread before proceeding */
thread->unbind();
_free_thread(thread_id);
if (verbose) _debug_log_threads();
}
Platform_pd::Platform_pd(signed pd_id, bool create)
{
/* check correct init */
if (!_init)
panic("init pd facility via Platform_pd::init() before using it!");
/* init threads */
_init_threads();
int ret = _alloc_pd(pd_id);
if (ret < 0) {
_debug_log_pds();
panic("pd alloc failed");
}
_create_pd(create);
}
Platform_pd::~Platform_pd()
{
/* unbind all threads */
while (Platform_thread *t = _next_thread()) unbind_thread(t);
_destroy_pd();
_free_pd();
}
/***********************
** Debugging support **
***********************/
void Platform_pd::_debug_log_threads()
{
int i;
printf("[%02x] ", _pd_id);
for (i = 0; i < THREAD_MAX; ++i) {
printf("%c", !_threads[i] ? '.' : 'X');
if (i == 63) printf("\n ");
}
printf("\n");
}
void Platform_pd::_debug_log_pds()
{
int i;
for (i = 0; i < PD_MAX; ++i)
printf("[%02x] %d %d %d\n", i, _pds()[i].reserved, _pds()[i].free,
_pds()[i].version);
}