mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-24 15:56:41 +00:00
NOVA: setup all exception handlers of a thread
This commit is contained in:
parent
fcd62729d4
commit
3b0e4372fe
@ -71,6 +71,10 @@ namespace Genode {
|
||||
static void _invoke_handler();
|
||||
static void _recall_handler();
|
||||
|
||||
__attribute__((regparm(1)))
|
||||
static void _exception_handler(addr_t portal_id);
|
||||
|
||||
static Nova::Utcb * _check_handler(Thread_base *&, Pager_object *&);
|
||||
public:
|
||||
|
||||
Pager_object(unsigned long badge);
|
||||
|
@ -27,36 +27,52 @@ using namespace Nova;
|
||||
enum { PF_HANDLER_STACK_SIZE = sizeof(addr_t) * 1024 };
|
||||
extern Genode::addr_t __core_pd_sel;
|
||||
|
||||
Utcb * Pager_object::_check_handler(Thread_base *&myself, Pager_object *&obj)
|
||||
{
|
||||
Utcb * utcb;
|
||||
myself = Thread_base::myself();
|
||||
obj = static_cast<Pager_object *>(myself);
|
||||
|
||||
if (!myself || !obj) goto dead;
|
||||
|
||||
utcb = reinterpret_cast<Utcb *>(myself->utcb());
|
||||
if (!utcb) goto dead;
|
||||
|
||||
return utcb;
|
||||
|
||||
dead:
|
||||
|
||||
PERR("unexpected exception-fault for non-existing pager object,"
|
||||
" going to sleep forever");
|
||||
|
||||
if (obj) obj->_state.dead = true;
|
||||
sleep_forever();
|
||||
}
|
||||
|
||||
void Pager_object::_page_fault_handler()
|
||||
{
|
||||
Ipc_pager ipc_pager;
|
||||
ipc_pager.wait_for_fault();
|
||||
|
||||
/* serialize page-fault handling */
|
||||
Thread_base *myself = Thread_base::myself();
|
||||
if (!myself) {
|
||||
PWRN("unexpected page-fault for non-existing pager object,"
|
||||
" going to sleep forever");
|
||||
sleep_forever();
|
||||
}
|
||||
Thread_base *myself;
|
||||
Pager_object *obj;
|
||||
Utcb *utcb = _check_handler(myself, obj);
|
||||
|
||||
Pager_object *obj = static_cast<Pager_object *>(myself);
|
||||
int ret = obj->pager(ipc_pager);
|
||||
|
||||
if (ret) {
|
||||
if (!obj->submit_exception_signal()) {
|
||||
if (obj->submit_exception_signal())
|
||||
/* Somebody takes care don't die - just recall and block */
|
||||
obj->client_recall();
|
||||
else {
|
||||
PWRN("unresolvable page-fault at address 0x%lx, ip=0x%lx",
|
||||
ipc_pager.fault_addr(), ipc_pager.fault_ip());
|
||||
|
||||
/* revoke paging capability, let thread die in kernel */
|
||||
Nova::revoke(Obj_crd(obj->exc_pt_sel() + PT_SEL_PAGE_FAULT, 0),
|
||||
true);
|
||||
Nova::revoke(Obj_crd(obj->exc_pt_sel() + PT_SEL_PAGE_FAULT, 0));
|
||||
obj->_state.dead = true;
|
||||
} else
|
||||
/* Somebody takes care don't die - just recall and block */
|
||||
obj->client_recall();
|
||||
}
|
||||
|
||||
Utcb *utcb = (Utcb *)Thread_base::myself()->utcb();
|
||||
utcb->set_msg_word(0);
|
||||
utcb->mtd = 0;
|
||||
}
|
||||
@ -64,10 +80,31 @@ void Pager_object::_page_fault_handler()
|
||||
ipc_pager.reply_and_wait_for_fault();
|
||||
}
|
||||
|
||||
void Pager_object::_exception_handler(addr_t portal_id)
|
||||
{
|
||||
Thread_base *myself;
|
||||
Pager_object *obj;
|
||||
Utcb *utcb = _check_handler(myself, obj);
|
||||
|
||||
if (obj->submit_exception_signal())
|
||||
/* Somebody takes care don't die - just recall and block */
|
||||
obj->client_recall();
|
||||
else {
|
||||
Nova::revoke(Obj_crd(portal_id, 0));
|
||||
obj->_state.dead = true;
|
||||
}
|
||||
|
||||
utcb->set_msg_word(0);
|
||||
utcb->mtd = 0;
|
||||
|
||||
reply(myself->stack_top());
|
||||
}
|
||||
|
||||
void Pager_object::_recall_handler()
|
||||
{
|
||||
Pager_object *obj = static_cast<Pager_object *>(Thread_base::myself());
|
||||
Utcb *utcb = (Utcb *)Thread_base::myself()->utcb();
|
||||
Thread_base *myself;
|
||||
Pager_object *obj;
|
||||
Utcb *utcb = _check_handler(myself, obj);
|
||||
|
||||
obj->_copy_state(utcb);
|
||||
|
||||
@ -89,34 +126,38 @@ void Pager_object::_recall_handler()
|
||||
|
||||
utcb->set_msg_word(0);
|
||||
utcb->mtd = 0;
|
||||
reply(Thread_base::myself()->stack_top());
|
||||
reply(myself->stack_top());
|
||||
}
|
||||
|
||||
void Pager_object::_startup_handler()
|
||||
{
|
||||
Pager_object *obj = static_cast<Pager_object *>(Thread_base::myself());
|
||||
Utcb *utcb = (Utcb *)Thread_base::myself()->utcb();
|
||||
Thread_base *myself;
|
||||
Pager_object *obj;
|
||||
Utcb *utcb = _check_handler(myself, obj);
|
||||
|
||||
utcb->ip = obj->_initial_eip;
|
||||
utcb->sp = obj->_initial_esp;
|
||||
|
||||
utcb->mtd = Mtd::EIP | Mtd::ESP;
|
||||
utcb->set_msg_word(0);
|
||||
reply(Thread_base::myself()->stack_top());
|
||||
|
||||
reply(myself->stack_top());
|
||||
}
|
||||
|
||||
|
||||
void Pager_object::_invoke_handler()
|
||||
{
|
||||
Utcb *utcb = (Utcb *)Thread_base::myself()->utcb();
|
||||
Pager_object *obj = static_cast<Pager_object *>(Thread_base::myself());
|
||||
Thread_base *myself;
|
||||
Pager_object *obj;
|
||||
Utcb *utcb = _check_handler(myself, obj);
|
||||
|
||||
/* send single portal as reply */
|
||||
addr_t event = utcb->msg_words() != 1 ? 0 : utcb->msg[0];
|
||||
utcb->mtd = 0;
|
||||
utcb->set_msg_word(0);
|
||||
|
||||
if (event == PT_SEL_STARTUP || event == PT_SEL_PAGE_FAULT ||
|
||||
event == SM_SEL_EC || event == PT_SEL_RECALL) {
|
||||
if (event < PT_SEL_PARENT || event == PT_SEL_STARTUP ||
|
||||
event == SM_SEL_EC || event == PT_SEL_RECALL) {
|
||||
|
||||
/**
|
||||
* Caller is requesting the SM cap of thread
|
||||
@ -130,7 +171,7 @@ void Pager_object::_invoke_handler()
|
||||
(void)res;
|
||||
}
|
||||
|
||||
reply(Thread_base::myself()->stack_top());
|
||||
reply(myself->stack_top());
|
||||
}
|
||||
|
||||
|
||||
@ -152,17 +193,30 @@ uint8_t Pager_object::client_recall() {
|
||||
Pager_object::Pager_object(unsigned long badge)
|
||||
: Thread_base("pager", PF_HANDLER_STACK_SIZE), _badge(badge)
|
||||
{
|
||||
class Create_exception_pt_failed { };
|
||||
uint8_t res;
|
||||
|
||||
addr_t pd_sel = __core_pd_sel;
|
||||
_pt_cleanup = cap_selector_allocator()->alloc();
|
||||
_sm_state_notify = cap_selector_allocator()->alloc();
|
||||
_state.valid = false;
|
||||
_state.dead = false;
|
||||
_state.sel_client_ec = ~0UL;
|
||||
_state.sel_client_ec = Native_thread::INVALID_INDEX;
|
||||
|
||||
/* Create portal for exception handlers 0x0 - 0xd */
|
||||
for (unsigned i = 0; i < PT_SEL_PAGE_FAULT; i++) {
|
||||
res = create_pt(exc_pt_sel() + i, pd_sel, _tid.ec_sel,
|
||||
Mtd(0), (addr_t)_exception_handler);
|
||||
if (res) {
|
||||
PERR("could not create exception portal, error = %u\n", res);
|
||||
throw Create_exception_pt_failed();
|
||||
}
|
||||
}
|
||||
|
||||
/* create portal for page-fault handler */
|
||||
addr_t pd_sel = __core_pd_sel;
|
||||
uint8_t res = create_pt(exc_pt_sel() + PT_SEL_PAGE_FAULT, pd_sel,
|
||||
_tid.ec_sel, Mtd(Mtd::QUAL | Mtd::EIP),
|
||||
(mword_t)_page_fault_handler);
|
||||
res = create_pt(exc_pt_sel() + 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);
|
||||
@ -170,6 +224,16 @@ Pager_object::Pager_object(unsigned long badge)
|
||||
throw Create_page_fault_pt_failed();
|
||||
}
|
||||
|
||||
/* Create portal for exception handlers 0xf - 0x19 */
|
||||
for (unsigned i = PT_SEL_PAGE_FAULT + 1; i < PT_SEL_PARENT; i++) {
|
||||
res = create_pt(exc_pt_sel() + i, pd_sel, _tid.ec_sel,
|
||||
Mtd(0), (addr_t)_exception_handler);
|
||||
if (res) {
|
||||
PERR("could not create exception portal, error = %u\n", res);
|
||||
throw Create_exception_pt_failed();
|
||||
}
|
||||
}
|
||||
|
||||
/* create portal for startup handler */
|
||||
res = create_pt(exc_pt_sel() + PT_SEL_STARTUP, pd_sel, _tid.ec_sel,
|
||||
Mtd(Mtd::ESP | Mtd::EIP), (mword_t)_startup_handler);
|
||||
@ -208,17 +272,18 @@ Pager_object::Pager_object(unsigned long badge)
|
||||
|
||||
Pager_object::~Pager_object()
|
||||
{
|
||||
/* Revoke portals of Pager_object */
|
||||
revoke(Obj_crd(exc_pt_sel() + PT_SEL_STARTUP, 0), true);
|
||||
revoke(Obj_crd(exc_pt_sel() + PT_SEL_RECALL, 0), true);
|
||||
revoke(Obj_crd(exc_pt_sel() + PT_SEL_PAGE_FAULT, 0), true);
|
||||
/**
|
||||
* Revoke all portals of Pager_object from others.
|
||||
* The portals will be finally revoked during thread destruction.
|
||||
*/
|
||||
revoke(Obj_crd(exc_pt_sel(), NUM_INITIAL_PT_LOG2), false);
|
||||
|
||||
/* Revoke semaphore cap to signal valid state after recall */
|
||||
addr_t sm_cap = _sm_state_notify;
|
||||
_sm_state_notify = Native_thread::INVALID_INDEX;
|
||||
/* If pager is blocked wake him up */
|
||||
sm_ctrl(sm_cap, SEMAPHORE_UP);
|
||||
revoke(Obj_crd(sm_cap, 0), true);
|
||||
revoke(Obj_crd(sm_cap, 0));
|
||||
|
||||
/* Make sure nobody is in the handler anymore by doing an IPC to a
|
||||
* local cap pointing to same serving thread (if not running in the
|
||||
@ -234,7 +299,7 @@ Pager_object::~Pager_object()
|
||||
}
|
||||
|
||||
/* Revoke portal used for the cleanup call */
|
||||
revoke(Obj_crd(_pt_cleanup, 0), true);
|
||||
revoke(Obj_crd(_pt_cleanup, 0));
|
||||
cap_selector_allocator()->free(_pt_cleanup, 0);
|
||||
cap_selector_allocator()->free(sm_cap, 0);
|
||||
}
|
||||
|
@ -222,10 +222,11 @@ Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size,
|
||||
env()->cpu_session()->start(_thread_cap, 0, thread_sp))
|
||||
throw Cpu_session::Thread_creation_failed();
|
||||
|
||||
for (unsigned i = 0; i < Nova::PT_SEL_PARENT; i++)
|
||||
request_event_portal(pager_cap, _tid.exc_pt_sel, i);
|
||||
|
||||
request_event_portal(pager_cap, _tid.exc_pt_sel,
|
||||
Nova::PT_SEL_STARTUP);
|
||||
request_event_portal(pager_cap, _tid.exc_pt_sel,
|
||||
Nova::PT_SEL_PAGE_FAULT);
|
||||
request_event_portal(pager_cap, _tid.exc_pt_sel,
|
||||
Nova::SM_SEL_EC);
|
||||
request_event_portal(pager_cap, _tid.exc_pt_sel,
|
||||
|
@ -136,8 +136,10 @@ void Thread_base::start()
|
||||
|
||||
/* request exception portals for normal threads */
|
||||
if (!_tid.is_vcpu) {
|
||||
for (unsigned i = 0; i < PT_SEL_PARENT; i++)
|
||||
request_event_portal(pager_cap, _tid.exc_pt_sel, i);
|
||||
|
||||
request_event_portal(pager_cap, _tid.exc_pt_sel, PT_SEL_STARTUP);
|
||||
request_event_portal(pager_cap, _tid.exc_pt_sel, PT_SEL_PAGE_FAULT);
|
||||
request_event_portal(pager_cap, _tid.exc_pt_sel, SM_SEL_EC);
|
||||
request_event_portal(pager_cap, _tid.exc_pt_sel, PT_SEL_RECALL);
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ int Platform_thread::start(void *ip, void *sp)
|
||||
addr_t utcb = _is_vcpu ? 0 : round_page(initial_sp);
|
||||
|
||||
_pager->initial_esp(initial_sp);
|
||||
if (_sel_exc_base == ~0UL) {
|
||||
if (_sel_exc_base == Native_thread::INVALID_INDEX) {
|
||||
PERR("exception base not specified");
|
||||
return -3;
|
||||
}
|
||||
@ -95,7 +95,7 @@ int Platform_thread::start(void *ip, void *sp)
|
||||
return res ? -5 : 0;
|
||||
}
|
||||
|
||||
if (_sel_exc_base != ~0UL) {
|
||||
if (_sel_exc_base != Native_thread::INVALID_INDEX) {
|
||||
PERR("thread already started");
|
||||
return -6;
|
||||
}
|
||||
@ -112,13 +112,11 @@ int Platform_thread::start(void *ip, void *sp)
|
||||
addr_t sm_alloc_sel = _sel_exc_base + PD_SEL_CAP_LOCK;
|
||||
addr_t sm_ec_sel = _pager->exc_pt_sel() + SM_SEL_EC_CLIENT;
|
||||
|
||||
addr_t remap_src[] = { _pager->exc_pt_sel() + PT_SEL_PAGE_FAULT,
|
||||
_pd->parent_pt_sel(),
|
||||
addr_t remap_src[] = { _pd->parent_pt_sel(),
|
||||
_pager->exc_pt_sel() + PT_SEL_STARTUP,
|
||||
_pager->exc_pt_sel() + PT_SEL_RECALL,
|
||||
sm_ec_sel };
|
||||
addr_t remap_dst[] = { PT_SEL_PAGE_FAULT,
|
||||
PT_SEL_PARENT,
|
||||
addr_t remap_dst[] = { PT_SEL_PARENT,
|
||||
PT_SEL_STARTUP,
|
||||
PT_SEL_RECALL,
|
||||
SM_SEL_EC };
|
||||
@ -135,16 +133,33 @@ int Platform_thread::start(void *ip, void *sp)
|
||||
goto cleanup_base;
|
||||
}
|
||||
|
||||
/* Remap portals to exception base window of first thread */
|
||||
/* Remap exception portals for first thread */
|
||||
if (map_local((Utcb *)Thread_base::myself()->utcb(),
|
||||
Obj_crd(_pager->exc_pt_sel(), 4),
|
||||
Obj_crd(_sel_exc_base, 4)))
|
||||
goto cleanup_base;
|
||||
|
||||
if (map_local((Utcb *)Thread_base::myself()->utcb(),
|
||||
Obj_crd(_pager->exc_pt_sel() + 0x10, 3),
|
||||
Obj_crd(_sel_exc_base + 0x10, 3)))
|
||||
goto cleanup_base;
|
||||
|
||||
if (map_local((Utcb *)Thread_base::myself()->utcb(),
|
||||
Obj_crd(_pager->exc_pt_sel() + 0x18, 1),
|
||||
Obj_crd(_sel_exc_base + 0x18, 1)))
|
||||
goto cleanup_base;
|
||||
|
||||
if (PT_SEL_PARENT != 0x1a) {
|
||||
PERR("PT_SEL_PARENT changed !! Adjust remap code !!");
|
||||
goto cleanup_base;
|
||||
}
|
||||
|
||||
/* Remap Genode specific, RECALL and STARTUP portals for first thread */
|
||||
for (unsigned i = 0; i < sizeof(remap_dst)/sizeof(remap_dst[0]); i++) {
|
||||
/* locally map portals to initial portal window */
|
||||
if (map_local((Utcb *)Thread_base::myself()->utcb(),
|
||||
Obj_crd(remap_src[i], 0),
|
||||
Obj_crd(_sel_exc_base + remap_dst[i], 0))) {
|
||||
PERR("could not remap portal %lx->%lx",
|
||||
remap_src[i], remap_dst[i]);
|
||||
Obj_crd(_sel_exc_base + remap_dst[i], 0)))
|
||||
goto cleanup_base;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create lock for cap allocator selector */
|
||||
@ -186,8 +201,8 @@ int Platform_thread::start(void *ip, void *sp)
|
||||
* Reset pd cap since thread got not running and pd cap will
|
||||
* be revoked during cleanup.
|
||||
*/
|
||||
_pd->assign_pd(~0UL);
|
||||
_pager->client_set_ec(~0UL);
|
||||
_pd->assign_pd(Native_thread::INVALID_INDEX);
|
||||
_pager->client_set_ec(Native_thread::INVALID_INDEX);
|
||||
|
||||
PERR("create_sc returned %d", res);
|
||||
goto cleanup_ec;
|
||||
@ -207,7 +222,7 @@ int Platform_thread::start(void *ip, void *sp)
|
||||
revoke(Obj_crd(sm_ec_sel, 0));
|
||||
revoke(Obj_crd(_sel_exc_base, NUM_INITIAL_PT_LOG2));
|
||||
cap_selector_allocator()->free(_sel_exc_base, NUM_INITIAL_PT_LOG2);
|
||||
_sel_exc_base = ~0UL;
|
||||
_sel_exc_base = Native_thread::INVALID_INDEX;
|
||||
|
||||
return -7;
|
||||
}
|
||||
@ -252,8 +267,10 @@ int Platform_thread::state(Thread_state *state_dst)
|
||||
if (state_dst->transfer) {
|
||||
/* Not permitted for main thread */
|
||||
if (_is_main_thread) return -2;
|
||||
|
||||
/* You can do it only once */
|
||||
if (_sel_exc_base != ~0UL) return -3;
|
||||
if (_sel_exc_base != Native_thread::INVALID_INDEX) return -3;
|
||||
|
||||
/**
|
||||
* _sel_exc_base exception base of thread in caller
|
||||
* protection domain - not in Core !
|
||||
@ -278,12 +295,16 @@ void Platform_thread::cancel_blocking()
|
||||
}
|
||||
|
||||
|
||||
unsigned long Platform_thread::pager_object_badge() const { return ~0UL; }
|
||||
unsigned long Platform_thread::pager_object_badge() const
|
||||
{
|
||||
return Native_thread::INVALID_INDEX;
|
||||
}
|
||||
|
||||
|
||||
Platform_thread::Platform_thread(const char *name, unsigned, int thread_id)
|
||||
: _pd(0), _pager(0), _id_base(cap_selector_allocator()->alloc(1)),
|
||||
_sel_exc_base(~0UL), _cpu_no(0), _is_main_thread(false), _is_vcpu(false) { }
|
||||
_sel_exc_base(Native_thread::INVALID_INDEX), _cpu_no(0),
|
||||
_is_main_thread(false), _is_vcpu(false) { }
|
||||
|
||||
|
||||
Platform_thread::~Platform_thread()
|
||||
@ -295,7 +316,7 @@ Platform_thread::~Platform_thread()
|
||||
cap_selector_allocator()->free(_id_base, 1);
|
||||
|
||||
/* free exc_base used by main thread */
|
||||
if (_is_main_thread && _sel_exc_base != ~0UL) {
|
||||
if (_is_main_thread && _sel_exc_base != Native_thread::INVALID_INDEX) {
|
||||
revoke(Obj_crd(_sel_exc_base, NUM_INITIAL_PT_LOG2));
|
||||
cap_selector_allocator()->free(_sel_exc_base,
|
||||
NUM_INITIAL_PT_LOG2);
|
||||
|
@ -536,20 +536,28 @@ class Vcpu_dispatcher : public Genode::Thread<STACK_SIZE>,
|
||||
throw Cpu_session::Thread_creation_failed();
|
||||
|
||||
addr_t thread_sp = (addr_t)&_context->stack[-4];
|
||||
Genode::Nova_cpu_connection cpu;
|
||||
cpu.start_exc_base_vcpu(_thread_cap, 0, thread_sp,
|
||||
_tid.exc_pt_sel);
|
||||
Thread_state state(true);
|
||||
state.sel_exc_base = _tid.exc_pt_sel;
|
||||
|
||||
if (env()->cpu_session()->state(_thread_cap, &state) ||
|
||||
env()->cpu_session()->start(_thread_cap, 0, thread_sp))
|
||||
throw Cpu_session::Thread_creation_failed();
|
||||
|
||||
/* Request exception portals for vCPU dispatcher */
|
||||
for (unsigned i = 0; i < Nova::PT_SEL_PARENT; i++)
|
||||
request_event_portal(pager_cap, _tid.exc_pt_sel, i);
|
||||
|
||||
request_event_portal(pager_cap, _tid.exc_pt_sel,
|
||||
Nova::PT_SEL_PAGE_FAULT);
|
||||
Nova::SM_SEL_EC);
|
||||
request_event_portal(pager_cap, _tid.exc_pt_sel,
|
||||
Nova::SM_SEL_EC);
|
||||
Nova::PT_SEL_RECALL);
|
||||
|
||||
/**
|
||||
* Request native thread cap, _thread_cap only a token.
|
||||
* The native thread cap is required to attach new rpc objects
|
||||
* (to create portals bound to the ec)
|
||||
*/
|
||||
Genode::Nova_cpu_connection cpu;
|
||||
Native_capability ec_cap = cpu.native_cap(_thread_cap);
|
||||
_tid.ec_sel = ec_cap.local_name();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user