mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-18 23:28:29 +00:00
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:
31
repos/base-linux/src/base/console/core_console.h
Normal file
31
repos/base-linux/src/base/console/core_console.h
Normal 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
60
repos/base-linux/src/base/env/debug.cc
vendored
Normal 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(); }
|
212
repos/base-linux/src/base/env/platform_env.cc
vendored
Normal file
212
repos/base-linux/src/base/env/platform_env.cc
vendored
Normal 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);
|
||||
}
|
||||
}
|
462
repos/base-linux/src/base/env/platform_env.h
vendored
Normal file
462
repos/base-linux/src/base/env/platform_env.h
vendored
Normal 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 ®ion)
|
||||
{
|
||||
/*
|
||||
* 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_ */
|
363
repos/base-linux/src/base/env/rm_session_mmap.cc
vendored
Normal file
363
repos/base-linux/src/base/env/rm_session_mmap.cc
vendored
Normal 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 ®ion)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
661
repos/base-linux/src/base/ipc/ipc.cc
Normal file
661
repos/base-linux/src/base/ipc/ipc.cc
Normal 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 ®istry;
|
||||
}
|
||||
|
||||
|
||||
/********************************************
|
||||
** 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();
|
||||
}
|
150
repos/base-linux/src/base/ipc/socket_descriptor_registry.h
Normal file
150
repos/base-linux/src/base/ipc/socket_descriptor_registry.h
Normal 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_ */
|
75
repos/base-linux/src/base/lock/lock_helper.h
Normal file
75
repos/base-linux/src/base/lock/lock_helper.h
Normal 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);
|
||||
}
|
92
repos/base-linux/src/base/process/process.cc
Normal file
92
repos/base-linux/src/base/process/process.cc
Normal 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() { }
|
49
repos/base-linux/src/base/thread/thread_env.cc
Normal file
49
repos/base-linux/src/base/thread/thread_env.cc
Normal 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];
|
||||
}
|
149
repos/base-linux/src/base/thread/thread_linux.cc
Normal file
149
repos/base-linux/src/base/thread/thread_linux.cc
Normal 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);
|
||||
}
|
Reference in New Issue
Block a user