diff --git a/repos/base-nova/run/platform.run b/repos/base-nova/run/platform.run index 5891bf0056..4ca4335432 100644 --- a/repos/base-nova/run/platform.run +++ b/repos/base-nova/run/platform.run @@ -1,8 +1,13 @@ build "core init test/platform" +set check_pat 1 +if {[have_include power_on/qemu]} { + set check_pat 0 +} + create_boot_directory -install_config { +set config { @@ -18,11 +23,18 @@ install_config { - + } + +append config " + " + +append config { } +install_config $config + build_boot_image "core init test-platform" append qemu_args "-nographic -m 128" diff --git a/repos/base-nova/src/test/platform/main.cc b/repos/base-nova/src/test/platform/main.cc index 4057fb3478..735fa9643e 100644 --- a/repos/base-nova/src/test/platform/main.cc +++ b/repos/base-nova/src/test/platform/main.cc @@ -19,12 +19,116 @@ #include #include +#include +#include + +#include + #include "server.h" static unsigned failed = 0; +static unsigned check_pat = 1; + using namespace Genode; +void test_pat() +{ + /* read out the tsc frequenzy once */ + Genode::Attached_rom_dataspace _ds("hypervisor_info_page"); + Nova::Hip * const hip = _ds.local_addr(); + + enum { DS_ORDER = 12, PAGE_4K = 12 }; + + Ram_dataspace_capability ds = env()->ram_session()->alloc (1 << (DS_ORDER + PAGE_4K), WRITE_COMBINED); + addr_t map_addr = env()->rm_session()->attach(ds); + + 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); + + Genode::Rm_connection rm_free_area(0, 1 << (DS_ORDER + PAGE_4K)); + addr_t remap_addr = Genode::env()->rm_session()->attach(rm_free_area.dataspace()); + + /* trigger mapping of whole area */ + for (addr_t i = map_addr; i < map_addr + (1 << (DS_ORDER + PAGE_4K)); i += (1 << PAGE_4K)) + touch_read(reinterpret_cast(map_addr)); + + /* + * Manipulate entrypoint + */ + Nova::Rights all(true, true, true); + Genode::addr_t utcb_ep_addr_t = client.leak_utcb_address(); + Nova::Utcb *utcb_ep = reinterpret_cast(utcb_ep_addr_t); + /* overwrite receive window of entrypoint */ + utcb_ep->crd_rcv = Nova::Mem_crd(remap_addr >> PAGE_4K, DS_ORDER, all); + + /* + * Set-up current (client) thread to delegate write-combined memory + */ + Nova::Mem_crd snd_crd(map_addr >> PAGE_4K, DS_ORDER, all); + + Nova::Utcb *utcb = reinterpret_cast(Thread_base::myself()->utcb()); + enum { + HOTSPOT = 0, USER_PD = false, HOST_PGT = false, SOLELY_MAP = false, + NO_DMA = false, EVILLY_DONT_WRITE_COMBINE = false + }; + + Nova::Crd old = utcb->crd_rcv; + + utcb->set_msg_word(0); + bool ok = utcb->append_item(snd_crd, HOTSPOT, USER_PD, HOST_PGT, + SOLELY_MAP, NO_DMA, EVILLY_DONT_WRITE_COMBINE); + (void)ok; + + uint8_t res = Nova::call(session_cap.local_name()); + (void)res; + + utcb->crd_rcv = old; + + /* sanity check - touch re-mapped area */ + for (addr_t i = remap_addr; i < remap_addr + (1 << (DS_ORDER + PAGE_4K)); i += (1 << PAGE_4K)) + touch_read(reinterpret_cast(remap_addr)); + + /* + * measure time to write to the memory + */ + memset(reinterpret_cast(map_addr), 0, 1 << (DS_ORDER + PAGE_4K)); + Trace::Timestamp map_start = Trace::timestamp(); + memset(reinterpret_cast(map_addr), 0, 1 << (DS_ORDER + PAGE_4K)); + Trace::Timestamp map_end = Trace::timestamp(); + + memset(reinterpret_cast(remap_addr), 0, 1 << (DS_ORDER + PAGE_4K)); + Trace::Timestamp remap_start = Trace::timestamp(); + memset(reinterpret_cast(remap_addr), 0, 1 << (DS_ORDER + PAGE_4K)); + Trace::Timestamp remap_end = Trace::timestamp(); + + Trace::Timestamp map_run = map_end - map_start; + Trace::Timestamp remap_run = remap_end - remap_start; + + Trace::Timestamp diff_run = map_run > remap_run ? map_run - remap_run : remap_run - map_run; + + if (check_pat && diff_run * 100 / hip->tsc_freq) { + failed ++; + + PERR("map=%llx remap=%llx --> diff=%llx freq_tsc=%u %llu us", + map_run, remap_run, diff_run, hip->tsc_freq, + diff_run * 1000 / hip->tsc_freq); + } + + Nova::revoke(Nova::Mem_crd(remap_addr >> PAGE_4K, DS_ORDER, all)); + + /* + * note: server entrypoint died because of unexpected receive window + * state - that is expected + */ +} + void test_server_oom() { using namespace Genode; @@ -155,6 +259,10 @@ int main(int argc, char **argv) { printf("testing base-nova platform\n"); + try { + Genode::config()->xml_node().attribute("check_pat").value(&check_pat); + } catch (...) { } + Thread_base * myself = Thread_base::myself(); if (!myself) return -__LINE__; @@ -197,6 +305,9 @@ int main(int argc, char **argv) index = range->base() + range->elements(); }; + /* test PAT kernel feature */ + test_pat(); + /** * 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 00eead2238..d0b09dd066 100644 --- a/repos/base-nova/src/test/platform/server.h +++ b/repos/base-nova/src/test/platform/server.h @@ -34,8 +34,9 @@ struct Test::Session : Genode::Session Genode::Native_capability); GENODE_RPC(Rpc_void_cap, Genode::Native_capability, void_cap); + GENODE_RPC(Rpc_leak_utcb_address, Genode::addr_t, leak_utcb_address); - GENODE_RPC_INTERFACE(Rpc_cap_void, Rpc_void_cap); + GENODE_RPC_INTERFACE(Rpc_cap_void, Rpc_void_cap, Rpc_leak_utcb_address); }; struct Test::Client : Genode::Rpc_client @@ -47,6 +48,9 @@ struct Test::Client : Genode::Rpc_client Genode::Native_capability void_cap() { return call(); } + + Genode::addr_t leak_utcb_address() { + return call(); } }; struct Test::Component : Genode::Rpc_object @@ -55,6 +59,8 @@ struct Test::Component : Genode::Rpc_object bool cap_void(Genode::Native_capability); /* Test to transfer a object capability during reply */ Genode::Native_capability void_cap(); + /* Leak utcb address of entrypoint to manipulate utcb receive window */ + Genode::addr_t leak_utcb_address(); }; namespace Test { typedef Genode::Capability Capability; } @@ -79,3 +85,6 @@ Genode::Native_capability Test::Component::void_cap() { send_cap.solely_map(); return send_cap; } + +Genode::addr_t Test::Component::leak_utcb_address() { + return reinterpret_cast(Genode::Thread_base::myself()->utcb()); } diff --git a/repos/base-nova/src/test/platform/target.mk b/repos/base-nova/src/test/platform/target.mk index 0c0447039d..327dd04d62 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 -LIBS = base +LIBS = base config