diff --git a/base-nova/include/base/pager.h b/base-nova/include/base/pager.h index 7021d378d..198f89093 100644 --- a/base-nova/include/base/pager.h +++ b/base-nova/include/base/pager.h @@ -44,14 +44,31 @@ namespace Genode { */ Signal_context_capability _exception_sigh; - addr_t _pt_cleanup; /* portal selector for object cleanup/destruction */ + /** + * Portal selector for object cleanup/destruction + */ + addr_t _pt_cleanup; + + /** + * Semaphore selector to synchronize pause/state/resume operations + */ + addr_t _sm_state_notify; addr_t _initial_esp; addr_t _initial_eip; + struct { + struct Thread_state thread; + bool valid; + bool dead; + } _state; + + void _copy_state(Nova::Utcb * utcb); + static void _page_fault_handler(); static void _startup_handler(); static void _invoke_handler(); + static void _recall_handler(); public: @@ -99,12 +116,14 @@ namespace Genode { /** * Notify exception handler about the occurrence of an exception */ - void submit_exception_signal() + bool submit_exception_signal() { - if (!_exception_sigh.valid()) return; + if (!_exception_sigh.valid()) return false; Signal_transmitter transmitter(_exception_sigh); transmitter.submit(); + + return true; } /** @@ -112,8 +131,40 @@ namespace Genode { */ addr_t handler_address() { - return reinterpret_cast(_invoke_handler); + return reinterpret_cast(_invoke_handler); } + + /** + * Return semaphore to block on until state of a recall is + * available. + */ + Native_capability notify_sm() + { + if (_state.valid) + return Native_capability::invalid_cap(); + if (_state.dead) + return Native_capability::invalid_cap(); + + return Native_capability(_sm_state_notify); + } + + /** + * Copy thread state of recalled thread. + */ + int copy_thread_state(Thread_state * state_dst) + { + if (!state_dst || !_state.valid) return -1; + + *state_dst = _state.thread; + + return 0; + } + + /** + * Cancel blocking in a lock so that recall exception can take + * place. + */ + void cancel_blocking_client(); }; diff --git a/base-nova/include/nova/syscall-generic.h b/base-nova/include/nova/syscall-generic.h index e23ead81e..3afef635e 100644 --- a/base-nova/include/nova/syscall-generic.h +++ b/base-nova/include/nova/syscall-generic.h @@ -172,9 +172,12 @@ namespace Nova { enum { ACDB = 1 << 0, /* eax, ecx, edx, ebx */ + EBSD = 1 << 1, /* ebp, esi, edi */ ESP = 1 << 2, EIP = 1 << 3, EFL = 1 << 4, /* eflags */ + FSGS = 1 << 6, + CSSS = 1 << 7, QUAL = 1 << 15, /* exit qualification */ CTRL = 1 << 16, /* execution controls */ INJ = 1 << 17, /* injection info */ @@ -384,9 +387,13 @@ namespace Nova { */ struct Utcb { - mword_t items; /* number of untyped items uses lowest 16 bit, number of typed items uses bit 16-31, bit 32+ are ignored on 64bit */ - Crd crd_xlt; /* receive capability-range descriptor for translation */ - Crd crd_rcv; /* receive capability-range descriptor for delegation */ + /** + * Number of untyped items uses lowest 16 bit, number of typed items + * uses bit 16-31, bit 32+ are ignored on 64bit + */ + mword_t items; + Crd crd_xlt; /* receive capability-range descriptor for translation */ + Crd crd_rcv; /* receive capability-range descriptor for delegation */ mword_t tls; /** @@ -402,19 +409,39 @@ namespace Nova { /* exception state */ struct { - mword_t mtd, instr_len, eip, eflags; - unsigned misc[4]; - mword_t eax, ecx, edx, ebx; - mword_t esp, ebp, esi, edi; + mword_t mtd, instr_len, ip, flags; + unsigned intr_state, actv_state, inj_info, inj_error; + mword_t ax, cx, dx, bx; + mword_t sp, bp, si, di; #ifdef __x86_64__ - mword_t rxx[8]; + mword_t r8, r9, r10, r11, r12, r13, r14, r15; #endif unsigned long long qual[2]; /* exit qualification */ unsigned ctrl[2]; unsigned long long tsc; mword_t cr0, cr2, cr3, cr4; -// unsigned misc3[44]; - }; +#ifdef __x86_64__ + mword_t cr8, reserved; +#endif + mword_t dr7, sysenter_cs, sysenter_sp, sysenter_ip; + + struct { + unsigned short sel, ar; + unsigned limit; + mword_t base; +#ifdef __x86_32__ + mword_t reserved; +#endif + } es, cs, ss, ds, fs, gs, ldtr, tr; + struct { + unsigned reserved0; + unsigned limit; + mword_t base; +#ifdef __x86_32__ + mword_t reserved1; +#endif + } gdtr, idtr; + } __attribute__((packed)); }; struct Item { @@ -499,9 +526,10 @@ namespace Nova { PT_SEL_PAGE_FAULT = 0xe, PT_SEL_PARENT = 0x1a, /* convention on Genode */ PT_SEL_STARTUP = 0x1e, + PT_SEL_RECALL = 0x1f, PD_SEL = 0x1b, PD_SEL_CAP_LOCK = 0x1c, /* convention on Genode */ - SM_SEL_EC_MAIN = 0x1c, /* convention on Genode */ + SM_SEL_EC_CLIENT = 0x1c, /* convention on Genode */ SM_SEL_EC = 0x1d, /* convention on Genode */ }; diff --git a/base-nova/lib/mk/pager.mk b/base-nova/lib/mk/pager.mk deleted file mode 100644 index 2c4f26b18..000000000 --- a/base-nova/lib/mk/pager.mk +++ /dev/null @@ -1,3 +0,0 @@ -SRC_CC = pager.cc - -vpath pager.cc $(REP_DIR)/src/base/pager diff --git a/base-nova/lib/mk/x86_32/pager.mk b/base-nova/lib/mk/x86_32/pager.mk new file mode 100644 index 000000000..018d9acb9 --- /dev/null +++ b/base-nova/lib/mk/x86_32/pager.mk @@ -0,0 +1,3 @@ +SRC_CC = pager.cc x86_32/pager.cc + +vpath %.cc $(REP_DIR)/src/base/pager diff --git a/base-nova/lib/mk/x86_64/pager.mk b/base-nova/lib/mk/x86_64/pager.mk new file mode 100644 index 000000000..e8abfd2fa --- /dev/null +++ b/base-nova/lib/mk/x86_64/pager.mk @@ -0,0 +1,3 @@ +SRC_CC = pager.cc x86_64/pager.cc + +vpath %.cc $(REP_DIR)/src/base/pager diff --git a/base-nova/src/base/ipc/pager.cc b/base-nova/src/base/ipc/pager.cc index 0e6d4fa9b..ab18d844c 100644 --- a/base-nova/src/base/ipc/pager.cc +++ b/base-nova/src/base/ipc/pager.cc @@ -46,7 +46,7 @@ void Ipc_pager::wait_for_fault() Nova::Utcb *utcb = (Nova::Utcb *)Thread_base::myself()->utcb(); _fault_type = (Pf_type)utcb->qual[0]; _fault_addr = utcb->qual[1]; - _fault_ip = utcb->eip; + _fault_ip = utcb->ip; if (verbose_page_fault) print_page_fault(_fault_type, _fault_addr, _fault_ip); diff --git a/base-nova/src/base/pager/pager.cc b/base-nova/src/base/pager/pager.cc index 511d707bc..21c649ef3 100644 --- a/base-nova/src/base/pager/pager.cc +++ b/base-nova/src/base/pager/pager.cc @@ -47,9 +47,13 @@ void Pager_object::_page_fault_handler() 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); + if (!obj->submit_exception_signal()) { + /* revoke paging capability, let thread die in kernel */ + Nova::revoke(Obj_crd(obj->exc_pt_sel() + PT_SEL_PAGE_FAULT, 0), + true); + obj->_state.dead = true; + } + Utcb *utcb = (Utcb *)Thread_base::myself()->utcb(); utcb->set_msg_word(0); } @@ -57,14 +61,41 @@ void Pager_object::_page_fault_handler() ipc_pager.reply_and_wait_for_fault(); } +void Pager_object::_recall_handler() +{ + Pager_object *obj = static_cast(Thread_base::myself()); + Utcb *utcb = (Utcb *)Thread_base::myself()->utcb(); + + 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.valid = true; + + if (sm_ctrl(obj->_sm_state_notify, SEMAPHORE_UP) != NOVA_OK) + PWRN("notify failed"); + + if (sm_ctrl(obj->exc_pt_sel() + SM_SEL_EC, SEMAPHORE_DOWNZERO) != NOVA_OK) + PWRN("blocking recall handler failed"); + + obj->_state.valid = false; + + utcb->set_msg_word(0); + utcb->mtd = 0; + reply(Thread_base::myself()->stack_top()); +} void Pager_object::_startup_handler() { Pager_object *obj = static_cast(Thread_base::myself()); Utcb *utcb = (Utcb *)Thread_base::myself()->utcb(); - utcb->eip = obj->_initial_eip; - utcb->esp = obj->_initial_esp; + 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()); @@ -82,12 +113,13 @@ void Pager_object::_invoke_handler() utcb->set_msg_word(0); if (event == PT_SEL_STARTUP || event == PT_SEL_PAGE_FAULT || - event == SM_SEL_EC) { + event == SM_SEL_EC || event == PT_SEL_RECALL) { + /** - * Caller is requesting the SM cap of main thread - * this object is paging - it is stored at SM_SEL_EC_MAIN + * Caller is requesting the SM cap of thread + * this object is paging - it is stored at SM_SEL_EC_CLIENT */ - if (event == SM_SEL_EC) event = SM_SEL_EC_MAIN; + if (event == SM_SEL_EC) event = SM_SEL_EC_CLIENT; bool res = utcb->append_item(Obj_crd(obj->exc_pt_sel() + event, 0), 0); @@ -99,13 +131,21 @@ void Pager_object::_invoke_handler() } -void Pager_object::wake_up() { PDBG("not yet implemented"); } +void Pager_object::wake_up() { cancel_blocking(); } +void Pager_object::cancel_blocking_client() { + uint8_t res = sm_ctrl(exc_pt_sel() + SM_SEL_EC_CLIENT, SEMAPHORE_UP); + if (res != NOVA_OK) + PWRN("cancel blocking failed"); +} Pager_object::Pager_object(unsigned long badge) : Thread_base("pager", PF_HANDLER_STACK_SIZE), _badge(badge) { - _pt_cleanup = cap_selector_allocator()->alloc(); + _pt_cleanup = cap_selector_allocator()->alloc(); + _sm_state_notify = cap_selector_allocator()->alloc(); + _state.valid = false; + _state.dead = false; /* create portal for page-fault handler */ addr_t pd_sel = __core_pd_sel; @@ -129,19 +169,46 @@ Pager_object::Pager_object(unsigned long badge) throw Create_startup_pt_failed(); } + /* Create portal for recall handler */ + Mtd mtd(Mtd::ESP | Mtd::EIP | Mtd::ACDB | Mtd::EFL | Mtd::EBSD | Mtd::FSGS); + res = create_pt(exc_pt_sel() + 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 final cleanup call used during destruction */ res = create_pt(_pt_cleanup, pd_sel, _tid.ec_sel, Mtd(0), reinterpret_cast(_invoke_handler)); - if (res) - PERR("could not create pager cleanup portal, error = %u\n", - res); + if (res) { + PERR("could not create pager cleanup portal, error = %u\n", res); + class Create_cleanup_pt_failed { }; + throw Create_cleanup_pt_failed(); + } + + res = Nova::create_sm(_sm_state_notify, pd_sel, 0); + if (res != Nova::NOVA_OK) { + class Create_state_notifiy_sm_failed { }; + throw Create_state_notifiy_sm_failed(); + } } 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 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); + /* 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 * context of the serving thread). When the call returns @@ -158,7 +225,7 @@ Pager_object::~Pager_object() /* Revoke portal used for the cleanup call */ revoke(Obj_crd(_pt_cleanup, 0), true); cap_selector_allocator()->free(_pt_cleanup, 0); - + cap_selector_allocator()->free(sm_cap, 0); } diff --git a/base-nova/src/base/pager/x86_32/pager.cc b/base-nova/src/base/pager/x86_32/pager.cc new file mode 100644 index 000000000..a07b3f6aa --- /dev/null +++ b/base-nova/src/base/pager/x86_32/pager.cc @@ -0,0 +1,34 @@ +/* + * \brief Copy thread state - x86_32 + * \author Alexander Boettcher + * \date 2012-08-23 + */ + +/* + * Copyright (C) 2012-2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* NOVA includes */ +#include + +using namespace Genode; + +void Pager_object::_copy_state(Nova::Utcb * utcb) +{ + _state.thread.ebp = utcb->bp; + _state.thread.eax = utcb->ax; + _state.thread.ebx = utcb->bx; + _state.thread.ecx = utcb->cx; + _state.thread.edx = utcb->dx; + _state.thread.esi = utcb->si; + _state.thread.edi = utcb->di; + + _state.thread.gs = utcb->gs.sel; + _state.thread.fs = utcb->fs.sel; +} diff --git a/base-nova/src/base/pager/x86_64/pager.cc b/base-nova/src/base/pager/x86_64/pager.cc new file mode 100644 index 000000000..4f7796abc --- /dev/null +++ b/base-nova/src/base/pager/x86_64/pager.cc @@ -0,0 +1,42 @@ +/* + * \brief Copy thread state - x86_64 + * \author Alexander Boettcher + * \date 2012-08-23 + */ + +/* + * Copyright (C) 2012-2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +/* NOVA includes */ +#include + +using namespace Genode; + +void Pager_object::_copy_state(Nova::Utcb * utcb) +{ + _state.thread.rbp = utcb->bp; + _state.thread.rax = utcb->ax; + _state.thread.rbx = utcb->bx; + _state.thread.rcx = utcb->cx; + _state.thread.rdx = utcb->dx; + _state.thread.rsi = utcb->si; + _state.thread.rdi = utcb->di; + + _state.thread.r8 = utcb->r8; + _state.thread.r9 = utcb->r9; + _state.thread.r10 = utcb->r10; + _state.thread.r11 = utcb->r11; + _state.thread.r12 = utcb->r12; + _state.thread.r13 = utcb->r13; + _state.thread.r14 = utcb->r14; + _state.thread.r15 = utcb->r15; + + _state.thread.ss = utcb->ss.sel; +} diff --git a/base-nova/src/base/server/server.cc b/base-nova/src/base/server/server.cc index 858f2a4b6..023d560b2 100644 --- a/base-nova/src/base/server/server.cc +++ b/base-nova/src/base/server/server.cc @@ -224,6 +224,8 @@ Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size, 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, + Nova::PT_SEL_RECALL); /** * Request native thread cap, _thread_cap only a token. diff --git a/base-nova/src/base/thread/thread_nova.cc b/base-nova/src/base/thread/thread_nova.cc index f4b94e745..c2dc02157 100644 --- a/base-nova/src/base/thread/thread_nova.cc +++ b/base-nova/src/base/thread/thread_nova.cc @@ -132,6 +132,7 @@ void Thread_base::start() 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); } /* request creation of SC to let thread run*/ diff --git a/base-nova/src/core/platform.cc b/base-nova/src/core/platform.cc index 4a0401e6e..3c6bfe363 100644 --- a/base-nova/src/core/platform.cc +++ b/base-nova/src/core/platform.cc @@ -98,11 +98,11 @@ static void page_fault_handler() Utcb *utcb = (Utcb *)CORE_PAGER_UTCB_ADDR; addr_t pf_addr = utcb->qual[1]; - addr_t pf_eip = utcb->eip; - addr_t pf_esp = utcb->esp; + addr_t pf_ip = utcb->ip; + addr_t pf_sp = utcb->sp; printf("\nPAGE-FAULT IN CORE: ADDR %lx IP %lx SP %lx stack trace follows...\n", - pf_addr, pf_eip, pf_esp); + pf_addr, pf_ip, pf_sp); /* dump stack trace */ struct Core_img @@ -127,9 +127,9 @@ static void page_fault_handler() }; int count = 1; - printf(" #%d %08lx %08lx\n", count++, pf_esp, pf_eip); + printf(" #%d %08lx %08lx\n", count++, pf_sp, pf_ip); - Core_img dump(pf_esp); + Core_img dump(pf_sp); while (dump.ip_valid()) { printf(" #%d %p %08lx\n", count++, dump.ip(), *dump.ip()); dump.next_ip(); diff --git a/base-nova/src/core/platform_thread.cc b/base-nova/src/core/platform_thread.cc index bd62b082f..ed228e712 100644 --- a/base-nova/src/core/platform_thread.cc +++ b/base-nova/src/core/platform_thread.cc @@ -26,6 +26,7 @@ /* NOVA includes */ #include +#include using namespace Genode; @@ -69,11 +70,11 @@ int Platform_thread::start(void *ip, void *sp, addr_t exc_base, bool vcpu) /** * Create semaphore required for Genode locking. * It is created at the root pager exception base + - * SM_SEL_EC_MAIN and can be later on requested by the thread + * SM_SEL_EC_CLIENT and can be later on requested by the thread * the same way as STARTUP and PAGEFAULT portal. */ uint8_t res = Nova::create_sm(_pager->exc_pt_sel() + - SM_SEL_EC_MAIN, + SM_SEL_EC_CLIENT, _pd->pd_sel(), 0); if (res != Nova::NOVA_OK) { PERR("creation of semaphore for new thread failed %u", @@ -107,20 +108,32 @@ int Platform_thread::start(void *ip, void *sp, addr_t exc_base, bool vcpu) addr_t pd_core_sel = Platform_pd::pd_core_sel(); addr_t sm_alloc_sel = _sel_exc_base + PD_SEL_CAP_LOCK; - addr_t sm_ec_sel = _sel_exc_base + SM_SEL_EC; + 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(), - _pager->exc_pt_sel() + PT_SEL_STARTUP }; + _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, - PT_SEL_STARTUP }; + PT_SEL_STARTUP, + PT_SEL_RECALL, + SM_SEL_EC }; addr_t pd_sel; Obj_crd initial_pts(_sel_exc_base, NUM_INITIAL_PT_LOG2); uint8_t res; + /* Create lock for EC used by lock_helper */ + res = create_sm(sm_ec_sel, pd_core_sel, 0); + if (res != NOVA_OK) { + PERR("could not create semaphore for new thread"); + goto cleanup_base; + } + + /* Remap portals to exception base window of 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(), @@ -139,13 +152,6 @@ int Platform_thread::start(void *ip, void *sp, addr_t exc_base, bool vcpu) goto cleanup_base; } - /* Create lock for EC used by lock_helper */ - res = create_sm(sm_ec_sel, pd_core_sel, 0); - if (res != NOVA_OK) { - PERR("could not create semaphore for new thread"); - goto cleanup_base; - } - pd_sel = cap_selector_allocator()->alloc(); /* create task */ @@ -192,6 +198,7 @@ int Platform_thread::start(void *ip, void *sp, addr_t exc_base, bool vcpu) cap_selector_allocator()->free(pd_sel, 0); cleanup_base: + 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; @@ -202,28 +209,51 @@ int Platform_thread::start(void *ip, void *sp, addr_t exc_base, bool vcpu) Native_capability Platform_thread::pause() { - PDBG("not implemented"); - return Native_capability::invalid_cap(); + if (!_pager) + return Native_capability::invalid_cap(); + + Native_capability notify_sm = _pager->notify_sm(); + if (!notify_sm.valid()) return notify_sm; + + if (Nova::ec_ctrl(_sel_ec()) != Nova::NOVA_OK) + return Native_capability::invalid_cap(); + + /* If the thread is blocked in the its own SM, get him out */ + cancel_blocking(); + + return notify_sm; } void Platform_thread::resume() { - uint8_t res = Nova::create_sc(_sel_sc(), _pd->pd_sel(), _sel_ec(), - Nova::Qpd()); - if (res) - PDBG("create_sc returned %u", res); + using namespace Nova; + + uint8_t res = create_sc(_sel_sc(), _pd->pd_sel(), _sel_ec(), Qpd()); + if (res == NOVA_OK) return; + + if (!_pager) return; + /* Thread was paused beforehand and blocked in pager - wake up pager */ + _pager->wake_up(); } int Platform_thread::state(Thread_state *state_dst) { - PWRN("not implemented"); - return -1; + if (!state_dst || !_pager) return -1; + + int res = _pager->copy_thread_state(state_dst); + + return res; } -void Platform_thread::cancel_blocking() { PWRN("not implemented"); } +void Platform_thread::cancel_blocking() +{ + if (!_pager) return; + + _pager->cancel_blocking_client(); +} unsigned long Platform_thread::pager_object_badge() const { return ~0UL; }