nova: use in core one pager per CPU

Fixes #1394
This commit is contained in:
Alexander Boettcher 2014-09-05 17:00:31 +02:00 committed by Christian Helmuth
parent 0f47ac958b
commit a31378476d
21 changed files with 750 additions and 308 deletions

View File

@ -180,11 +180,13 @@ namespace Nova {
ALWAYS_INLINE
__attribute__((noreturn))
inline void reply(void *next_sp)
inline void reply(void *next_sp, unsigned sm = 0)
{
mword_t reg = eax(NOVA_REPLY, 0, sm);
asm volatile ("sysenter;"
:
: "a" (NOVA_REPLY), "c" (next_sp)
: "a" (reg), "c" (next_sp)
: "memory");
__builtin_unreachable();
}

View File

@ -142,12 +142,14 @@ namespace Nova {
ALWAYS_INLINE
__attribute__((noreturn))
inline void reply(void *next_sp)
inline void reply(void *next_sp, unsigned long sm = 0)
{
mword_t syscall = rdi(NOVA_REPLY, 0, sm);
asm volatile ("mov %1, %%rsp;"
"syscall;"
:
: "D" (NOVA_REPLY), "ir" (next_sp)
: "D" (syscall), "ir" (next_sp)
: "memory");
__builtin_unreachable();
}

View File

@ -394,6 +394,8 @@ namespace Genode {
*/
if (rcv_window != INVALID_INDEX)
_rcv_pt_base = INVALID_INDEX;
utcb->crd_rcv = 0;
}
};

View File

@ -103,7 +103,7 @@ namespace Genode {
/**
* Answer current page fault
*/
void reply_and_wait_for_fault();
void reply_and_wait_for_fault(unsigned sm = 0);
/**
* Request instruction pointer of current fault

View File

@ -24,17 +24,28 @@
namespace Genode {
class Pager_entrypoint;
class Pager_object;
/*
* On NOVA, each pager object is an EC that corresponds to one user thread.
*/
class Pager_object : public Object_pool<Pager_object>::Entry,
Thread_base
class Exception_handlers
{
private:
void entry() { }
void start() { }
template <uint8_t EV>
__attribute__((regparm(1))) static void _handler(addr_t);
public:
Exception_handlers(Pager_object *);
template <uint8_t EV>
void register_handler(Pager_object *, Nova::Mtd,
void (__attribute__((regparm(1)))*)(addr_t) = nullptr);
};
class Pager_object : public Object_pool<Pager_object>::Entry
{
private:
unsigned long _badge; /* used for debugging */
@ -45,9 +56,12 @@ namespace Genode {
Signal_context_capability _exception_sigh;
/**
* Portal selector for object cleanup/destruction
* selectors for
* - cleanup portal
* - semaphore used by caller used to notify paused state
* - semaphore used to block during page fault handling or pausing
*/
addr_t _pt_cleanup;
addr_t _selectors;
addr_t _initial_esp;
addr_t _initial_eip;
@ -59,58 +73,77 @@ namespace Genode {
struct Thread_state thread;
addr_t sel_client_ec;
enum {
VALID = 0x1U,
DEAD = 0x2U,
SINGLESTEP = 0x4U,
CLIENT_CANCEL = 0x8U,
SIGNAL_SM = 0x10U,
BLOCKED = 0x1U,
DEAD = 0x2U,
SINGLESTEP = 0x4U,
NOTIFY_REQUEST = 0x8U,
SIGNAL_SM = 0x10U,
DISSOLVED = 0x20U,
SUBMIT_SIGNAL = 0x40U,
SKIP_EXCEPTION = 0x80U,
};
uint8_t _status;
/* convenience function to access pause/recall state */
inline bool is_valid() { return _status & VALID; }
inline void mark_valid() { _status |= VALID; }
inline void mark_invalid() { _status &= ~VALID; }
inline bool is_client_cancel() { return _status & CLIENT_CANCEL; }
inline void mark_client_cancel() { _status |= CLIENT_CANCEL; }
inline void unmark_client_cancel() { _status &= ~CLIENT_CANCEL; }
inline bool blocked() { return _status & BLOCKED;}
inline void block() { _status |= BLOCKED; }
inline void unblock() { _status &= ~BLOCKED; }
inline void mark_dead() { _status |= DEAD; }
inline bool is_dead() { return _status & DEAD; }
inline bool singlestep() { return _status & SINGLESTEP; }
inline void notify_request() { _status |= NOTIFY_REQUEST; }
inline bool notify_requested() { return _status & NOTIFY_REQUEST; }
inline void notify_cancel() { _status &= ~NOTIFY_REQUEST; }
inline void mark_signal_sm() { _status |= SIGNAL_SM; }
inline bool has_signal_sm() { return _status & SIGNAL_SM; }
inline void mark_dissolved() { _status |= DISSOLVED; }
inline bool dissolved() { return _status & DISSOLVED; }
inline bool to_submit() { return _status & SUBMIT_SIGNAL; }
inline void submit_signal() { _status |= SUBMIT_SIGNAL; }
inline void reset_submit() { _status &= ~SUBMIT_SIGNAL; }
inline bool skip_requested() { return _status & SKIP_EXCEPTION; }
inline void skip_request() { _status |= SKIP_EXCEPTION; }
inline void skip_reset() { _status &= ~SKIP_EXCEPTION; }
} _state;
Thread_capability _thread_cap;
Thread_capability _thread_cap;
Exception_handlers _exceptions;
void _copy_state(Nova::Utcb * utcb);
/**
* Semaphore selector to synchronize pause/state/resume operations
*/
addr_t sm_state_notify() { return _pt_cleanup + 1; }
static void _page_fault_handler();
static void _startup_handler();
static void _invoke_handler();
static void _recall_handler();
addr_t sel_pt_cleanup() { return _selectors; }
addr_t sel_sm_notify() { return _selectors + 1; }
addr_t sel_sm_block() { return _selectors + 2; }
__attribute__((regparm(1)))
static void _exception_handler(addr_t portal_id);
static void _page_fault_handler(addr_t pager_obj);
static Nova::Utcb * _check_handler(Thread_base *&, Pager_object *&);
__attribute__((regparm(1)))
static void _startup_handler(addr_t pager_obj);
__attribute__((regparm(1)))
static void _invoke_handler(addr_t pager_obj);
__attribute__((regparm(1)))
static void _recall_handler(addr_t pager_obj);
public:
const Affinity::Location location;
Pager_object(unsigned long badge, Affinity::Location location);
virtual ~Pager_object();
unsigned long badge() const { return _badge; }
void reset_badge() { _badge = 0; }
virtual int pager(Ipc_pager &ps) = 0;
@ -122,15 +155,11 @@ namespace Genode {
_exception_sigh = sigh;
}
/**
* Return base of initial portal window
*/
addr_t ec_sel() { return _tid.ec_sel; }
void exception(uint8_t exit_id);
/**
* Return base of initial portal window
*/
addr_t exc_pt_sel() { return _tid.exc_pt_sel; }
addr_t exc_pt_sel_client() { return _client_exc_pt_sel; }
addr_t exc_pt_vcpu() { return _client_exc_vcpu; }
@ -157,6 +186,8 @@ namespace Genode {
{
if (!_exception_sigh.valid()) return false;
_state.reset_submit();
Signal_transmitter transmitter(_exception_sigh);
transmitter.submit();
@ -177,10 +208,12 @@ namespace Genode {
*/
Native_capability notify_sm()
{
if (_state.is_valid() || _state.is_dead())
if (_state.blocked() || _state.is_dead())
return Native_capability();
return Native_capability(sm_state_notify());
_state.notify_request();
return Native_capability(sel_sm_notify());
}
/**
@ -188,7 +221,7 @@ namespace Genode {
*/
bool copy_thread_state(Thread_state * state_dst)
{
if (!state_dst || !_state.is_valid())
if (!state_dst || !_state.blocked())
return false;
*state_dst = _state.thread;
@ -205,12 +238,31 @@ namespace Genode {
uint8_t client_recall();
void client_set_ec(addr_t ec) { _state.sel_client_ec = ec; }
inline void single_step(bool on)
inline Native_capability single_step(bool on)
{
if (_state.is_dead() ||
(on && (_state._status & _state.SINGLESTEP)) ||
(!on && !(_state._status & _state.SINGLESTEP)))
return Native_capability();
if (on)
_state._status |= _state.SINGLESTEP;
else
_state._status &= ~_state.SINGLESTEP;
/* we want to be notified if state change is done */
_state.notify_request();
/* the first single step exit ignore when switching it on */
if (on && _state.blocked())
_state.skip_request();
/* force client in exit and thereby apply single_step change */
client_recall();
/* single_step mode changes don't apply if blocked - wake up */
if (_state.blocked())
wake_up();
return Native_capability(sel_sm_notify());
}
/**
@ -220,7 +272,7 @@ namespace Genode {
Thread_capability thread_cap() { return _thread_cap; } const
void thread_cap(Thread_capability cap) { _thread_cap = cap; }
/*
/**
* Note in the thread state that an unresolved page
* fault occurred.
*/
@ -244,53 +296,109 @@ namespace Genode {
void prepare_vCPU_portals()
{
_client_exc_vcpu = cap_map()->insert(Nova::NUM_INITIAL_VCPU_PT_LOG2);
}
};
Nova::Utcb *utcb = reinterpret_cast<Nova::Utcb *>(Thread_base::utcb());
/**
* A 'Pager_activation' processes one page fault of a 'Pager_object' at a time.
*/
class Pager_entrypoint;
class Pager_activation_base: public Thread_base
{
private:
utcb->crd_rcv = Nova::Obj_crd(_client_exc_vcpu, Nova::NUM_INITIAL_VCPU_PT_LOG2);
Native_capability _cap;
Pager_entrypoint *_ep; /* entry point to which the
activation belongs */
/**
* Lock used for blocking until '_cap' is initialized
*/
Lock _cap_valid;
public:
/**
* Constructor
*
* \param name name of the new thread
* \param stack_size stack size of the new thread
*/
Pager_activation_base(char const * const name,
size_t const stack_size);
/**
* Set entry point, which the activation serves
*
* This function is only called by the 'Pager_entrypoint'
* constructor.
*/
void ep(Pager_entrypoint *ep) { _ep = ep; }
/**
* Thread interface
*/
void entry();
/**
* Return capability to this activation
*
* This function should only be called from 'Pager_entrypoint'
*/
Native_capability cap()
{
/* ensure that the initialization of our 'Ipc_pager' is done */
if (!_cap.valid())
_cap_valid.lock();
return _cap;
}
};
/**
* Dummy pager activation
* Paging entry point
*
* Because on NOVA each pager object can be invoked separately,
* there is no central pager activation.
*/
class Pager_activation_base { };
template <unsigned STACK_SIZE>
class Pager_activation : public Pager_activation_base
{ };
/**
* Dummy pager entrypoint
* For a paging entry point can hold only one activation. So, paging is
* strictly serialized for one entry point.
*/
class Pager_entrypoint : public Object_pool<Pager_object>
{
private:
Cap_session *_cap_session;
Pager_activation_base *_activation;
Cap_session *_cap_session;
public:
Pager_entrypoint(Cap_session *cap_session,
Pager_activation_base *a = 0)
: _cap_session(cap_session) { }
/**
* Constructor
*
* \param cap_session Cap_session for creating capabilities
* for the pager objects managed by this
* entry point
* \param a initial activation
*/
Pager_entrypoint(Cap_session *cap_session, Pager_activation_base *a = 0);
/**
* Return capability for 'Pager_object'
* Associate Pager_object with the entry point
*/
Pager_capability manage(Pager_object *obj);
/**
* Dissolve 'Pager_object' from entry point
* Dissolve Pager_object from entry point
*/
void dissolve(Pager_object *obj);
};
template <int STACK_SIZE>
class Pager_activation : public Pager_activation_base
{
public:
Pager_activation() : Pager_activation_base("pager", STACK_SIZE)
{ }
};
}
#endif /* _INCLUDE__BASE__PAGER_H_ */

View File

@ -67,8 +67,14 @@ namespace Genode {
void exception_handler(Thread_capability thread, Signal_context_capability handler) {
call<Rpc_exception_handler>(thread, handler); }
void single_step(Thread_capability thread, bool enable) {
call<Rpc_single_step>(thread, enable); }
void single_step(Thread_capability thread, bool enable)
{
Native_capability block = call<Rpc_single_step_sync>(thread, enable);
if (!block.valid())
return;
Nova::sm_ctrl(block.local_name(), Nova::SEMAPHORE_DOWN);
}
Affinity::Space affinity_space() const {
return call<Rpc_affinity_space>(); }
@ -100,8 +106,11 @@ namespace Genode {
private:
Native_capability pause_sync(Thread_capability target) {
return Native_capability::invalid_cap(); }
Native_capability pause_sync(Thread_capability) {
return Native_capability(); }
Native_capability single_step_sync(Thread_capability, bool) {
return Native_capability(); }
};
}

View File

@ -129,16 +129,24 @@ namespace Nova {
unsigned cpus() const {
unsigned cpu_num = 0;
const char * cpu_desc =
reinterpret_cast<const char *>(this) + cpu_desc_offset;
for (unsigned i = 0; i < cpu_max(); i++) {
if ((*cpu_desc) & 0x1) cpu_num++;
cpu_desc += cpu_desc_size;
}
for (unsigned i = 0; i < cpu_max(); i++)
if (is_cpu_enabled(i))
cpu_num++;
return cpu_num;
}
bool is_cpu_enabled(unsigned i) const {
if (i >= cpu_max())
return false;
const char * cpu_desc = reinterpret_cast<const char *>(this) +
cpu_desc_offset + i * cpu_desc_size;
return (*cpu_desc) & 0x1;
}
} __attribute__((packed));

View File

@ -56,17 +56,22 @@ inline void request_event_portal(Genode::Native_capability const &cap,
inline void request_native_ec_cap(Genode::Native_capability const &cap,
Genode::addr_t sel, unsigned no_pager_cap = 0) {
request_event_portal(cap, sel , ~0UL, no_pager_cap); }
Genode::addr_t const sel,
unsigned const no_pager_cap = 0)
{
request_event_portal(cap, sel , ~0UL, no_pager_cap);
}
inline void request_signal_sm_cap(Genode::Native_capability const &cap,
Genode::addr_t sel) {
request_event_portal(cap, sel, ~0UL - 1, 0); }
Genode::addr_t const sel)
{
request_event_portal(cap, sel, ~0UL - 1, 0);
}
inline void delegate_vcpu_portals(Genode::Native_capability const &cap,
Genode::addr_t sel)
Genode::addr_t const sel)
{
Genode::Thread_base * myself = Genode::Thread_base::myself();
Nova::Utcb *utcb = reinterpret_cast<Nova::Utcb *>(myself->utcb());
@ -74,14 +79,33 @@ inline void delegate_vcpu_portals(Genode::Native_capability const &cap,
/* save original receive window */
Nova::Crd orig_crd = utcb->crd_rcv;
Nova::Obj_crd obj_crd(sel, Nova::NUM_INITIAL_VCPU_PT_LOG2);
utcb->crd_rcv = Nova::Obj_crd();
utcb->set_msg_word(0);
Genode::uint8_t res = utcb->append_item(obj_crd, 0);
(void)res;
res = Nova::call(cap.local_name());
Genode::uint8_t res = Nova::NOVA_OK;
enum {
TRANSLATE = true, THIS_PD = false, NON_GUEST = false, HOTSPOT = 0,
TRANSFER_ITEMS = 1U << (Nova::NUM_INITIAL_VCPU_PT_LOG2 - 1)
};
/* prepare translation items for every portal separately */
for (unsigned half = 0; !res && half < 2; half++) {
/* translate half of portals - due to size constraints on 64bit */
utcb->msg[0] = half;
utcb->set_msg_word(1);
/* add one translate item per portal */
for (unsigned i = 0; !res && i < TRANSFER_ITEMS; i++) {
Nova::Obj_crd obj_crd(sel + half * TRANSFER_ITEMS + i, 0);
if (!utcb->append_item(obj_crd, HOTSPOT, THIS_PD, NON_GUEST,
TRANSLATE))
res = 0xff;
}
if (res != Nova::NOVA_OK)
break;
/* trigger the translation */
res = Nova::call(cap.local_name());
}
/* restore original receive window */
utcb->crd_rcv = orig_crd;

View File

@ -24,6 +24,7 @@ namespace Genode {
virtual ~Nova_cpu_session() { }
virtual Native_capability pause_sync(Thread_capability) = 0;
virtual Native_capability single_step_sync(Thread_capability, bool) = 0;
/*********************
@ -32,8 +33,11 @@ namespace Genode {
GENODE_RPC(Rpc_pause_sync, Native_capability, pause_sync,
Thread_capability);
GENODE_RPC(Rpc_single_step_sync, Native_capability, single_step_sync,
Thread_capability, bool);
GENODE_RPC_INTERFACE_INHERIT(Cpu_session, Rpc_pause_sync);
GENODE_RPC_INTERFACE_INHERIT(Cpu_session, Rpc_pause_sync,
Rpc_single_step_sync);
};
}

View File

@ -1 +1 @@
0c75755a7d208d1f114b25becd129c760d57f456
6425874c3de23cd3cd5ab08a9273b60aebbd1b80

View File

@ -4,7 +4,7 @@ DOWNLOADS := nova.git
URL(nova) := https://github.com/alex-ab/NOVA.git
# r8 branch
REV(nova) := 41868231ae25678e1f2e344e40fbdbd0fd13e8ee
REV(nova) := d82533c18bdae96c036c900c7adf431ec7efc377
DIR(nova) := src/kernel/nova
PATCHES := $(wildcard $(REP_DIR)/patches/*.patch)

View File

@ -64,7 +64,7 @@ void Ipc_pager::set_reply_mapping(Mapping m)
}
void Ipc_pager::reply_and_wait_for_fault()
void Ipc_pager::reply_and_wait_for_fault(unsigned sm)
{
Nova::reply(Thread_base::myself()->stack_top());
Nova::reply(Thread_base::myself()->stack_top(), sm);
}

View File

@ -17,111 +17,156 @@
#include <base/pager.h>
#include <base/sleep.h>
#include <util/construct_at.h>
#include <rm_session/rm_session.h>
/* NOVA includes */
#include <nova/syscalls.h>
#include <nova_util.h> /* map_local */
using namespace Genode;
using namespace Nova;
enum { PF_HANDLER_STACK_SIZE = 2 * sizeof(addr_t) * 1024 };
extern Genode::addr_t __core_pd_sel;
Utcb * Pager_object::_check_handler(Thread_base *&myself, Pager_object *&obj)
static Nova::Hip * kernel_hip()
{
Utcb * utcb;
myself = Thread_base::myself();
obj = static_cast<Pager_object *>(myself);
/**
* Initial value of esp register, saved by the crt0 startup code.
* This value contains the address of the hypervisor information page.
*/
extern addr_t __initial_sp;
return reinterpret_cast<Hip *>(__initial_sp);
}
if (!myself || !obj) goto dead;
/* pager activation threads storage and handling - one thread per CPU */
enum { PAGER_CPUS = 128, PAGER_STACK_SIZE = 2*4096 };
utcb = reinterpret_cast<Utcb *>(myself->utcb());
if (!utcb) goto dead;
static char pager_activation_mem[sizeof (Pager_activation<PAGER_STACK_SIZE>) * PAGER_CPUS];
static Pager_activation_base * pager_threads[PAGER_CPUS];
return utcb;
dead:
static unsigned which_cpu(Pager_activation_base * pager)
{
Pager_activation_base * start = reinterpret_cast<Pager_activation_base *>(&pager_activation_mem);
Pager_activation_base * end = start + PAGER_CPUS;
PERR("unexpected exception-fault for non-existing pager object,"
" going to sleep forever");
if (start <= pager && pager < end) {
/* pager of one of the non boot CPUs */
unsigned cpu_id = pager - start;
return cpu_id;
}
if (obj) obj->_state.mark_dead();
sleep_forever();
/* pager of boot CPU */
return Affinity::Location().xpos();
}
void Pager_object::_page_fault_handler()
void Pager_object::_page_fault_handler(addr_t pager_obj)
{
Ipc_pager ipc_pager;
ipc_pager.wait_for_fault();
Thread_base *myself;
Pager_object *obj;
Utcb *utcb = _check_handler(myself, obj);
Thread_base * myself = Thread_base::myself();
Pager_object * obj = reinterpret_cast<Pager_object *>(pager_obj);
Utcb * utcb = reinterpret_cast<Utcb *>(myself->utcb());
Pager_activation_base * pager_thread = static_cast<Pager_activation_base *>(myself);
/* lookup fault address and decide what to do */
int ret = obj->pager(ipc_pager);
if (ret) {
if (obj->client_recall() != Nova::NOVA_OK) {
char client_name[Context::NAME_LEN];
myself->name(client_name, sizeof(client_name));
/* don't open receive window for pager threads */
if (utcb->crd_rcv.value())
nova_die();
PWRN("unresolvable page fault since recall failed, '%s' "
"address=0x%lx ip=0x%lx", client_name, ipc_pager.fault_addr(),
ipc_pager.fault_ip());
/* good case - found a valid region which is mappable */
if (!ret)
ipc_pager.reply_and_wait_for_fault();
Native_capability pager_obj = obj->Object_pool<Pager_object>::Entry::cap();
revoke(pager_obj.dst(), true);
obj->_state.thread.ip = ipc_pager.fault_ip();
obj->_state.thread.sp = 0;
obj->_state.thread.trapno = PT_SEL_PAGE_FAULT;
revoke(Obj_crd(obj->exc_pt_sel_client(), NUM_INITIAL_PT_LOG2), false);
obj->_state.block();
obj->_state.mark_dead();
}
if (ret == 1) {
char client_name[Context::NAME_LEN];
myself->name(client_name, sizeof(client_name));
PDBG("unhandled page fault, '%s' address=0x%lx ip=0x%lx",
client_name, ipc_pager.fault_addr(), ipc_pager.fault_ip());
}
char const * client = reinterpret_cast<char const *>(obj->_badge);
/* region manager fault - to be handled */
if (ret == 1) {
PDBG("page fault, thread '%s', cpu %u, ip=%lx, fault address=0x%lx",
client, which_cpu(pager_thread), ipc_pager.fault_ip(),
ipc_pager.fault_addr());
utcb->set_msg_word(0);
utcb->mtd = 0;
/* block the faulting thread until region manager is done */
ipc_pager.reply_and_wait_for_fault(obj->sel_sm_block());
}
/* unhandled case */
obj->_state.mark_dead();
PWRN("unresolvable page fault, thread '%s', cpu %u, ip=%lx, "
"fault address=0x%lx ret=%u", client, which_cpu(pager_thread),
ipc_pager.fault_ip(), ipc_pager.fault_addr(), ret);
Native_capability pager_cap = obj->Object_pool<Pager_object>::Entry::cap();
revoke(pager_cap.dst());
revoke(Obj_crd(obj->exc_pt_sel_client(), NUM_INITIAL_PT_LOG2));
utcb->set_msg_word(0);
utcb->mtd = 0;
ipc_pager.reply_and_wait_for_fault();
}
void Pager_object::_exception_handler(addr_t portal_id)
void Pager_object::exception(uint8_t exit_id)
{
Thread_base *myself;
Pager_object *obj;
Utcb *utcb = _check_handler(myself, obj);
Thread_base *myself = Thread_base::myself();
Utcb * utcb = reinterpret_cast<Utcb *>(myself->utcb());
Pager_activation_base * pager_thread = static_cast<Pager_activation_base *>(myself);
if (exit_id > PT_SEL_PARENT || !pager_thread)
nova_die();
addr_t fault_ip = utcb->ip;
uint8_t res = 0xFF;
addr_t mtd = 0;
if (obj->submit_exception_signal())
res = obj->client_recall();
if (_state.skip_requested()) {
_state.skip_reset();
utcb->set_msg_word(0);
utcb->mtd = 0;
reply(myself->stack_top());
}
/* remember exception type for cpu_session()->state() calls */
_state.thread.trapno = exit_id;
_state.thread.ip = fault_ip;
if (_exception_sigh.valid()) {
_state.submit_signal();
res = client_recall();
}
if (res != NOVA_OK) {
char client_name[Context::NAME_LEN];
myself->name(client_name, sizeof(client_name));
/* nobody handles this exception - so thread will be stopped finally */
_state.mark_dead();
PWRN("unresolvable exception at ip 0x%lx, exception portal 0x%lx, %s, "
"'%s'", fault_ip,
portal_id, res == 0xFF ? "no signal handler" :
res == NOVA_OK ? "" : "recall failed",
client_name);
char const * client = reinterpret_cast<char const *>(_badge);
PWRN("unresolvable exception %u, thread '%s', cpu %u, ip=0x%lx, %s",
exit_id, client, which_cpu(pager_thread), fault_ip,
res == 0xFF ? "no signal handler" :
(res == NOVA_OK ? "" : "recall failed"));
Nova::revoke(Obj_crd(portal_id, 0));
obj->_state.mark_dead();
Nova::revoke(Obj_crd(exc_pt_sel_client(), NUM_INITIAL_PT_LOG2));
enum { TRAP_BREAKPOINT = 3 };
if ((portal_id & 0x1f) == TRAP_BREAKPOINT) {
if (exit_id == TRAP_BREAKPOINT) {
utcb->ip = fault_ip - 1;
mtd = Mtd::EIP;
}
@ -134,55 +179,58 @@ void Pager_object::_exception_handler(addr_t portal_id)
}
void Pager_object::_recall_handler()
void Pager_object::_recall_handler(addr_t pager_obj)
{
Thread_base *myself;
Pager_object *obj;
Utcb *utcb = _check_handler(myself, obj);
Thread_base * myself = Thread_base::myself();
Pager_object * obj = reinterpret_cast<Pager_object *>(pager_obj);
Utcb * utcb = reinterpret_cast<Utcb *>(myself->utcb());
/* save state - can be requested via cpu_session->state */
obj->_copy_state(utcb);
obj->_state.thread.ip = utcb->ip;
obj->_state.thread.sp = utcb->sp;
obj->_state.thread.eflags = utcb->flags;
obj->_state.thread.trapno = PT_SEL_RECALL;
obj->_state.mark_valid();
/* thread becomes blocked */
obj->_state.block();
if (obj->_state.is_client_cancel())
if (sm_ctrl(obj->sm_state_notify(), SEMAPHORE_UP) != NOVA_OK)
PWRN("notify failed");
/* deliver signal if it was requested */
if (obj->_state.to_submit())
obj->submit_exception_signal();
do {
if (sm_ctrl(obj->exc_pt_sel() + SM_SEL_EC, SEMAPHORE_DOWNZERO) != NOVA_OK)
PWRN("blocking recall handler failed");
} while (obj->_state.is_client_cancel());
obj->_state.mark_invalid();
/* notify callers of cpu_session()->pause that the state is now valid */
if (obj->_state.notify_requested()) {
obj->_state.notify_cancel();
if (sm_ctrl(obj->sel_sm_notify(), SEMAPHORE_UP) != NOVA_OK)
PWRN("paused notification failed");
}
/* switch on/off single step */
bool singlestep_state = obj->_state.thread.eflags & 0x100UL;
if (obj->_state.singlestep() && !singlestep_state) {
utcb->flags = obj->_state.thread.eflags | 0x100UL;
utcb->mtd = Nova::Mtd(Mtd::EFL).value();
} else
} else {
if (!obj->_state.singlestep() && singlestep_state) {
utcb->flags = obj->_state.thread.eflags & ~0x100UL;
utcb->mtd = Nova::Mtd(Mtd::EFL).value();
} else
utcb->mtd = 0;
}
/* block until cpu_session()->resume() respectively wake_up() call */
utcb->set_msg_word(0);
reply(myself->stack_top());
reply(myself->stack_top(), obj->sel_sm_block());
}
void Pager_object::_startup_handler()
void Pager_object::_startup_handler(addr_t pager_obj)
{
Thread_base *myself;
Pager_object *obj;
Utcb *utcb = _check_handler(myself, obj);
Thread_base *myself = Thread_base::myself();
Pager_object * obj = reinterpret_cast<Pager_object *>(pager_obj);
Utcb * utcb = reinterpret_cast<Utcb *>(myself->utcb());
utcb->ip = obj->_initial_eip;
utcb->sp = obj->_initial_esp;
@ -194,22 +242,55 @@ void Pager_object::_startup_handler()
}
void Pager_object::_invoke_handler()
void Pager_object::_invoke_handler(addr_t pager_obj)
{
Thread_base *myself;
Pager_object *obj;
Utcb *utcb = _check_handler(myself, obj);
Thread_base *myself = Thread_base::myself();
Pager_object * obj = reinterpret_cast<Pager_object *>(pager_obj);
Utcb * utcb = reinterpret_cast<Utcb *>(myself->utcb());
/* if protocol is violated ignore request and close receive window */
if (utcb->msg_words() != 2) {
utcb->crd_rcv = Obj_crd();
reply(myself->stack_top());
}
/* receive window must be closed - otherwise implementation bug */
if (utcb->crd_rcv.value())
nova_die();
/* send single portal as reply */
addr_t const event = utcb->msg[0];
addr_t const logcount = utcb->msg[1];
/* check for translated vCPU portals */
unsigned const items_count = 1U << (Nova::NUM_INITIAL_VCPU_PT_LOG2 - 1);
if ((obj->_client_exc_vcpu != Native_thread::INVALID_INDEX) &&
(utcb->msg_items() == items_count) &&
(utcb->msg_words() == 1 && (event == 0UL || event == 1UL))) {
/* check all translated item and remap if valid */
for (unsigned i = 0; i < items_count; i++) {
Nova::Utcb::Item * item = utcb->get_item(i);
if (!item)
break;
Nova::Crd cap(item->crd);
if (cap.is_null() || item->is_del())
continue;
/**
* Remap portal to dense packed region - required for vCPU running
* in separate PD (non-colocated case)
*/
Obj_crd snd(cap.base(), 0);
Obj_crd rcv(obj->_client_exc_vcpu + event * items_count + i, 0);
if (map_local(utcb, snd, rcv))
PWRN("could not remap vCPU portal 0x%x", i);
}
}
/* if protocol is violated ignore request */
if (utcb->msg_words() != 2) {
utcb->mtd = 0;
utcb->set_msg_word(0);
reply(myself->stack_top());
}
utcb->mtd = 0;
utcb->set_msg_word(0);
@ -276,19 +357,19 @@ void Pager_object::_invoke_handler()
void Pager_object::wake_up()
{
_state.unmark_client_cancel();
if (!_state.blocked())
return;
cancel_blocking();
_state.unblock();
uint8_t res = sm_ctrl(sel_sm_block(), SEMAPHORE_UP);
if (res != NOVA_OK)
PWRN("canceling blocked client failed (thread sm)");
}
void Pager_object::client_cancel_blocking()
{
if (_state.is_client_cancel())
return;
_state.mark_client_cancel();
uint8_t res = sm_ctrl(exc_pt_sel_client() + SM_SEL_EC, SEMAPHORE_UP);
if (res != NOVA_OK)
PWRN("canceling blocked client failed (thread sm)");
@ -310,111 +391,155 @@ uint8_t Pager_object::client_recall()
void Pager_object::cleanup_call()
{
_state.mark_dissolved();
/* revoke all portals handling the client. */
revoke(Obj_crd(exc_pt_sel_client(), NUM_INITIAL_PT_LOG2));
Utcb *utcb = (Utcb *)Thread_base::myself()->utcb();
if (reinterpret_cast<Utcb *>(this->utcb()) == utcb) return;
/* if pager is blocked wake him up */
wake_up();
/* if we are paused or waiting for a page fault nothing is in-flight */
if (_state.blocked())
return;
Utcb *utcb = reinterpret_cast<Utcb *>(Thread_base::myself()->utcb());
utcb->set_msg_word(0);
if (uint8_t res = call(_pt_cleanup))
PERR("%8p - cleanup call to pager (%8p) failed res=%d",
utcb, this->utcb(), res);
utcb->mtd = 0;
if (uint8_t res = call(sel_pt_cleanup()))
PERR("%8p - cleanup call to pager failed res=%d", utcb, res);
}
static uint8_t create_portal(addr_t pt, addr_t pd, addr_t ec, Mtd mtd,
addr_t eip)
addr_t eip, addr_t localname)
{
uint8_t res = create_pt(pt, pd, ec, mtd, eip);
if (res != NOVA_OK)
return res;
res = pt_ctrl(pt, localname);
if (res == NOVA_OK)
revoke(Obj_crd(pt, 0, Obj_crd::RIGHT_PT_CTRL));
else
revoke(Obj_crd(pt, 0));
return res;
}
/************************
** Exception handlers **
************************/
template <uint8_t EV>
void Exception_handlers::register_handler(Pager_object *obj, Mtd mtd,
void (* __attribute__((regparm(1))) func)(addr_t))
{
unsigned use_cpu = obj->location.xpos();
if (!kernel_hip()->is_cpu_enabled(use_cpu) || !pager_threads[use_cpu])
throw Rm_session::Invalid_thread();
addr_t const ec_sel = pager_threads[use_cpu]->tid().ec_sel;
/* compiler generates instance of exception entry if not specified */
addr_t entry = func ? (addr_t)func : (addr_t)(&_handler<EV>);
uint8_t res = create_portal(obj->exc_pt_sel_client() + EV,
__core_pd_sel, ec_sel, mtd, entry,
reinterpret_cast<addr_t>(obj));
if (res != Nova::NOVA_OK)
throw Rm_session::Invalid_thread();
}
template <uint8_t EV>
void Exception_handlers::_handler(addr_t obj)
{
Pager_object * pager_obj = reinterpret_cast<Pager_object *>(obj);
pager_obj->exception(EV);
}
Exception_handlers::Exception_handlers(Pager_object *obj)
{
register_handler<0>(obj, Mtd(Mtd::EIP));
register_handler<1>(obj, Mtd(Mtd::EIP));
register_handler<2>(obj, Mtd(Mtd::EIP));
register_handler<3>(obj, Mtd(Mtd::EIP));
register_handler<4>(obj, Mtd(Mtd::EIP));
register_handler<5>(obj, Mtd(Mtd::EIP));
register_handler<6>(obj, Mtd(Mtd::EIP));
register_handler<7>(obj, Mtd(Mtd::EIP));
register_handler<8>(obj, Mtd(Mtd::EIP));
register_handler<9>(obj, Mtd(Mtd::EIP));
register_handler<10>(obj, Mtd(Mtd::EIP));
register_handler<11>(obj, Mtd(Mtd::EIP));
register_handler<12>(obj, Mtd(Mtd::EIP));
register_handler<13>(obj, Mtd(Mtd::EIP));
register_handler<15>(obj, Mtd(Mtd::EIP));
register_handler<16>(obj, Mtd(Mtd::EIP));
register_handler<17>(obj, Mtd(Mtd::EIP));
register_handler<18>(obj, Mtd(Mtd::EIP));
register_handler<19>(obj, Mtd(Mtd::EIP));
register_handler<20>(obj, Mtd(Mtd::EIP));
register_handler<21>(obj, Mtd(Mtd::EIP));
register_handler<22>(obj, Mtd(Mtd::EIP));
register_handler<23>(obj, Mtd(Mtd::EIP));
register_handler<24>(obj, Mtd(Mtd::EIP));
register_handler<25>(obj, Mtd(Mtd::EIP));
}
/******************
** Pager object **
******************/
Pager_object::Pager_object(unsigned long badge, Affinity::Location location)
:
Thread_base(0, "pager:", PF_HANDLER_STACK_SIZE),
_badge(reinterpret_cast<unsigned long>(_context->name + 6)),
_client_exc_vcpu(Native_thread::INVALID_INDEX)
_badge(badge),
_selectors(cap_map()->insert(2)),
_client_exc_pt_sel(cap_map()->insert(NUM_INITIAL_PT_LOG2)),
_client_exc_vcpu(Native_thread::INVALID_INDEX),
_exceptions(this),
location(location)
{
class Create_exception_pt_failed { };
uint8_t res;
/* construct pager name out of client name */
strncpy(_context->name + 6, reinterpret_cast<char const *>(badge),
sizeof(_context->name) - 6);
addr_t pd_sel = __core_pd_sel;
_pt_cleanup = cap_map()->insert(1);
_client_exc_pt_sel = cap_map()->insert(NUM_INITIAL_PT_LOG2);
_state._status = 0;
_state.sel_client_ec = Native_thread::INVALID_INDEX;
if (_pt_cleanup == Native_thread::INVALID_INDEX ||
_client_exc_pt_sel == Native_thread::INVALID_INDEX)
throw Create_exception_pt_failed();
if (Native_thread::INVALID_INDEX == _selectors ||
Native_thread::INVALID_INDEX == _client_exc_pt_sel)
throw Rm_session::Invalid_thread();
/* tell thread starting code on which CPU to let run the pager */
reinterpret_cast<Affinity::Location *>(stack_base())[0] = location;
/* creates local EC */
Thread_base::start();
/* create portal for exception handlers 0x0 - 0xd */
for (unsigned i = 0; i < PT_SEL_PAGE_FAULT; i++) {
res = create_portal(exc_pt_sel_client() + i, pd_sel, _tid.ec_sel,
Mtd(Mtd::EIP), (addr_t)_exception_handler);
if (res) {
PERR("could not create exception portal, error = %u\n", res);
throw Create_exception_pt_failed();
}
/* ypos information not supported by now */
if (location.ypos()) {
PWRN("Unsupported location %ux%u", location.xpos(), location.ypos());
throw Rm_session::Invalid_thread();
}
/* create portal for page-fault handler */
res = create_portal(exc_pt_sel_client() + PT_SEL_PAGE_FAULT, pd_sel, _tid.ec_sel,
Mtd(Mtd::QUAL | Mtd::EIP), (mword_t)_page_fault_handler);
if (res) {
PERR("could not create page-fault portal, error = %u\n", res);
class Create_page_fault_pt_failed { };
throw Create_page_fault_pt_failed();
}
/* place Pager_object on specified CPU by selecting proper pager thread */
unsigned use_cpu = location.xpos();
if (!kernel_hip()->is_cpu_enabled(use_cpu) || !pager_threads[use_cpu])
throw Rm_session::Invalid_thread();
/* create portal for exception handlers 0xf - 0x19 */
for (unsigned i = PT_SEL_PAGE_FAULT + 1; i < PT_SEL_PARENT; i++) {
res = create_portal(exc_pt_sel_client() + i, pd_sel, _tid.ec_sel,
Mtd(Mtd::EIP), (addr_t)_exception_handler);
if (res) {
PERR("could not create exception portal, error = %u\n", res);
throw Create_exception_pt_failed();
}
}
addr_t ec_sel = pager_threads[use_cpu]->tid().ec_sel;
/* create portal for startup handler */
res = create_portal(exc_pt_sel_client() + PT_SEL_STARTUP, pd_sel, _tid.ec_sel,
Mtd(Mtd::ESP | Mtd::EIP), (mword_t)_startup_handler);
if (res) {
PERR("could not create startup portal, error = %u\n",
res);
class Create_startup_pt_failed { };
throw Create_startup_pt_failed();
}
/* create portal for page-fault handler - 14 */
_exceptions.register_handler<14>(this, Mtd::QUAL | Mtd::EIP,
_page_fault_handler);
/* create portal for recall handler */
Mtd mtd(Mtd::ESP | Mtd::EIP | Mtd::ACDB | Mtd::EFL | Mtd::EBSD | Mtd::FSGS);
res = create_portal(exc_pt_sel_client() + PT_SEL_RECALL, pd_sel, _tid.ec_sel,
mtd, (addr_t)_recall_handler);
if (res) {
PERR("could not create recall portal, error = %u\n", res);
class Create_recall_pt_failed { };
throw Create_recall_pt_failed();
}
/* create portal for startup handler - 26 */
Mtd const mtd_startup(Mtd::ESP | Mtd::EIP);
_exceptions.register_handler<PT_SEL_STARTUP>(this, mtd_startup,
_startup_handler);
/* create portal for recall handler - 31 */
Mtd const mtd_recall(Mtd::ESP | Mtd::EIP | Mtd::ACDB | Mtd::EFL |
Mtd::EBSD | Mtd::FSGS);
_exceptions.register_handler<PT_SEL_RECALL>(this, mtd_recall,
_recall_handler);
/*
* Create semaphore required for Genode locking. It can be later on
@ -422,40 +547,41 @@ Pager_object::Pager_object(unsigned long badge, Affinity::Location location)
*/
res = Nova::create_sm(exc_pt_sel_client() + SM_SEL_EC, pd_sel, 0);
if (res != Nova::NOVA_OK) {
class Create_state_notifiy_sm_failed { };
throw Create_state_notifiy_sm_failed();
throw Rm_session::Invalid_thread();
}
/* create portal for final cleanup call used during destruction */
res = create_portal(_pt_cleanup, pd_sel, _tid.ec_sel, Mtd(0),
reinterpret_cast<addr_t>(_invoke_handler));
if (res) {
res = create_portal(sel_pt_cleanup(), pd_sel, ec_sel, Mtd(0),
reinterpret_cast<addr_t>(_invoke_handler),
reinterpret_cast<addr_t>(this));
if (res != Nova::NOVA_OK) {
PERR("could not create pager cleanup portal, error = %u\n", res);
class Create_cleanup_pt_failed { };
throw Create_cleanup_pt_failed();
throw Rm_session::Invalid_thread();
}
res = Nova::create_sm(sm_state_notify(), pd_sel, 0);
/* used to notify caller of as soon as pause succeeded */
res = Nova::create_sm(sel_sm_notify(), pd_sel, 0);
if (res != Nova::NOVA_OK) {
class Create_state_notifiy_sm_failed { };
throw Create_state_notifiy_sm_failed();
throw Rm_session::Invalid_thread();
}
/* semaphore used to block paged thread during page fault or recall */
res = Nova::create_sm(sel_sm_block(), pd_sel, 0);
if (res != Nova::NOVA_OK) {
throw Rm_session::Invalid_thread();
}
}
Pager_object::~Pager_object()
{
/* if pager is blocked wake him up */
sm_ctrl(sm_state_notify(), SEMAPHORE_UP);
revoke(Obj_crd(sm_state_notify(), 0));
/* sanity check that object got dissolved already - otherwise bug */
if (!_state.dissolved())
nova_die();
/* take care nobody is handled anymore by this object */
cleanup_call();
/* revoke portal used for the cleanup call */
revoke(Obj_crd(_pt_cleanup, 0));
cap_map()->remove(_pt_cleanup, 1, false);
/* revoke portal used for the cleanup call and sm cap for blocking state */
revoke(Obj_crd(_selectors, 2));
cap_map()->remove(_selectors, 2, false);
cap_map()->remove(exc_pt_sel_client(), NUM_INITIAL_PT_LOG2, false);
if (_client_exc_vcpu == Native_thread::INVALID_INDEX)
@ -467,14 +593,86 @@ Pager_object::~Pager_object()
}
/**********************
** Pager activation **
**********************/
Pager_activation_base::Pager_activation_base(const char *name, size_t stack_size)
:
Thread_base(0, name, stack_size),
_cap(Native_capability()), _ep(0), _cap_valid(Lock::LOCKED)
{
/* tell thread starting code on which CPU to let run the pager */
reinterpret_cast<Affinity::Location *>(stack_base())[0] = Affinity::Location(which_cpu(this), 0);
/* creates local EC */
Thread_base::start();
reinterpret_cast<Nova::Utcb *>(Thread_base::utcb())->crd_xlt = Obj_crd(0, ~0UL);
}
void Pager_activation_base::entry() { }
/**********************
** Pager entrypoint **
**********************/
Pager_entrypoint::Pager_entrypoint(Cap_session *cap_session,
Pager_activation_base *a)
: _activation(a), _cap_session(cap_session)
{
/* sanity check space for pager threads */
if (kernel_hip()->cpu_max() > PAGER_CPUS) {
PERR("kernel supports more CPUs (%u) than Genode (%u)",
kernel_hip()->cpu_max(), PAGER_CPUS);
nova_die();
}
/* determine boot cpu */
unsigned master_cpu = boot_cpu();
/* detect enabled CPUs and create per CPU a pager thread */
typedef Pager_activation<PAGER_STACK_SIZE> Pager;
Pager * pager_of_cpu = reinterpret_cast<Pager *>(&pager_activation_mem);
for (unsigned i = 0; i < kernel_hip()->cpu_max(); i++, pager_of_cpu++) {
if (i == master_cpu) {
pager_threads[master_cpu] = a;
a->ep(this);
continue;
}
if (!kernel_hip()->is_cpu_enabled(i))
continue;
pager_threads[i] = pager_of_cpu;
construct_at<Pager>(pager_threads[i]);
pager_threads[i]->ep(this);
}
}
Pager_capability Pager_entrypoint::manage(Pager_object *obj)
{
/* let handle pager_object of pager thread on same CPU */
unsigned use_cpu = obj->location.xpos();
if (!kernel_hip()->is_cpu_enabled(use_cpu) || !pager_threads[use_cpu]) {
PWRN("invalid CPU parameter used in pager object");
return Pager_capability();
}
Native_capability pager_thread_cap(pager_threads[use_cpu]->tid().ec_sel);
/* request creation of portal bind to pager thread */
Native_capability pager_thread_cap(obj->ec_sel());
Native_capability cap_session =
_cap_session->alloc(pager_thread_cap, obj->handler_address());
/* disable PT_CTRL feature */
if (NOVA_OK != pt_ctrl(cap_session.local_name(), reinterpret_cast<mword_t>(obj)))
nova_die();
/* disable the feature for security reasons now */
revoke(Obj_crd(cap_session.local_name(), 0, Obj_crd::RIGHT_PT_CTRL));
/* add server object to object pool */
@ -490,14 +688,12 @@ Pager_capability Pager_entrypoint::manage(Pager_object *obj)
void Pager_entrypoint::dissolve(Pager_object *obj)
{
Native_capability pager_obj = obj->Object_pool<Pager_object>::Entry::cap();
/* cleanup at cap session */
_cap_session->free(pager_obj);
/* cleanup locally */
/* revoke cap selector locally */
revoke(pager_obj.dst(), true);
/* remove object from pool */
remove_locked(obj);
/* take care that no faults are in-flight */
obj->cleanup_call();
}

View File

@ -125,11 +125,9 @@ void Rpc_entrypoint::_activation_entry()
ep->_snd_buf.snd_reset();
/* prepare ipc server object (copying utcb content to message buffer */
Ipc_server srv(&ep->_snd_buf, &ep->_rcv_buf);
ep->_rcv_buf.post_ipc(reinterpret_cast<Nova::Utcb *>(ep->utcb()));
int opcode = 0;
Ipc_server srv(&ep->_snd_buf, &ep->_rcv_buf);
srv >> IPC_WAIT >> opcode;
/* set default return value */

View File

@ -26,21 +26,24 @@ Cpu_session_component::pause_sync(Thread_capability thread_cap)
Object_pool<Cpu_thread_component>::Guard
thread(_thread_ep->lookup_and_lock(thread_cap));
if (!thread || !thread->platform_thread())
return Native_capability::invalid_cap();
return Native_capability();
return thread->platform_thread()->pause();
}
void
Cpu_session_component::single_step(Thread_capability thread_cap, bool enable)
Native_capability
Cpu_session_component::single_step_sync(Thread_capability thread_cap, bool enable)
{
using namespace Genode;
Object_pool<Cpu_thread_component>::Guard
thread(_thread_ep->lookup_and_lock(thread_cap));
if (!thread || !thread->platform_thread())
return;
return Native_capability();
thread->platform_thread()->single_step(enable);
return thread->platform_thread()->single_step(enable);
}
void Cpu_session_component::single_step(Thread_capability, bool) { return; }

View File

@ -231,6 +231,7 @@ namespace Genode {
******************************/
Native_capability pause_sync(Thread_capability);
Native_capability single_step_sync(Thread_capability, bool);
};
}

View File

@ -163,7 +163,7 @@ namespace Genode {
if (main_thread) _features |= MAIN_THREAD;
}
void single_step(bool on);
Native_capability single_step(bool on);
};
}

View File

@ -108,15 +108,14 @@ int Platform_thread::start(void *ip, void *sp)
pd_utcb = Native_config::context_area_virtual_base() +
Native_config::context_virtual_size() - get_page_size();
addr_t remap_src[] = { _pd->parent_pt_sel(),
_pager->Object_pool<Pager_object>::Entry::cap().local_name() };
addr_t remap_src[] = { _pd->parent_pt_sel(), _pager->Object_pool<Pager_object>::Entry::cap().local_name() };
addr_t remap_dst[] = { PT_SEL_PARENT, PT_SEL_MAIN_PAGER };
/* remap exception portals for first thread */
for (unsigned i = 0; i < sizeof(remap_dst)/sizeof(remap_dst[0]); i++) {
if (map_local((Utcb *)Thread_base::myself()->utcb(),
Obj_crd(remap_src[i], 0),
Obj_crd(_sel_exc_base + remap_dst[i], 0)))
Obj_crd(remap_src[i], 0),
Obj_crd(_sel_exc_base + remap_dst[i], 0)))
return -6;
}
}
@ -272,11 +271,15 @@ void Platform_thread::cancel_blocking()
}
void Platform_thread::single_step(bool on)
Native_capability Platform_thread::single_step(bool on)
{
if (!_pager) return;
if (!_pager) return Native_capability();
_pager->single_step(on);
Native_capability cap = _pager->single_step(on);
if (is_worker()) return Native_capability();
return cap;
}
@ -305,6 +308,12 @@ Platform_thread::Platform_thread(const char *name, unsigned prio, int thread_id)
Platform_thread::~Platform_thread()
{
if (_pager) {
/* reset pager and badge used for debug output */
_pager->reset_badge();
_pager = 0;
}
using namespace Nova;
/* free ec and sc caps */

View File

@ -93,8 +93,8 @@ void Thread_base::start()
addr_t pd_sel = Platform_pd::pd_core_sel();
/*
* In core, the affinity location was write to the stack base by the server
* code. So, thry to read the value from there.
* In core, the affinity location has been written to the stack base by
* the server or pager code. So - read the value from there.
*/
Affinity::Location location = reinterpret_cast<Affinity::Location *>(stack_base())[0];
@ -106,7 +106,7 @@ void Thread_base::start()
uint8_t res = create_ec(_tid.ec_sel, pd_sel, location.xpos(),
utcb, sp, _tid.exc_pt_sel, LOCAL_THREAD);
if (res != NOVA_OK) {
PERR("create_ec returned %d", res);
PERR("create_ec returned %d cpu=%u", res, location.xpos());
throw Cpu_session::Thread_creation_failed();
}

View File

@ -22,4 +22,19 @@ build_boot_image "core init test-thread"
append qemu_args "-nographic -m 64"
run_genode_until {child "test-thread" exited with exit value 0.*\n} 20
run_genode_until {child "test-thread" exited with exit value .*\n} 20
# determine error code of child exit
set exit_code [regexp -inline {child "test-thread" exited with exit value .*\n} $output]
set exit_code [regexp -inline {[-+]?[0-9]+} $exit_code]
# good case
if {$exit_code eq 0} {
return
}
# no pause/resume support for Fiasco and Pistachio - they may return a error
if {[expr [have_spec fiasco] || [have_spec pistachio]] && $exit_code eq -11} { return }
# no puase/resume support for Linux - it may return a error
if {[have_spec linux] && $exit_code eq -10} { return }
exit -1

View File

@ -185,6 +185,66 @@ static void test_cpu_session()
}
struct Pause_helper : Thread<0x1000>
{
volatile unsigned loop = 0;
volatile bool beep = false;
Pause_helper(const char * name, Cpu_session * cpu)
: Thread<0x1000>(name, cpu) { }
void entry()
{
while (1) {
/**
* Don't use printf here, since this thread becomes "paused".
* If it is holding the lock of the printf backend being paused,
* all other threads of this task trying to do printf will
* block - looks like a deadlock.
*/
// printf("stop me if you can\n");
loop ++;
if (beep) {
PINF("beep");
beep = false;
}
}
}
};
static void test_pause_resume()
{
Pause_helper thread("pause", env()->cpu_session());
thread.start();
while (thread.loop < 1) { }
Thread_state state;
printf("--- pausing ---\n");
env()->cpu_session()->pause(thread.cap());
unsigned loop_paused = thread.loop;
printf("--- paused ---\n");
printf("--- reading thread state ---\n");
try {
state = env()->cpu_session()->state(thread.cap());
} catch (Cpu_session::State_access_failed) {
throw -10;
}
if (loop_paused != thread.loop)
throw -11;
thread.beep = true;
printf("--- resuming thread ---\n");
env()->cpu_session()->resume(thread.cap());
while (thread.loop == loop_paused) { }
printf("--- thread resumed ---\n");
}
int main()
{
printf("--- thread test started ---\n");
@ -194,6 +254,7 @@ int main()
test_stack_alignment();
test_main_thread();
test_cpu_session();
test_pause_resume();
} catch (int error) {
return error;
}