diff --git a/base-nova/include/base/ipc_msgbuf.h b/base-nova/include/base/ipc_msgbuf.h index 79776a577..c18a85a37 100644 --- a/base-nova/include/base/ipc_msgbuf.h +++ b/base-nova/include/base/ipc_msgbuf.h @@ -45,17 +45,23 @@ namespace Genode { /** * Portal capability selectors to delegate */ - int _snd_pt_sel[MAX_CAP_ARGS]; + addr_t _snd_pt_sel[MAX_CAP_ARGS]; /** * Base of portal receive window */ int _rcv_pt_base; + struct { + addr_t sel; + bool del; + } _rcv_pt_sel [MAX_CAP_ARGS]; + /** * Read counter for unmarshalling portal capability selectors */ - int _rcv_pt_sel_cnt; + unsigned _rcv_pt_sel_cnt; + unsigned _rcv_pt_sel_max; /** * Flag set to true if receive window must be re-initialized @@ -98,7 +104,7 @@ namespace Genode { /** * Append portal capability selector to message buffer */ - inline bool snd_append_pt_sel(int pt_sel) + inline bool snd_append_pt_sel(addr_t pt_sel) { if (_snd_pt_sel_cnt >= MAX_CAP_ARGS - 1) return false; @@ -119,25 +125,35 @@ namespace Genode { * \return portal-capability selector, or * -1 if index is invalid */ - int snd_pt_sel(unsigned i) { return i < _snd_pt_sel_cnt ? _snd_pt_sel[i] : -1; } + addr_t snd_pt_sel(unsigned i) { return i < _snd_pt_sel_cnt ? _snd_pt_sel[i] : -1; } /** * Request current portal-receive window */ - int rcv_pt_base() { return _rcv_pt_base; } + addr_t rcv_pt_base() { return _rcv_pt_base; } /** * Reset portal-capability receive window */ - void rcv_reset() { _rcv_pt_sel_cnt = 0; } + void rcv_reset() + { + if (!rcv_invalid()) { rcv_cleanup(false); } + + _rcv_pt_sel_cnt = 0; + _rcv_pt_sel_max = 0; + _rcv_pt_base = ~0UL; + } /** * Return received portal-capability selector */ - int rcv_pt_sel() + addr_t rcv_pt_sel() { - _rcv_dirty = true; - return _rcv_pt_base + _rcv_pt_sel_cnt++; + /* return only received or translated caps */ + if (_rcv_pt_sel_cnt < _rcv_pt_sel_max) + return _rcv_pt_sel[_rcv_pt_sel_cnt++].sel; + else + return 0; } /** @@ -148,8 +164,62 @@ namespace Genode { * current receive window with one or more portal capabilities. * To enable the reception of portal capability selectors for the * next IDC, we need a fresh receive window. + * + * \param keep If 'true', try to keep receive window if it's clean. + * If 'false', free caps of receive window because object is freed afterwards. + * + * \result 'true' if receive window was freed, 'false' if it was kept. */ - bool rcv_dirty() { return _rcv_dirty; } + bool rcv_cleanup(bool keep) + { + /* If nothing has been used, revoke/free at once */ + if (_rcv_pt_sel_cnt == 0) { + _rcv_pt_sel_max = 0; + + if (_rcv_items) + Nova::revoke(Nova::Obj_crd(rcv_pt_base(), MAX_CAP_ARGS_LOG2), true); + + if (keep) return false; + + cap_selector_allocator()->free(rcv_pt_base(), MAX_CAP_ARGS_LOG2); + return true; + } + + /* Revoke received unused caps, skip translated caps */ + for (unsigned i = _rcv_pt_sel_cnt; i < _rcv_pt_sel_max; i++) { + if (_rcv_pt_sel[i].del) { + /* revoke cap we received but didn't use */ + Nova::revoke(Nova::Obj_crd(_rcv_pt_sel[i].sel, 0), true); + /* mark cap as free */ + cap_selector_allocator()->free(_rcv_pt_sel[i].sel, 0); + } + } + + /* free all caps which are unused from the receive window */ + for (unsigned i=0; i < MAX_CAP_ARGS; i++) { + unsigned j=0; + for (j=0; j < _rcv_pt_sel_max; j++) { + if (_rcv_pt_sel[j].sel == rcv_pt_base() + i) break; + } + if (j < _rcv_pt_sel_max) continue; + + /* Revoke seems needless here, but is required. + * It is possible that an evil guy translated us + * more then MAX_CAP_ARGS and then mapped us + * something to the receive window. + * The mappings would not show up + * in _rcv_pt_sel, but would be there. + */ + Nova::revoke(Nova::Obj_crd(rcv_pt_base() + i, 0), true); + /* i was unused, free at allocator */ + cap_selector_allocator()->free(rcv_pt_base() + i, 0); + } + + _rcv_pt_sel_cnt = 0; + _rcv_pt_sel_max = 0; + + return true; + } /** * Initialize receive window for portal capability selectors @@ -168,7 +238,34 @@ namespace Genode { /* register receive window at the UTCB */ utcb->crd_rcv = Nova::Obj_crd(rcv_pt_base(),MAX_CAP_ARGS_LOG2); + utcb->crd_xlt = Nova::Obj_crd(0, sizeof(addr_t) * 8 - 12); } + + /** + * Post IPC processing. + * + * Remember where and which caps have been received + * respectively have been translated. + * The information is required to correctly free + * cap indexes and to revoke unused received caps. + * + * \param utcb UTCB of designated receiver thread + */ + void post_ipc(Nova::Utcb *utcb) { + _rcv_items = (utcb->items >> 16) & 0xffffu; + _rcv_pt_sel_max = 0; + _rcv_pt_sel_cnt = 0; + + for (unsigned i=0; i < _rcv_items; i++) { + Nova::Utcb::Item * item = utcb->get_item(i); + if (!item) break; + if (_rcv_pt_sel_max >= MAX_CAP_ARGS) break; + + _rcv_pt_sel[_rcv_pt_sel_max].sel = item->crd >> 12; + _rcv_pt_sel[_rcv_pt_sel_max++].del = item->is_del(); + } + } + }; diff --git a/base-nova/include/nova/syscall-generic.h b/base-nova/include/nova/syscall-generic.h index 871272c4d..d3690b340 100644 --- a/base-nova/include/nova/syscall-generic.h +++ b/base-nova/include/nova/syscall-generic.h @@ -392,6 +392,7 @@ namespace Nova { struct Item { mword_t crd; mword_t hotspot; + bool is_del() { return hotspot & 0x1; } }; /** @@ -415,7 +416,8 @@ namespace Nova { __attribute__((warn_unused_result)) bool append_item(Crd crd, mword_t sel_hotspot, bool kern_pd = false, - bool update_guest_pt = false) + bool update_guest_pt = false, + bool translate_map = false) { /* transfer items start at the end of the UTCB */ items += 1 << 16; @@ -433,12 +435,24 @@ namespace Nova { /* update guest page table */ unsigned g = update_guest_pt ? (1 << 10) : 0; - item->hotspot = crd.hotspot(sel_hotspot) | g | h | 1; + item->hotspot = crd.hotspot(sel_hotspot) | g | h | (translate_map ? 2 : 1); item->crd = crd.value(); return true; } + /** + * Return typed item at postion i in UTCB + * + * \param i position of item requested, starts with 0 + */ + Item * get_item(const unsigned i) { + if (i > (PAGE_SIZE_BYTE / sizeof(struct Item))) return 0; + Item * item = reinterpret_cast(this) + (PAGE_SIZE_BYTE / sizeof(struct Item)) - i - 1; + if (reinterpret_cast(item) < this->msg) return 0; + return item; + } + mword_t mtd_value() const { return static_cast(mtd).value(); } } __attribute__((packed)); diff --git a/base-nova/patches/xlt_rcv.patch b/base-nova/patches/xlt_rcv.patch new file mode 100644 index 000000000..4eb83dba3 --- /dev/null +++ b/base-nova/patches/xlt_rcv.patch @@ -0,0 +1,51 @@ +diff --git a/src/pd.cpp b/src/pd.cpp +index 8160d73..be9aa63 100644 +--- a/src/pd.cpp ++++ b/src/pd.cpp +@@ -173,8 +173,9 @@ void Pd::xlt_crd (Pd *pd, Crd xlt, Crd &crd) + sb = (sb - mdb->node_base) + (mdb->node_phys - node->node_phys) + node->node_base; + + if ((ro = clamp (sb, rb, so, ro)) != ~0UL) { ++ trace (TRACE_DEL, "XLT OBJ PD:%p->%p SB:%#010lx RB:%#010lx O:%#04lx", pd, this, crd.base(), rb, so); + crd = Crd (crd.type(), rb, ro, mdb->node_attr); +- return; ++ return; + } + } + } +@@ -245,22 +246,32 @@ void Pd::rev_crd (Crd crd, bool self) + + void Pd::xfer_items (Pd *src, Crd xlt, Crd del, Xfer *s, Xfer *d, unsigned long ti) + { +- for (Crd crd; ti--; s--) { ++ mword set_as_del; + ++ for (Crd crd; ti--; s--) { ++ + crd = *s; ++ set_as_del = 0; + +- switch (s->flags() & 1) { ++ switch (s->flags() & 3) { + + case 0: + xlt_crd (src, xlt, crd); + break; + ++ case 2: ++ xlt_crd (src, xlt, crd); ++ if (crd.type()) break; ++ ++ crd = *s; ++ set_as_del = 1; ++ + case 1: + del_crd (src == &root && s->flags() & 0x800 ? &kern : src, del, crd, s->flags() >> 9 & 3, s->hotspot()); + break; + }; + + if (d) +- *d-- = Xfer (crd, s->flags()); ++ *d-- = Xfer (crd, s->flags() | set_as_del); + } + } diff --git a/base-nova/src/base/ipc/ipc.cc b/base-nova/src/base/ipc/ipc.cc index 458d4a139..e9fefe1be 100644 --- a/base-nova/src/base/ipc/ipc.cc +++ b/base-nova/src/base/ipc/ipc.cc @@ -179,6 +179,7 @@ void Ipc_server::_wait() Nova::Utcb *utcb = (Nova::Utcb *)Thread_base::myself()->utcb(); + _rcv_msg->post_ipc(utcb); copy_utcb_to_msgbuf(utcb, _rcv_msg); /* reset unmarshaller */