diff --git a/repos/base-nova/include/base/cap_map.h b/repos/base-nova/include/base/cap_map.h index 41ab802ee0..3b5231b0be 100644 --- a/repos/base-nova/include/base/cap_map.h +++ b/repos/base-nova/include/base/cap_map.h @@ -60,7 +60,7 @@ namespace Genode { Cap_range *find_by_id(addr_t); - void inc(unsigned id, bool inc_if_one = false); + void inc(unsigned id); void dec(unsigned id, bool revoke = true, unsigned num_log2 = 0); addr_t alloc(size_t const num_log2); @@ -88,10 +88,10 @@ namespace Genode { bool valid() const { return _range; } - inline void inc(bool inc_if_one = false) + inline void inc() { if (_range) - _range->inc(_local_name - _range->base(), inc_if_one); + _range->inc(_local_name - _range->base()); } inline void dec() diff --git a/repos/base-nova/include/base/native_types.h b/repos/base-nova/include/base/native_types.h index 246536bbf3..dade6aa975 100644 --- a/repos/base-nova/include/base/native_types.h +++ b/repos/base-nova/include/base/native_types.h @@ -60,10 +60,10 @@ namespace Genode { protected: - inline void _inc(bool inc_if_one = false) const + inline void _inc() const { Cap_index idx(cap_map()->find(local_name())); - idx.inc(inc_if_one); + idx.inc(); } inline void _dec() const @@ -106,15 +106,6 @@ namespace Genode { bool operator==(const Native_capability &o) const { return local_name() == o.local_name(); } - /** - * Inhibit removal of capability from cap map if it's the last reference - */ - void keep_if_last_reference() - { - if (valid()) - _inc(true); - } - /** * Copy constructor */ diff --git a/repos/base-nova/include/spec/32bit/nova/syscalls.h b/repos/base-nova/include/spec/32bit/nova/syscalls.h index d345f9ad99..94bad1a928 100644 --- a/repos/base-nova/include/spec/32bit/nova/syscalls.h +++ b/repos/base-nova/include/spec/32bit/nova/syscalls.h @@ -298,21 +298,37 @@ namespace Nova { * \param sm SM selector which gets an up() by the kernel if the * memory of the current revoke invocation gets freed up * (end of RCU period) + * \param kim keep_in_mdb - if set to true the kernel will make the + * resource inaccessible for solely for the specified pd. + * All already beforehand delegated resources will not be + * changed, e.g. revoked. All rights of the local resource + * will be removed (independent of what is specified by crd). */ ALWAYS_INLINE inline uint8_t revoke(Crd crd, bool self = true, bool remote = false, - mword_t pd = 0, mword_t sm = 0) + mword_t pd = 0, mword_t sm = 0, bool kim = false) { uint8_t flags = self ? 0x1 : 0; if (remote) flags |= 0x2; + if (kim) + flags |= 0x4; mword_t value_crd = crd.value(); return syscall_5(NOVA_REVOKE, flags, sm, value_crd, pd); } + /* + * Shortcut for revoke, where solely the local cap should be revoked and + * not all subsequent delegations of the local cap. + */ + ALWAYS_INLINE + inline uint8_t drop(Crd crd) { + return revoke(crd, true, false, 0, 0, true); } + + ALWAYS_INLINE inline uint8_t lookup(Crd &crd) { diff --git a/repos/base-nova/include/spec/64bit/nova/syscalls.h b/repos/base-nova/include/spec/64bit/nova/syscalls.h index 40063bfb45..80c68fb31e 100644 --- a/repos/base-nova/include/spec/64bit/nova/syscalls.h +++ b/repos/base-nova/include/spec/64bit/nova/syscalls.h @@ -250,21 +250,38 @@ namespace Nova { * \param sm SM selector which gets an up() by the kernel if the * memory of the current revoke invocation gets freed up * (end of RCU period) + * \param kim keep_in_mdb - if set to true the kernel will make the + * resource inaccessible solely inside the specified pd. + * All already beforehand delegated resources will not be + * changed, e.g. revoked. All rights of the local resource + * will be removed (independent of what is specified by crd). */ ALWAYS_INLINE inline uint8_t revoke(Crd crd, bool self = true, bool remote = false, - mword_t pd = 0, mword_t sm = 0) + mword_t pd = 0, mword_t sm = 0, bool kim = false) { uint8_t flags = self ? 0x1 : 0; if (remote) flags |= 0x2; + if (kim) + flags |= 0x4; + mword_t value_crd = crd.value(); return syscall_5(NOVA_REVOKE, flags, sm, value_crd, pd); } + /* + * Shortcut for revoke, where solely the local cap should be revoked and + * not all subsequent delegations of the local cap. + */ + ALWAYS_INLINE + inline uint8_t drop(Crd crd) { + return revoke(crd, true, false, 0, 0, true); } + + ALWAYS_INLINE inline uint8_t lookup(Crd &crd) { diff --git a/repos/base-nova/ports/nova.hash b/repos/base-nova/ports/nova.hash index 8ee2b74ac8..a675f6a8aa 100644 --- a/repos/base-nova/ports/nova.hash +++ b/repos/base-nova/ports/nova.hash @@ -1 +1 @@ -061565ecaea829f5a5ab48e39b4fae9148da7e4b +bde8909f6367ea2767c54b85ddf90afc2e6baee8 diff --git a/repos/base-nova/ports/nova.port b/repos/base-nova/ports/nova.port index 2e0f1333a4..0e29278868 100644 --- a/repos/base-nova/ports/nova.port +++ b/repos/base-nova/ports/nova.port @@ -4,7 +4,7 @@ DOWNLOADS := nova.git # r9 branch - use r9_debug for more verbose kernel messages URL(nova) := https://github.com/alex-ab/NOVA.git -REV(nova) := 172fe0dc1b6228fec20e220d2524f25b1016f6ab +REV(nova) := 8548c34df01504789b01f4e1a31b71e6d08a79c7 DIR(nova) := src/kernel/nova PATCHES := $(wildcard $(REP_DIR)/patches/*.patch) diff --git a/repos/base-nova/src/lib/base/cap_map.cc b/repos/base-nova/src/lib/base/cap_map.cc index 5a85b15b96..6217eb6e98 100644 --- a/repos/base-nova/src/lib/base/cap_map.cc +++ b/repos/base-nova/src/lib/base/cap_map.cc @@ -42,15 +42,12 @@ Cap_range *Cap_range::find_by_id(addr_t id) } -void Cap_range::inc(unsigned id, bool inc_if_one) +void Cap_range::inc(unsigned id) { bool failure = false; { Lock::Guard guard(_lock); - if (inc_if_one && _cap_array[id] != 1) - return; - if (_cap_array[id] + 1 == 0) failure = true; else @@ -78,7 +75,7 @@ void Cap_range::dec(unsigned const id_start, bool revoke, unsigned num_log_2) } if (revoke && _cap_array[id] == 1) - Nova::revoke(Nova::Obj_crd(_base + id, 0)); + Nova::drop(Nova::Obj_crd(_base + id, 0)); _cap_array[id]--; } diff --git a/repos/base-nova/src/lib/base/rpc_entrypoint.cc b/repos/base-nova/src/lib/base/rpc_entrypoint.cc index 7442f71400..ad4f786293 100644 --- a/repos/base-nova/src/lib/base/rpc_entrypoint.cc +++ b/repos/base-nova/src/lib/base/rpc_entrypoint.cc @@ -166,15 +166,6 @@ void Rpc_entrypoint::_activation_entry() return; } - /* - * Inhibit removal of capabilities sent as results of client requests. - * This prevents the recursive revocation of NOVA portal caps and, - * therefore, permits clients to use result capabilities after server - * code dropped all references. - */ - for (unsigned i = 0; i < ep._snd_buf.used_caps(); ++i) - ep._snd_buf.cap(i).keep_if_last_reference(); - /* dispatch request */ ep._snd_buf.reset(); try { exc = obj->dispatch(opcode, unmarshaller, ep._snd_buf); } diff --git a/repos/base-nova/src/test/platform/ipc.cc b/repos/base-nova/src/test/platform/ipc.cc new file mode 100644 index 0000000000..c3a819e495 --- /dev/null +++ b/repos/base-nova/src/test/platform/ipc.cc @@ -0,0 +1,59 @@ +/* + * \brief Helper classes to make raw Nova IPC calls which can't be expressed + * via the Genode base RPC abstractions + * \author Alexander Boettcher + * + */ + +/* + * Copyright (C) 2016 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. + */ + +#include + +/* Genode includes */ +#include +#include + +/* test specific includes */ +#include "server.h" + +using namespace Test; + +long Test::cap_void_manual(Genode::Native_capability dst, + Genode::Native_capability arg1, + Genode::addr_t &local_reply) +{ + Genode::Thread * myself = Genode::Thread::myself(); + Nova::Utcb *utcb = reinterpret_cast(myself->utcb()); + + /* save original receive window */ + Nova::Crd orig_crd = utcb->crd_rcv; + + /* don't open receive window */ + utcb->crd_rcv = Nova::Obj_crd(); + /* not used on base-nova */ + utcb->msg[0] = 0; + /* method number of RPC interface to be called on server side */ + utcb->msg[1] = 0; + utcb->set_msg_word(2); + + if (!arg1.valid()) + return Genode::Rpc_exception_code::INVALID_OBJECT; + + Nova::Obj_crd crd(arg1.local_name(), 0, arg1.dst().rights()); + if (!utcb->append_item(crd, 0, false, false, false)) + return Genode::Rpc_exception_code::INVALID_OBJECT; + + Genode::uint8_t res = Nova::call(dst.local_name()); + + /* restore original receive window */ + utcb->crd_rcv = orig_crd; + + local_reply = utcb->msg[2]; + return (res == Nova::NOVA_OK && utcb->msg_words() == 3) + ? utcb->msg[0] : Genode::Rpc_exception_code::INVALID_OBJECT; +} diff --git a/repos/base-nova/src/test/platform/main.cc b/repos/base-nova/src/test/platform/main.cc index bd00ae80cf..f911ab658d 100644 --- a/repos/base-nova/src/test/platform/main.cc +++ b/repos/base-nova/src/test/platform/main.cc @@ -32,8 +32,236 @@ static unsigned failed = 0; static unsigned check_pat = 1; +static Genode::Cap_connection cap; + using namespace Genode; +void test_translate() +{ + enum { STACK_SIZE = 4096 }; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "rpc_ep_translate"); + + Test::Component component; + Test::Capability session_cap = ep.manage(&component); + Test::Client client(session_cap); + + Genode::addr_t local_name = Native_thread::INVALID_INDEX; + + long rpc = Test::cap_void_manual(session_cap, session_cap, local_name); + if (rpc != Genode::Rpc_exception_code::SUCCESS || + local_name == session_cap.local_name() || + local_name == Native_thread::INVALID_INDEX) + { + failed ++; + PERR("%s: ipc call failed %lx", __func__, rpc); + ep.dissolve(&component); + return; + } + + Genode::Native_capability copy1(local_name); + + rpc = Test::cap_void_manual(session_cap, copy1, local_name); + if (rpc != Genode::Rpc_exception_code::SUCCESS || + local_name == copy1.local_name() || + local_name == Native_thread::INVALID_INDEX) + { + failed ++; + PERR("%s: ipc call failed %lx", __func__, rpc); + ep.dissolve(&component); + return; + } + + Genode::Native_capability copy2(local_name); + + PINF("delegation session_cap->copy1->copy2 0x%lx->0x%lx->0x%lx", + session_cap.local_name(), copy1.local_name(), copy2.local_name()); + + /* sanity checks translate which must work */ + Genode::Native_capability got_cap = client.cap_cap(copy2.local_name()); + if (got_cap.local_name() != copy1.local_name()) { + failed ++; + PERR("%u:%s translate failed", __LINE__, __func__); + ep.dissolve(&component); + return; + } + + got_cap = client.cap_cap(copy1.local_name()); + if (got_cap.local_name() != session_cap.local_name()) { + failed ++; + PERR("%u:%s translate failed", __LINE__, __func__); + ep.dissolve(&component); + return; + } + + got_cap = client.cap_cap(session_cap.local_name()); + if (got_cap.local_name() != session_cap.local_name()) { + failed ++; + PERR("%u:%s translate failed", __LINE__, __func__); + ep.dissolve(&component); + return; + } + + /** + * Test special revoke by make the intermediate cap (copy1) inaccessible + * and check that translate of copy2 get the right results. + */ + + Nova::Obj_crd crd_ses(copy1.local_name(), 0); + enum { SELF = true, LOCAL_REVOKE = false, LOCAL_PD = 0, NO_BLOCKING = 0, KEEP_IN_MDB = true }; + Nova::revoke(crd_ses, SELF, LOCAL_REVOKE, LOCAL_PD, NO_BLOCKING, KEEP_IN_MDB); + + crd_ses = Nova::Obj_crd(copy1.local_name(), 0); + Genode::uint8_t res = Nova::lookup(crd_ses); + if (res != Nova::NOVA_OK || !crd_ses.is_null()) { + failed ++; + PERR("%u - lookup call failed err=%x", __LINE__, res); + ep.dissolve(&component); + return; + } + + /* copy1 should be skipped and session_cap is the valid response */ + got_cap = client.cap_cap(copy2.local_name()); + if (got_cap.local_name() != session_cap.local_name()) { + failed ++; + PERR("%u:%s translate failed", __LINE__, __func__); + ep.dissolve(&component); + return; + } + + ep.dissolve(&component); +} + +void test_revoke() +{ + enum { STACK_SIZE = 4096 }; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "rpc_ep_revoke"); + + Test::Component component; + Test::Capability session_cap = ep.manage(&component); + Test::Client client(session_cap); + + Genode::addr_t local_name = Native_thread::INVALID_INDEX; + + long rpc = Test::cap_void_manual(session_cap, session_cap, local_name); + if (rpc != Genode::Rpc_exception_code::SUCCESS || + local_name == session_cap.local_name() || + local_name == Native_thread::INVALID_INDEX) + { + failed ++; + PERR("test_revoke ipc call failed %lx", rpc); + ep.dissolve(&component); + return; + } + + Genode::Native_capability copy_session_cap(local_name); + + rpc = Test::cap_void_manual(copy_session_cap, copy_session_cap, local_name); + if (rpc != Genode::Rpc_exception_code::SUCCESS || + local_name == copy_session_cap.local_name() || + local_name == Native_thread::INVALID_INDEX) + { + failed ++; + PERR("test_revoke ipc call failed %lx", rpc); + ep.dissolve(&component); + return; + } + + Nova::Obj_crd crd_dst(local_name, 0); + Genode::uint8_t res = Nova::lookup(crd_dst); + if (res != Nova::NOVA_OK || crd_dst.base() != local_name || crd_dst.type() != 3 || + crd_dst.order() != 0) { + failed ++; + PERR("%u - lookup call failed %x", __LINE__, res); + ep.dissolve(&component); + return; + } + + Nova::Obj_crd crd_ses(copy_session_cap.local_name(), 0); + res = Nova::lookup(crd_ses); + if (res != Nova::NOVA_OK || crd_ses.base() != copy_session_cap.local_name() || crd_ses.type() != 3 || + crd_ses.order() != 0) { + failed ++; + PERR("%u - lookup call failed err=%x is_null=%u", __LINE__, res, crd_ses.is_null()); + ep.dissolve(&component); + return; + } + + res = Nova::lookup(crd_dst); + if (res != Nova::NOVA_OK || crd_dst.base() != local_name || crd_dst.type() != 3 || + crd_dst.order() != 0) { + failed ++; + PERR("%u - lookup call failed err=%x is_null=%u", __LINE__, res, crd_dst.is_null()); + ep.dissolve(&component); + return; + } + + crd_ses = Nova::Obj_crd(copy_session_cap.local_name(), 0); + enum { SELF = true, LOCAL_REVOKE = false, LOCAL_PD = 0, NO_BLOCKING = 0, KEEP_IN_MDB = true }; + Nova::revoke(crd_ses, SELF, LOCAL_REVOKE, LOCAL_PD, NO_BLOCKING, KEEP_IN_MDB); + + crd_ses = Nova::Obj_crd(copy_session_cap.local_name(), 0); + res = Nova::lookup(crd_ses); + if (res != Nova::NOVA_OK || !crd_ses.is_null()) { + failed ++; + PERR("%u - lookup call failed err=%x", __LINE__, res); + ep.dissolve(&component); + return; + } + + res = Nova::lookup(crd_dst); + if (res != Nova::NOVA_OK || crd_dst.base() != local_name || crd_dst.type() != 3 || + crd_dst.order() != 0) { + failed ++; + PERR("%u - lookup call failed err=%x is_null=%u", __LINE__, res, crd_dst.is_null()); + ep.dissolve(&component); + return; + } + + /* + * Request some other capability and place it on very same selector + * as used before by copy_session_cap + */ + Genode::Thread * myself = Genode::Thread::myself(); + Genode::Native_capability pager_cap(myself->native_thread().ec_sel + 1); + request_event_portal(pager_cap, copy_session_cap.local_name(), 0, 0); + + /* check whether the requested cap before is valid and placed well */ + crd_ses = Nova::Obj_crd(copy_session_cap.local_name(), 0); + res = Nova::lookup(crd_ses); + if (res != Nova::NOVA_OK || crd_ses.base() != copy_session_cap.local_name() || + crd_ses.type() != 3 || crd_ses.order() != 0) { + failed ++; + PERR("%u - lookup call failed err=%x is_null=%u", __LINE__, res, crd_ses.is_null()); + ep.dissolve(&component); + return; + } + + /* revoke it */ + Nova::revoke(crd_ses, SELF, LOCAL_REVOKE, LOCAL_PD, NO_BLOCKING); + + /* the delegated cap to the client should still be there */ + res = Nova::lookup(crd_dst); + if (res != Nova::NOVA_OK || crd_dst.base() != local_name || crd_dst.type() != 3 || + crd_dst.order() != 0) { + failed ++; + PERR("%u - lookup call failed err=%x is_null=%u", __LINE__, res, crd_dst.is_null()); + ep.dissolve(&component); + return; + } + + /* kill the original session capability */ + ep.dissolve(&component); + /* manually: cap.free_rpc_cap(session_cap); */ + + /* the delegated cap to the client should be now invalid */ + res = Nova::lookup(crd_dst); + if (res != Nova::NOVA_OK || !crd_dst.is_null()) { + failed ++; + PERR("%u - lookup call failed err=%x is_null=%u", __LINE__, res, crd_dst.is_null()); + return; + } +} + void test_pat() { /* read out the tsc frequenzy once */ @@ -47,8 +275,7 @@ void test_pat() enum { STACK_SIZE = 4096 }; - static Cap_connection cap; - static Rpc_entrypoint ep(&cap, STACK_SIZE, "rpc_ep"); + static Rpc_entrypoint ep(&cap, STACK_SIZE, "rpc_ep_pat"); Test::Component component; Test::Capability session_cap = ep.manage(&component); @@ -138,8 +365,7 @@ void test_server_oom() enum { STACK_SIZE = 4096 }; - static Cap_connection cap; - static Rpc_entrypoint ep(&cap, STACK_SIZE, "rpc_ep"); + static Rpc_entrypoint ep(&cap, STACK_SIZE, "rpc_ep_oom"); Test::Component component; Test::Capability session_cap = ep.manage(&component); @@ -317,6 +543,12 @@ int main(int argc, char **argv) /* test PAT kernel feature */ test_pat(); + /* test special revoke */ + test_revoke(); + + /* test translate together with special revoke */ + test_translate(); + /** * Test to provoke out of memory during capability transfer of * server/client. diff --git a/repos/base-nova/src/test/platform/server.h b/repos/base-nova/src/test/platform/server.h index 74e8576705..dfd0c87a36 100644 --- a/repos/base-nova/src/test/platform/server.h +++ b/repos/base-nova/src/test/platform/server.h @@ -21,7 +21,15 @@ #include #include -namespace Test { struct Session; struct Client; struct Component; } +namespace Test { + struct Session; + struct Client; + struct Component; + + long cap_void_manual(Genode::Native_capability dst, + Genode::Native_capability arg1, + Genode::addr_t &local_name); +} /** * Test session interface definition @@ -30,25 +38,31 @@ struct Test::Session : Genode::Session { static const char *service_name() { return "TEST"; } - GENODE_RPC(Rpc_cap_void, bool, cap_void, - Genode::Native_capability); + GENODE_RPC(Rpc_cap_void, bool, cap_void, Genode::Native_capability, + Genode::addr_t &); GENODE_RPC(Rpc_void_cap, Genode::Native_capability, void_cap); + GENODE_RPC(Rpc_cap_cap, Genode::Native_capability, cap_cap, + Genode::addr_t); GENODE_RPC(Rpc_leak_utcb_address, Genode::addr_t, leak_utcb_address); - GENODE_RPC_INTERFACE(Rpc_cap_void, Rpc_void_cap, Rpc_leak_utcb_address); + GENODE_RPC_INTERFACE(Rpc_cap_void, Rpc_void_cap, Rpc_cap_cap, + Rpc_leak_utcb_address); }; struct Test::Client : Genode::Rpc_client { Client(Capability cap) : Rpc_client(cap) { } - bool cap_void(Genode::Native_capability cap) { - return call(cap); } + bool cap_void(Genode::Native_capability cap, Genode::addr_t &local_name) { + return call(cap, local_name); } Genode::Native_capability void_cap() { return call(); } + Genode::Native_capability cap_cap(Genode::addr_t cap) { + return call(cap); } + Genode::addr_t leak_utcb_address() { return call(); } }; @@ -56,9 +70,11 @@ struct Test::Client : Genode::Rpc_client struct Test::Component : Genode::Rpc_object { /* Test to transfer a object capability during send */ - bool cap_void(Genode::Native_capability); + bool cap_void(Genode::Native_capability, Genode::addr_t &); /* Test to transfer a object capability during reply */ Genode::Native_capability void_cap(); + /* Test to transfer a specific object capability during reply */ + Genode::Native_capability cap_cap(Genode::addr_t); /* Leak utcb address of entrypoint to manipulate utcb receive window */ Genode::addr_t leak_utcb_address(); }; @@ -68,7 +84,11 @@ namespace Test { typedef Genode::Capability Capability; } /** * Session implementation */ -bool Test::Component::cap_void(Genode::Native_capability got_cap) { +inline bool Test::Component::cap_void(Genode::Native_capability got_cap, + Genode::addr_t &local_name) +{ + local_name = got_cap.local_name(); + if (!got_cap.valid()) return false; @@ -79,7 +99,7 @@ bool Test::Component::cap_void(Genode::Native_capability got_cap) { return true; } -Genode::Native_capability Test::Component::void_cap() { +inline Genode::Native_capability Test::Component::void_cap() { Genode::Native_capability send_cap = cap(); /* XXX this code does does no longer work since the removal of 'solely_map' */ @@ -91,5 +111,8 @@ Genode::Native_capability Test::Component::void_cap() { return send_cap; } -Genode::addr_t Test::Component::leak_utcb_address() { +inline Genode::addr_t Test::Component::leak_utcb_address() { return reinterpret_cast(Genode::Thread::myself()->utcb()); } + +inline Genode::Native_capability Test::Component::cap_cap(Genode::addr_t cap) { + return Genode::Native_capability(cap); } diff --git a/repos/base-nova/src/test/platform/target.mk b/repos/base-nova/src/test/platform/target.mk index 327dd04d62..e4820e69db 100644 --- a/repos/base-nova/src/test/platform/target.mk +++ b/repos/base-nova/src/test/platform/target.mk @@ -1,3 +1,3 @@ TARGET = test-platform -SRC_CC = main.cc +SRC_CC = main.cc ipc.cc LIBS = base config