diff --git a/base-nova/include/base/pager.h b/base-nova/include/base/pager.h index 8454fec467..c109f39579 100644 --- a/base-nova/include/base/pager.h +++ b/base-nova/include/base/pager.h @@ -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); diff --git a/base-nova/src/base/pager/pager.cc b/base-nova/src/base/pager/pager.cc index 835850894a..3fa9d31e66 100644 --- a/base-nova/src/base/pager/pager.cc +++ b/base-nova/src/base/pager/pager.cc @@ -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(myself); + + if (!myself || !obj) goto dead; + + utcb = reinterpret_cast(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(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(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(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(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); } diff --git a/base-nova/src/base/server/server.cc b/base-nova/src/base/server/server.cc index 18d742c9c3..635d24b4b1 100644 --- a/base-nova/src/base/server/server.cc +++ b/base-nova/src/base/server/server.cc @@ -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, diff --git a/base-nova/src/base/thread/thread_nova.cc b/base-nova/src/base/thread/thread_nova.cc index 889ebf260f..131d1a4772 100644 --- a/base-nova/src/base/thread/thread_nova.cc +++ b/base-nova/src/base/thread/thread_nova.cc @@ -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); } diff --git a/base-nova/src/core/platform_thread.cc b/base-nova/src/core/platform_thread.cc index 4a3ef5d911..279605d6e3 100644 --- a/base-nova/src/core/platform_thread.cc +++ b/base-nova/src/core/platform_thread.cc @@ -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); diff --git a/ports/src/vancouver/main.cc b/ports/src/vancouver/main.cc index 85be929715..532773b043 100644 --- a/ports/src/vancouver/main.cc +++ b/ports/src/vancouver/main.cc @@ -536,20 +536,28 @@ class Vcpu_dispatcher : public Genode::Thread, 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();