Move repositories to 'repos/' subdirectory

This patch changes the top-level directory layout as a preparatory
step for improving the tools for managing 3rd-party source codes.
The rationale is described in the issue referenced below.

Issue #1082
This commit is contained in:
Norman Feske
2014-05-07 11:48:19 +02:00
parent 1f9890d635
commit ca971bbfd8
3943 changed files with 454 additions and 430 deletions

View File

@ -0,0 +1,31 @@
/*
* \brief Printf backend using Linux stdout
* \author Norman Feske
* \date 2006-04-08
*
* This console back-end should only be used by core.
*/
/*
* 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 <base/console.h>
/* Linux syscall bindings */
#include <linux_syscalls.h>
namespace Genode {
class Core_console : public Console
{
protected:
void _out_char(char c) { lx_write(1, &c, sizeof(c)); }
};
}

60
repos/base-linux/src/base/env/debug.cc vendored Normal file
View File

@ -0,0 +1,60 @@
/*
* \brief Linux-specific debug utilities
* \author Norman Feske
* \date 2009-05-16
*/
/*
* Copyright (C) 2009-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.
*/
/*
* With the enabled 'DEBUG' flag, status information can be printed directly
* via a Linux system call by using the 'raw_write_str' function. This output
* bypasses the Genode 'LOG' mechanism, which is useful for debugging low-level
* code such as a libC back-end.
*/
#define DEBUG 1
#if DEBUG
#include <linux_syscalls.h>
#endif /* DEBUG */
/**
* Write function targeting directly the Linux system call layer and bypassing
* any Genode code.
*/
extern "C" int raw_write_str(const char *str)
{
#if DEBUG
unsigned len = 0;
for (; str[len] != 0; len++);
lx_syscall(SYS_write, (int)1, str, len);
return len;
#endif /* DEBUG */
}
/**
* Debug function waiting until the user presses return
*
* This function is there to delay the execution of a back-end function such
* that we have time to attack the GNU debugger to the running process. Once
* attached, we can continue execution and use 'gdb' for debugging. In the
* normal mode of operation, this function is never used.
*/
extern "C" void wait_for_continue(void)
{
#if DEBUG
char buf[16];
lx_syscall(SYS_read, (int)0, buf, sizeof(buf));
#endif /* DEBUG */
}
extern "C" int get_pid() { return lx_getpid(); }

View File

@ -0,0 +1,212 @@
/*
* \brief Support for the Linux-specific environment
* \author Norman Feske
* \date 2008-12-12
*/
/*
* Copyright (C) 2008-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 <util/arg_string.h>
#include <base/thread.h>
#include <linux_dataspace/client.h>
#include <linux_syscalls.h>
/* local includes */
#include <platform_env.h>
using namespace Genode;
/****************************************************
** Support for Platform_env_base::Rm_session_mmap **
****************************************************/
Genode::size_t
Platform_env_base::Rm_session_mmap::_dataspace_size(Dataspace_capability ds)
{
if (ds.valid())
return Dataspace_client(ds).size();
return Dataspace_capability::deref(ds)->size();
}
int Platform_env_base::Rm_session_mmap::_dataspace_fd(Dataspace_capability ds)
{
return Linux_dataspace_client(ds).fd().dst().socket;
}
bool
Platform_env_base::Rm_session_mmap::_dataspace_writable(Dataspace_capability ds)
{
return Dataspace_client(ds).writable();
}
/********************************
** Platform_env::Local_parent **
********************************/
static inline size_t get_page_size_log2() { return 12; }
Session_capability
Platform_env::Local_parent::session(Service_name const &service_name,
Session_args const &args,
Affinity const &affinity)
{
if (strcmp(service_name.string(),
Rm_session::service_name()) == 0)
{
size_t size =
Arg_string::find_arg(args.string(),"size")
.ulong_value(~0);
if (size == 0)
return Expanding_parent_client::session(service_name, args, affinity);
if (size != ~0UL)
size = align_addr(size, get_page_size_log2());
Rm_session_mmap *rm = new (env()->heap())
Rm_session_mmap(true, size);
return Session_capability::local_cap(rm);
}
return Expanding_parent_client::session(service_name, args, affinity);
}
void Platform_env::Local_parent::close(Session_capability session)
{
/*
* Handle non-local capabilities
*/
if (session.valid()) {
Parent_client::close(session);
return;
}
/*
* Detect capability to local RM session
*/
Capability<Rm_session_mmap> rm = static_cap_cast<Rm_session_mmap>(session);
destroy(env()->heap(), Capability<Rm_session_mmap>::deref(rm));
}
Platform_env::Local_parent::Local_parent(Parent_capability parent_cap,
Emergency_ram_reserve &reserve)
: Expanding_parent_client(parent_cap, reserve)
{ }
/******************
** Platform_env **
******************/
/**
* List of Unix environment variables, initialized by the startup code
*/
extern char **lx_environ;
/**
* Read environment variable as long value
*/
static unsigned long get_env_ulong(const char *key)
{
for (char **curr = lx_environ; curr && *curr; curr++) {
Arg arg = Arg_string::find_arg(*curr, key);
if (arg.valid())
return arg.ulong_value(0);
}
return 0;
}
static Parent_capability obtain_parent_cap()
{
long local_name = get_env_ulong("parent_local_name");
/* produce typed capability manually */
typedef Native_capability::Dst Dst;
Dst const dst(PARENT_SOCKET_HANDLE);
return reinterpret_cap_cast<Parent>(Native_capability(dst, local_name));
}
Platform_env::Local_parent &Platform_env::_parent()
{
static Local_parent local_parent(obtain_parent_cap(), *this);
return local_parent;
}
void Platform_env::reinit(Native_capability::Dst, long) { }
void Platform_env::reinit_main_thread(Rm_session_capability &) { }
Platform_env::Platform_env()
:
Platform_env_base(static_cap_cast<Ram_session>(_parent().session("Env::ram_session", "")),
static_cap_cast<Cpu_session>(_parent().session("Env::cpu_session", "")),
static_cap_cast<Pd_session> (_parent().session("Env::pd_session", ""))),
_heap(Platform_env_base::ram_session(), Platform_env_base::rm_session()),
_emergency_ram_ds(ram_session()->alloc(_emergency_ram_size()))
{
/* register TID and PID of the main thread at core */
cpu_session()->thread_id(parent()->main_thread_cap(),
lx_getpid(), lx_gettid());
}
/*****************************
** Support for IPC library **
*****************************/
namespace Genode {
Native_connection_state server_socket_pair()
{
/*
* Obtain access to Linux-specific extension of the CPU session
* interface. We can cast to the specific type because the Linux
* version of 'Platform_env' is hosting a 'Linux_cpu_client' object.
*/
Linux_cpu_session *cpu = dynamic_cast<Linux_cpu_session *>(env()->cpu_session());
if (!cpu) {
PERR("could not obtain Linux extension to CPU session interface");
struct Could_not_access_linux_cpu_session { };
throw Could_not_access_linux_cpu_session();
}
Native_connection_state ncs;
Thread_base *thread = Thread_base::myself();
if (thread) {
ncs.server_sd = cpu->server_sd(thread->cap()).dst().socket;
ncs.client_sd = cpu->client_sd(thread->cap()).dst().socket;
}
return ncs;
}
void destroy_server_socket_pair(Native_connection_state const &ncs)
{
/* close local file descriptor if it is valid */
if (ncs.server_sd != -1) lx_close(ncs.server_sd);
if (ncs.client_sd != -1) lx_close(ncs.client_sd);
}
}

View File

@ -0,0 +1,462 @@
/*
* \brief Linux-specific environment
* \author Norman Feske
* \author Christian Helmuth
* \date 2006-07-28
*/
/*
* 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.
*/
#ifndef _PLATFORM_ENV_H_
#define _PLATFORM_ENV_H_
/* Linux includes */
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
/* Genode includes */
#include <util/misc_math.h>
#include <base/heap.h>
#include <linux_cpu_session/client.h>
/* local includes (from 'base/src/base/env/') */
#include <platform_env_common.h>
namespace Genode {
struct Expanding_cpu_session_client;
class Platform_env;
}
struct Genode::Expanding_cpu_session_client
:
Upgradeable_client<Genode::Linux_cpu_session_client>
{
Expanding_cpu_session_client(Genode::Capability<Linux_cpu_session> cap)
: Upgradeable_client<Genode::Linux_cpu_session_client>(cap) { }
Thread_capability create_thread(Name const &name, addr_t utcb)
{
return retry<Cpu_session::Out_of_metadata>(
[&] () { return Linux_cpu_session_client::create_thread(name, utcb); },
[&] () { upgrade_ram(8*1024); });
}
};
namespace Genode {
/**
* Common base class of the 'Platform_env' implementations for core and
* non-core processes.
*/
class Platform_env_base : public Env
{
private:
/**************************
** Local region manager **
**************************/
class Region
{
private:
addr_t _start;
off_t _offset;
Dataspace_capability _ds;
size_t _size;
/**
* Return offset of first byte after the region
*/
addr_t _end() const { return _start + _size; }
public:
Region() : _start(0), _offset(0), _size(0) { }
Region(addr_t start, off_t offset, Dataspace_capability ds, size_t size)
: _start(start), _offset(offset), _ds(ds), _size(size) { }
bool used() const { return _size > 0; }
addr_t start() const { return _start; }
off_t offset() const { return _offset; }
size_t size() const { return _size; }
Dataspace_capability dataspace() const { return _ds; }
bool intersects(Region const &r) const
{
return (r.start() < _end()) && (_start < r._end());
}
};
/**
* Meta data about dataspaces attached to an RM session
*/
class Region_map
{
public:
enum { MAX_REGIONS = 4096 };
private:
Region _map[MAX_REGIONS];
bool _id_valid(int id) const {
return (id >= 0 && id < MAX_REGIONS); }
public:
/**
* Add region to region map
*
* \return region ID, or
* -1 if out of metadata, or
* -2 if region conflicts existing region
*/
int add_region(Region const &region)
{
/*
* Check for region conflicts
*/
for (int i = 0; i < MAX_REGIONS; i++) {
if (_map[i].intersects(region))
return -2;
}
/*
* Allocate new region metadata
*/
int i;
for (i = 0; i < MAX_REGIONS; i++)
if (!_map[i].used()) break;
if (i == MAX_REGIONS) {
PERR("maximum number of %d regions reached",
MAX_REGIONS);
return -1;
}
_map[i] = region;
return i;
}
Region region(int id) const
{
return _id_valid(id) ? _map[id] : Region();
}
Region lookup(addr_t start)
{
for (int i = 0; i < MAX_REGIONS; i++)
if (_map[i].start() == start)
return _map[i];
return Region();
}
void remove_region(addr_t start)
{
for (int i = 0; i < MAX_REGIONS; i++)
if (_map[i].start() == start)
_map[i] = Region();
}
};
protected:
/*
* 'Rm_session_mmap' is 'protected' because it is instantiated by
* 'Platform_env::Local_parent::session()'.
*/
/*
* On Linux, we use a local region manager session that attaches
* dataspaces via mmap to the local address space.
*/
class Rm_session_mmap : public Rm_session,
public Dataspace
{
private:
Lock _lock; /* protect '_rmap' */
Region_map _rmap;
bool const _sub_rm; /* false if RM session is root */
size_t const _size;
/**
* Base offset of the RM session
*
* For a normal RM session (the one that comes with the
* 'env()', this value is zero. If the RM session is
* used as nested dataspace, '_base' contains the address
* where the managed dataspace is attached in the root RM
* session.
*
* Note that a managed dataspace cannot be attached more
* than once. Furthermore, managed dataspace cannot be
* attached to another managed dataspace. The nested
* dataspace emulation is solely implemented to support
* the common use case of managed dataspaces as mechanism
* to reserve parts of the local address space from being
* populated by the 'env()->rm_session()'. (i.e., for the
* context area, or for the placement of consecutive
* shared-library segments)
*/
addr_t _base;
bool _is_attached() const { return _base > 0; }
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
*/
void *_map_local(Dataspace_capability ds,
Genode::size_t size,
addr_t offset,
bool use_local_addr,
addr_t local_addr,
bool executable,
bool overmap = false);
/**
* Determine size of dataspace
*
* For core, this function performs a local lookup of the
* 'Dataspace_component' object. For non-core programs, the
* dataspace size is determined via an RPC to core
* (calling 'Dataspace::size()').
*/
size_t _dataspace_size(Capability<Dataspace>);
/**
* Determine file descriptor of dataspace
*/
int _dataspace_fd(Capability<Dataspace>);
/**
* Determine whether dataspace is writable
*/
bool _dataspace_writable(Capability<Dataspace>);
public:
Rm_session_mmap(bool sub_rm, size_t size = ~0)
: _sub_rm(sub_rm), _size(size), _base(0) { }
~Rm_session_mmap()
{
/* detach sub RM session when destructed */
if (_sub_rm && _is_attached())
env()->rm_session()->detach((void *)_base);
}
/**************************************
** Region manager session interface **
**************************************/
Local_addr attach(Dataspace_capability ds, size_t size,
off_t, bool, Local_addr,
bool executable);
void detach(Local_addr local_addr);
Pager_capability add_client(Thread_capability thread) {
return Pager_capability(); }
void remove_client(Pager_capability pager) { }
void fault_handler(Signal_context_capability handler) { }
State state() { return State(); }
/*************************
** Dataspace interface **
*************************/
size_t size() { return _size; }
addr_t phys_addr() { return 0; }
bool writable() { return true; }
/**
* Return pseudo dataspace capability of the RM session
*
* The capability returned by this function is only usable
* as argument to 'Rm_session_mmap::attach'. It is not a
* real capability.
*/
Dataspace_capability dataspace()
{
return Dataspace_capability::local_cap(this);
}
};
private:
/*******************************
** Platform-specific members **
*******************************/
Ram_session_capability _ram_session_cap;
Expanding_ram_session_client _ram_session_client;
Cpu_session_capability _cpu_session_cap;
Expanding_cpu_session_client _cpu_session_client;
Rm_session_mmap _rm_session_mmap;
Pd_session_client _pd_session_client;
public:
/**
* Constructor
*/
Platform_env_base(Ram_session_capability ram_cap,
Cpu_session_capability cpu_cap,
Pd_session_capability pd_cap)
:
_ram_session_cap(ram_cap),
_ram_session_client(_ram_session_cap),
_cpu_session_cap(cpu_cap),
_cpu_session_client(static_cap_cast<Linux_cpu_session>(cpu_cap)),
_rm_session_mmap(false),
_pd_session_client(pd_cap)
{ }
/*******************
** Env interface **
*******************/
Ram_session *ram_session() { return &_ram_session_client; }
Ram_session_capability ram_session_cap() { return _ram_session_cap; }
Rm_session *rm_session() { return &_rm_session_mmap; }
Linux_cpu_session *cpu_session() { return &_cpu_session_client; }
Cpu_session_capability cpu_session_cap() { return _cpu_session_cap; }
Pd_session *pd_session() { return &_pd_session_client; }
};
/**
* 'Platform_env' used by all processes except for core
*/
class Platform_env : public Platform_env_base, public Emergency_ram_reserve
{
private:
/**
* Local interceptor of parent requests
*
* On Linux, we need to intercept calls to the parent interface to
* implement the RM service locally. This particular service is
* used for creating managed dataspaces, which allow the
* reservation of parts of the local address space from being
* automatically managed by the 'env()->rm_session()'.
*
* All requests that do not refer to the RM service are passed
* through the real parent interface.
*/
class Local_parent : public Expanding_parent_client
{
public:
/**********************
** Parent interface **
**********************/
Session_capability session(Service_name const &,
Session_args const &,
Affinity const & = Affinity());
void close(Session_capability);
/**
* Constructor
*
* \param parent_cap real parent capability used to
* promote requests to non-local
* services
*/
Local_parent(Parent_capability parent_cap,
Emergency_ram_reserve &);
};
/**
* Return instance of parent interface
*/
Local_parent &_parent();
Heap _heap;
/*
* Emergency RAM reserve
*
* See the comment of '_fallback_sig_cap()' in 'env/env.cc'.
*/
constexpr static size_t _emergency_ram_size() { return 4*1024; }
Ram_dataspace_capability _emergency_ram_ds;
/*************************************
** Linux-specific helper functions **
*************************************/
public:
/**
* Constructor
*/
Platform_env();
/**
* Destructor
*/
~Platform_env() { _parent().exit(0); }
/*
* Support functions for implementing fork on Noux.
*
* Not supported on Linux.
*
* See the documentation in 'base/src/base/env/platform_env.h'
*/
void reinit(Native_capability::Dst, long);
void reinit_main_thread(Rm_session_capability &);
/*************************************
** Emergency_ram_reserve interface **
*************************************/
void release() { ram_session()->free(_emergency_ram_ds); }
/*******************
** Env interface **
*******************/
Parent *parent() { return &_parent(); }
Heap *heap() { return &_heap; }
};
}
#endif /* _PLATFORM_ENV_H_ */

View File

@ -0,0 +1,363 @@
/*
* \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.
*/
/*
* Copyright (C) 2008-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 <base/thread.h>
#include <linux_dataspace/client.h>
#include <linux_syscalls.h>
#include <context_area.h>
/* local includes */
#include <platform_env.h>
using namespace Genode;
static bool is_sub_rm_session(Dataspace_capability ds)
{
if (ds.valid())
return false;
return Dataspace_capability::deref(ds) != 0;
}
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 overmap)
{
int const fd = _dataspace_fd(ds);
bool const writable = _dataspace_writable(ds);
int const flags = MAP_SHARED | (overmap ? MAP_FIXED : 0);
int const prot = PROT_READ
| (writable ? PROT_WRITE : 0)
| (executable ? PROT_EXEC : 0);
void * const addr_in = use_local_addr ? (void*)local_addr : 0;
void * const addr_out = lx_mmap(addr_in, size, prot, flags, fd, offset);
/*
* We can close the file after calling mmap. The Linux kernel will still
* keep the file mapped. By immediately closing the file descriptor, we
* won't need to keep track of dataspace file descriptors within the
* process.
*/
lx_close(fd);
/* 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();
}
return addr_out;
}
void Platform_env::Rm_session_mmap::_add_to_rmap(Region const &region)
{
if (_rmap.add_region(region) < 0) {
PERR("_add_to_rmap: could not add region to sub RM session");
throw Region_conflict();
}
}
Rm_session::Local_addr
Platform_env::Rm_session_mmap::attach(Dataspace_capability ds,
size_t size, off_t offset,
bool use_local_addr,
Rm_session::Local_addr local_addr,
bool executable)
{
Lock::Guard lock_guard(_lock);
/* only support attach_at for sub RM sessions */
if (_sub_rm && !use_local_addr) {
PERR("Rm_session_mmap::attach: attaching w/o local addr not supported\n");
throw Out_of_metadata();
}
if (offset < 0) {
PERR("Rm_session_mmap::attach: negative offset not supported\n");
throw Region_conflict();
}
size_t const remaining_ds_size = _dataspace_size(ds) > (addr_t)offset
? _dataspace_size(ds) - (addr_t)offset : 0;
/* determine size of virtual address region */
size_t const region_size = size ? min(remaining_ds_size, size)
: remaining_ds_size;
if (region_size == 0)
throw Region_conflict();
/*
* We have to distinguish the following cases
*
* 1 we are a root RM session and ds is a plain dataspace
* 2 we are a root RM session and ds is a sub RM session
* 2.1 ds is already attached (base != 0)
* 2.2 ds is not yet attached
* 3 we are a sub RM session and ds is a plain dataspace
* 3.1 we are attached to a root RM session
* 3.2 we are not yet attached
* 4 we are a sub RM session and ds is a sub RM session (not supported)
*/
if (_sub_rm) {
/*
* Case 4
*/
if (is_sub_rm_session(ds)) {
PERR("Rm_session_mmap::attach: nesting sub RM sessions is not supported");
throw Invalid_dataspace();
}
/*
* Check for the dataspace to not exceed the boundaries of the
* sub RM session
*/
if (region_size + (addr_t)local_addr > _size) {
PERR("Rm_session_mmap::attach: dataspace does not fit in sub RM session");
throw Region_conflict();
}
_add_to_rmap(Region(local_addr, offset, ds, region_size));
/*
* Case 3.1
*
* This RM session is a sub RM session. If the sub RM session is
* 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, true);
return (void *)local_addr;
} else {
if (is_sub_rm_session(ds)) {
Dataspace *ds_if = Dataspace_capability::deref(ds);
Rm_session_mmap *rm = dynamic_cast<Rm_session_mmap *>(ds_if);
if (!rm)
throw Invalid_dataspace();
/*
* Case 2.1
*
* Detect if sub RM session is already attached
*/
if (rm->_base) {
PERR("Rm_session_mmap::attach: mapping a sub RM session twice is not supported");
throw Out_of_metadata();
}
/*
* Reserve local address range that can hold the entire sub RM
* session.
*/
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 and map
* each of them.
*/
for (int i = 0; i < Region_map::MAX_REGIONS; i++) {
Region region = rm->_rmap.region(i);
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, true);
}
return rm->_base;
} else {
/*
* 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);
_add_to_rmap(Region((addr_t)addr, offset, ds, region_size));
return addr;
}
}
}
void Platform_env::Rm_session_mmap::detach(Rm_session::Local_addr local_addr)
{
Lock::Guard lock_guard(_lock);
/*
* Cases
*
* 1 we are root RM
* 2 we are sub RM (region must be normal dataspace)
* 2.1 we are not attached
* 2.2 we are attached to a root RM
*/
Region region = _rmap.lookup(local_addr);
if (!region.used())
return;
/*
* Remove meta data from region map
*/
_rmap.remove_region(local_addr);
if (_sub_rm) {
/*
* Case 2.1, 2.2
*
* By removing a region from an attached sub RM session we mark the
* corresponding local address range as reserved. A plain 'munmap'
* would mark this range as free to use for the root RM session, which
* we need to prevent.
*
* If we are not attached, no local address-space manipulation is
* needed.
*/
if (_is_attached()) {
lx_munmap((void *)((addr_t)local_addr + _base), region.size());
_reserve_local(true, (addr_t)local_addr + _base, region.size());
}
} else {
/*
* Case 1
*
* We need no distiction between detaching normal dataspaces and
* sub RM session. In both cases, we simply mark the local address
* range as free.
*/
lx_munmap(local_addr, region.size());
}
/*
* If the detached dataspace is sub RM session, mark it as detached
*/
if (is_sub_rm_session(region.dataspace())) {
Dataspace *ds_if = Dataspace_capability::deref(region.dataspace());
Rm_session_mmap *rm = dynamic_cast<Rm_session_mmap *>(ds_if);
if (rm)
rm->_base = 0;
}
}

View File

@ -0,0 +1,661 @@
/*
* \brief Socket-based IPC implementation for Linux
* \author Norman Feske
* \author Christian Helmuth
* \date 2011-10-11
*
* The current request message layout is:
*
* long server_local_name;
* int opcode;
* ...payload...
*
* Response messages look like this:
*
* long scratch_word;
* int exc_code;
* ...payload...
*
* All fields are naturally aligned, i.e., aligend on 4 or 8 byte boundaries on
* 32-bit resp. 64-bit systems.
*/
/*
* Copyright (C) 2011-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 <base/ipc.h>
#include <base/thread.h>
#include <base/blocking.h>
#include <base/env.h>
#include <linux_cpu_session/linux_cpu_session.h>
/* local includes */
#include <socket_descriptor_registry.h>
/* Linux includes */
#include <linux_syscalls.h>
#include <sys/un.h>
#include <sys/socket.h>
using namespace Genode;
/*****************************
** IPC marshalling support **
*****************************/
void Ipc_ostream::_marshal_capability(Native_capability const &cap)
{
if (cap.valid()) {
_write_to_buf(cap.local_name());
_snd_msg->append_cap(cap.dst().socket);
} else {
_write_to_buf(-1L);
}
}
void Ipc_istream::_unmarshal_capability(Native_capability &cap)
{
long local_name = 0;
_read_from_buf(local_name);
if (local_name == -1) {
/* construct invalid capability */
cap = Genode::Native_capability();
} else {
/* construct valid capability */
int const socket = _rcv_msg->read_cap();
cap = Native_capability(Cap_dst_policy::Dst(socket), local_name);
}
}
namespace Genode {
/*
* Helper for obtaining a bound and connected socket pair
*
* For core, the implementation is just a wrapper around
* 'lx_server_socket_pair()'. For all other processes, the implementation
* requests the socket pair from the Env::CPU session interface using a
* Linux-specific interface extension.
*/
Native_connection_state server_socket_pair();
/*
* Helper to destroy the server socket pair
*
* For core, this is a no-op. For all other processes, the server and client
* sockets are closed.
*/
void destroy_server_socket_pair(Native_connection_state const &ncs);
}
/******************************
** File-descriptor registry **
******************************/
Genode::Ep_socket_descriptor_registry *Genode::ep_sd_registry()
{
static Genode::Ep_socket_descriptor_registry registry;
return &registry;
}
/********************************************
** Communication over Unix-domain sockets **
********************************************/
enum {
LX_EINTR = 4,
LX_ECONNREFUSED = 111
};
/**
* Utility: Return thread ID to which the given socket is directed to
*
* \return -1 if the socket is pointing to a valid entrypoint
*/
static int lookup_tid_by_client_socket(int sd)
{
/*
* Synchronize calls so that the large 'sockaddr_un' can be allocated
* in the BSS rather than the stack.
*/
static Lock lock;
Lock::Guard guard(lock);
static sockaddr_un name;
socklen_t name_len = sizeof(name);
int ret = lx_getpeername(sd, (sockaddr *)&name, &name_len);
if (ret < 0)
return -1;
struct Prefix_len
{
typedef Genode::size_t size_t;
size_t const len;
static int _init_len(char const *s)
{
char const * const pattern = "/ep-";
static size_t const pattern_len = Genode::strlen(pattern);
for (size_t i = 0; Genode::strlen(s + i) >= pattern_len; i++)
if (Genode::strcmp(s + i, pattern, pattern_len) == 0)
return i + pattern_len;
struct Unexpected_rpath_prefix { };
throw Unexpected_rpath_prefix();
}
Prefix_len(char const *s) : len(_init_len(s)) { }
};
/*
* The name of the Unix-domain socket has the form <rpath>-<uid>/ep-<tid>.
* We are only interested in the <tid> part. Hence, we determine the length
* of the <rpath>-<uid>/ep- portion only once and keep it in a static
* variable.
*/
static Prefix_len prefix_len(name.sun_path);
unsigned tid = 0;
if (Genode::ascii_to(name.sun_path + prefix_len.len, &tid) == 0) {
PRAW("Error: could not parse tid number");
return -1;
}
return tid;
}
namespace {
/**
* Message object encapsulating data for sendmsg/recvmsg
*/
struct Message
{
public:
enum { MAX_SDS_PER_MSG = Genode::Msgbuf_base::MAX_CAPS_PER_MSG };
private:
typedef Genode::size_t size_t;
msghdr _msg;
sockaddr_un _addr;
iovec _iovec;
char _cmsg_buf[CMSG_SPACE(MAX_SDS_PER_MSG*sizeof(int))];
unsigned _num_sds;
public:
Message(void *buffer, size_t buffer_len) : _num_sds(0)
{
Genode::memset(&_msg, 0, sizeof(_msg));
/* initialize control message */
struct cmsghdr *cmsg;
_msg.msg_control = _cmsg_buf;
_msg.msg_controllen = sizeof(_cmsg_buf); /* buffer space available */
_msg.msg_flags |= MSG_CMSG_CLOEXEC;
cmsg = CMSG_FIRSTHDR(&_msg);
cmsg->cmsg_len = CMSG_LEN(0);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
_msg.msg_controllen = cmsg->cmsg_len; /* actual cmsg length */
/* initialize iovec */
_msg.msg_iov = &_iovec;
_msg.msg_iovlen = 1;
_iovec.iov_base = buffer;
_iovec.iov_len = buffer_len;
}
msghdr * msg() { return &_msg; }
void marshal_socket(int sd)
{
*((int *)CMSG_DATA((cmsghdr *)_cmsg_buf) + _num_sds) = sd;
_num_sds++;
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&_msg);
cmsg->cmsg_len = CMSG_LEN(_num_sds*sizeof(int));
_msg.msg_controllen = cmsg->cmsg_len; /* actual cmsg length */
}
void accept_sockets(int num_sds)
{
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&_msg);
cmsg->cmsg_len = CMSG_LEN(num_sds*sizeof(int));
_msg.msg_controllen = cmsg->cmsg_len; /* actual cmsg length */
}
int socket_at_index(int index) const
{
return *((int *)CMSG_DATA((cmsghdr *)_cmsg_buf) + index);
}
unsigned num_sockets() const
{
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&_msg);
if (!cmsg)
return 0;
return (cmsg->cmsg_len - CMSG_ALIGN(sizeof(cmsghdr)))/sizeof(int);
}
};
} /* unnamed namespace */
/**
* Utility: Extract socket desriptors from SCM message into 'Genode::Msgbuf'
*/
static void extract_sds_from_message(unsigned start_index, Message const &msg,
Genode::Msgbuf_base &buf)
{
buf.reset_caps();
/* start at offset 1 to skip the reply channel */
for (unsigned i = start_index; i < msg.num_sockets(); i++) {
int const sd = msg.socket_at_index(i);
int const id = lookup_tid_by_client_socket(sd);
int const associated_sd = Genode::ep_sd_registry()->try_associate(sd, id);
buf.append_cap(associated_sd);
if ((associated_sd >= 0) && (associated_sd != sd)) {
/*
* The association already existed under a different name, use
* already associated socket descriptor and and drop 'sd'.
*/
lx_close(sd);
}
}
}
/**
* Send request to server and wait for reply
*/
static inline void lx_call(int dst_sd,
Genode::Msgbuf_base &send_msgbuf, Genode::size_t send_msg_len,
Genode::Msgbuf_base &recv_msgbuf)
{
int ret;
Message send_msg(send_msgbuf.buf, send_msg_len);
/*
* Create reply channel
*
* The reply channel will be closed when leaving the scope of 'lx_call'.
*/
struct Reply_channel
{
enum { LOCAL_SOCKET = 0, REMOTE_SOCKET = 1 };
int sd[2];
Reply_channel()
{
sd[LOCAL_SOCKET] = -1; sd[REMOTE_SOCKET] = -1;
int ret = lx_socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0, sd);
if (ret < 0) {
PRAW("[%d] lx_socketpair failed with %d", lx_getpid(), ret);
throw Genode::Ipc_error();
}
}
~Reply_channel()
{
if (sd[LOCAL_SOCKET] != -1) lx_close(sd[LOCAL_SOCKET]);
if (sd[REMOTE_SOCKET] != -1) lx_close(sd[REMOTE_SOCKET]);
}
int local_socket() const { return sd[LOCAL_SOCKET]; }
int remote_socket() const { return sd[REMOTE_SOCKET]; }
} reply_channel;
/* assemble message */
/* marshal reply capability */
send_msg.marshal_socket(reply_channel.remote_socket());
/* marshal capabilities contained in 'send_msgbuf' */
for (unsigned i = 0; i < send_msgbuf.used_caps(); i++)
send_msg.marshal_socket(send_msgbuf.cap(i));
ret = lx_sendmsg(dst_sd, send_msg.msg(), 0);
if (ret < 0) {
PRAW("[%d] lx_sendmsg to sd %d failed with %d in lx_call()",
lx_getpid(), dst_sd, ret);
throw Genode::Ipc_error();
}
/* receive reply */
Message recv_msg(recv_msgbuf.buf, recv_msgbuf.size());
recv_msg.accept_sockets(Message::MAX_SDS_PER_MSG);
ret = lx_recvmsg(reply_channel.local_socket(), recv_msg.msg(), 0);
/* system call got interrupted by a signal */
if (ret == -LX_EINTR)
throw Genode::Blocking_canceled();
if (ret < 0) {
PRAW("[%d] lx_recvmsg failed with %d in lx_call()", lx_getpid(), ret);
throw Genode::Ipc_error();
}
extract_sds_from_message(0, recv_msg, recv_msgbuf);
}
/**
* for request from client
*
* \return socket descriptor of reply capability
*/
static inline int lx_wait(Genode::Native_connection_state &cs,
Genode::Msgbuf_base &recv_msgbuf)
{
Message msg(recv_msgbuf.buf, recv_msgbuf.size());
msg.accept_sockets(Message::MAX_SDS_PER_MSG);
int ret = lx_recvmsg(cs.server_sd, msg.msg(), 0);
/* system call got interrupted by a signal */
if (ret == -LX_EINTR)
throw Genode::Blocking_canceled();
if (ret < 0) {
PRAW("lx_recvmsg failed with %d in lx_wait(), sd=%d", ret, cs.server_sd);
throw Genode::Ipc_error();
}
int const reply_socket = msg.socket_at_index(0);
extract_sds_from_message(1, msg, recv_msgbuf);
return reply_socket;
}
/**
* Send reply to client
*/
static inline void lx_reply(int reply_socket,
Genode::Msgbuf_base &send_msgbuf,
Genode::size_t msg_len)
{
Message msg(send_msgbuf.buf, msg_len);
/*
* Marshall capabilities to be transferred to the client
*/
for (unsigned i = 0; i < send_msgbuf.used_caps(); i++)
msg.marshal_socket(send_msgbuf.cap(i));
int ret = lx_sendmsg(reply_socket, msg.msg(), 0);
/* ignore reply send error caused by disappearing client */
if (ret >= 0 || ret == -LX_ECONNREFUSED) {
lx_close(reply_socket);
return;
}
if (ret < 0)
PRAW("[%d] lx_sendmsg failed with %d in lx_reply()", lx_getpid(), ret);
}
/*****************
** Ipc_ostream **
*****************/
/*
* XXX This class will be removed soon.
*/
void Ipc_ostream::_prepare_next_send()
{
PRAW("unexpected call to %s (%p)", __PRETTY_FUNCTION__, this);
}
void Ipc_ostream::_send()
{
PRAW("unexpected call to %s (%p)", __PRETTY_FUNCTION__, this);
}
Ipc_ostream::Ipc_ostream(Native_capability dst, Msgbuf_base *snd_msg):
Ipc_marshaller(snd_msg->buf, snd_msg->size()), _snd_msg(snd_msg), _dst(dst)
{ }
/*****************
** Ipc_istream **
*****************/
/*
* XXX This class will be removed soon.
*/
void Ipc_istream::_prepare_next_receive()
{
PRAW("unexpected call to %s (%p)", __PRETTY_FUNCTION__, this);
}
void Ipc_istream::_wait()
{
PRAW("unexpected call to %s (%p)", __PRETTY_FUNCTION__, this);
}
Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg)
:
Ipc_unmarshaller(rcv_msg->buf, rcv_msg->size()),
Native_capability(Dst(-1), 0),
_rcv_msg(rcv_msg)
{ }
Ipc_istream::~Ipc_istream()
{
/*
* The association of the capability (client) socket must be invalidated on
* server destruction. We implement it here as the IPC server currently has
* no destructor. We have the plan to remove Ipc_istream and Ipc_ostream
* in the future and, then, move this into the server destructor.
*
* IPC clients have -1 as client_sd and need no disassociation.
*/
if (_rcv_cs.client_sd != -1) {
Genode::ep_sd_registry()->disassociate(_rcv_cs.client_sd);
/*
* Reset thread role to non-server such that we can enter 'sleep_forever'
* without getting a warning.
*/
Thread_base *thread = Thread_base::myself();
if (thread)
thread->tid().is_ipc_server = false;
}
destroy_server_socket_pair(_rcv_cs);
_rcv_cs.client_sd = -1;
_rcv_cs.server_sd = -1;
}
/****************
** Ipc_client **
****************/
void Ipc_client::_prepare_next_call()
{
/* prepare next request in buffer */
long const local_name = Ipc_ostream::_dst.local_name();
_write_offset = 0;
_write_to_buf(local_name);
/* prepare response buffer */
_read_offset = sizeof(long);
_snd_msg->reset_caps();
}
void Ipc_client::_call()
{
if (Ipc_ostream::_dst.valid())
lx_call(Ipc_ostream::_dst.dst().socket, *_snd_msg, _write_offset, *_rcv_msg);
_prepare_next_call();
}
Ipc_client::Ipc_client(Native_capability const &srv, Msgbuf_base *snd_msg,
Msgbuf_base *rcv_msg, unsigned short)
: Ipc_istream(rcv_msg), Ipc_ostream(srv, snd_msg), _result(0)
{
_prepare_next_call();
}
/****************
** Ipc_server **
****************/
void Ipc_server::_prepare_next_reply_wait()
{
/* skip server-local name */
_read_offset = sizeof(long);
/* prepare next reply */
_write_offset = 0;
long local_name = Ipc_ostream::_dst.local_name();
_write_to_buf(local_name); /* XXX unused, needed by de/marshaller */
/* leave space for exc code at the beginning of the msgbuf */
_write_offset += align_natural(sizeof(int));
/* reset capability slots of send message buffer */
_snd_msg->reset_caps();
}
void Ipc_server::_wait()
{
_reply_needed = true;
/*
* Block infinitely if called from the main thread. This may happen if the
* main thread calls 'sleep_forever()'.
*/
if (!Thread_base::myself()) {
struct timespec ts = { 1000, 0 };
for (;;) lx_nanosleep(&ts, 0);
}
try {
int const reply_socket = lx_wait(_rcv_cs, *_rcv_msg);
/*
* Remember reply capability
*
* The 'local_name' of a capability is meaningful for addressing server
* objects only. Because a reply capabilities does not address a server
* object, the 'local_name' is meaningless.
*/
enum { DUMMY_LOCAL_NAME = -1 };
typedef Native_capability::Dst Dst;
Ipc_ostream::_dst = Native_capability(Dst(reply_socket), DUMMY_LOCAL_NAME);
_prepare_next_reply_wait();
} catch (Blocking_canceled) { }
}
void Ipc_server::_reply()
{
try {
lx_reply(Ipc_ostream::_dst.dst().socket, *_snd_msg, _write_offset); }
catch (Ipc_error) { }
_prepare_next_reply_wait();
}
void Ipc_server::_reply_wait()
{
/* when first called, there was no request yet */
if (_reply_needed)
lx_reply(Ipc_ostream::_dst.dst().socket, *_snd_msg, _write_offset);
_wait();
}
Ipc_server::Ipc_server(Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg)
:
Ipc_istream(rcv_msg),
Ipc_ostream(Native_capability(), snd_msg), _reply_needed(false)
{
Thread_base *thread = Thread_base::myself();
/*
* If 'thread' is 0, the constructor was called by the main thread. By
* definition, main is never an RPC entrypoint. However, the main thread
* may call 'sleep_forever()', which instantiates 'Ipc_server'.
*/
if (thread && thread->tid().is_ipc_server) {
PRAW("[%d] unexpected multiple instantiation of Ipc_server by one thread",
lx_gettid());
struct Ipc_server_multiple_instance { };
throw Ipc_server_multiple_instance();
}
if (thread) {
_rcv_cs = server_socket_pair();
thread->tid().is_ipc_server = true;
}
/* override capability initialization performed by 'Ipc_istream' */
*static_cast<Native_capability *>(this) =
Native_capability(Native_capability::Dst(_rcv_cs.client_sd), 0);
_prepare_next_reply_wait();
}

View File

@ -0,0 +1,150 @@
/*
* \brief Linux-specific socket-descriptor registry
* \author Norman Feske
* \date 2012-07-26
*
* We use the names of Unix-domain sockets as keys to uniquely identify
* entrypoints. When receiving a socket descriptor as IPC payload, we first
* lookup the corresponding entrypoint ID. If we already possess a socket
* descriptor pointing to the same entrypoint, we close the received one and
* use the already known descriptor instead.
*/
/*
* Copyright (C) 2012-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 _BASE__IPC__SOCKET_DESCRIPTOR_REGISTRY_H_
#define _BASE__IPC__SOCKET_DESCRIPTOR_REGISTRY_H_
#include <base/lock.h>
namespace Genode
{
template <unsigned MAX_FDS>
class Socket_descriptor_registry;
typedef Socket_descriptor_registry<100> Ep_socket_descriptor_registry;
/**
* Return singleton instance of registry for tracking entrypoint sockets
*/
Ep_socket_descriptor_registry *ep_sd_registry();
}
template <unsigned MAX_FDS>
class Genode::Socket_descriptor_registry
{
public:
class Limit_reached { };
class Aliased_global_id { };
private:
struct Entry
{
int fd;
int global_id;
/**
* Default constructor creates empty entry
*/
Entry() : fd(-1), global_id(-1) { }
Entry(int fd, int global_id) : fd(fd), global_id(global_id) { }
bool is_free() const { return fd == -1; }
void mark_as_free() { fd = -1; }
};
Entry _entries[MAX_FDS];
Genode::Lock mutable _lock;
Entry &_find_free_entry()
{
for (unsigned i = 0; i < MAX_FDS; i++)
if (_entries[i].is_free())
return _entries[i];
throw Limit_reached();
}
Entry &_find_entry_by_fd(int fd)
{
for (unsigned i = 0; i < MAX_FDS; i++)
if (_entries[i].fd == fd)
return _entries[i];
throw Limit_reached();
}
/**
* Lookup file descriptor that belongs to specified global ID
*
* \return file descriptor or -1 if lookup failed
*/
int _lookup_fd_by_global_id(int global_id) const
{
for (unsigned i = 0; i < MAX_FDS; i++)
if (_entries[i].global_id == global_id)
return _entries[i].fd;
return -1;
}
public:
void disassociate(int sd)
{
Genode::Lock::Guard guard(_lock);
for (unsigned i = 0; i < MAX_FDS; i++)
if (_entries[i].fd == sd) {
_entries[i].mark_as_free();
return;
}
}
/**
* Try to associate socket descriptor with corresponding ID
*
* \return socket descriptor associated with the ID
* \throw Limit_reached
*
* If the ID was already associated, the return value is the originally
* registered socket descriptor. In this case, the caller should drop
* the new socket descriptor and use the one returned by this function.
*/
int try_associate(int sd, int global_id)
{
/* ignore invalid capabilities */
if (sd == -1)
return sd;
/* ignore invalid capabilities */
if (sd == -1 || global_id == -1)
return sd;
Genode::Lock::Guard guard(_lock);
int const existing_sd = _lookup_fd_by_global_id(global_id);
if (existing_sd < 0) {
Entry &entry = _find_free_entry();
entry = Entry(sd, global_id);
return sd;
} else {
return existing_sd;
}
}
};
#endif /* _BASE__IPC__SOCKET_DESCRIPTOR_REGISTRY_H_ */

View File

@ -0,0 +1,75 @@
/*
* \brief Linux-specific helper functions for the Lock implementation
* \author Norman Feske
* \date 2009-07-20
*
* This file serves as adapter between the generic lock implementation
* in 'lock.cc' and the underlying kernel.
*
* For documentation about the interface, please revisit the 'base-pistachio'
* implementation.
*/
/*
* Copyright (C) 2009-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 <base/native_types.h>
#include <base/thread.h>
/* Linux includes */
#include <linux_syscalls.h>
extern int main_thread_futex_counter;
/**
* Resolve 'Thread_base::myself' when not linking the thread library
*
* This weak symbol is primarily used by test cases. Most other Genode programs
* use the thread library. If the thread library is not used, 'myself' can only
* be called by the main thread, for which 'myself' is defined as zero.
*/
Genode::Thread_base * __attribute__((weak)) Genode::Thread_base::myself() { return 0; }
static inline void thread_yield()
{
struct timespec ts = { 0, 1000 };
lx_nanosleep(&ts, 0);
}
static inline bool thread_check_stopped_and_restart(Genode::Thread_base *thread_base)
{
const int *futex_counter_ptr = thread_base ?
&thread_base->tid().futex_counter :
&main_thread_futex_counter;
return lx_futex(futex_counter_ptr, LX_FUTEX_WAKE, 1);
}
static inline void thread_switch_to(Genode::Thread_base *thread_base)
{
thread_yield();
}
static inline void thread_stop_myself()
{
/*
* Just go to sleep without modifying the counter value. The
* 'thread_check_stopped_and_restart()' function will get called
* repeatedly until this thread has actually executed the syscall.
*/
Genode::Thread_base *myself = Genode::Thread_base::myself();
const int *futex_counter_ptr = myself ?
&myself->tid().futex_counter :
&main_thread_futex_counter;
lx_futex(futex_counter_ptr, LX_FUTEX_WAIT, 0);
}

View File

@ -0,0 +1,92 @@
/*
* \brief Implementation of process creation for Linux
* \author Norman Feske
* \date 2006-07-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 <base/elf.h>
#include <base/env.h>
#include <base/process.h>
#include <base/printf.h>
#include <linux_pd_session/client.h>
/* framework-internal includes */
#include <linux_syscalls.h>
using namespace Genode;
Dataspace_capability Process::_dynamic_linker_cap;
/**
* Check for dynamic ELF header
*/
static bool _check_dynamic_elf(Dataspace_capability elf_ds_cap)
{
/* attach ELF locally */
addr_t elf_addr;
try { elf_addr = env()->rm_session()->attach(elf_ds_cap); }
catch (...) { return false; }
/*
* If attach is called within core, it will return zero because
* Linux uses Core_rm_session.
*/
if (!elf_addr) return false;
/* read program header and interpreter */
Elf_binary elf((addr_t)elf_addr);
env()->rm_session()->detach((void *)elf_addr);
return elf.is_dynamically_linked();
}
Process::Process(Dataspace_capability elf_data_ds_cap,
Ram_session_capability ram_session_cap,
Cpu_session_capability cpu_session_cap,
Rm_session_capability rm_session_cap,
Parent_capability parent_cap,
char const *name,
Native_pd_args const *pd_args)
:
_pd(name, pd_args),
_cpu_session_client(cpu_session_cap),
_rm_session_client(Rm_session_capability())
{
/* check for dynamic program header */
if (_check_dynamic_elf(elf_data_ds_cap)) {
if (!_dynamic_linker_cap.valid()) {
PERR("Dynamically linked file found, but no dynamic linker binary present");
return;
}
elf_data_ds_cap = _dynamic_linker_cap;
}
/*
* Register main thread at core
*
* At this point in time, we do not yet know the TID and PID of the new
* thread. Those information will be provided to core by the constructor of
* the 'Platform_env' of the new process.
*/
_thread0_cap = _cpu_session_client.create_thread(name);
Linux_pd_session_client lx_pd(static_cap_cast<Linux_pd_session>(_pd.cap()));
lx_pd.assign_parent(parent_cap);
lx_pd.start(elf_data_ds_cap);
}
Process::~Process() { }

View File

@ -0,0 +1,49 @@
/*
* \brief Thread-environment support common to all programs
* \author Martin Stein
* \date 2013-12-13
*/
/*
* Copyright (C) 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 <base/stdint.h>
#include <base/env.h>
using namespace Genode;
extern addr_t * __initial_sp;
/*
* Define 'lx_environ' pointer.
*/
char **lx_environ;
/**
* Natively aligned memory location used in the lock implementation
*/
int main_thread_futex_counter __attribute__((aligned(sizeof(addr_t))));
/*****************************
** Startup library support **
*****************************/
void prepare_init_main_thread()
{
/*
* Initialize the 'lx_environ' pointer
*
* environ = &argv[argc + 1]
* __initial_sp[0] = argc (always 1 in Genode)
* __initial_sp[1] = argv[0]
* __initial_sp[2] = NULL
* __initial_sp[3] = environ
*/
lx_environ = (char**)&__initial_sp[3];
}

View File

@ -0,0 +1,149 @@
/*
* \brief Implementation of the Thread API via Linux threads
* \author Norman Feske
* \author Martin Stein
* \date 2006-06-13
*/
/*
* 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 <base/env.h>
#include <base/thread.h>
#include <base/snprintf.h>
#include <base/sleep.h>
#include <linux_cpu_session/linux_cpu_session.h>
/* Linux syscall bindings */
#include <linux_syscalls.h>
using namespace Genode;
extern int main_thread_futex_counter;
static void empty_signal_handler(int) { }
static Lock &startup_lock()
{
static Lock lock(Lock::LOCKED);
return lock;
}
/**
* Signal handler for killing the thread
*/
static void thread_exit_signal_handler(int) { lx_exit(0); }
void Thread_base::_thread_start()
{
/*
* Set signal handler such that canceled system calls get not
* transparently retried after a signal gets received.
*/
lx_sigaction(LX_SIGUSR1, empty_signal_handler);
Thread_base * const thread = Thread_base::myself();
/* inform core about the new thread and process ID of the new thread */
Linux_cpu_session *cpu = dynamic_cast<Linux_cpu_session *>(thread->_cpu_session);
if (cpu)
cpu->thread_id(thread->cap(), thread->tid().pid, thread->tid().tid);
/* wakeup 'start' function */
startup_lock().unlock();
thread->entry();
/* unblock caller of 'join()' */
thread->_join_lock.unlock();
sleep_forever();
}
void Thread_base::_init_platform_thread(Type type)
{
/* if no cpu session is given, use it from the environment */
if (!_cpu_session)
_cpu_session = env()->cpu_session();
/* for normal threads create an object at the CPU session */
if (type == NORMAL) {
_thread_cap = _cpu_session->create_thread(_context->name);
return;
}
/* adjust initial object state for main threads */
tid().futex_counter = main_thread_futex_counter;
_thread_cap = env()->parent()->main_thread_cap();
}
void Thread_base::_deinit_platform_thread()
{
/*
* Kill thread until it is really really dead
*
* We use the 'tgkill' system call to kill the thread. This system call
* returns immediately and just flags the corresponding signal at the
* targeted thread context. However, the thread still lives until the
* signal flags are evaluated. When leaving this function, however, we want
* to be sure that the thread is no more executing any code such that we
* an safely free and unmap the thread's stack. So we call 'tgkill' in a
* loop until we get an error indicating that the thread does not exists
* anymore.
*/
for (;;) {
/* destroy thread locally */
int ret = lx_tgkill(_tid.pid, _tid.tid, LX_SIGCANCEL);
if (ret < 0) break;
/* if thread still exists, wait a bit and try to kill it again */
struct timespec ts = { 0, 500 };
lx_nanosleep(&ts, 0);
}
/* inform core about the killed thread */
_cpu_session->kill_thread(_thread_cap);
}
void Thread_base::start()
{
/* synchronize calls of the 'start' function */
static Lock lock;
Lock::Guard guard(lock);
/*
* The first time we enter this code path, the 'start' function is
* called by the main thread as there cannot exist other threads
* without executing this function. When first called, we initialize
* the thread lib here.
*/
static bool threadlib_initialized = false;
if (!threadlib_initialized) {
lx_sigaction(LX_SIGCANCEL, thread_exit_signal_handler);
threadlib_initialized = true;
}
_tid.tid = lx_create_thread(Thread_base::_thread_start, stack_top(), this);
_tid.pid = lx_getpid();
/* wait until the 'thread_start' function got entered */
startup_lock().lock();
}
void Thread_base::cancel_blocking()
{
_cpu_session->cancel_blocking(_thread_cap);
}

View File

@ -0,0 +1,119 @@
/*
* \brief Linux-specific support code for the thread API
* \author Norman Feske
* \date 2010-01-13
*/
/*
* 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.
*/
/* Genode includes */
#include <rm_session/rm_session.h>
#include <ram_session/ram_session.h>
#include <base/printf.h>
#include <base/thread.h>
#include <context_area.h>
/**
* Region-manager session for allocating thread contexts
*
* This class corresponds to the managed dataspace that is normally
* used for organizing thread contexts with the thread context area.
* It "emulates" the sub address space by adjusting the local address
* argument to 'attach' with the offset of the thread context area.
*/
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
*/
Local_addr attach(Genode::Dataspace_capability ds_cap,
Genode::size_t size, Genode::off_t offset,
bool use_local_addr, Local_addr local_addr,
bool executable)
{
using namespace Genode;
/* convert context-area-relative to absolute virtual address */
addr_t addr = local_addr;
addr += Native_config::context_area_virtual_base();
/* use anonymous mmap for allocating stack backing store */
int flags = MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE;
int prot = PROT_READ | PROT_WRITE;
void *res = lx_mmap((void*)addr, size, prot, flags, -1, 0);
if ((addr_t)res != addr)
throw Region_conflict();
return local_addr;
}
void detach(Local_addr local_addr) {
PWRN("context area detach from 0x%p - not implemented", (void *)local_addr); }
Genode::Pager_capability add_client(Genode::Thread_capability) {
return Genode::Pager_capability(); }
void remove_client(Genode::Pager_capability) { }
void fault_handler(Genode::Signal_context_capability) { }
State state() { return State(); }
Genode::Dataspace_capability dataspace() {
return Genode::Dataspace_capability(); }
};
class Context_area_ram_session : public Genode::Ram_session
{
public:
Genode::Ram_dataspace_capability alloc(Genode::size_t size, bool) {
return Genode::Ram_dataspace_capability(); }
void free(Genode::Ram_dataspace_capability) { }
int ref_account(Genode::Ram_session_capability) { return 0; }
int transfer_quota(Genode::Ram_session_capability, Genode::size_t) { return 0; }
size_t quota() { return 0; }
size_t used() { return 0; }
};
/**
* Return single instance of the context-area RM and RAM session
*/
namespace Genode {
Rm_session *env_context_area_rm_session()
{
static Context_area_rm_session inst;
return &inst;
}
Ram_session *env_context_area_ram_session()
{
static Context_area_ram_session inst;
return &inst;
}
}

View File

@ -0,0 +1,49 @@
/*
* \brief Linux-specific extension of the CPU session implementation
* \author Norman Feske
* \date 2012-08-09
*/
/* core includes */
#include <cpu_session_component.h>
/* Linux includes */
#include <core_linux_syscalls.h>
using namespace Genode;
void Cpu_session_component::thread_id(Thread_capability thread_cap, int pid, int tid)
{
Object_pool<Cpu_thread_component>::Guard
thread(_thread_ep->lookup_and_lock(thread_cap));
if (!thread) return;
thread->platform_thread()->thread_id(pid, tid);
}
Untyped_capability Cpu_session_component::server_sd(Thread_capability thread_cap)
{
Object_pool<Cpu_thread_component>::Guard
thread(_thread_ep->lookup_and_lock(thread_cap));
if (!thread) return Untyped_capability();
enum { DUMMY_LOCAL_NAME = 0 };
typedef Native_capability::Dst Dst;
return Untyped_capability(Dst(thread->platform_thread()->server_sd()),
DUMMY_LOCAL_NAME);
}
Untyped_capability Cpu_session_component::client_sd(Thread_capability thread_cap)
{
Object_pool<Cpu_thread_component>::Guard
thread(_thread_ep->lookup_and_lock(thread_cap));
if (!thread) return Untyped_capability();
enum { DUMMY_LOCAL_NAME = 0 };
typedef Native_capability::Dst Dst;
return Untyped_capability(Dst(thread->platform_thread()->client_sd()),
DUMMY_LOCAL_NAME);
}

View File

@ -0,0 +1,28 @@
/*
* \brief Platform-specific parts of cores CPU-service
* \author Martin Stein
* \date 2012-04-17
*/
/*
* Copyright (C) 2009-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 <base/printf.h>
/* Core includes */
#include <cpu_session_component.h>
using namespace Genode;
Ram_dataspace_capability Cpu_session_component::utcb(Thread_capability thread_cap)
{
PERR("%s: Not implemented", __PRETTY_FUNCTION__);
return Ram_dataspace_capability();
}

View File

@ -0,0 +1,49 @@
/*
* \brief Capability allocation service
* \author Norman Feske
* \date 2006-06-26
*/
/*
* 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.
*/
#ifndef _CORE__INCLUDE__LINUX__CAP_SESSION_COMPONENT_H_
#define _CORE__INCLUDE__LINUX__CAP_SESSION_COMPONENT_H_
#include <cap_session/cap_session.h>
#include <base/rpc_server.h>
#include <base/lock.h>
namespace Genode {
class Cap_session_component : public Rpc_object<Cap_session>
{
private:
static long _unique_id_cnt;
static Lock &_lock()
{
static Lock static_lock;
return static_lock;
}
public:
Cap_session_component(Allocator *md_alloc, const char *args) {}
Native_capability alloc(Native_capability ep)
{
Lock::Guard lock_guard(_lock());
return Native_capability(ep.dst(), ++_unique_id_cnt);
}
void free(Native_capability cap) { }
};
}
#endif /* _CORE__INCLUDE__LINUX__CAP_SESSION_COMPONENT_H_ */

View File

@ -0,0 +1,209 @@
/*
* \brief Core-specific environment for Linux
* \author Norman Feske
* \author Christian Helmuth
* \date 2006-07-28
*
* The Core-specific environment ensures that all sessions of Core's
* environment a local.
*/
/*
* 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.
*/
#ifndef _CORE__INCLUDE__CORE_ENV_H_
#define _CORE__INCLUDE__CORE_ENV_H_
/* core includes */
#include <platform.h>
#include <core_parent.h>
#include <cap_session_component.h>
#include <ram_session_component.h>
/* internal base includes */
#include <platform_env.h>
namespace Genode {
/**
* Lock-guarded version of a RAM-session implementation
*
* \param RAM_SESSION_IMPL non-thread-safe RAM-session class
*
* In contrast to normal processes, core's 'env()->ram_session()' is not
* synchronized by an RPC interface. However, it is accessed by different
* threads using the 'env()->heap()' and the sliced heap used for
* allocating sessions to core's services.
*/
template <typename RAM_SESSION_IMPL>
class Synchronized_ram_session : public RAM_SESSION_IMPL
{
private:
Lock _lock;
public:
/**
* Constructor
*/
Synchronized_ram_session(Rpc_entrypoint *ds_ep,
Rpc_entrypoint *ram_session_ep,
Range_allocator *ram_alloc,
Allocator *md_alloc,
const char *args,
size_t quota_limit = 0)
:
RAM_SESSION_IMPL(ds_ep, ram_session_ep, ram_alloc, md_alloc, args, quota_limit)
{ }
/***************************
** RAM-session interface **
***************************/
Ram_dataspace_capability alloc(size_t size, bool cached)
{
Lock::Guard lock_guard(_lock);
return RAM_SESSION_IMPL::alloc(size, cached);
}
void free(Ram_dataspace_capability ds)
{
Lock::Guard lock_guard(_lock);
RAM_SESSION_IMPL::free(ds);
}
int ref_account(Ram_session_capability session)
{
Lock::Guard lock_guard(_lock);
return RAM_SESSION_IMPL::ref_account(session);
}
int transfer_quota(Ram_session_capability session, size_t size)
{
Lock::Guard lock_guard(_lock);
return RAM_SESSION_IMPL::transfer_quota(session, size);
}
size_t quota()
{
Lock::Guard lock_guard(_lock);
return RAM_SESSION_IMPL::quota();
}
size_t used()
{
Lock::Guard lock_guard(_lock);
return RAM_SESSION_IMPL::used();
}
};
class Core_env : public Platform_env_base
{
public:
/**
* Entrypoint with support for local object access
*
* Within core, there are a few cases where the RPC objects must
* be invoked by direct function calls instead of using RPC.
* I.e., when an entrypoint dispatch function performs a memory
* allocation via the 'Sliced_heap', the 'attach' function of
* 'Rm_session_mmap' tries to obtain the dataspace's size and fd.
* Normally, this would be done by calling the entrypoint but the
* entrypoint cannot call itself. To support this special case,
* the 'Entrypoint' extends the 'Rpc_entrypoint' with the
* functionality needed to lookup an RPC object by its capability.
*/
struct Entrypoint : Rpc_entrypoint
{
enum { STACK_SIZE = 2048 * sizeof(Genode::addr_t) };
Entrypoint(Cap_session *cap_session)
:
Rpc_entrypoint(cap_session, STACK_SIZE, "entrypoint")
{ }
};
private:
typedef Synchronized_ram_session<Ram_session_component> Core_ram_session;
Core_parent _core_parent;
Cap_session_component _cap_session;
Entrypoint _entrypoint;
Core_ram_session _ram_session;
Heap _heap;
Ram_session_capability const _ram_session_cap;
public:
/**
* Constructor
*/
Core_env()
:
Platform_env_base(Ram_session_capability(),
Cpu_session_capability(),
Pd_session_capability()),
_cap_session(platform()->core_mem_alloc(), "ram_quota=4K"),
_entrypoint(&_cap_session),
_ram_session(&_entrypoint, &_entrypoint,
platform()->ram_alloc(), platform()->core_mem_alloc(),
"ram_quota=4M", platform()->ram_alloc()->avail()),
_heap(&_ram_session, Platform_env_base::rm_session()),
_ram_session_cap(_entrypoint.manage(&_ram_session))
{ }
/**
* Destructor
*/
~Core_env() { parent()->exit(0); }
/**************************************
** Core-specific accessor functions **
**************************************/
Cap_session *cap_session() { return &_cap_session; }
Entrypoint *entrypoint() { return &_entrypoint; }
/*******************
** Env interface **
*******************/
Parent *parent() { return &_core_parent; }
Ram_session *ram_session() { return &_ram_session; }
Ram_session_capability ram_session_cap() { return _ram_session_cap; }
Allocator *heap() { return &_heap; }
Cpu_session_capability cpu_session_cap() {
PWRN("%s:%u not implemented", __FILE__, __LINE__);
return Cpu_session_capability();
}
Pd_session *pd_session()
{
PWRN("%s:%u not implemented", __FILE__, __LINE__);
return 0;
}
void reload_parent_cap(Capability<Parent>::Dst, long) { }
};
/**
* Request pointer to static environment of Core
*/
extern Core_env *core_env();
}
#endif /* _CORE__INCLUDE__CORE_ENV_H_ */

View File

@ -0,0 +1,229 @@
/*
* \brief Linux system calls that are used in core only
* \author Norman Feske
* \date 2012-08-10
*/
/*
* Copyright (C) 2011-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 _CORE__INCLUDE__CORE_LINUX_SYSCALLS_H_
#define _CORE__INCLUDE__CORE_LINUX_SYSCALLS_H_
/* basic Linux syscall bindings */
#include <linux_syscalls.h>
#include <sys/stat.h>
#include <fcntl.h>
/*******************************************************
** Functions used by core's ram-session support code **
*******************************************************/
inline int lx_mkdir(char const *pathname, mode_t mode)
{
return lx_syscall(SYS_mkdir, pathname, mode);
}
inline int lx_ftruncate(int fd, unsigned long length)
{
return lx_syscall(SYS_ftruncate, fd, length);
}
inline int lx_unlink(const char *fname)
{
return lx_syscall(SYS_unlink, fname);
}
inline int lx_dup(int fd)
{
return lx_syscall(SYS_dup, fd);
}
/*******************************************************
** Functions used by core's rom-session support code **
*******************************************************/
inline int lx_open(const char *pathname, int flags, mode_t mode = 0)
{
return lx_syscall(SYS_open, pathname, flags, mode);
}
inline int lx_stat(const char *path, struct stat64 *buf)
{
#ifdef _LP64
return lx_syscall(SYS_stat, path, buf);
#else
return lx_syscall(SYS_stat64, path, buf);
#endif
}
/**************************************
** Process creation and destruction **
**************************************/
inline int lx_execve(const char *filename, char *const argv[],
char *const envp[])
{
return lx_syscall(SYS_execve, filename, argv, envp);
}
inline int lx_kill(int pid, int signal)
{
return lx_syscall(SYS_kill, pid, signal);
}
inline int lx_create_process(int (*entry)(void *), void *stack, void *arg)
{
/*
* The low byte of the flags denotes the signal to be sent to the parent
* when the process terminates. We want core to receive SIGCHLD signals on
* this condition.
*/
int const flags = CLONE_VFORK | LX_SIGCHLD;
return lx_clone((int (*)(void *))entry, stack, flags, arg);
}
inline int lx_setuid(unsigned int uid)
{
return lx_syscall(SYS_setuid, uid);
}
inline int lx_setgid(unsigned int gid)
{
return lx_syscall(SYS_setgid, gid);
}
/**
* Query PID of any terminated child
*
* This function is called be core after having received a SIGCHLD signal to
* determine the PID of a terminated Genode process.
*
* \return PID of terminated process or -1 if no process was terminated
*/
inline int lx_pollpid()
{
return lx_syscall(SYS_wait4, -1 /* any PID */, (int *)0, 1 /* WNOHANG */, 0);
}
/*********************
** Chroot handling **
*********************/
inline int lx_chroot(char const *path)
{
return lx_syscall(SYS_chroot, path);
}
inline int lx_chdir(char const *path)
{
return lx_syscall(SYS_chdir, path);
}
inline int lx_getcwd(char *dst, size_t dst_len)
{
return lx_syscall(SYS_getcwd, dst, dst_len);
}
inline int lx_bindmount(char const *source, char const *target)
{
enum { MS_BIND = 4096 };
return lx_syscall(SYS_mount, source, target, 0, MS_BIND, 0);
}
inline int lx_umount(char const *target)
{
return lx_syscall(SYS_umount2, target, 0);
}
/********************************************
** Communication over Unix-domain sockets **
********************************************/
#ifdef SYS_socketcall
inline int lx_socket(int domain, int type, int protocol)
{
long args[3] = { domain, type, protocol };
return lx_socketcall(SYS_SOCKET, args);
}
inline int lx_bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen)
{
long args[3] = { sockfd, (long)addr, (long)addrlen };
return lx_socketcall(SYS_BIND, args);
}
inline int lx_connect(int sockfd, const struct sockaddr *serv_addr,
socklen_t addrlen)
{
long args[3] = { sockfd, (long)serv_addr, (long)addrlen };
return lx_socketcall(SYS_CONNECT, args);
}
#else
inline int lx_socket(int domain, int type, int protocol)
{
return lx_syscall(SYS_socket, domain, type, protocol);
}
inline int lx_bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen)
{
return lx_syscall(SYS_bind, sockfd, addr, addrlen);
}
inline int lx_connect(int sockfd, const struct sockaddr *serv_addr,
socklen_t addrlen)
{
return lx_syscall(SYS_connect, sockfd, serv_addr, addrlen);
}
#endif /* SYS_socketcall */
/******************************
** Linux signal dispatching **
******************************/
inline int lx_pipe(int pipefd[2])
{
return lx_syscall(SYS_pipe, pipefd);
}
inline int lx_read(int fd, void *buf, Genode::size_t count)
{
return lx_syscall(SYS_read, fd, buf, count);
}
#endif /* _CORE__INCLUDE__CORE_LINUX_SYSCALLS_H_ */

View File

@ -0,0 +1,205 @@
/*
* \brief Core-specific instance of the CPU session/thread interfaces
* \author Christian Helmuth
* \author Norman Feske
* \date 2006-07-17
*/
/*
* 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.
*/
#ifndef _CORE__INCLUDE__CPU_SESSION_COMPONENT_H_
#define _CORE__INCLUDE__CPU_SESSION_COMPONENT_H_
/* Genode includes */
#include <util/list.h>
#include <base/allocator_guard.h>
#include <base/lock.h>
#include <base/pager.h>
#include <base/rpc_server.h>
#include <linux_cpu_session/linux_cpu_session.h>
/* core includes */
#include <cpu_thread_allocator.h>
#include <platform_thread.h>
#include <trace/control_area.h>
#include <trace/source_registry.h>
namespace Genode {
/**
* RPC interface of CPU thread
*
* We make 'Cpu_thread' a RPC object only to be able to lookup CPU threads
* from thread capabilities supplied as arguments to CPU-session functions.
* A CPU thread does not provide an actual RPC interface.
*/
struct Cpu_thread
{
GENODE_RPC_INTERFACE();
};
class Cpu_thread_component : public Rpc_object<Cpu_thread>,
public List<Cpu_thread_component>::Element
{
public:
typedef Trace::Session_label Session_label;
typedef Trace::Thread_name Thread_name;
private:
Thread_name const _name;
Platform_thread _platform_thread;
bool _bound; /* pd binding flag */
Signal_context_capability _sigh; /* exception handler */
unsigned const _trace_control_index;
Trace::Source _trace_source;
public:
Cpu_thread_component(Session_label const &label,
Thread_name const &name,
unsigned priority, addr_t utcb,
Signal_context_capability sigh,
unsigned trace_control_index,
Trace::Control &trace_control)
:
_name(name),
_platform_thread(name.string(), priority, utcb), _bound(false),
_sigh(sigh), _trace_control_index(trace_control_index),
_trace_source(label, _name, trace_control)
{
update_exception_sigh();
}
/************************
** Accessor functions **
************************/
Platform_thread *platform_thread() { return &_platform_thread; }
bool bound() const { return _bound; }
void bound(bool b) { _bound = b; }
Trace::Source *trace_source() { return &_trace_source; }
void sigh(Signal_context_capability sigh)
{
_sigh = sigh;
update_exception_sigh();
}
/**
* Propagate exception handler to platform thread
*/
void update_exception_sigh();
/**
* Return index within the CPU-session's trace control area
*/
unsigned trace_control_index() const { return _trace_control_index; }
};
class Cpu_session_component : public Rpc_object<Linux_cpu_session>
{
public:
typedef Cpu_thread_component::Session_label Session_label;
private:
Session_label _label;
Rpc_entrypoint *_thread_ep;
Pager_entrypoint *_pager_ep;
Allocator_guard _md_alloc; /* guarded meta-data allocator */
Cpu_thread_allocator _thread_alloc; /* meta-data allocator */
Lock _thread_alloc_lock; /* protect allocator access */
List<Cpu_thread_component> _thread_list;
Lock _thread_list_lock; /* protect thread list */
unsigned _priority; /* priority of threads
created with this
session */
Affinity::Location _location; /* CPU affinity of this
session */
Trace::Source_registry &_trace_sources;
Trace::Control_area _trace_control_area;
/**
* Exception handler that will be invoked unless overridden by a
* call of 'Cpu_session::exception_handler'.
*/
Signal_context_capability _default_exception_handler;
/**
* Raw thread-killing functionality
*
* This function is called from the 'kill_thread' function and
* the destructor. Each these functions grab the list lock
* by themselves and call this function to perform the actual
* killing.
*/
void _unsynchronized_kill_thread(Cpu_thread_component *thread);
public:
/**
* Constructor
*/
Cpu_session_component(Rpc_entrypoint *thread_ep,
Pager_entrypoint *pager_ep,
Allocator *md_alloc,
Trace::Source_registry &trace_sources,
const char *args, Affinity const &affinity);
/**
* Destructor
*/
~Cpu_session_component();
/**
* Register quota donation at allocator guard
*/
void upgrade_ram_quota(size_t ram_quota) { _md_alloc.upgrade(ram_quota); }
/***************************
** CPU session interface **
***************************/
Thread_capability create_thread(Name const &, addr_t);
Ram_dataspace_capability utcb(Thread_capability thread);
void kill_thread(Thread_capability);
int set_pager(Thread_capability, Pager_capability);
int start(Thread_capability, addr_t, addr_t);
void pause(Thread_capability thread_cap);
void resume(Thread_capability thread_cap);
void cancel_blocking(Thread_capability);
int name(Thread_capability, char *, size_t);
Thread_state state(Thread_capability);
void state(Thread_capability, Thread_state const &);
void exception_handler(Thread_capability, Signal_context_capability);
Affinity::Space affinity_space() const;
void affinity(Thread_capability, Affinity::Location);
Dataspace_capability trace_control();
unsigned trace_control_index(Thread_capability);
Dataspace_capability trace_buffer(Thread_capability);
Dataspace_capability trace_policy(Thread_capability);
/*******************************
** Linux-specific extensions **
*******************************/
void thread_id(Thread_capability, int, int);
Untyped_capability server_sd(Thread_capability);
Untyped_capability client_sd(Thread_capability);
};
}
#endif /* _CORE__INCLUDE__CPU_SESSION_COMPONENT_H_ */

View File

@ -0,0 +1,125 @@
/*
* \brief Core-internal dataspace representation on Linux
* \author Norman Feske
* \date 2006-05-19
*
* On Linux userland, we do not deal with physical memory. Instead,
* we create a file for each dataspace that is to be mmapped.
* Therefore, the allocator is not really used for allocating
* memory but only as a container for quota.
*/
/*
* 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.
*/
#ifndef _CORE__INCLUDE__LINUX__DATASPACE_COMPONENT_H_
#define _CORE__INCLUDE__LINUX__DATASPACE_COMPONENT_H_
#include <linux_dataspace/linux_dataspace.h>
#include <util/string.h>
#include <util/misc_math.h>
#include <base/rpc_server.h>
#include <base/printf.h>
namespace Genode {
/**
* Deriving classes can own a dataspace to implement conditional behavior
*/
class Dataspace_owner { };
class Dataspace_component : public Rpc_object<Linux_dataspace>
{
private:
size_t _size; /* size of dataspace in bytes */
addr_t _addr; /* meaningless on linux */
Filename _fname; /* filename for mmap */
int _fd; /* file descriptor */
bool _writable; /* false if read-only */
/* Holds the dataspace owner if a distinction between owner and
* others is necessary on the dataspace, otherwise it is 0 */
Dataspace_owner * _owner;
public:
/**
* Constructor
*/
Dataspace_component(size_t size, addr_t addr,
bool /* write_combined */, bool writable,
Dataspace_owner * owner)
: _size(size), _addr(addr), _fd(-1), _writable(writable),
_owner(owner) { }
/**
* Default constructor returns invalid dataspace
*/
Dataspace_component()
: _size(0), _addr(0), _fd(-1), _writable(false), _owner(0) { }
/**
* This constructor is only provided for compatibility
* reasons and should not be used.
*/
Dataspace_component(size_t size, addr_t core_local_addr,
addr_t phys_addr, bool write_combined,
bool writable, Dataspace_owner * _owner)
:
_size(size), _addr(phys_addr), _fd(-1), _owner(_owner)
{
PWRN("Should only be used for IOMEM and not within Linux.");
_fname.buf[0] = 0;
}
/**
* Define corresponding filename of dataspace
*
* The file name is only relevant for ROM dataspaces that should
* be executed via execve.
*/
void fname(const char *fname) { strncpy(_fname.buf, fname, sizeof(_fname.buf)); }
/**
* Assign file descriptor to dataspace
*
* The file descriptor assigned to the dataspace will be enable
* processes outside of core to mmap the dataspace.
*/
void fd(int fd) { _fd = fd; }
/**
* Check if dataspace is owned by a specified object
*/
bool owner(Dataspace_owner * const o) const { return _owner == o; }
/*************************
** Dataspace interface **
*************************/
size_t size() { return _size; }
addr_t phys_addr() { return _addr; }
bool writable() { return _writable; }
/****************************************
** Linux-specific dataspace interface **
****************************************/
Filename fname() { return _fname; }
Untyped_capability fd()
{
typedef Untyped_capability::Dst Dst;
enum { DUMMY_LOCAL_NAME = 0 };
return Untyped_capability(Dst(_fd), DUMMY_LOCAL_NAME);
}
};
}
#endif /* _CORE__INCLUDE__LINUX__DATASPACE_COMPONENT_H_ */

View File

@ -0,0 +1,64 @@
/*
* \brief Core-specific instance of the IO_MEM session interface (Linux)
* \author Christian Helmuth
* \date 2007-09-14
*/
/*
* Copyright (C) 2007-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 _CORE__INCLUDE__LINUX__IO_MEM_SESSION_COMPONENT_H_
#define _CORE__INCLUDE__LINUX__IO_MEM_SESSION_COMPONENT_H_
/* Genode includes */
#include <base/allocator.h>
#include <base/printf.h>
#include <base/rpc_server.h>
#include <io_mem_session/io_mem_session.h>
/* core includes */
#include <dataspace_component.h>
namespace Genode {
class Io_mem_session_component : public Rpc_object<Io_mem_session>
{
public:
/**
* Constructor
*
* \param io_mem_alloc MMIO region allocator
* \param ram_alloc RAM allocator that will be checked for
* region collisions
* \param ds_ep entry point to manage the dataspace
* corresponding the io_mem session
* \param args session construction arguments, in
* particular MMIO region base, size and
* caching demands
*/
Io_mem_session_component(Range_allocator *io_mem_alloc,
Range_allocator *ram_alloc,
Rpc_entrypoint *ds_ep,
const char *args);
/**
* Destructor
*/
~Io_mem_session_component() { }
/*****************************
** Io-mem session interface **
*****************************/
Io_mem_dataspace_capability dataspace() {
return Io_mem_dataspace_capability(); }
};
}
#endif /* _CORE__INCLUDE__LINUX__IO_MEM_SESSION_COMPONENT_H_ */

View File

@ -0,0 +1,60 @@
/*
* \brief Core-specific instance of the IRQ session interface for Linux
* \author Christian Helmuth
* \date 2007-09-13
*/
/*
* Copyright (C) 2007-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 _CORE__INCLUDE__LINUX__IRQ_SESSION_COMPONENT_H_
#define _CORE__INCLUDE__LINUX__IRQ_SESSION_COMPONENT_H_
#include <util/list.h>
#include <base/lock.h>
#include <base/rpc_server.h>
#include <irq_session/irq_session.h>
namespace Genode {
class Irq_session_component : public List<Irq_session_component>::Element
{
public:
/**
* Constructor
*
* \param cap_session capability session to use
* \param irq_alloc platform-dependent IRQ allocator
* \param args session construction arguments
*/
Irq_session_component(Cap_session *cap_session,
Range_allocator *irq_alloc,
const char *args) { }
/**
* Destructor
*/
~Irq_session_component() { }
/**
* Return capability to this session
*
* Capability is always invalid under Linux.
*/
Session_capability cap() const { return Session_capability(); }
/***************************
** Irq session interface **
***************************/
void wait_for_irq() { }
};
}
#endif /* _CORE__INCLUDE__LINUX__IRQ_SESSION_COMPONENT_H_ */

View File

@ -0,0 +1,69 @@
/*
* \brief Linux-specific PD session
* \author Norman Feske
* \date 2012-08-15
*/
/*
* Copyright (C) 2012-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 _CORE__INCLUDE__PD_SESSION_COMPONENT_H_
#define _CORE__INCLUDE__PD_SESSION_COMPONENT_H_
/* Genode includes */
#include <base/rpc_server.h>
#include <linux_pd_session/linux_pd_session.h>
/* core includes */
#include <platform_pd.h>
namespace Genode {
class Pd_session_component : public Rpc_object<Linux_pd_session, Pd_session_component>
{
private:
enum { LABEL_MAX_LEN = 1024 };
enum { ROOT_PATH_MAX_LEN = 512 };
unsigned long _pid;
char _label[LABEL_MAX_LEN];
char _root[ROOT_PATH_MAX_LEN];
unsigned _uid;
unsigned _gid;
Parent_capability _parent;
Rpc_entrypoint *_ds_ep;
public:
/**
* Constructor
*
* \param ds_ep entrypoint where the dataspaces are managed
*/
Pd_session_component(Rpc_entrypoint *ds_ep, const char *args);
~Pd_session_component();
/**************************
** PD session interface **
**************************/
int bind_thread(Thread_capability);
int assign_parent(Parent_capability parent);
/******************************
** Linux-specific extension **
******************************/
void start(Capability<Dataspace> binary);
};
}
#endif /* _CORE__INCLUDE__PD_SESSION_COMPONENT_H_ */

View File

@ -0,0 +1,100 @@
/*
* \brief Linux platform
* \author Christian Helmuth
* \author Norman Feske
* \date 2007-09-10
*/
/*
* Copyright (C) 2007-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 _CORE__INCLUDE__LINUX__PLATFORM_H_
#define _CORE__INCLUDE__LINUX__PLATFORM_H_
#include <base/sync_allocator.h>
#include <base/allocator_avl.h>
#include <base/lock_guard.h>
#include <platform_generic.h>
#include <platform_pd.h>
#include <platform_thread.h>
namespace Genode {
using namespace Genode;
class Platform : public Platform_generic
{
private:
/**
* Allocator for core-internal meta data
*/
Synchronized_range_allocator<Allocator_avl> _core_mem_alloc;
/**
* Allocator for pseudo physical memory
*/
struct Pseudo_ram_allocator : Range_allocator
{
bool alloc(size_t size, void **out_addr)
{
*out_addr = 0;
return true;
}
Alloc_return alloc_aligned(size_t, void **out_addr, int)
{
*out_addr = 0;
return Alloc_return::OK;
}
Alloc_return alloc_addr(size_t, addr_t)
{
return Alloc_return::OK;;
}
int add_range(addr_t, size_t) { return 0; }
int remove_range(addr_t, size_t) { return 0; }
void free(void *) { }
void free(void *, size_t) { }
size_t avail() { return ~0; }
bool valid_addr(addr_t) { return true; }
size_t overhead(size_t) { return 0; }
bool need_size_for_free() const override { return true; }
};
Pseudo_ram_allocator _ram_alloc;
public:
/**
* Constructor
*/
Platform();
/********************************
** Generic platform interface **
********************************/
Range_allocator *core_mem_alloc() { return &_core_mem_alloc; }
Range_allocator *ram_alloc() { return &_ram_alloc; }
Range_allocator *io_mem_alloc() { return 0; }
Range_allocator *io_port_alloc() { return 0; }
Range_allocator *irq_alloc() { return 0; }
Range_allocator *region_alloc() { return 0; }
addr_t vm_start() const { return 0; }
size_t vm_size() const { return 0; }
Rom_fs *rom_fs() { return 0; }
void wait_for_exit();
};
}
#endif /* _CORE__INCLUDE__PLATFORM_H_ */

View File

@ -0,0 +1,25 @@
/*
* \brief Linux protection domain facility
* \author Norman Feske
* \date 2006-06-13
*
* Pretty dumb.
*/
/*
* 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.
*/
#ifndef _CORE__INCLUDE__LINUX__PLATFORM_PD_H_
#define _CORE__INCLUDE__LINUX__PLATFORM_PD_H_
namespace Genode {
class Platform_pd
{ };
}
#endif /* _CORE__INCLUDE__LINUX__PLATFORM_PD_H_ */

View File

@ -0,0 +1,159 @@
/*
* \brief Linux thread facility
* \author Norman Feske
* \date 2006-06-13
*
* Pretty dumb.
*/
/*
* 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.
*/
#ifndef _CORE__INCLUDE__LINUX__PLATFORM_THREAD_H_
#define _CORE__INCLUDE__LINUX__PLATFORM_THREAD_H_
#include <base/pager.h>
#include <base/thread_state.h>
#include <cpu_session/cpu_session.h>
namespace Genode {
class Platform_thread;
/*
* We hold all Platform_thread objects in a list in order to be able to
* reflect SIGCHLD as exception signals. When a SIGCHILD occurs, we
* determine the PID of the terminated child process via 'wait4'. We use
* the list to find the 'Platform_thread' matching the TID, wherei, in
* turn, we find the exception handler's 'Signal_context_capability'.
*/
class Platform_thread : public List<Platform_thread>::Element
{
private:
struct Registry
{
Lock _lock;
List<Platform_thread> _list;
void insert(Platform_thread *thread);
void remove(Platform_thread *thread);
/**
* Trigger exception handler for 'Platform_thread' with matching PID.
*/
void submit_exception(unsigned long pid);
};
/**
* Return singleton instance of 'Platform_thread::Registry'
*/
static Registry *_registry();
unsigned long _tid;
unsigned long _pid;
char _name[32];
/**
* Unix-domain socket pair bound to the thread
*/
Native_connection_state _ncs;
/*
* Dummy pager object that is solely used for storing the
* 'Signal_context_capability' for the thread's exception handler.
*/
Pager_object _pager;
public:
/**
* Constructor
*/
Platform_thread(const char *name, unsigned priority, addr_t);
~Platform_thread();
/**
* Cancel currently blocking operation
*/
void cancel_blocking();
/**
* Pause this thread
*/
void pause();
/**
* Resume this thread
*/
void resume();
/**
* Dummy implementation of platform-thread interface
*/
Pager_object *pager() { return &_pager; }
void pager(Pager_object *) { }
int start(void *ip, void *sp) { return 0; }
Thread_state state()
{
PDBG("Not implemented");
throw Cpu_session::State_access_failed();
}
void state(Thread_state)
{
PDBG("Not implemented");
throw Cpu_session::State_access_failed();
}
const char *name() { return _name; }
/**
* Set the executing CPU for this thread
*
* SMP is currently not directly supported on Genode/Linux
* (but indirectly by the Linux kernel).
*/
void affinity(Affinity::Location) { }
/**
* Request the affinity of this thread
*/
Affinity::Location affinity() { return Affinity::Location(); }
/**
* Register process ID and thread ID of thread
*/
void thread_id(int pid, int tid) { _pid = pid, _tid = tid; }
/**
* Return client-side socket descriptor
*
* For more information, please refer to the comments in
* 'linux_cpu_session/linux_cpu_session.h'.
*/
int client_sd();
/**
* Return server-side socket descriptor
*/
int server_sd();
/**
* Notify Genode::Signal handler about sigchld
*/
static void submit_exception(int pid)
{
_registry()->submit_exception(pid);
}
};
}
#endif /* _CORE__INCLUDE__LINUX__PLATFORM_THREAD_H_ */

View File

@ -0,0 +1,44 @@
/*
* \brief Genode resource path
* \author Norman Feske
* \date 2012-08-10
*/
/*
* Copyright (C) 2012-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 _CORE__INCLUDE__RPATH_H_
#define _CORE__INCLUDE__RPATH_H_
/* Linux syscall bindings */
#include <linux_syscalls.h>
/* Genode includes */
#include <base/snprintf.h>
/**
* Return resource path for Genode
*
* Genode creates files for dataspaces and endpoints under in this directory.
*/
static inline char const *resource_path()
{
struct Resource_path
{
char string[32];
Resource_path()
{
Genode::snprintf(string, sizeof(string), "/tmp/genode-%d", lx_getuid());
}
};
static Resource_path path;
return path.string;
}
#endif /* _CORE__INCLUDE__RPATH_H_ */

View File

@ -0,0 +1,76 @@
/*
* \brief Core-specific instance of the RM session interface
* \author Christian Helmuth
* \date 2006-07-17
*
* Dummies for Linux platform
*/
/*
* 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.
*/
#ifndef _CORE__INCLUDE__LINUX__RM_SESSION_COMPONENT_H_
#define _CORE__INCLUDE__LINUX__RM_SESSION_COMPONENT_H_
/* Genode */
#include <base/pager.h>
#include <base/allocator.h>
#include <base/rpc_server.h>
#include <rm_session/rm_session.h>
namespace Genode {
struct Rm_client;
class Rm_session_component : public Rpc_object<Rm_session>
{
private:
class Rm_dataspace_component {
public:
void sub_rm_session(Native_capability _cap) { }
};
public:
Rm_session_component(Rpc_entrypoint *ds_ep,
Rpc_entrypoint *thread_ep,
Rpc_entrypoint *session_ep,
Allocator *md_alloc,
size_t ram_quota,
Pager_entrypoint *pager_ep,
addr_t vm_start,
size_t vm_size) { }
void upgrade_ram_quota(size_t ram_quota) { }
Local_addr attach(Dataspace_capability, size_t, off_t, bool, Local_addr, bool) {
return (addr_t)0; }
void detach(Local_addr) { }
Pager_capability add_client(Thread_capability) {
return Pager_capability(); }
void remove_client(Pager_capability) { }
void fault_handler(Signal_context_capability) { }
State state() { return State(); }
Dataspace_capability dataspace() { return Dataspace_capability(); }
Rm_dataspace_component *dataspace_component() { return 0; }
};
struct Rm_member { Rm_session_component *member_rm_session() { return 0; } };
struct Rm_client : Pager_object, Rm_member { };
}
#endif /* _CORE__INCLUDE__LINUX__RM_SESSION_COMPONENT_H_ */

View File

@ -0,0 +1,107 @@
/*
* \brief Support for communication over Unix domain sockets
* \author Norman Feske
* \date 2012-08-10
*/
/*
* Copyright (C) 2011-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 _CORE__INCLUDE__SERVER_SOCKET_PAIR_H_
#define _CORE__INCLUDE__SERVER_SOCKET_PAIR_H_
/* Linux syscall bindings */
#include <core_linux_syscalls.h>
#include <sys/socket.h>
#include <sys/un.h>
/* include from 'src/base/ipc' */
#include <socket_descriptor_registry.h>
/* core-local includes */
#include <resource_path.h>
/**
* Utility: Create socket address for server entrypoint at thread ID
*/
struct Uds_addr : sockaddr_un
{
Uds_addr(long thread_id)
{
sun_family = AF_UNIX;
Genode::snprintf(sun_path, sizeof(sun_path), "%s/ep-%ld",
resource_path(), thread_id);
}
};
/**
* Utility: Create named socket pair for given unique ID
*/
static inline Genode::Native_connection_state create_server_socket_pair(long id)
{
Genode::Native_connection_state ncs;
/*
* Main thread uses 'Ipc_server' for 'sleep_forever()' only. No need for
* binding.
*/
if (id == -1)
return ncs;
Uds_addr addr(id);
/*
* Create server-side socket
*/
ncs.server_sd = lx_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (ncs.server_sd < 0) {
PRAW("Error: Could not create server-side socket (ret=%d)", ncs.server_sd);
class Server_socket_failed { };
throw Server_socket_failed();
}
/* make sure bind succeeds */
lx_unlink(addr.sun_path);
int const bind_ret = lx_bind(ncs.server_sd, (sockaddr *)&addr, sizeof(addr));
if (bind_ret < 0) {
PRAW("Error: Could not bind server socket (ret=%d)", bind_ret);
class Bind_failed { };
throw Bind_failed();
}
/*
* Create client-side socket
*/
ncs.client_sd = lx_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (ncs.client_sd < 0) {
PRAW("Error: Could not create client-side socket (ret=%d)", ncs.client_sd);
class Client_socket_failed { };
throw Client_socket_failed();
}
int const conn_ret = lx_connect(ncs.client_sd, (sockaddr *)&addr, sizeof(addr));
if (conn_ret < 0) {
PRAW("Error: Could not connect client-side socket (ret=%d)", conn_ret);
class Connect_failed { };
throw Connect_failed();
}
ncs.client_sd = Genode::ep_sd_registry()->try_associate(ncs.client_sd, id);
/*
* Wipe Unix domain socket from the file system. It will live as long as
* there exist references to it in the form of file descriptors.
*/
lx_unlink(addr.sun_path);
return ncs;
}
#endif /* _CORE__INCLUDE__SERVER_SOCKET_PAIR_H_ */

View File

@ -0,0 +1,22 @@
/*
* \brief Core-internal utilities
* \author Stefan Kalkowski
* \date 2014-03-10
*/
/*
* Copyright (C) 2014 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 _CORE__INCLUDE__UTIL_H_
#define _CORE__INCLUDE__UTIL_H_
namespace Genode {
constexpr size_t get_page_size_log2() { return 12; }
constexpr size_t get_page_size() { return 1 << get_page_size_log2(); }
}
#endif /* _CORE__INCLUDE__UTIL_H_ */

View File

@ -0,0 +1,27 @@
/*
* \brief Linux-specific IO_MEM service
* \author Christian Helmuth
* \date 2006-09-01
*/
/*
* 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.
*/
#include <base/printf.h>
#include <io_mem_session_component.h>
using namespace Genode;
Io_mem_session_component::Io_mem_session_component(Range_allocator *io_mem_alloc,
Range_allocator *ram_alloc,
Rpc_entrypoint *ds_ep,
const char *args)
{
PWRN("no io_mem support on Linux (args=\"%s\")", args);
}

View File

@ -0,0 +1,455 @@
/*
* \brief Core implementation of the PD session interface
* \author Norman Feske
* \date 2012-08-15
*/
/*
* Copyright (C) 2012-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 <util/arg_string.h>
#include <base/printf.h>
#include <base/snprintf.h>
/* core-local includes */
#include <pd_session_component.h>
#include <dataspace_component.h>
/* Linux includes */
#include <core_linux_syscalls.h>
using namespace Genode;
/***********************************
** Utilities for chroot handling **
***********************************/
enum { MAX_PATH_LEN = 256 };
/**
* Return true if specified path is an existing directory
*/
static bool is_directory(char const *path)
{
struct stat64 s;
if (lx_stat(path, &s) != 0)
return false;
if (!(s.st_mode & S_IFDIR))
return false;
return true;
}
static bool is_path_delimiter(char c) { return c == '/'; }
static bool has_trailing_path_delimiter(char const *path)
{
char last_char = 0;
for (; *path; path++)
last_char = *path;
return is_path_delimiter(last_char);
}
/**
* Return number of path elements of given path
*/
static Genode::size_t num_path_elements(char const *path)
{
Genode::size_t count = 0;
/*
* If path starts with non-slash, the first characters belongs to a path
* element.
*/
if (*path && !is_path_delimiter(*path))
count = 1;
/* count slashes */
for (; *path; path++)
if (is_path_delimiter(*path))
count++;
return count;
}
static bool leading_path_elements(char const *path, unsigned num,
char *dst, Genode::size_t dst_len)
{
/* counter of path delimiters */
unsigned count = 0;
unsigned i = 0;
if (is_path_delimiter(path[0]))
num++;
for (; path[i] && (count < num) && (i < dst_len); i++)
{
if (is_path_delimiter(path[i]))
count++;
if (count == num)
break;
dst[i] = path[i];
}
if (i + 1 < dst_len) {
dst[i] = 0;
return true;
}
/* string is cut, append null termination anyway */
dst[dst_len - 1] = 0;
return false;
}
static void mirror_path_to_chroot(char const *chroot_path, char const *path)
{
char target_path[MAX_PATH_LEN];
Genode::snprintf(target_path, sizeof(target_path), "%s%s",
chroot_path, path);
/*
* Create directory hierarchy pointing to the target path except for the
* last element. The last element will be bind-mounted to refer to the
* original 'path'.
*/
for (unsigned i = 1; i <= num_path_elements(target_path); i++)
{
char buf[MAX_PATH_LEN];
leading_path_elements(target_path, i, buf, sizeof(buf));
/* skip existing directories */
if (is_directory(buf))
continue;
/* create new directory */
lx_mkdir(buf, 0777);
}
lx_umount(target_path);
int ret = 0;
if ((ret = lx_bindmount(path, target_path)))
PERR("bind mount failed (errno=%d)", ret);
}
/**
* Setup content of chroot environment as prerequisite to 'execve' new
* processes within the environment. I.e., the current working directory
* containing the ROM modules must be mounted at the same location within the
* chroot environment.
*/
static bool setup_chroot_environment(char const *chroot_path)
{
using namespace Genode;
static char cwd_path[MAX_PATH_LEN];
lx_getcwd(cwd_path, sizeof(cwd_path));
/*
* Validate chroot path
*/
if (!is_directory(chroot_path)) {
PERR("chroot path does not point to valid directory");
return false;
}
if (has_trailing_path_delimiter(chroot_path)) {
PERR("chroot path has trailing slash");
return false;
}
/*
* Hardlink directories needed for running Genode within the chroot
* environment.
*/
mirror_path_to_chroot(chroot_path, cwd_path);
return true;
}
/***************
** Utilities **
***************/
/**
* Argument frame for passing 'execve' paremeters through 'clone'
*/
struct Execve_args
{
char const *filename;
char const *root;
char * const *argv;
char * const *envp;
unsigned int const uid;
unsigned int const gid;
int const parent_sd;
Execve_args(char const *filename,
char const *root,
char * const *argv,
char * const *envp,
unsigned int uid,
unsigned int gid,
int parent_sd)
:
filename(filename), root(root), argv(argv), envp(envp),
uid(uid), gid(gid), parent_sd(parent_sd)
{ }
};
/**
* Startup code of the new child process
*/
static int _exec_child(Execve_args *arg)
{
lx_dup2(arg->parent_sd, PARENT_SOCKET_HANDLE);
/* change to chroot environment */
if (arg->root && arg->root[0]) {
char cwd[1024];
PDBG("arg->root='%s'", arg->root);
if (setup_chroot_environment(arg->root) == false) {
PERR("Could not setup chroot environment");
return -1;
}
if (!lx_getcwd(cwd, sizeof(cwd))) {
PERR("Failed to getcwd");
return -1;
}
PLOG("changing root of %s (PID %d) to %s",
arg->filename, lx_getpid(), arg->root);
int ret = lx_chroot(arg->root);
if (ret < 0) {
PERR("Syscall chroot failed (errno %d)", ret);
return -1;
}
ret = lx_chdir(cwd);
if (ret < 0) {
PERR("chdir to new chroot failed");
return -1;
}
}
/*
* Set UID and GID
*
* We must set the GID prior setting the UID because setting the GID won't
* be possible anymore once we set the UID to non-root.
*/
if (arg->gid) {
int const ret = lx_setgid(arg->gid);
if (ret)
PWRN("Could not set PID %d (%s) to GID %u (error %d)",
lx_getpid(), arg->filename, arg->gid, ret);
}
if (arg->uid) {
int const ret = lx_setuid(arg->uid);
if (ret)
PWRN("Could not set PID %d (%s) to UID %u (error %d)",
lx_getpid(), arg->filename, arg->uid, ret);
}
return lx_execve(arg->filename, arg->argv, arg->envp);
}
/**
* List of Unix environment variables, initialized by the startup code
*/
extern char **lx_environ;
/**
* Read environment variable as string
*
* If no matching key exists, return an empty string.
*/
static const char *get_env(const char *key)
{
Genode::size_t key_len = Genode::strlen(key);
for (char **curr = lx_environ; curr && *curr; curr++)
if ((Genode::strcmp(*curr, key, key_len) == 0) && (*curr)[key_len] == '=')
return (const char *)(*curr + key_len + 1);
return "";
}
/**************************
** PD session interface **
**************************/
Pd_session_component::Pd_session_component(Rpc_entrypoint *ep, const char *args)
:
_pid(0), _uid(0), _gid(0), _ds_ep(ep)
{
Arg_string::find_arg(args, "label").string(_label, sizeof(_label),
"<unlabeled>");
/*
* Read Linux-specific session arguments
*/
Arg_string::find_arg(args, "root").string(_root, sizeof(_root), "");
_uid = Arg_string::find_arg(args, "uid").ulong_value(0);
_gid = Arg_string::find_arg(args, "gid").ulong_value(0);
bool const is_chroot = (Genode::strcmp(_root, "") != 0);
/*
* If a UID is specified but no GID, we use the UID as GID. This way, a
* configuration error where the UID is defined but the GID is left
* undefined won't result in the execution of the new process with the
* root user's GID.
*/
if (_gid == 0)
_gid = _uid;
/*
* Print Linux-specific session arguments if specified
*
* This output used for the automated 'lx_pd_args' test.
*/
if (is_chroot || _uid || _gid)
printf("PD session for '%s'\n", _label);
if (is_chroot) printf(" root: %s\n", _root);
if (_uid) printf(" uid: %u\n", _uid);
if (_gid) printf(" gid: %u\n", _gid);
}
Pd_session_component::~Pd_session_component()
{
if (_pid)
lx_kill(_pid, 9);
}
int Pd_session_component::bind_thread(Thread_capability) { return -1; }
int Pd_session_component::assign_parent(Parent_capability parent)
{
_parent = parent;
return 0;
}
void Pd_session_component::start(Capability<Dataspace> binary)
{
const char *tmp_filename = "temporary_executable_elf_dataspace_file_for_execve";
/* lookup binary dataspace */
Object_pool<Dataspace_component>::Guard ds(_ds_ep->lookup_and_lock(binary));
if (!ds) {
PERR("could not lookup binary, aborted PD startup");
return; /* XXX reflect error to client */
}
const char *filename = ds->fname().buf;
/*
* In order to be executable via 'execve', a program must be represented as
* a file on the Linux file system. However, this is not the case for a
* plain RAM dataspace that contains an ELF image. In this case, we copy
* the dataspace content into a temporary file whose path is passed to
* 'execve()'.
*/
if (strcmp(filename, "") == 0) {
filename = tmp_filename;
int tmp_binary_fd = lx_open(filename, O_CREAT | O_EXCL | O_WRONLY, S_IRWXU);
if (tmp_binary_fd < 0) {
PERR("Could not create file '%s'", filename);
return; /* XXX reflect error to client */
}
char buf[4096];
int num_bytes = 0;
while ((num_bytes = lx_read(ds->fd().dst().socket, buf, sizeof(buf))) != 0)
lx_write(tmp_binary_fd, buf, num_bytes);
lx_close(tmp_binary_fd);
}
/* pass parent capability as environment variable to the child */
enum { ENV_STR_LEN = 256 };
static char envbuf[5][ENV_STR_LEN];
Genode::snprintf(envbuf[1], ENV_STR_LEN, "parent_local_name=%lu",
_parent.local_name());
Genode::snprintf(envbuf[2], ENV_STR_LEN, "DISPLAY=%s",
get_env("DISPLAY"));
Genode::snprintf(envbuf[3], ENV_STR_LEN, "HOME=%s",
get_env("HOME"));
Genode::snprintf(envbuf[4], ENV_STR_LEN, "LD_LIBRARY_PATH=%s",
get_env("LD_LIBRARY_PATH"));
char *env[] = { &envbuf[0][0], &envbuf[1][0], &envbuf[2][0],
&envbuf[3][0], &envbuf[4][0], 0 };
/* prefix name of Linux program (helps killing some zombies) */
char const *prefix = "[Genode] ";
char pname_buf[sizeof(_label) + sizeof(prefix)];
snprintf(pname_buf, sizeof(pname_buf), "%s%s", prefix, _label);
char *argv_buf[2];
argv_buf[0] = pname_buf;
argv_buf[1] = 0;
/*
* We cannot create the new process via 'fork()' because all our used
* memory including stack memory is backed by dataspaces, which had been
* mapped with the 'MAP_SHARED' flag. Therefore, after being created, the
* new process starts using the stack with the same physical memory pages
* as used by parent process. This would ultimately lead to stack
* corruption. To prevent both processes from concurrently accessing the
* same stack, we pause the execution of the parent until the child calls
* 'execve'. From then on, the child has its private memory layout. The
* desired behaviour is normally provided by 'vfork' but we use the more
* modern 'clone' call for this purpose.
*/
enum { STACK_SIZE = 4096 };
static char stack[STACK_SIZE]; /* initial stack used by the child until
calling 'execve' */
/*
* Argument frame as passed to 'clone'. Because, we can only pass a single
* pointer, all arguments are embedded within the 'execve_args' struct.
*/
Execve_args arg(filename, _root, argv_buf, env, _uid, _gid,
_parent.dst().socket);
_pid = lx_create_process((int (*)(void *))_exec_child,
stack + STACK_SIZE - sizeof(umword_t), &arg);
if (strcmp(filename, tmp_filename) == 0)
lx_unlink(filename);
};

View File

@ -0,0 +1,247 @@
/*
* \brief Linux platform interface implementation
* \author Norman Feske
* \date 2006-06-13
*/
/*
* 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 <base/lock.h>
#include <linux_dataspace/client.h>
/* local includes */
#include "platform.h"
#include "core_env.h"
#include "server_socket_pair.h"
/* Linux includes */
#include <core_linux_syscalls.h>
using namespace Genode;
/**
* Memory pool used for for core-local meta data
*/
static char _core_mem[80*1024*1024];
/*
* Basic semaphore implementation based on the 'pipe' syscall.
*
* This alternative implementation is needed to be able to wake up the
* blocked main thread from a signal handler executed by the same thread.
*/
class Pipe_semaphore
{
private:
int _pipefd[2];
public:
Pipe_semaphore()
{
lx_pipe(_pipefd);
}
void down()
{
char dummy;
while(lx_read(_pipefd[0], &dummy, 1) != 1);
}
void up()
{
char dummy;
while (lx_write(_pipefd[1], &dummy, 1) != 1);
}
};
static Pipe_semaphore _wait_for_exit_sem; /* wakeup of '_wait_for_exit' */
static bool _do_exit = false; /* exit condition */
static void sigint_handler(int signum)
{
_do_exit = true;
_wait_for_exit_sem.up();
}
static void sigchld_handler(int signnum)
{
_wait_for_exit_sem.up();
}
Platform::Platform()
: _core_mem_alloc(0)
{
/* catch control-c */
lx_sigaction(LX_SIGINT, sigint_handler);
/* catch SIGCHLD */
lx_sigaction(LX_SIGCHLD, sigchld_handler);
/* create resource directory under /tmp */
lx_mkdir(resource_path(), S_IRWXU);
_core_mem_alloc.add_range((addr_t)_core_mem, sizeof(_core_mem));
/*
* Occupy the socket handle that will be used to propagate the parent
* capability new processes. Otherwise, there may be the chance that the
* parent capability as supplied by the process creator will be assigned to
* this handle, which would result in a 'dup2' syscall taking
* PARENT_SOCKET_HANDLE as both source and target descriptor.
*/
lx_dup2(0, PARENT_SOCKET_HANDLE);
}
void Platform::wait_for_exit()
{
for (;;) {
/*
* Block until a signal occurs.
*/
_wait_for_exit_sem.down();
/*
* Each time, the '_wait_for_exit_sem' gets unlocked, we could have
* received either a SIGINT or SIGCHLD. If a SIGINT was received, the
* '_exit' condition will be set.
*/
if (_do_exit)
return;
/*
* Reflect SIGCHLD as exception signal to the signal context of the CPU
* session of the process. Because multiple children could have been
* terminated, we iterate until 'pollpid' (wrapper around 'wait4')
* returns -1.
*/
for (;;) {
int const pid = lx_pollpid();
if (pid <= 0)
break;
Platform_thread::submit_exception(pid);
}
}
}
void Core_parent::exit(int exit_value)
{
lx_exit_group(exit_value);
}
/*****************************
** Support for IPC library **
*****************************/
namespace Genode {
Native_connection_state server_socket_pair()
{
return create_server_socket_pair(Thread_base::myself()->tid().tid);
}
void destroy_server_socket_pair(Native_connection_state const &ncs)
{
/*
* As entrypoints in core are never destructed, this function is only
* called on IPC-client destruction. In this case, it's a no-op in core
* as well as in Genode processes.
*/
if (ncs.server_sd != -1 || ncs.client_sd != -1)
PERR("%s called for IPC server which should never happen", __func__);
}
}
/****************************************************
** Support for Platform_env_base::Rm_session_mmap **
****************************************************/
Genode::size_t
Platform_env_base::Rm_session_mmap::_dataspace_size(Capability<Dataspace> ds_cap)
{
if (!ds_cap.valid())
return Dataspace_capability::deref(ds_cap)->size();
/* use RPC if called from a different thread */
if (!core_env()->entrypoint()->is_myself()) {
/* release Rm_session_mmap::_lock during RPC */
_lock.unlock();
Genode::size_t size = Dataspace_client(ds_cap).size();
_lock.lock();
return size;
}
/* use local function call if called from the entrypoint */
Object_pool<Rpc_object_base>::Guard
ds_rpc(core_env()->entrypoint()->lookup_and_lock(ds_cap));
Dataspace * ds = dynamic_cast<Dataspace *>(&*ds_rpc);
return ds ? ds->size() : 0;
}
int Platform_env_base::Rm_session_mmap::_dataspace_fd(Capability<Dataspace> ds_cap)
{
if (!core_env()->entrypoint()->is_myself()) {
/* release Rm_session_mmap::_lock during RPC */
_lock.unlock();
int socket = Linux_dataspace_client(ds_cap).fd().dst().socket;
_lock.lock();
return socket;
}
Capability<Linux_dataspace> lx_ds_cap = static_cap_cast<Linux_dataspace>(ds_cap);
Object_pool<Rpc_object_base>::Guard
ds_rpc(core_env()->entrypoint()->lookup_and_lock(lx_ds_cap));
Linux_dataspace * ds = dynamic_cast<Linux_dataspace *>(&*ds_rpc);
/*
* Return a duplicate of the dataspace file descriptor, which will be freed
* immediately after mmap'ing the file (see 'Rm_session_mmap').
*
* Handing out the original file descriptor would result in the premature
* release of the descriptor. So the descriptor could be reused (i.e., as a
* socket descriptor during the RPC handling). When later destroying the
* dataspace, the descriptor would unexpectedly be closed again.
*/
return ds ? lx_dup(ds->fd().dst().socket) : -1;
}
bool Platform_env_base::Rm_session_mmap::_dataspace_writable(Dataspace_capability ds_cap)
{
if (!core_env()->entrypoint()->is_myself()) {
/* release Rm_session_mmap::_lock during RPC */
_lock.unlock();
bool writable = Dataspace_client(ds_cap).writable();
_lock.lock();
return writable;
}
Object_pool<Rpc_object_base>::Guard
ds_rpc(core_env()->entrypoint()->lookup_and_lock(ds_cap));
Dataspace * ds = dynamic_cast<Dataspace *>(&*ds_rpc);
return ds ? ds->writable() : false;
}

View File

@ -0,0 +1,133 @@
/*
* \brief Linux-specific platform thread implementation
* \author Norman Feske
* \date 2007-10-15
*/
/*
* Copyright (C) 2007-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 <util/token.h>
#include <util/misc_math.h>
#include <base/printf.h>
/* local includes */
#include "platform_thread.h"
#include "server_socket_pair.h"
using namespace Genode;
typedef Token<Scanner_policy_identifier_with_underline> Tid_token;
/*******************************
** Platform_thread::Registry **
*******************************/
void Platform_thread::Registry::insert(Platform_thread *thread)
{
Lock::Guard guard(_lock);
_list.insert(thread);
}
void Platform_thread::Registry::remove(Platform_thread *thread)
{
Lock::Guard guard(_lock);
_list.remove(thread);
}
void Platform_thread::Registry::submit_exception(unsigned long pid)
{
Lock::Guard guard(_lock);
/* traverse list to find 'Platform_thread' with matching PID */
for (Platform_thread *curr = _list.first(); curr; curr = curr->next()) {
if (curr->_tid == pid) {
Signal_context_capability sigh = curr->_pager._sigh;
if (sigh.valid())
Signal_transmitter(sigh).submit();
return;
}
}
}
Platform_thread::Registry *Platform_thread::_registry()
{
static Platform_thread::Registry registry;
return &registry;
}
/*********************
** Platform_thread **
*********************/
Platform_thread::Platform_thread(const char *name, unsigned, addr_t)
: _tid(-1), _pid(-1)
{
strncpy(_name, name, min(sizeof(_name), strlen(name) + 1));
_registry()->insert(this);
}
Platform_thread::~Platform_thread()
{
ep_sd_registry()->disassociate(_ncs.client_sd);
if (_ncs.client_sd)
lx_close(_ncs.client_sd);
if (_ncs.server_sd)
lx_close(_ncs.server_sd);
_registry()->remove(this);
}
void Platform_thread::cancel_blocking()
{
PDBG("send cancel-blocking signal to %ld\n", _tid);
lx_tgkill(_pid, _tid, LX_SIGUSR1);
}
void Platform_thread::pause()
{
PDBG("not implemented");
}
void Platform_thread::resume()
{
PDBG("not implemented");
}
int Platform_thread::client_sd()
{
/* construct socket pair on first call */
if (_ncs.client_sd == -1)
_ncs = create_server_socket_pair(_tid);
return _ncs.client_sd;
}
int Platform_thread::server_sd()
{
client_sd();
return _ncs.server_sd;
}

View File

@ -0,0 +1,64 @@
/*
* \brief Make dataspace accessible to other Linux processes
* \author Norman Feske
* \date 2006-07-03
*/
/*
* 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.
*/
/* glibc includes */
#include <fcntl.h>
/* Genode includes */
#include <base/snprintf.h>
/* local includes */
#include <ram_session_component.h>
#include <resource_path.h>
/* Linux syscall bindings */
#include <core_linux_syscalls.h>
using namespace Genode;
static int ram_ds_cnt = 0; /* counter for creating unique dataspace IDs */
void Ram_session_component::_export_ram_ds(Dataspace_component *ds)
{
char fname[Linux_dataspace::FNAME_LEN];
/* create file using a unique file name in the resource path */
snprintf(fname, sizeof(fname), "%s/ds-%d", resource_path(), ram_ds_cnt++);
lx_unlink(fname);
int const fd = lx_open(fname, O_CREAT|O_RDWR|O_TRUNC|LX_O_CLOEXEC, S_IRWXU);
lx_ftruncate(fd, ds->size());
/* remember file descriptor in dataspace component object */
ds->fd(fd);
/*
* Wipe the file from the Linux file system. The kernel will still keep the
* then unnamed file around until the last reference to the file will be
* gone (i.e., an open file descriptor referring to the file). A process
* w/o the right file descriptor won't be able to open and access the file.
*/
lx_unlink(fname);
}
void Ram_session_component::_revoke_ram_ds(Dataspace_component *ds)
{
int const fd = ds->fd().dst().socket;
if (fd != -1)
lx_close(fd);
}
void Ram_session_component::_clear_ds(Dataspace_component *ds) { }

View File

@ -0,0 +1,84 @@
/*
* \brief Linux-specific core implementation of the ROM session interface
* \author Norman Feske
* \date 2006-07-06
*
* The Linux version of ROM session component does not use the
* Rom_fs as provided as constructor argument. Instead, we map
* rom modules directly to files of the host file system.
*/
/*
* 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.
*/
/* Linux includes */
#include <core_linux_syscalls.h>
#include <sys/fcntl.h>
/* Genode includes */
#include <linux_dataspace/linux_dataspace.h>
#include <util/arg_string.h>
#include <root/root.h>
/* local includes */
#include "rom_session_component.h"
using namespace Genode;
static Genode::size_t file_size(const char *path)
{
struct stat64 s;
if (lx_stat(path, &s) < 0)
return 0;
else
return s.st_size;
}
Rom_session_component::Rom_session_component(Rom_fs *rom_fs,
Rpc_entrypoint *ds_ep,
const char *args)
: _ds_ep(ds_ep)
{
/* extract filename from session arguments */
char fname[Linux_dataspace::FNAME_LEN];
Arg_string::find_arg(args, "filename").string(fname, sizeof(fname), "");
/* only files inside the current working directory are allowed */
for (const char *c = fname; *c; c++)
if (*c == '/')
throw Root::Invalid_args();
Genode::size_t const fsize = file_size(fname);
/* use invalid capability as default value */
_ds_cap = Rom_dataspace_capability();
/* ROM module not found */
if (fsize == 0)
throw Root::Invalid_args();
int const fd = lx_open(fname, O_RDONLY | LX_O_CLOEXEC, S_IRUSR | S_IXUSR);
_ds = Dataspace_component(fsize, 0, false, false, 0);
_ds.fd(fd);
_ds.fname(fname);
Dataspace_capability ds_cap = _ds_ep->manage(&_ds);
_ds_cap = static_cap_cast<Rom_dataspace>(ds_cap);
}
Rom_session_component::~Rom_session_component()
{
_ds_ep->dissolve(&_ds);
int const fd = _ds.fd().dst().socket;
if (fd != -1)
lx_close(fd);
}

View File

@ -0,0 +1,60 @@
TARGET = core
REQUIRES = linux
LIBS = cxx base-common syscall startup
GEN_CORE_DIR = $(BASE_DIR)/src/core
SRC_CC = main.cc \
platform.cc \
platform_thread.cc \
platform_services.cc \
ram_session_component.cc \
ram_session_support.cc \
rom_session_component.cc \
cpu_session_component.cc \
cpu_session_extension.cc \
cpu_session_support.cc \
pd_session_component.cc \
io_mem_session_component.cc \
signal_session_component.cc \
signal_source_component.cc \
trace_session_component.cc \
thread_linux.cc \
context_area.cc \
core_printf.cc \
thread.cc
INC_DIR += $(REP_DIR)/src/core/include \
$(GEN_CORE_DIR)/include \
$(REP_DIR)/src/platform \
$(REP_DIR)/src/base/ipc \
$(REP_DIR)/src/base/env \
$(BASE_DIR)/src/base/env \
$(REP_DIR)/src/base/console \
$(BASE_DIR)/src/base/thread \
HOST_INC_DIR += /usr/include
#
# core does not use POSIX threads when built for the 'lx_hybrid_x86' platform,
# so we need to reserve the thread-context area via a segment in the program to
# prevent clashes with vdso and shared libraries.
#
ifeq ($(findstring always_hybrid, $(SPECS)), always_hybrid)
LD_SCRIPT_STATIC = $(LD_SCRIPT_DEFAULT) \
$(call select_from_repositories,src/platform/context_area.stdlib.ld)
endif
include $(GEN_CORE_DIR)/version.inc
vpath main.cc $(GEN_CORE_DIR)
vpath ram_session_component.cc $(GEN_CORE_DIR)
vpath cpu_session_component.cc $(GEN_CORE_DIR)
vpath platform_services.cc $(GEN_CORE_DIR)
vpath signal_session_component.cc $(GEN_CORE_DIR)
vpath signal_source_component.cc $(GEN_CORE_DIR)
vpath trace_session_component.cc $(GEN_CORE_DIR)
vpath core_printf.cc $(BASE_DIR)/src/base/console
vpath thread.cc $(BASE_DIR)/src/base/thread
vpath trace.cc $(BASE_DIR)/src/base/thread
vpath %.cc $(PRG_DIR)

View File

@ -0,0 +1,62 @@
/*
* \brief Implementation of the core-internal Thread API via Linux threads
* \author Norman Feske
* \date 2006-06-13
*/
/*
* 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 <base/thread.h>
#include <base/sleep.h>
/* Linux syscall bindings */
#include <linux_syscalls.h>
using namespace Genode;
static void empty_signal_handler(int) { }
void Thread_base::_thread_start()
{
/*
* Set signal handler such that canceled system calls get not transparently
* retried after a signal gets received.
*/
lx_sigaction(LX_SIGUSR1, empty_signal_handler);
/*
* Deliver SIGCHLD signals to no thread other than the main thread. Core's
* main thread will handle the signals while executing the 'wait_for_exit'
* function, which is known to not hold any locks that would interfere with
* the handling of the signal.
*/
lx_sigsetmask(LX_SIGCHLD, false);
Thread_base::myself()->entry();
Thread_base::myself()->_join_lock.unlock();
sleep_forever();
}
void Thread_base::_init_platform_thread(Type) { }
void Thread_base::_deinit_platform_thread() { }
void Thread_base::start()
{
_tid.tid = lx_create_thread(Thread_base::_thread_start, stack_top(), this);
_tid.pid = lx_getpid();
}
void Thread_base::cancel_blocking() { }

View File

@ -0,0 +1,94 @@
/*
* \brief Linux clone() binding
* \author Christian Prochaska
* \date 2012-05-05
*
* based on eglibc-2.11.3/ports/sysdeps/unix/sysv/linux/arm/clone.S
*/
#define SYS_clone 120
#define SYS_exit 1
#define SYS_getpid 20
#define __ARM_EABI__ 1
#define CLONE_VM 0x00000100
#define CLONE_THREAD 0x00010000
.text
.globl lx_clone
.type lx_clone, #function
lx_clone:
@ insert the args onto the new stack
str r3, [r1, #-4]!
str r0, [r1, #-4]!
@ do the system call
@ get flags
mov r0, r2
#ifdef RESET_PID
mov ip, r2
#endif
@ new sp is already in r1
#ifdef __ARM_EABI__
stmfd sp!, {r4, r7}
#else
str r4, [sp, #-8]!
#endif
ldr r2, [sp, #8]
ldr r3, [sp, #12]
ldr r4, [sp, #16]
#ifdef __ARM_EABI__
ldr r7, =SYS_clone
swi 0x0
#else
swi SYS_clone
#endif
cmp r0, #0
beq 1f
#ifdef __ARM_EABI__
ldmfd sp!, {r4, r7}
#else
ldr r4, [sp], #8
#endif
#blt PLTJMP(C_SYMBOL_NAME(__syscall_error))
bx lr
1:
#ifdef RESET_PID
tst ip, #CLONE_THREAD
bne 3f
mov r0, #0xffff0fff
mov lr, pc
sub pc, r0, #31
mov r1, r0
tst ip, #CLONE_VM
movne r0, #-1
#ifdef __ARM_EABI__
ldr r7, =SYS_getpid
swieq 0x0
#else
swieq SYS_getpid
#endif
str r0, [r1, #PID_OFFSET]
str r0, [r1, #TID_OFFSET]
3:
#endif
@ pick the function arg and call address off the stack and execute
ldr r0, [sp, #4]
#if defined(__ARM_ARCH_4T__) && defined(__THUMB_INTERWORK__)
ldr ip, [sp], #8
mov lr, pc
bx ip
#else
mov lr, pc
ldr pc, [sp], #8
#endif
@ and we are done, passing the return value through r0
ldr r7, =SYS_exit
swi 0x0
/* tell the linker that this code does not need an executable stack */
.section .note.GNU-stack, "", %progbits

View File

@ -0,0 +1,29 @@
/*
* \brief Linux syscall() binding
* \author Christian Prochaska
* \date 2012-05-05
*
* based on eglibc-2.11.3/ports/sysdeps/unix/sysv/linux/arm/syscall.S
*
* error case:
* glibc's syscall() function returns -1 and sets errno
* lx_syscall() returns -errno
*/
.text
.globl lx_syscall
.type lx_syscall, #function
lx_syscall:
mov ip, sp
stmfd sp!, {r4, r5, r6, r7}
mov r7, r0
mov r0, r1
mov r1, r2
mov r2, r3
ldmfd ip, {r3, r4, r5, r6}
swi 0x0
ldmfd sp!, {r4, r5, r6, r7}
bx lr
/* tell the linker that this code does not need an executable stack */
.section .note.GNU-stack, "", %progbits

View File

@ -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 <base/thread.h>
#include <rm_session/rm_session.h>
#include <linux_syscalls.h>
/* Linux includes */
#include <sys/mman.h>
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_ */

View File

@ -0,0 +1,25 @@
/*
* \brief Linux-specific linker script additions (STDLIB = no)
* \author Christian Helmuth
* \date 2010-09-22
*/
/*
* 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.
*/
PHDRS
{
context_area PT_LOAD FLAGS(0);
}
SECTIONS
{
. = 0x40000000;
_context_area_start = .;
.context_area : { . += 0x10000000; } : context_area
_context_area_end = .;
}

View File

@ -0,0 +1,20 @@
/*
* \brief Linux-specific linker script additions (STDLIB = yes)
* \author Christian Helmuth
* \date 2010-09-22
*/
/*
* 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.
*/
SECTIONS
{
. = 0x40000000;
_context_area_start = .;
.context_area : { . += 0x10000000; }
_context_area_end = .;
}

View File

@ -0,0 +1,391 @@
/*
* \brief Linux system-call bindings
* \author Norman Feske
* \date 2008-10-22
*
* This file is meant to be internally used by the framework. It is not public
* interface.
*
* From within the framework libraries, we have to use the Linux syscall
* interface directly rather than relying on convenient libc functions to be
* able to link this part of the framework to a custom libc. Otherwise, we
* would end up with very nasty cyclic dependencies when using framework
* functions such as IPC from the libc back end.
*
* The Linux syscall interface looks different for 32bit and 64bit system, in
* particular regarding the socket interface. On 32bit systems, all socket
* operations are invoked via the 'socketcall' syscall. On 64bit systems, the
* different socket functions have distinct syscalls.
*/
/*
* Copyright (C) 2008-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__LINUX_SYSCALLS_H_
#define _PLATFORM__LINUX_SYSCALLS_H_
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1 /* needed to enable the definition of 'stat64' */
#endif
/* Linux includes */
#include <linux/futex.h>
#include <unistd.h>
#include <signal.h>
#include <sched.h>
#include <sys/syscall.h>
/* Genode includes */
#include <util/string.h>
#include <base/printf.h>
#include <base/snprintf.h>
/***********************************
** Low-level debugging utilities **
***********************************/
extern "C" void wait_for_continue(void);
extern "C" int raw_write_str(const char *str);
#define PRAW(fmt, ...) \
do { \
char str[128]; \
Genode::snprintf(str, sizeof(str), \
ESC_ERR fmt ESC_END "\n", ##__VA_ARGS__); \
raw_write_str(str); \
} while (0)
/*********************************************************
** System-call bindings implemented in syscall library **
*********************************************************/
extern "C" long lx_syscall(int number, ...);
extern "C" int lx_clone(int (*fn)(void *), void *child_stack,
int flags, void *arg);
/*****************************************
** General syscalls used by base-linux **
*****************************************/
inline int lx_write(int fd, const void *buf, Genode::size_t count)
{
return lx_syscall(SYS_write, fd, buf, count);
}
inline int lx_close(int fd)
{
return lx_syscall(SYS_close, fd);
}
inline int lx_dup2(int fd, int to)
{
return lx_syscall(SYS_dup2, fd, to);
}
/*****************************************
** Functions used by the IPC framework **
*****************************************/
#include <linux/net.h>
#ifdef SYS_socketcall
inline int lx_socketcall(int call, long *args)
{
int res = lx_syscall(SYS_socketcall, call, args);
return res;
}
inline int lx_socketpair(int domain, int type, int protocol, int sd[2])
{
long args[4] = { domain, type, protocol, (long)sd };
return lx_socketcall(SYS_SOCKETPAIR, args);
}
inline int lx_sendmsg(int sockfd, const struct msghdr *msg, int flags)
{
long args[3] = { sockfd, (long)msg, flags };
return lx_socketcall(SYS_SENDMSG, args);
}
inline int lx_recvmsg(int sockfd, struct msghdr *msg, int flags)
{
long args[3] = { sockfd, (long)msg, flags };
return lx_socketcall(SYS_RECVMSG, args);
}
inline int lx_getpeername(int sockfd, struct sockaddr *name, socklen_t *namelen)
{
long args[3] = { sockfd, (long)name, (long)namelen };
return lx_socketcall(SYS_GETPEERNAME, args);
}
#else
inline int lx_socketpair(int domain, int type, int protocol, int sd[2])
{
return lx_syscall(SYS_socketpair, domain, type, protocol, (unsigned long)sd);
}
inline int lx_sendmsg(int sockfd, const struct msghdr *msg, int flags)
{
return lx_syscall(SYS_sendmsg, sockfd, msg, flags);
}
inline int lx_recvmsg(int sockfd, struct msghdr *msg, int flags)
{
return lx_syscall(SYS_recvmsg, sockfd, msg, flags);
}
inline int lx_getpeername(int sockfd, struct sockaddr *name, socklen_t *namelen)
{
return lx_syscall(SYS_getpeername, sockfd, name, namelen);
}
/* TODO add missing socket system calls */
#endif /* SYS_socketcall */
/*******************************************
** Functions used by the process library **
*******************************************/
inline void lx_exit(int status)
{
lx_syscall(SYS_exit, status);
}
inline void lx_exit_group(int status)
{
lx_syscall(SYS_exit_group, status);
}
/************************************************************
** Functions used by the env library and local rm session **
************************************************************/
/* O_CLOEXEC is a GNU extension so we provide it here */
enum { LX_O_CLOEXEC = 02000000 };
inline void *lx_mmap(void *start, Genode::size_t length, int prot, int flags,
int fd, off_t offset)
{
#ifdef _LP64
return (void *)lx_syscall(SYS_mmap, start, length, prot, flags, fd, offset);
#else
return (void *)lx_syscall(SYS_mmap2, start, length, prot, flags, fd, offset/4096);
#endif /* _LP64 */
}
inline int lx_munmap(void *addr, size_t length)
{
return lx_syscall(SYS_munmap, addr, length);
}
/***********************************************************************
** Functions used by thread lib and core's cancel-blocking mechanism **
***********************************************************************/
enum {
LX_SIGINT = 2, /* used by core to catch Control-C */
LX_SIGUSR1 = 10, /* used for cancel-blocking mechanism */
LX_SIGCHLD = 17, /* child process changed state, i.e., terminated */
LX_SIGCANCEL = 32, /* accoring to glibc, this equals SIGRTMIN,
used for killing threads */
};
struct kernel_sigaction
{
void (*handler)(int);
unsigned long flags;
void (*restorer)(void);
sigset_t mask;
};
inline int lx_sigemptyset(sigset_t *set)
{
if (set == 0)
return -1;
Genode::memset(set, 0, sizeof(sigset_t));
return 0;
}
#ifdef _LP64
extern "C" void lx_restore_rt (void);
#endif
/**
* Simplified binding for sigaction system call
*/
inline int lx_sigaction(int signum, void (*handler)(int))
{
struct kernel_sigaction act;
act.handler = handler;
#ifdef _LP64
/*
* The SA_RESTORER flag is not officially documented, but used internally
* by the glibc implementation of sigaction(). Without specifying this flag
* tgkill() does not work on x86_64. The restorer function gets called
* when leaving the signal handler and it should call the rt_sigreturn syscall.
*/
enum { SA_RESTORER = 0x04000000 };
act.flags = SA_RESTORER;
act.restorer = lx_restore_rt;;
#else
act.flags = 0;
act.restorer = 0;
#endif
lx_sigemptyset(&act.mask);
return lx_syscall(SYS_rt_sigaction, signum, &act, 0UL, _NSIG/8);
}
/**
* Send signal to thread
*
* This function is used by core to cancel blocking operations of
* threads, and by the thread library to kill threads.
*/
inline int lx_tgkill(int pid, int tid, int signal)
{
return lx_syscall(SYS_tgkill, pid, tid, signal);
}
inline int lx_create_thread(void (*entry)(), void *stack, void *arg)
{
int flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND
| CLONE_THREAD | CLONE_SYSVSEM;
/*
* The syscall binding for clone does not exist in the FreeBSD libc, which
* we are using as libc for Genode. In glibc, clone is implemented as a
* assembler binding without external libc references. Hence, we are safe
* to rely on the glibc version of 'clone' here.
*/
return lx_clone((int (*)(void *))entry, stack, flags, arg);
}
inline pid_t lx_getpid() { return lx_syscall(SYS_getpid); }
inline pid_t lx_gettid() { return lx_syscall(SYS_gettid); }
inline uid_t lx_getuid() { return lx_syscall(SYS_getuid); }
/************************************
** Functions used by lock library **
************************************/
struct timespec;
inline int lx_nanosleep(const struct timespec *req, struct timespec *rem)
{
return lx_syscall(SYS_nanosleep, req, rem);
}
enum {
LX_FUTEX_WAIT = FUTEX_WAIT,
LX_FUTEX_WAKE = FUTEX_WAKE,
};
inline int lx_futex(const int *uaddr, int op, int val)
{
return lx_syscall(SYS_futex, uaddr, op, val, 0, 0, 0);
}
/**
* Signal set corrsponding to glibc's 'sigset_t'
*/
class Lx_sigset
{
unsigned long int _value[_SIGSET_NWORDS];
public:
/**
* Constructor
*/
Lx_sigset() { }
/**
* Constructor
*
* \param signum set specified entry of sigset
*/
Lx_sigset(int signum)
{
for (unsigned i = 0; i < _SIGSET_NWORDS; i++)
_value[i] = 0;
/*
* Both '__sigword' and '__sigmask' are macros, defined in the
* glibc header file 'bits/sigset.h' and not external functions.
* Therefore we can use them here without getting into conflicts
* with the linkage of another libc.
*/
_value[__sigword(signum)] = __sigmask(signum);
}
bool is_set(int signum) {
return _value[__sigword(signum)] && __sigmask(signum); }
};
/**
* Check if signal is pending
*
* \return true if signal is pending
*/
inline bool lx_sigpending(int signum)
{
Lx_sigset sigset;
lx_syscall(SYS_rt_sigpending, &sigset, _NSIG/8);
return sigset.is_set(signum);
}
/**
* Set signal mask state
*
* \param signum signal to mask or unmask
* \param state mask state for the signal,
* true enables the signal,
* false blocks the signal
*/
inline bool lx_sigsetmask(int signum, bool state)
{
Lx_sigset old_sigmask, sigset(signum);
lx_syscall(SYS_rt_sigprocmask, state ? SIG_UNBLOCK : SIG_BLOCK, &sigset, &old_sigmask, _NSIG/8);
return old_sigmask.is_set(signum);
}
#endif /* _PLATFORM__LINUX_SYSCALLS_H_ */

View File

@ -0,0 +1,460 @@
/*
* \brief Supplemental code for hybrid Genode/Linux programs
* \author Norman Feske
* \date 2011-09-02
*/
/*
* Copyright (C) 2011-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.
*/
#include <base/crt0.h>
#include <base/printf.h>
#include <linux_syscalls.h>
#include <linux_cpu_session/linux_cpu_session.h>
extern "C" int raw_write_str(const char *str);
enum { verbose_atexit = false };
/**
* Dummy for symbol that is normally provided by '_main.cc'
*/
int genode___cxa_atexit(void (*func)(void*), void *arg, void *dso)
{
if (verbose_atexit)
raw_write_str("genode___cxa_atexit called, not implemented\n");
return 0;
}
/*
* Manually initialize the 'lx_environ' pointer. For non-hybrid programs, this
* pointer is initialized by the startup code.
*/
extern char **environ;
extern char **lx_environ;
static void empty_signal_handler(int) { }
/*
* This function must be called before any other static constructor in the Genode
* application, so it gets the highest priority (lowest priority number >100)
*/
__attribute__((constructor(101))) void lx_hybrid_init()
{
lx_environ = environ;
/*
* Set signal handler such that canceled system calls get not
* transparently retried after a signal gets received.
*/
lx_sigaction(LX_SIGUSR1, empty_signal_handler);
}
/*
* Dummy symbols to let generic tests programs (i.e., 'test-config_args') link
* successfully. Please note that such programs are not expected to work when
* built as hybrid Linux/Genode programs because when using the glibc startup
* code, we cannot manipulate argv prior executing main. However, by defining
* these symbols, we prevent the automated build bot from stumbling over such
* binaries.
*/
char **genode_argv = 0;
int genode_argc = 1;
/************
** Thread **
************/
/*
* For hybrid Linux/Genode programs, Genode's thread API is implemented via
* POSIX threads.
*
* Hybrid Linux/Genode programs are linked against the glibc along with other
* native Linux libraries. Such libraries may use the 'pthread' API to spawn
* threads, which then may call Genode code. Vice versa, Genode threads may
* interact with code of a native Linux libraries. Hence, both worlds Genode
* and native Linux libraries should use the same underlying threading API.
* Furthermore, using the pthread API is a precondition to satisfy the glibc's
* assumption about thread-local storage, which is particularly important
* for the correct thread-local handling of 'errno'. As another benefit of
* using the pthread API over the normal Genode thread implementation, hybrid
* Linux/Genode programs comply with the GNU debugger's expectations. Such
* programs can be debugged as normal Linux programs.
*
* Genode's normal thread API for Linux was introduced to decouple Genode
* from the glibc. This is especially important when using Genode's libc
* Mixing both Genode's libc and glibc won't work.
*/
/* Genode includes */
#include <base/thread.h>
#include <base/env.h>
/* libc includes */
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
using namespace Genode;
/**
* Return TLS key used to storing the thread meta data
*/
static pthread_key_t tls_key()
{
struct Tls_key
{
pthread_key_t key;
Tls_key()
{
pthread_key_create(&key, 0);
}
};
static Tls_key inst;
return inst.key;
}
namespace Genode {
struct Thread_meta_data
{
/**
* Filled out by 'thread_start' function in the context of the new
* thread
*/
Thread_base * const thread_base;
/**
* POSIX thread handle
*/
pthread_t pt;
/**
* Constructor
*
* \param thread associated 'Thread_base' object
*/
Thread_meta_data(Thread_base *thread) : thread_base(thread) { }
/**
* Used to block the constructor until the new thread has initialized
* 'id'
*/
virtual void wait_for_construction() = 0;
virtual void constructed() = 0;
/**
* Used to block the new thread until 'start' is called
*/
virtual void wait_for_start() = 0;
virtual void started() = 0;
/**
* Used to block the 'join()' function until the 'entry()' is done
*/
virtual void wait_for_join() = 0;
virtual void joined() = 0;
};
/*
* Thread meta data for a thread created by Genode
*/
class Thread_meta_data_created : public Thread_meta_data
{
private:
/**
* Lock with the initial state set to LOCKED
*/
struct Barrier : Lock { Barrier() : Lock(Lock::LOCKED) { } };
/**
* Used to block the constructor until the new thread has initialized
* 'id'
*/
Barrier _construct_lock;
/**
* Used to block the new thread until 'start' is called
*/
Barrier _start_lock;
/**
* Used to block the 'join()' function until the 'entry()' is done
*/
Barrier _join_lock;
public:
Thread_meta_data_created(Thread_base *thread) : Thread_meta_data(thread) { }
void wait_for_construction()
{
_construct_lock.lock();
}
void constructed()
{
_construct_lock.unlock();
}
void wait_for_start()
{
_start_lock.lock();
}
void started()
{
_start_lock.unlock();
}
void wait_for_join()
{
_join_lock.lock();
}
void joined()
{
_join_lock.unlock();
}
};
/*
* Thread meta data for an adopted thread
*/
class Thread_meta_data_adopted : public Thread_meta_data
{
public:
Thread_meta_data_adopted(Thread_base *thread) : Thread_meta_data(thread) { }
void wait_for_construction()
{
PERR("wait_for_construction() called for an adopted thread");
}
void constructed()
{
PERR("constructed() called for an adopted thread");
}
void wait_for_start()
{
PERR("wait_for_start() called for an adopted thread");
}
void started()
{
PERR("started() called for an adopted thread");
}
void wait_for_join()
{
PERR("wait_for_join() called for an adopted thread");
}
void joined()
{
PERR("joined() called for an adopted thread");
}
};
}
/**
* Return Linux-specific extension of the Env::CPU session interface
*/
Linux_cpu_session *cpu_session(Cpu_session * cpu_session)
{
Linux_cpu_session *cpu = dynamic_cast<Linux_cpu_session *>(cpu_session);
if (!cpu) {
PERR("could not obtain Linux extension to CPU session interface");
struct Could_not_access_linux_cpu_session { };
throw Could_not_access_linux_cpu_session();
}
return cpu;
}
static void adopt_thread(Thread_meta_data *meta_data)
{
/*
* Set signal handler such that canceled system calls get not
* transparently retried after a signal gets received.
*/
lx_sigaction(LX_SIGUSR1, empty_signal_handler);
/*
* Prevent children from becoming zombies. (SIG_IGN = 1)
*/
lx_sigaction(LX_SIGCHLD, (void (*)(int))1);
/* assign 'Thread_meta_data' pointer to TLS entry */
pthread_setspecific(tls_key(), meta_data);
/* enable immediate cancellation when calling 'pthread_cancel' */
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
/*
* Initialize thread meta data
*/
Native_thread &native_thread = meta_data->thread_base->tid();
native_thread.tid = lx_gettid();
native_thread.pid = lx_getpid();
}
static void *thread_start(void *arg)
{
Thread_meta_data *meta_data = (Thread_meta_data *)arg;
adopt_thread(meta_data);
/* unblock 'Thread_base' constructor */
meta_data->constructed();
/* block until the 'Thread_base::start' gets called */
meta_data->wait_for_start();
Thread_base::myself()->entry();
meta_data->joined();
return 0;
}
extern "C" void *malloc(::size_t size);
Thread_base *Thread_base::myself()
{
void * const tls = pthread_getspecific(tls_key());
if (tls != 0)
return ((Thread_meta_data *)tls)->thread_base;
bool const is_main_thread = (lx_getpid() == lx_gettid());
if (is_main_thread)
return 0;
/*
* The function was called from a thread created by other means than
* Genode's thread API. This may happen if a native Linux library creates
* threads via the pthread library. If such a thread calls Genode code,
* which then tries to perform IPC, the program fails because there exists
* no 'Thread_base' object. We recover from this unfortunate situation by
* creating a dummy 'Thread_base' object and associate it with the calling
* thread.
*/
/*
* Create dummy 'Thread_base' object but suppress the execution of its
* constructor. If we called the constructor, we would create a new Genode
* thread, which is not what we want. For the allocation, we use glibc
* malloc because 'Genode::env()->heap()->alloc()' uses IPC.
*
* XXX Both the 'Thread_base' and 'Threadm_meta_data' objects are never
* freed.
*/
Thread_base *thread = (Thread_base *)malloc(sizeof(Thread_base));
memset(thread, 0, sizeof(*thread));
Thread_meta_data *meta_data = new Thread_meta_data_adopted(thread);
/*
* Initialize 'Thread_base::_tid' using the default constructor of
* 'Native_thread'. This marks the client and server sockets as
* uninitialized and prompts the IPC framework to create those as needed.
*/
meta_data->thread_base->tid() = Native_thread();
adopt_thread(meta_data);
return thread;
}
void Thread_base::start()
{
/*
* Unblock thread that is supposed to slumber in 'thread_start'.
*/
_tid.meta_data->started();
}
void Thread_base::join()
{
_tid.meta_data->wait_for_join();
}
Thread_base::Thread_base(const char *name, size_t stack_size, Type type,
Cpu_session * cpu_sess)
: _cpu_session(cpu_sess)
{
_tid.meta_data = new (env()->heap()) Thread_meta_data_created(this);
int const ret = pthread_create(&_tid.meta_data->pt, 0, thread_start,
_tid.meta_data);
if (ret) {
PERR("pthread_create failed (returned %d, errno=%d)",
ret, errno);
destroy(env()->heap(), _tid.meta_data);
throw Context_alloc_failed();
}
_tid.meta_data->wait_for_construction();
Linux_cpu_session *cpu = cpu_session(_cpu_session);
_thread_cap = cpu->create_thread(name);
cpu->thread_id(_thread_cap, _tid.pid, _tid.tid);
}
Thread_base::Thread_base(const char *name, size_t stack_size, Type type)
: Thread_base(name, stack_size, type, env()->cpu_session()) { }
void Thread_base::cancel_blocking()
{
/*
* XXX implement interaction with CPU session
*/
}
Thread_base::~Thread_base()
{
bool const needs_join = (pthread_cancel(_tid.meta_data->pt) == 0);
if (needs_join) {
int const ret = pthread_join(_tid.meta_data->pt, 0);
if (ret)
PWRN("pthread_join unexpectedly returned with %d (errno=%d)",
ret, errno);
}
Thread_meta_data_created *meta_data =
dynamic_cast<Thread_meta_data_created *>(_tid.meta_data);
if (meta_data)
destroy(env()->heap(), meta_data);
_tid.meta_data = 0;
/* inform core about the killed thread */
cpu_session(_cpu_session)->kill_thread(_thread_cap);
}

View File

@ -0,0 +1,108 @@
/*
* \brief Linux clone() binding
* \author Christian Prochaska
* \date 2009-07-14
*
* based on glibc-2.9/sysdeps/unix/sysv/linux/i386/clone.S
*/
#define L(name) name
#define ENTER_KERNEL int $0x80
#define SYS_clone 120
#define SYS_exit 1
#define LINKAGE 4
#define PTR_SIZE 4
#define PARMS LINKAGE /* no space for saved regs */
#define FUNC PARMS
#define STACK FUNC+4
#define FLAGS STACK+PTR_SIZE
#define ARG FLAGS+4
#define PTID ARG+PTR_SIZE
#define TLS PTID+PTR_SIZE
#define CTID TLS+PTR_SIZE
.text
.globl lx_clone
.type lx_clone, @function
lx_clone:
.cfi_startproc
/* Insert the argument onto the new stack. Make sure the new
thread is started with an alignment of (mod 16). */
movl STACK(%esp),%ecx
andl $0xfffffff0, %ecx
subl $28,%ecx
movl ARG(%esp),%eax /* no negative argument counts */
movl %eax,12(%ecx)
/* Save the function pointer as the zeroth argument.
It will be popped off in the child in the ebx frobbing below. */
movl FUNC(%esp),%eax
movl %eax,8(%ecx)
/* Don't leak any information. */
movl $0,4(%ecx)
movl $0,(%ecx)
/* Do the system call */
pushl %ebx
.cfi_adjust_cfa_offset (4)
pushl %esi
.cfi_adjust_cfa_offset (4)
pushl %edi
.cfi_adjust_cfa_offset (4)
movl TLS+12(%esp),%esi
.cfi_rel_offset %esi, 4
movl PTID+12(%esp),%edx
movl FLAGS+12(%esp),%ebx
.cfi_rel_offset %ebx, 8
movl CTID+12(%esp),%edi
.cfi_rel_offset %edi, 0
movl $SYS_clone,%eax
/* End FDE now, because in the child the unwind info will be
wrong. */
.cfi_endproc
ENTER_KERNEL
popl %edi
popl %esi
popl %ebx
test %eax,%eax
jz L(thread_start)
L(pseudo_end):
ret
L(thread_start):
.cfi_startproc;
/* Clearing frame pointer is insufficient, use CFI. */
.cfi_undefined %eip;
xorl %ebp,%ebp /* terminate the stack frame */
call *%ebx
#ifdef PIC
call L(here)
L(here):
popl %ebx
addl $_GLOBAL_OFFSET_TABLE_+[.-L(here)], %ebx
#endif
movl %eax, %ebx
movl $SYS_exit, %eax
ENTER_KERNEL
.cfi_endproc;
.cfi_startproc
.cfi_endproc
/*
* Allow stacks to be mapped executable (needed because Genode does not
* offer an API to handle non-execute mappings yet).
*/
.section .note.GNU-stack, "", @progbits

View File

@ -0,0 +1,83 @@
/*
* \brief Linux syscall() binding
* \author Christian Prochaska
* \date 2009-07-14
*
* based on glibc-2.9/sysdeps/unix/sysv/linux/i386/syscall.S
*
* error case:
* glibc's syscall() function returns -1 and sets errno
* lx_syscall() returns -errno
*/
#define L(name) name
#define ENTER_KERNEL int $0x80
.text
.globl lx_syscall
.type lx_syscall, @function
lx_syscall:
.cfi_startproc
/* PUSHARGS_6*/ /* Save register contents. */
/* PUSHARGS_6 begin */
pushl %ebp;
.cfi_adjust_cfa_offset 4;
.cfi_rel_offset %ebp, 0;
L(PUSHBP1):
pushl %edi;
.cfi_adjust_cfa_offset 4;
.cfi_rel_offset %edi, 0;
L(PUSHDI1):
pushl %esi;
.cfi_adjust_cfa_offset 4;
.cfi_rel_offset %esi, 0;
L(PUSHSI1):
pushl %ebx;
.cfi_adjust_cfa_offset 4;
.cfi_rel_offset %ebx, 0;
L(PUSHBX1):
/* PUSHARGS_6 end */
/*_DOARGS_6(44)*/ /* Load arguments. */
/*_DOARGS_6(44) begin*/
movl 44(%esp), %ebp;
movl 40(%esp), %edi;
movl 36(%esp), %esi;
movl 32(%esp), %edx;
movl 28(%esp), %ecx;
movl 24(%esp), %ebx;
/*_DOARGS_6(44) end*/
movl 20(%esp), %eax /* Load syscall number into %eax. */
ENTER_KERNEL /* Do the system call. */
/* POPARGS_6*/ /* Restore register contents. */
/* POPARGS_6 begin */
popl %ebx;
.cfi_adjust_cfa_offset -4;
.cfi_restore %ebx;
L(POPBX1):
popl %esi;
.cfi_adjust_cfa_offset -4;
.cfi_restore %esi;
L(POPSI1):
popl %edi;
.cfi_adjust_cfa_offset -4;
.cfi_restore %edi;
L(POPDI1):
popl %ebp;
.cfi_adjust_cfa_offset -4;
.cfi_restore %ebp;
L(POPBP1):
/* POPARGS_6 end */
L(pseudo_end):
ret /* Return to caller. */
.cfi_endproc
/*
* Allow stacks to be mapped executable (needed because Genode does not
* offer an API to handle non-execute mappings yet).
*/
.section .note.GNU-stack, "", @progbits

View File

@ -0,0 +1,74 @@
/*
* \brief Linux clone() binding
* \author Christian Prochaska
* \date 2009-07-14
*
* based on glibc-2.9/sysdeps/unix/sysv/linux/x86_64/clone.S
*/
#define L(name) name
#define SYS_clone 56
#define SYS_exit 60
.text
.globl lx_clone
.type lx_clone, @function
lx_clone:
.cfi_startproc
/* Align the new stack pointer to 16 bytes. */
andq $0xfffffffffffffff0, %rsi
/* Insert the argument onto the new stack. */
subq $16,%rsi
movq %rcx,8(%rsi)
/* Save the function pointer. It will be popped off in the
child in the ebx frobbing below. */
movq %rdi,0(%rsi)
/* Do the system call. */
movq %rdx, %rdi
movq %r8, %rdx
movq %r9, %r8
movq 8(%rsp), %r10
movl $SYS_clone,%eax
/* End FDE now, because in the child the unwind info will be
wrong. */
.cfi_endproc
syscall
testq %rax,%rax
jz L(thread_start)
L(pseudo_end):
/* parent returns */
ret
L(thread_start):
.cfi_startproc
/* Clearing frame pointer is insufficient, use CFI. */
.cfi_undefined (%rip);
/* Clear the frame pointer. The ABI suggests this be done, to mark
the outermost frame obviously. */
xorq %rbp, %rbp
/* Set up arguments for the function call. */
popq %rax /* Function to call. */
popq %rdi /* Argument. */
call *%rax
/* Call exit with return value from function call. */
movq %rax, %rdi
movq $SYS_exit, %rax
syscall
.cfi_endproc
/*
* Allow stacks to be mapped executable (needed because Genode does not
* offer an API to handle non-executable mappings yet).
*/
.section .note.GNU-stack, "", @progbits

View File

@ -0,0 +1,23 @@
/*
* \brief Linux signal handler restorer function
* \author Christian Prochaska
* \date 2009-07-14
*
*/
#define SYS_rt_sigreturn 15
.text
.globl lx_restore_rt
.type lx_restore_rt, @function
lx_restore_rt:
movq $SYS_rt_sigreturn, %rax
syscall
/*
* Allow stacks to be mapped executable (needed because Genode does not
* offer an API to handle non-execute mappings yet).
*/
.section .note.GNU-stack, "", @progbits

View File

@ -0,0 +1,36 @@
/*
* \brief Linux syscall() binding
* \author Christian Prochaska
* \date 2009-07-14
*
* based on glibc-2.9/sysdeps/unix/sysv/linux/x86_64/syscall.S
*
* error case:
* glibc's syscall() function returns -1 and sets errno
* lx_syscall() returns -errno
*/
#define L(name) name
.text
.globl lx_syscall
.type lx_syscall, @function
lx_syscall:
movq %rdi, %rax /* Syscall number -> rax. */
movq %rsi, %rdi /* shift arg1 - arg5. */
movq %rdx, %rsi
movq %rcx, %rdx
movq %r8, %r10
movq %r9, %r8
movq 8(%rsp),%r9 /* arg6 is on the stack. */
syscall /* Do the system call. */
L(pseudo_end):
ret /* Return to caller. */
/*
* Allow stacks to be mapped executable (needed because Genode does not
* offer an API to handle non-execute mappings yet).
*/
.section .note.GNU-stack, "", @progbits

View File

@ -0,0 +1,52 @@
/*
* \brief Test if global static constructors in hybrid applications get called
* \author Christian Prochaska
* \date 2011-11-24
*/
/*
* Copyright (C) 2011-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.
*/
#include <base/printf.h>
#include "testlib.h"
using namespace Genode;
struct Testapp_testclass
{
Testapp_testclass()
{
Genode::printf("Global static constructor of Genode application called\n");
}
void dummy() { }
};
extern Testlib_testclass testlib_testobject;
Testapp_testclass testapp_testobject;
int main(int argc, char *argv[])
{
printf("--- lx_hybrid global static constructor test ---\n");
/*
* Call a dummy function on each test object to make sure that the
objects don't get optimized out.
*/
testlib_testobject.dummy();
testapp_testobject.dummy();
printf("--- returning from main ---\n");
return 0;
}

View File

@ -0,0 +1,22 @@
TARGET = test-lx_hybrid_ctors
SRC_CC = main.cc
LIBS = lx_hybrid
TESTLIB_SO = libtestlib.so
TESTLIB_SRC_CC = testlib.cc
EXT_OBJECTS += $(BUILD_BASE_DIR)/test/lx_hybrid_ctors/$(TESTLIB_SO)
$(TARGET): $(TESTLIB_SO)
$(TESTLIB_SO): $(TESTLIB_SRC_CC)
$(MSG_BUILD)$(TESTLIB_SO)
$(VERBOSE)g++ $(CC_MARCH) -fPIC -c $^
$(VERBOSE)g++ $(CC_MARCH) -shared -o $@ $(notdir $(^:.cc=.o))
clean_libtestlib:
$(VERBOSE)rm -f $(TESTLIB_SO) $(TESTLIB_SRC_CC:.cc=.o)
clean: clean_libtestlib
vpath testlib.cc $(PRG_DIR)

View File

@ -0,0 +1,28 @@
/*
* \brief Test if global static constructors in host shared libs get called
* \author Christian Prochaska
* \date 2011-11-24
*/
/*
* Copyright (C) 2011-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.
*/
#include <stdio.h>
#include "testlib.h"
Testlib_testclass::Testlib_testclass()
{
printf("[init -> test-lx_hybrid_ctors] Global static constructor of host library called.\n");
}
void Testlib_testclass::dummy() { }
Testlib_testclass testlib_testobject;

View File

@ -0,0 +1,18 @@
/*
* \brief Test if global static constructors in host shared libs get called
* \author Christian Prochaska
* \date 2011-11-24
*/
/*
* Copyright (C) 2011-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.
*/
struct Testlib_testclass
{
Testlib_testclass();
void dummy();
};

View File

@ -0,0 +1,68 @@
/*
* \brief Test for thread-local errno handling of hybrid Linux/Genode programs
* \author Norman Feske
* \date 2011-12-05
*/
/* Genode includes */
#include <base/thread.h>
#include <base/printf.h>
/* libc includes */
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
enum { STACK_SIZE = 4096 };
struct Thread : Genode::Thread<STACK_SIZE>
{
Genode::Lock &_barrier;
Thread(Genode::Lock &barrier)
: Genode::Thread<STACK_SIZE>("stat"), _barrier(barrier) { start(); }
void entry()
{
/*
* Stat syscall should return with errno ENOENT
*/
struct stat buf;
int ret = stat("", &buf);
Genode::printf("thread: stat returned %d, errno=%d\n", ret, errno);
/*
* Let main thread procees
*/
_barrier.unlock();
}
};
int main(int, char **)
{
Genode::printf("--- thread-local errno test ---\n");
static Genode::Lock barrier(Genode::Lock::LOCKED);
int const orig_errno = errno;
Genode::printf("main: before thread creation, errno=%d\n", orig_errno);
/* create thread, which modifies its thread-local errno value */
static Thread thread(barrier);
/* block until the thread performed a 'stat' syscall */
barrier.lock();
Genode::printf("main: after thread completed, errno=%d\n", errno);
if (orig_errno != errno) {
PERR("unexpected change of main thread's errno value");
return -1;
}
Genode::printf("--- finished thread-local errno test ---\n");
return 0;
}

View File

@ -0,0 +1,3 @@
TARGET = test-lx_hybrid_errno
SRC_CC = main.c
LIBS = lx_hybrid

View File

@ -0,0 +1,37 @@
/*
* \brief Test if the exception mechanism works in hybrid applications
* \author Christian Prochaska
* \date 2011-11-22
*/
/*
* Copyright (C) 2011-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.
*/
#include <base/printf.h>
using namespace Genode;
class Test_exception { };
/**
* Main program
*/
int main(int, char **)
{
printf("--- lx_hybrid exception test ---\n");
try {
printf("Throwing Test_exception\n");
throw Test_exception();
} catch(Test_exception) {
printf("Caught Test_exception\n");
}
printf("--- returning from main ---\n");
return 0;
}

View File

@ -0,0 +1,3 @@
TARGET = test-lx_hybrid_exception
SRC_CC = main.cc
LIBS = lx_hybrid

View File

@ -0,0 +1,62 @@
/*
* \brief Test for performing IPC from a pthread created outside of Genode
* \author Norman Feske
* \date 2011-12-20
*/
/*
* Copyright (C) 2011-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 <base/thread.h>
#include <base/printf.h>
/* libc includes */
#include <pthread.h>
static Genode::Lock *main_wait_lock()
{
static Genode::Lock inst(Genode::Lock::LOCKED);
return &inst;
}
static void *pthread_entry(void *)
{
PINF("first message");
/*
* Without the lazy initialization of 'Thread_base' objects for threads
* created w/o Genode's Thread API, the printing of the first message will
* never return because the IPC reply could not be delivered.
*
* With the on-demand creation of 'Thread_base' objects, the second message
* will appear in the LOG output.
*/
PINF("second message");
main_wait_lock()->unlock();
return 0;
}
int main(int, char **)
{
Genode::printf("--- pthread IPC test ---\n");
/* create thread w/o Genode's thread API */
pthread_t pth;
pthread_create(&pth, 0, pthread_entry, 0);
/* wait until 'pthread_entry' finished */
main_wait_lock()->lock();
Genode::printf("--- finished pthread IPC test ---\n");
return 0;
}

View File

@ -0,0 +1,3 @@
TARGET = test-lx_hybrid_pthread_ipc
SRC_CC = main.c
LIBS = lx_hybrid

View File

@ -0,0 +1,10 @@
# dynamic variant is not supported in hybrid mode
ifeq ($(filter-out $(SPECS),always_hybrid),)
REQUIRES = plain_linux
endif
TARGET = test-lx_rmap_dynamic
SRC_CC = main.cc
LIBS = base ld
vpath main.cc $(PRG_DIR)/..

View File

@ -0,0 +1,105 @@
/*
* \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 <base/printf.h>
#include <base/env.h>
#include <base/crt0.h>
#include <base/sleep.h>
#include <base/thread.h>
#include <util/misc_math.h>
#include <rm_session/connection.h>
static void blob() __attribute__((used));
static void blob()
{
asm volatile (
".balign 4096, -1\n"
"blob_beg:\n"
".space 16*4096, -2\n"
"blob_end:\n"
".global blob_beg\n"
".global blob_end\n"
: : : );
}
extern unsigned long blob_beg;
extern unsigned long blob_end;
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)&blob_beg);
addr_t end(align_addr((addr_t)&blob_end, 12));
size_t size(end - beg);
PLOG("blob region 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();
}

View File

@ -0,0 +1,5 @@
TARGET = test-lx_rmap_static
SRC_CC = main.cc
LIBS = base
vpath main.cc $(PRG_DIR)/..

View File

@ -0,0 +1,53 @@
/*
* \brief Linux: Test bug in rm_session_mmap.cc
* \author Christian Helmuth
* \date 2012-12-19
*/
/*
* Copyright (C) 2012-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.
*/
#include <base/env.h>
#include <base/printf.h>
#include <ram_session/connection.h>
#include <timer_session/connection.h>
static void test_linux_rmmap_bug()
{
enum { QUOTA = 1*1024*1024, CHUNK = 0x1000, ROUNDS = 0x10 };
using namespace Genode;
PLOG("line: %d", __LINE__);
Ram_connection ram;
#if 1 /* transfer quota */
PLOG("line: %d", __LINE__);
ram.ref_account(env()->ram_session_cap());
env()->ram_session()->transfer_quota(ram.cap(), QUOTA);
#endif
PLOG("line: %d", __LINE__);
for (unsigned i = 0; i < ROUNDS; ++i) {
Ram_dataspace_capability ds(ram.alloc(CHUNK));
PLOG("%d of %d pages allocated", (i + 1), ROUNDS);
}
PLOG("Done.");
}
int main()
{
Genode::printf("--- test-rm_session_mmap started ---\n");
// Timer::Connection timer;
// timer.msleep(1000);
test_linux_rmmap_bug();
}

View File

@ -0,0 +1,3 @@
TARGET = test-rm_session_mmap
LIBS = base
SRC_CC = main.cc

View File

@ -0,0 +1,22 @@
/*
* \brief Linux-specific policy for sub_rm test
* \author Norman Feske
* \date 2011-11-22
*/
/*
* Copyright (C) 2011-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.
*/
/*
* The Linux implementation of the RM service does not support attaching
* the same sub RM session twice. This configuration enables the respective
* error-handling test.
*/
enum { attach_twice_forbidden = true };
enum { support_attach_sub_any = false };