diff --git a/base-linux/run/lx_rmap.inc b/base-linux/run/lx_rmap.inc
new file mode 100644
index 000000000..9d1eebc2c
--- /dev/null
+++ b/base-linux/run/lx_rmap.inc
@@ -0,0 +1,81 @@
+#
+# \brief Test for Linux-specific region map
+# \author Christian Helmuth
+# \date 2013-09-06
+#
+
+assert_spec linux
+
+if {[have_spec always_hybrid]} {
+ puts "\nTest does not support always_hybrid mode."
+ exit 0
+}
+
+#
+# Build
+#
+
+set build_components { core init }
+
+lappend_if [expr {$test_type eq "static"}] build_components test/lx_rmap/static
+lappend_if [expr {$test_type eq "dynamic"}] build_components test/lx_rmap/dynamic
+
+build $build_components
+
+create_boot_directory
+
+#
+# Config
+#
+
+set config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+append_if [expr {$test_type eq "static"}] config {
+ }
+append_if [expr {$test_type eq "dynamic"}] config {
+ }
+
+append config {
+
+
+}
+
+install_config $config
+
+#
+# Boot modules
+#
+
+set boot_modules { core init}
+
+lappend_if [expr {$test_type eq "static"}] boot_modules test-lx_rmap_static
+lappend_if [expr {$test_type eq "dynamic"}] boot_modules test-lx_rmap_dynamic
+lappend_if [expr {$test_type eq "dynamic"}] boot_modules ld.lib.so
+lappend_if [expr {$test_type eq "dynamic"}] boot_modules libc.lib.so
+lappend_if [expr {$test_type eq "dynamic"}] boot_modules libc_log.lib.so
+lappend_if [expr {$test_type eq "dynamic"}] boot_modules libm.lib.so
+
+build_boot_image $boot_modules
+
+#
+# Execute test
+#
+
+run_genode_until forever
+
+# vi: set ft=tcl :
diff --git a/base-linux/run/lx_rmap_dynamic.run b/base-linux/run/lx_rmap_dynamic.run
new file mode 100644
index 000000000..d2f0e7eeb
--- /dev/null
+++ b/base-linux/run/lx_rmap_dynamic.run
@@ -0,0 +1,11 @@
+#
+# \brief Test for Linux-specific region map (dynamic binary)
+# \author Christian Helmuth
+# \date 2013-09-06
+#
+
+set test_type "dynamic"
+
+source ${genode_dir}/base-linux/run/lx_rmap.inc
+
+# vi: set ft=tcl :
diff --git a/base-linux/run/lx_rmap_static.run b/base-linux/run/lx_rmap_static.run
new file mode 100644
index 000000000..6aaa40462
--- /dev/null
+++ b/base-linux/run/lx_rmap_static.run
@@ -0,0 +1,11 @@
+#
+# \brief Test for Linux-specific region map (static binary)
+# \author Christian Helmuth
+# \date 2013-09-06
+#
+
+set test_type "static"
+
+source ${genode_dir}/base-linux/run/lx_rmap.inc
+
+# vi: set ft=tcl :
diff --git a/base-linux/src/base/env/platform_env.h b/base-linux/src/base/env/platform_env.h
index cbda866c1..bc58ade66 100644
--- a/base-linux/src/base/env/platform_env.h
+++ b/base-linux/src/base/env/platform_env.h
@@ -196,6 +196,13 @@ namespace Genode {
void _add_to_rmap(Region const &);
+ /**
+ * Reserve VM region for sub-rm dataspace
+ */
+ addr_t _reserve_local(bool use_local_addr,
+ addr_t local_addr,
+ Genode::size_t size);
+
/**
* Map dataspace into local address space
*/
@@ -204,7 +211,8 @@ namespace Genode {
addr_t offset,
bool use_local_addr,
addr_t local_addr,
- bool executable);
+ bool executable,
+ bool overmap = false);
/**
* Determine size of dataspace
diff --git a/base-linux/src/base/env/rm_session_mmap.cc b/base-linux/src/base/env/rm_session_mmap.cc
index 59ecdad1d..c521c1362 100644
--- a/base-linux/src/base/env/rm_session_mmap.cc
+++ b/base-linux/src/base/env/rm_session_mmap.cc
@@ -2,6 +2,27 @@
* \brief Implementation of Linux-specific local region manager
* \author Norman Feske
* \date 2008-10-22
+ *
+ * Under Linux, region management happens at the mercy of the Linux kernel. So,
+ * all we can do in user land is 1) keep track of regions and (managed)
+ * dataspaces and 2) get the kernel to manage VM regions as we intent.
+ *
+ * The kernel sets up mappings for the binary on execve(), which are text and
+ * data segments, the context area and special regions (stack, vdso, vsyscall).
+ * Later mappings are done by the Genode program itself, which knows nothing
+ * about these initial mappings. Therefore, most mmap() operations are _soft_
+ * to detect region conflicts with existing mappings or let the kernel find
+ * some empty VM area (as core does on other platforms). The only _hard_
+ * overmaps happen on attachment and population of managed dataspaces. Mapped,
+ * but not populated dataspaces are "holes" in the Linux VM space represented
+ * by PROT_NONE mappings (see _reserve_local()).
+ *
+ * The context area is a managed dataspace as on other platforms, which is
+ * created and attached during program launch. The managed dataspace replaces
+ * the inital reserved area, which is therefore flushed beforehand. Hybrid
+ * programs have no context area.
+ *
+ * Note, we do not support nesting of managed dataspaces.
*/
/*
@@ -15,6 +36,7 @@
#include
#include
#include
+#include
/* local includes */
#include
@@ -31,18 +53,66 @@ static bool is_sub_rm_session(Dataspace_capability ds)
}
+addr_t Platform_env_base::Rm_session_mmap::_reserve_local(bool use_local_addr,
+ addr_t local_addr,
+ Genode::size_t size)
+{
+ /* special handling for context area */
+ if (use_local_addr
+ && local_addr == Native_config::context_area_virtual_base()
+ && size == Native_config::context_area_virtual_size()) {
+
+ /*
+ * On the first request to reserve the context area, we flush the
+ * initial mapping preserved in linker script and apply the actual
+ * reservation. Subsequent requests are just ignored.
+ */
+
+ static struct Context
+ {
+ Context()
+ {
+ flush_context_area();
+ reserve_context_area();
+ }
+ } inst;
+
+ return local_addr;
+ }
+
+ int const flags = MAP_ANONYMOUS | MAP_PRIVATE;
+ int const prot = PROT_NONE;
+ void * const addr_in = use_local_addr ? (void *)local_addr : 0;
+ void * const addr_out = lx_mmap(addr_in, size, prot, flags, -1, 0);
+
+ /* reserve at local address failed - unmap incorrect mapping */
+ if (use_local_addr && addr_in != addr_out)
+ lx_munmap((void *)addr_out, size);
+
+ if ((use_local_addr && addr_in != addr_out)
+ || (((long)addr_out < 0) && ((long)addr_out > -4095))) {
+ PERR("_reserve_local: lx_mmap failed (addr_in=%p,addr_out=%p/%ld)",
+ addr_in, addr_out, (long)addr_out);
+ throw Rm_session::Region_conflict();
+ }
+
+ return (addr_t) addr_out;
+}
+
+
void *
Platform_env_base::Rm_session_mmap::_map_local(Dataspace_capability ds,
Genode::size_t size,
addr_t offset,
bool use_local_addr,
addr_t local_addr,
- bool executable)
+ bool executable,
+ bool overmap)
{
int const fd = _dataspace_fd(ds);
bool const writable = _dataspace_writable(ds);
- int const flags = MAP_SHARED | (use_local_addr ? MAP_FIXED : 0);
+ int const flags = MAP_SHARED | (overmap ? MAP_FIXED : 0);
int const prot = PROT_READ
| (writable ? PROT_WRITE : 0)
| (executable ? PROT_EXEC : 0);
@@ -57,8 +127,14 @@ Platform_env_base::Rm_session_mmap::_map_local(Dataspace_capability ds,
*/
lx_close(fd);
- if (((long)addr_out < 0) && ((long)addr_out > -4095)) {
- PERR("_map_local: return value of mmap is %ld", (long)addr_out);
+ /* attach at local address failed - unmap incorrect mapping */
+ if (use_local_addr && addr_in != addr_out)
+ lx_munmap((void *)addr_out, size);
+
+ if ((use_local_addr && addr_in != addr_out)
+ || (((long)addr_out < 0) && ((long)addr_out > -4095))) {
+ PERR("_map_local: lx_mmap failed (addr_in=%p,addr_out=%p/%ld) overmap=%d",
+ addr_in, addr_out, (long)addr_out, overmap);
throw Rm_session::Region_conflict();
}
@@ -142,11 +218,12 @@ Platform_env::Rm_session_mmap::attach(Dataspace_capability ds,
* Case 3.1
*
* This RM session is a sub RM session. If the sub RM session is
- * attached (_base > 0), add its attachement offset to the local base
- * and map it.
+ * attached (_base > 0), add its attachment offset to the local base
+ * and map it. We have to enforce the mapping via the 'overmap'
+ * argument as the region was reserved by a PROT_NONE mapping.
*/
if (_is_attached())
- _map_local(ds, region_size, offset, true, _base + (addr_t)local_addr, executable);
+ _map_local(ds, region_size, offset, true, _base + (addr_t)local_addr, executable, true);
return (void *)local_addr;
@@ -171,20 +248,19 @@ Platform_env::Rm_session_mmap::attach(Dataspace_capability ds,
throw Out_of_metadata();
}
- _add_to_rmap(Region(local_addr, offset, ds, region_size));
-
/*
- * Allocate local address range that can hold the entire sub RM
+ * Reserve local address range that can hold the entire sub RM
* session.
*/
- rm->_base = lx_vm_reserve(use_local_addr ? (addr_t)local_addr : 0,
- region_size);
+ rm->_base = _reserve_local(use_local_addr, local_addr, region_size);
+
+ _add_to_rmap(Region(rm->_base, offset, ds, region_size));
/*
* Cases 2.2, 3.2
*
* The sub rm session was not attached until now but it may have
- * been populated with dataspaces. Go through all regions an map
+ * been populated with dataspaces. Go through all regions and map
* each of them.
*/
for (int i = 0; i < Region_map::MAX_REGIONS; i++) {
@@ -192,9 +268,13 @@ Platform_env::Rm_session_mmap::attach(Dataspace_capability ds,
if (!region.used())
continue;
+ /*
+ * We have to enforce the mapping via the 'overmap' argument as
+ * the region was reserved by a PROT_NONE mapping.
+ */
_map_local(region.dataspace(), region.size(), region.offset(),
true, rm->_base + region.start() + region.offset(),
- executable);
+ executable, true);
}
return rm->_base;
@@ -205,6 +285,7 @@ Platform_env::Rm_session_mmap::attach(Dataspace_capability ds,
* Case 1
*
* Boring, a plain dataspace is attached to a root RM session.
+ * Note, we do not overmap.
*/
void *addr = _map_local(ds, region_size, offset, use_local_addr,
local_addr, executable);
@@ -252,8 +333,10 @@ void Platform_env::Rm_session_mmap::detach(Rm_session::Local_addr local_addr)
* If we are not attached, no local address-space manipulation is
* needed.
*/
- if (_is_attached())
- lx_vm_reserve((addr_t)local_addr + _base, region.size());
+ if (_is_attached()) {
+ lx_munmap((void *)((addr_t)local_addr + _base), region.size());
+ _reserve_local(true, (addr_t)local_addr + _base, region.size());
+ }
} else {
@@ -277,5 +360,4 @@ void Platform_env::Rm_session_mmap::detach(Rm_session::Local_addr local_addr)
if (rm)
rm->_base = 0;
}
-
}
diff --git a/base-linux/src/core/context_area.cc b/base-linux/src/core/context_area.cc
index c5b6859b0..43ff8907a 100644
--- a/base-linux/src/core/context_area.cc
+++ b/base-linux/src/core/context_area.cc
@@ -17,9 +17,7 @@
#include
#include
-/* Linux includes */
-#include
-#include
+#include
/**
@@ -34,6 +32,12 @@ class Context_area_rm_session : public Genode::Rm_session
{
public:
+ Context_area_rm_session()
+ {
+ flush_context_area();
+ reserve_context_area();
+ }
+
/**
* Attach backing store to thread-context area
*/
diff --git a/base-linux/src/platform/context_area.h b/base-linux/src/platform/context_area.h
new file mode 100644
index 000000000..489218c19
--- /dev/null
+++ b/base-linux/src/platform/context_area.h
@@ -0,0 +1,64 @@
+/*
+ * \brief Linux-specific utilities for context area
+ * \author Christian Helmuth
+ * \date 2013-09-26
+ */
+
+/*
+ * Copyright (C) 2010-2013 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.
+ */
+
+#ifndef _PLATFORM__CONTEXT_AREA_H_
+#define _PLATFORM__CONTEXT_AREA_H_
+
+/* Genode includes */
+#include
+#include
+
+#include
+
+/* Linux includes */
+#include
+
+
+static inline void flush_context_area()
+{
+ using namespace Genode;
+
+ void * const base = (void *) Native_config::context_area_virtual_base();
+ size_t const size = Native_config::context_area_virtual_size();
+
+ int ret;
+ if ((ret = lx_munmap(base, size)) < 0) {
+ PERR("%s: failed ret=%d", __func__, ret);
+ throw Rm_session::Region_conflict();
+ }
+}
+
+
+static inline Genode::addr_t reserve_context_area()
+{
+ using namespace Genode;
+
+ int const flags = MAP_ANONYMOUS | MAP_PRIVATE;
+ int const prot = PROT_NONE;
+ size_t const size = Native_config::context_area_virtual_size();
+ void * const addr_in = (void *)Native_config::context_area_virtual_base();
+ void * const addr_out = lx_mmap(addr_in, size, prot, flags, -1, 0);
+
+ /* reserve at local address failed - unmap incorrect mapping */
+ if (addr_in != addr_out) {
+ lx_munmap((void *)addr_out, size);
+
+ PERR("%s: failed addr_in=%p addr_out=%p ret=%ld)", __func__,
+ addr_in, addr_out, (long)addr_out);
+ throw Rm_session::Region_conflict();
+ }
+
+ return (addr_t) addr_out;
+}
+
+#endif /* _PLATFORM__CONTEXT_AREA_H_ */
diff --git a/base-linux/src/platform/linux_syscalls.h b/base-linux/src/platform/linux_syscalls.h
index e75ddbd75..6e0d47d9d 100644
--- a/base-linux/src/platform/linux_syscalls.h
+++ b/base-linux/src/platform/linux_syscalls.h
@@ -203,36 +203,6 @@ inline int lx_munmap(void *addr, size_t length)
}
-/**
- * Exclude local virtual memory area from being used by mmap
- *
- * \param base base address of area to reserve
- * \param size number of bytes to reserve
- *
- * \return start of allocated reserved area, or ~0 on failure
- */
-inline Genode::addr_t lx_vm_reserve(Genode::addr_t base, Genode::size_t size)
-{
- /* we cannot include sys/mman.h from here */
- enum {
- LX_MAP_PRIVATE = 0x02,
- LX_MAP_FIXED = 0x10,
- LX_MAP_ANONYMOUS = 0x20,
- LX_PROT_NONE = 0x0
- };
-
- int const flags = LX_MAP_ANONYMOUS | LX_MAP_PRIVATE
- | (base ? LX_MAP_FIXED : 0);
-
- void * const res = lx_mmap((void *)base, size, LX_PROT_NONE, flags, -1, 0);
-
- if (base)
- return ((Genode::addr_t)res == base) ? base : ~0;
- else
- return (Genode::addr_t)res;
-}
-
-
/***********************************************************************
** Functions used by thread lib and core's cancel-blocking mechanism **
***********************************************************************/
diff --git a/base-linux/src/platform/main_bootstrap.cc b/base-linux/src/platform/main_bootstrap.cc
index e6f4eb440..67cfecf41 100644
--- a/base-linux/src/platform/main_bootstrap.cc
+++ b/base-linux/src/platform/main_bootstrap.cc
@@ -56,16 +56,6 @@ void Genode::platform_main_bootstrap()
* __initial_sp[3] = environ
*/
lx_environ = (char**)&__initial_sp[3];
-
- /*
- * Free context area preserved in linker script
- */
- addr_t base = Native_config::context_area_virtual_base();
- Genode::size_t size = Native_config::context_area_virtual_size();
- int ret;
- if ((ret = lx_munmap((void *)base, size)) < 0)
- PERR("flushing of context area [%lx,%lx) failed (ret=%d)",
- (unsigned long) base, (unsigned long) base + size, ret);
}
} bootstrap;
}
diff --git a/base-linux/src/test/lx_rmap/dynamic/target.mk b/base-linux/src/test/lx_rmap/dynamic/target.mk
new file mode 100644
index 000000000..8ad37993e
--- /dev/null
+++ b/base-linux/src/test/lx_rmap/dynamic/target.mk
@@ -0,0 +1,5 @@
+TARGET = test-lx_rmap_dynamic
+SRC_CC = main.cc
+LIBS = base libc
+
+vpath main.cc $(PRG_DIR)/..
diff --git a/base-linux/src/test/lx_rmap/main.cc b/base-linux/src/test/lx_rmap/main.cc
new file mode 100644
index 000000000..55f215218
--- /dev/null
+++ b/base-linux/src/test/lx_rmap/main.cc
@@ -0,0 +1,89 @@
+/*
+ * \brief Linux region-map test
+ * \author Christian Helmuth
+ * \date 2013-09-06
+ */
+
+/*
+ * Copyright (C) 2006-2013 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.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+extern "C" void wait_for_continue();
+
+
+int main()
+{
+ using namespace Genode;
+
+ /* activate for early printf in Rm_session_mmap::attach() etc. */
+ if (0) Thread_base::trace("FOO");
+
+ /* induce initial heap expansion to remove RM noise */
+ if (1) {
+ void *addr(env()->heap()->alloc(0x100000));
+ env()->heap()->free(addr, 0);
+ }
+
+ addr_t beg((addr_t)&_prog_img_beg);
+ addr_t end(align_addr((addr_t)&_prog_img_end, 12));
+
+ size_t size(end - beg);
+
+ PLOG("program-image region [%016lx,%016lx) size=%zx", beg, end, size);
+
+ /* RAM dataspace attachment overlapping binary */
+ try {
+ Ram_dataspace_capability ds(env()->ram_session()->alloc(size));
+
+ PLOG("before RAM dataspace attach");
+ env()->rm_session()->attach_at(ds, beg);
+ PERR("after RAM dataspace attach -- ERROR");
+ } catch (Rm_session::Region_conflict) {
+ PLOG("OK caught Region_conflict exception");
+ }
+
+ /* empty managed dataspace overlapping binary */
+ try {
+ Rm_connection rm(0, size);
+ Dataspace_capability ds(rm.dataspace());
+
+ PLOG("before sub-RM dataspace attach");
+ env()->rm_session()->attach_at(ds, beg);
+ PERR("after sub-RM dataspace attach -- ERROR");
+ } catch (Rm_session::Region_conflict) {
+ PLOG("OK caught Region_conflict exception");
+ }
+
+ /* sparsely populated managed dataspace in free VM area */
+ try {
+ Rm_connection rm(0, 0x100000);
+
+ rm.attach_at(env()->ram_session()->alloc(0x1000), 0x1000);
+
+ Dataspace_capability ds(rm.dataspace());
+
+ PLOG("before populated sub-RM dataspace attach");
+ char *addr = (char *)env()->rm_session()->attach(ds) + 0x1000;
+ PLOG("after populated sub-RM dataspace attach / before touch");
+ char const val = *addr;
+ *addr = 0x55;
+ PLOG("after touch (%x/%x)", val, *addr);
+ } catch (Rm_session::Region_conflict) {
+ PLOG("OK caught Region_conflict exception -- ERROR");
+ }
+
+ sleep_forever();
+}
diff --git a/base-linux/src/test/lx_rmap/static/target.mk b/base-linux/src/test/lx_rmap/static/target.mk
new file mode 100644
index 000000000..491e93ee9
--- /dev/null
+++ b/base-linux/src/test/lx_rmap/static/target.mk
@@ -0,0 +1,5 @@
+TARGET = test-lx_rmap_static
+SRC_CC = main.cc
+LIBS = base
+
+vpath main.cc $(PRG_DIR)/..