diff --git a/repos/base-nova/run/platform.run b/repos/base-nova/run/platform.run index 638432ea7..5891bf005 100644 --- a/repos/base-nova/run/platform.run +++ b/repos/base-nova/run/platform.run @@ -18,7 +18,7 @@ install_config { - + } @@ -27,6 +27,6 @@ build_boot_image "core init test-platform" append qemu_args "-nographic -m 128" -run_genode_until {Test finished} 15 +run_genode_until {Test finished} 150 puts "\nTest succeeded" diff --git a/repos/base-nova/src/test/platform/main.cc b/repos/base-nova/src/test/platform/main.cc index 779032b0f..4057fb347 100644 --- a/repos/base-nova/src/test/platform/main.cc +++ b/repos/base-nova/src/test/platform/main.cc @@ -16,10 +16,121 @@ #include #include -using namespace Genode; +#include +#include + +#include "server.h" static unsigned failed = 0; +using namespace Genode; + +void test_server_oom() +{ + using namespace Genode; + + enum { STACK_SIZE = 4096 }; + + static Cap_connection cap; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "rpc_ep"); + + Test::Component component; + Test::Capability session_cap = ep.manage(&component); + Test::Client client(session_cap); + + /* case that during reply we get oom */ + for (unsigned i = 0; i < 20000; i++) { + Genode::Native_capability got_cap = client.void_cap(); + + if (!got_cap.valid()) { + PERR("%u cap id %lx invalid", i, got_cap.local_name()); + failed ++; + break; + } + + /* be evil and keep this cap by manually incrementing the ref count */ + Cap_index idx(cap_map()->find(got_cap.local_name())); + idx.inc(); + + if (i % 5000 == 4999) + PINF("received %u. cap", i); + } + + /* case that during send we get oom */ + for (unsigned i = 0; i < 20000; i++) { + /* be evil and switch translation off - server ever uses a new selector */ + Genode::Native_capability send_cap = session_cap; + send_cap.solely_map(); + + if (!client.cap_void(send_cap)) { + PERR("sending %4u. cap failed", i); + failed ++; + break; + } + + if (i % 5000 == 4999) + PINF("sent %u. cap", i); + } + + ep.dissolve(&component); +} + +class Greedy : public Thread<4096> { + + public: + + Greedy() + : + Thread<0x1000>("greedy") + { } + + void entry() { + PINF("starting"); + + enum { SUB_RM_SIZE = 2UL * 1024 * 1024 * 1024 }; + + Genode::Rm_connection sub_rm(0, SUB_RM_SIZE); + addr_t const mem = env()->rm_session()->attach(sub_rm.dataspace()); + + Nova::Utcb * nova_utcb = reinterpret_cast(utcb()); + Nova::Rights const mapping_rwx(true, true, true); + + addr_t const page_fault_portal = tid().exc_pt_sel + 14; + + PERR("cause mappings in range [0x%lx, 0x%lx) %p", mem, + mem + SUB_RM_SIZE - 1, &mem); + + for (addr_t map_to = mem; map_to < mem + SUB_RM_SIZE; map_to += 4096) { + + /* setup faked page fault information */ + nova_utcb->items = ((addr_t)&nova_utcb->qual[2] - (addr_t)nova_utcb->msg) / sizeof(addr_t); + nova_utcb->ip = 0xbadaffe; + nova_utcb->qual[1] = (addr_t)&mem; + nova_utcb->crd_rcv = Nova::Mem_crd(map_to >> 12, 0, mapping_rwx); + + /* trigger faked page fault */ + Genode::uint8_t res = Nova::call(page_fault_portal); + if (res != Nova::NOVA_OK) { + PINF("call result=%u", res); + failed++; + return; + } + + /* check that we really got the mapping */ + touch_read(reinterpret_cast(map_to)); + + /* print status information in interval of 32M */ + if (!(map_to & (32UL * 1024 * 1024 - 1))) { + printf("0x%lx\n", map_to); + /* trigger some work to see quota in kernel decreasing */ +// Nova::Rights rwx(true, true, true); +// Nova::revoke(Nova::Mem_crd((map_to - 32 * 1024 * 1024) >> 12, 12, rwx)); + } + } + printf("still alive - done\n"); + } +}; + void check(uint8_t res, const char *format, ...) { static char buf[128]; @@ -73,6 +184,33 @@ int main(int argc, char **argv) check(res, "pt_ctrl %2u", i); } + /* upgrade available capability indices for this process */ + unsigned index = 512 * 1024; + static char local[128][sizeof(Cap_range)]; + + for (unsigned i = 0; i < sizeof(local) / sizeof (local[0]); i++) { + Cap_range * range = reinterpret_cast(local[i]); + *range = Cap_range(index); + + cap_map()->insert(range); + + index = range->base() + range->elements(); + }; + + /** + * Test to provoke out of memory during capability transfer of + * server/client. + * + * Set in hypervisor.ld the memory to a low value of about 1M to let + * trigger the test. + */ + test_server_oom(); + + /* Test to provoke out of memory in kernel during interaction with core */ + static Greedy core_pagefault_oom; + core_pagefault_oom.start(); + core_pagefault_oom.join(); + if (!failed) printf("Test finished\n"); diff --git a/repos/base-nova/src/test/platform/server.h b/repos/base-nova/src/test/platform/server.h new file mode 100644 index 000000000..00eead223 --- /dev/null +++ b/repos/base-nova/src/test/platform/server.h @@ -0,0 +1,81 @@ +/* + * \brief Dummy server interface + * \author Alexander Boettcher + */ + +/* + * Copyright (C) 2013-2015 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. + */ + +#pragma once + +/* Genode includes */ +#include +#include +#include +#include + +#include +#include + +namespace Test { struct Session; struct Client; struct Component; } + +/** + * Test session interface definition + */ +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_void_cap, Genode::Native_capability, + void_cap); + + GENODE_RPC_INTERFACE(Rpc_cap_void, Rpc_void_cap); +}; + +struct Test::Client : Genode::Rpc_client +{ + Client(Capability cap) : Rpc_client(cap) { } + + bool cap_void(Genode::Native_capability cap) { + return call(cap); } + + Genode::Native_capability void_cap() { + return call(); } +}; + +struct Test::Component : Genode::Rpc_object +{ + /* Test to transfer a object capability during send */ + bool cap_void(Genode::Native_capability); + /* Test to transfer a object capability during reply */ + Genode::Native_capability void_cap(); +}; + +namespace Test { typedef Genode::Capability Capability; } + +/** + * Session implementation + */ +bool Test::Component::cap_void(Genode::Native_capability got_cap) { + if (!got_cap.valid()) + return false; + + /* be evil and keep this cap by manually incrementing the ref count */ + Genode::Cap_index idx(Genode::cap_map()->find(got_cap.local_name())); + idx.inc(); + + return true; +} + +Genode::Native_capability Test::Component::void_cap() { + Genode::Native_capability send_cap = cap(); + /* be evil and switch translation off - client ever uses a new selector */ + send_cap.solely_map(); + return send_cap; +}