mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-20 09:46:20 +00:00
parent
0f47ac958b
commit
a31378476d
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -394,6 +394,8 @@ namespace Genode {
|
||||
*/
|
||||
if (rcv_window != INVALID_INDEX)
|
||||
_rcv_pt_base = INVALID_INDEX;
|
||||
|
||||
utcb->crd_rcv = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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_ */
|
||||
|
@ -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(); }
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1 +1 @@
|
||||
0c75755a7d208d1f114b25becd129c760d57f456
|
||||
6425874c3de23cd3cd5ab08a9273b60aebbd1b80
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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; }
|
||||
|
@ -231,6 +231,7 @@ namespace Genode {
|
||||
******************************/
|
||||
|
||||
Native_capability pause_sync(Thread_capability);
|
||||
Native_capability single_step_sync(Thread_capability, bool);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,7 @@ namespace Genode {
|
||||
if (main_thread) _features |= MAIN_THREAD;
|
||||
}
|
||||
|
||||
void single_step(bool on);
|
||||
Native_capability single_step(bool on);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user