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