base-linux: socket descriptor caps for RPC

On Linux, Genode used to represent each RPC object by a socket
descriptor of the receiving thread (entrypoint) and a globally-unique
value that identifies the object. Because the latter was transferred as
plain message payload, clients had to be trusted to not forge the
values. For this reason, Linux could not be considered as a productive
Genode base platform but remained merely a development vehicle.

This patch changes the RPC mechanism such that each RPC object is
represented by a dedicated socket pair. Entrypoints wait on a set of
the local ends of the socket pairs of all RPC objects managed by the
respective entrypoint. The epoll kernel interface is used as the
underlying mechanism to wait for a set of socket descriptors at the
server side.

When delegating a capability, the remote end of the socket pair is
transferred to the recipient along with a plaintext copy of the
socket-descriptor value of the local end. The latter value serves as a
hint for re-identifiying a capability whenever it is delegated back to
its origin. Note that the client is not trusted to preserve this
information. The integrity of the hint value is protected by comparing
the inode values of incoming and already present capablities at the
originating site (whenever the capability is invoked or presented to the
owner of the RPC object).

The new mechanism effectively equips base-linux with Genode's capablity
model as described in the Chapter 3 of the Genode Foundations book.
That said, the sandboxing of components cannot be assumed at this point
because each component has still direct access to the Linux system-call
interface.

This patch is based on the extensive exploration work conducted by
Stefan Thoeni who strongly motivated the inclusion of this feature into
Genode.

Issue #3581
This commit is contained in:
Norman Feske 2020-04-09 12:30:21 +02:00 committed by Christian Helmuth
parent 319d2be1af
commit 132569d12b
31 changed files with 964 additions and 762 deletions

View File

@ -13,5 +13,5 @@ SRC_CC += rpc_dispatch_loop.cc
SRC_CC += rpc_entrypoint_manage.cc
SRC_CC += thread_env.cc
SRC_CC += capability.cc
SRC_CC += platform.cc
SRC_CC += rpc_entry.cc
SRC_CC += native_thread.cc

View File

@ -11,3 +11,4 @@ SRC_CC += thread.cc thread_myself.cc thread_linux.cc
SRC_CC += capability_space.cc capability_raw.cc
SRC_CC += attach_stack_area.cc
SRC_CC += signal_transmitter.cc signal.cc
SRC_CC += platform.cc

View File

@ -0,0 +1,33 @@
/*
* \brief Core-specific back end of the RPC entrypoint
* \author Norman Feske
* \date 2020-04-07
*/
/*
* Copyright (C) 2020 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/rpc_server.h>
/* base-internal includes */
#include <base/internal/native_thread.h>
using namespace Genode;
Native_capability Rpc_entrypoint::_alloc_rpc_cap(Pd_session &, Native_capability,
addr_t)
{
return Thread::native_thread().epoll.alloc_rpc_cap();
}
void Rpc_entrypoint::_free_rpc_cap(Pd_session &, Native_capability cap)
{
Thread::native_thread().epoll.free_rpc_cap(cap);
}

View File

@ -48,12 +48,6 @@ inline int lx_unlink(const char *fname)
}
inline int lx_dup(int fd)
{
return lx_syscall(SYS_dup, fd);
}
/*******************************************************
** Functions used by core's rom-session support code **
*******************************************************/

View File

@ -38,11 +38,11 @@ namespace Genode {
{
private:
Filename _fname { }; /* filename for mmap */
size_t const _size; /* size of dataspace in bytes */
addr_t const _addr; /* meaningless on linux */
int _fd { -1 }; /* file descriptor */
bool const _writable; /* false if read-only */
Filename _fname { }; /* filename for mmap */
size_t const _size; /* size of dataspace in bytes */
addr_t const _addr; /* meaningless on linux */
Native_capability _cap; /* capability / file descriptor */
bool const _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 */
@ -57,6 +57,11 @@ namespace Genode {
Dataspace_component(Dataspace_component const &);
Dataspace_component &operator = (Dataspace_component const &);
static Native_capability _fd_to_cap(int const fd)
{
return Capability_space::import(Rpc_destination(Lx_sd{fd}), Rpc_obj_key());
}
public:
/**
@ -65,14 +70,14 @@ namespace Genode {
Dataspace_component(size_t size, addr_t addr,
Cache_attribute, bool writable,
Dataspace_owner * owner)
: _size(size), _addr(addr), _fd(-1), _writable(writable),
: _size(size), _addr(addr), _cap(), _writable(writable),
_owner(owner) { }
/**
* Default constructor returns invalid dataspace
*/
Dataspace_component()
: _size(0), _addr(0), _fd(-1), _writable(false), _owner(nullptr) { }
: _size(0), _addr(0), _cap(), _writable(false), _owner(nullptr) { }
/**
* This constructor is only provided for compatibility
@ -94,7 +99,7 @@ namespace Genode {
* The file descriptor assigned to the dataspace will be enable
* processes outside of core to mmap the dataspace.
*/
void fd(int fd) { _fd = fd; }
void fd(int fd) { _cap = _fd_to_cap(fd); }
/**
* Check if dataspace is owned by a specified object
@ -122,13 +127,7 @@ namespace Genode {
Filename fname() override { return _fname; }
Untyped_capability fd() override
{
Untyped_capability fd_cap =
Capability_space::import(Rpc_destination(_fd), Rpc_obj_key());
return fd_cap;
}
Untyped_capability fd() override { return _cap; }
};
}

View File

@ -40,8 +40,6 @@ class Genode::Native_cpu_component : public Rpc_object<Linux_native_cpu,
~Native_cpu_component();
void thread_id(Thread_capability, int, int) override;
Untyped_capability server_sd(Thread_capability) override;
Untyped_capability client_sd(Thread_capability) override;
};
#endif /* _CORE__INCLUDE__NATIVE_CPU_COMPONENT_H_ */

View File

@ -22,9 +22,6 @@
#include <base/weak_ptr.h>
#include <cpu_session/cpu_session.h>
/* base-internal includes */
#include <base/internal/server_socket_pair.h>
/* core includes */
#include <pager.h>
@ -67,11 +64,6 @@ namespace Genode {
unsigned long _pid = -1;
char _name[32] { };
/**
* Unix-domain socket pair bound to the thread
*/
Socket_pair _socket_pair { };
/*
* Dummy pager object that is solely used for storing the
* 'Signal_context_capability' for the thread's exception handler.
@ -147,19 +139,6 @@ namespace Genode {
*/
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
*/

View File

@ -1,111 +0,0 @@
/*
* \brief Support for communication over Unix domain sockets
* \author Norman Feske
* \date 2012-08-10
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#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>
/* base-internal includes */
#include <base/internal/socket_descriptor_registry.h>
#include <base/internal/server_socket_pair.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)
:
sockaddr_un({.sun_family = AF_UNIX, .sun_path = { }})
{
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::Socket_pair create_server_socket_pair(long id)
{
Genode::Socket_pair socket_pair;
using Genode::raw;
/*
* Main thread uses 'Ipc_server' for 'sleep_forever()' only. No need for
* binding.
*/
if (id == -1)
return socket_pair;
Uds_addr addr(id);
/*
* Create server-side socket
*/
socket_pair.server_sd = lx_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (socket_pair.server_sd < 0) {
raw("Error: Could not create server-side socket (ret=", socket_pair.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(socket_pair.server_sd, (sockaddr *)&addr, sizeof(addr));
if (bind_ret < 0) {
raw("Error: Could not bind server socket (ret=", bind_ret, ")");
class Bind_failed { };
throw Bind_failed();
}
/*
* Create client-side socket
*/
socket_pair.client_sd = lx_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (socket_pair.client_sd < 0) {
raw("Error: Could not create client-side socket (ret=", socket_pair.client_sd, ")");
class Client_socket_failed { };
throw Client_socket_failed();
}
int const conn_ret = lx_connect(socket_pair.client_sd, (sockaddr *)&addr, sizeof(addr));
if (conn_ret < 0) {
raw("Error: Could not connect client-side socket (ret=", conn_ret, ")");
class Connect_failed { };
throw Connect_failed();
}
socket_pair.client_sd = Genode::ep_sd_registry().try_associate(socket_pair.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 socket_pair;
}
#endif /* _CORE__INCLUDE__SERVER_SOCKET_PAIR_H_ */

View File

@ -21,7 +21,7 @@ SRC_CC = main.cc \
native_pd_component.cc \
native_cpu_component.cc \
capability_space.cc \
rpc_cap_factory_l4.cc \
rpc_cap_factory_linux.cc \
ram_dataspace_factory.cc \
core_rpc_cap_alloc.cc \
io_mem_session_component.cc \
@ -63,13 +63,11 @@ vpath cpu_thread_component.cc $(GEN_CORE_DIR)
vpath pd_upgrade_ram_quota.cc $(GEN_CORE_DIR)
vpath pd_session_support.cc $(GEN_CORE_DIR)
vpath capability_space.cc $(GEN_CORE_DIR)
vpath rpc_cap_factory_l4.cc $(GEN_CORE_DIR)
vpath ram_dataspace_factory.cc $(GEN_CORE_DIR)
vpath signal_source_component.cc $(GEN_CORE_DIR)
vpath signal_transmitter_proxy.cc $(GEN_CORE_DIR)
vpath signal_receiver.cc $(GEN_CORE_DIR)
vpath trace_session_component.cc $(GEN_CORE_DIR)
vpath core_rpc_cap_alloc.cc $(GEN_CORE_DIR)
vpath default_log.cc $(GEN_CORE_DIR)
vpath heartbeat.cc $(GEN_CORE_DIR)
vpath io_port_session_support.cc $(GEN_CORE_DIR)/spec/x86

View File

@ -28,30 +28,6 @@ void Native_cpu_component::thread_id(Thread_capability thread_cap, int pid, int
}
Untyped_capability Native_cpu_component::server_sd(Thread_capability thread_cap)
{
auto lambda = [] (Cpu_thread_component *thread) {
if (!thread) return Untyped_capability();
return Capability_space::import(Rpc_destination(thread->platform_thread().server_sd()),
Rpc_obj_key());
};
return _thread_ep.apply(thread_cap, lambda);
}
Untyped_capability Native_cpu_component::client_sd(Thread_capability thread_cap)
{
auto lambda = [] (Cpu_thread_component *thread) {
if (!thread) return Untyped_capability();
return Capability_space::import(Rpc_destination(thread->platform_thread().client_sd()),
Rpc_obj_key());
};
return _thread_ep.apply(thread_cap, lambda);
}
Native_cpu_component::Native_cpu_component(Cpu_session_component &cpu_session, char const *)
:
_cpu_session(cpu_session), _thread_ep(_cpu_session._thread_ep)

View File

@ -42,12 +42,12 @@ struct Execve_args
char const *filename;
char * const *argv;
char * const *envp;
int const parent_sd;
Lx_sd const parent_sd;
Execve_args(char const *filename,
char * const *argv,
char * const *envp,
int parent_sd)
Lx_sd parent_sd)
:
filename(filename), argv(argv), envp(envp), parent_sd(parent_sd)
{ }
@ -59,7 +59,7 @@ struct Execve_args
*/
static int _exec_child(Execve_args *arg)
{
lx_dup2(arg->parent_sd, PARENT_SOCKET_HANDLE);
lx_dup2(arg->parent_sd.value, PARENT_SOCKET_HANDLE);
return lx_execve(arg->filename, arg->argv, arg->envp);
}
@ -118,7 +118,7 @@ void Native_pd_component::_start(Dataspace_component &ds)
char buf[4096];
int num_bytes = 0;
int const fd_socket = Capability_space::ipc_cap_data(ds.fd()).dst.socket;
int const fd_socket = Capability_space::ipc_cap_data(ds.fd()).dst.socket.value;
while ((num_bytes = lx_read(fd_socket, buf, sizeof(buf))) != 0)
lx_write(tmp_binary_fd, buf, num_bytes);

View File

@ -23,7 +23,7 @@
/* local includes */
#include "platform.h"
#include "core_env.h"
#include "server_socket_pair.h"
#include "resource_path.h"
/* Linux includes */
#include <core_linux_syscalls.h>
@ -155,30 +155,6 @@ void Platform::wait_for_exit()
}
/*****************************
** Support for IPC library **
*****************************/
namespace Genode {
Socket_pair server_socket_pair()
{
return create_server_socket_pair(Thread::myself()->native_thread().tid);
}
void destroy_server_socket_pair(Socket_pair socket_pair)
{
/*
* 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 (socket_pair.server_sd != -1 || socket_pair.client_sd != -1)
error(__func__, " called for IPC server which should never happen");
}
}
/****************************************************
** Support for Platform_env_base::Region_map_mmap **
****************************************************/
@ -208,7 +184,7 @@ int Region_map_mmap::_dataspace_fd(Capability<Dataspace> ds_cap)
* dataspace, the descriptor would unexpectedly be closed again.
*/
return core_env().entrypoint().apply(lx_ds_cap, [] (Linux_dataspace *ds) {
return ds ? lx_dup(Capability_space::ipc_cap_data(ds->fd()).dst.socket) : -1; });
return ds ? lx_dup(Capability_space::ipc_cap_data(ds->fd()).dst.socket.value) : -1; });
}

View File

@ -18,7 +18,7 @@
/* local includes */
#include "platform_thread.h"
#include "server_socket_pair.h"
#include <linux_syscalls.h>
using namespace Genode;
@ -85,14 +85,6 @@ Platform_thread::Platform_thread(size_t, const char *name, unsigned,
Platform_thread::~Platform_thread()
{
ep_sd_registry().disassociate(_socket_pair.client_sd);
if (_socket_pair.client_sd)
lx_close(_socket_pair.client_sd);
if (_socket_pair.server_sd)
lx_close(_socket_pair.server_sd);
_registry().remove(this);
}
@ -114,19 +106,3 @@ void Platform_thread::resume()
warning(__func__, "not implemented");
}
int Platform_thread::client_sd()
{
/* construct socket pair on first call */
if (_socket_pair.client_sd == -1)
_socket_pair = create_server_socket_pair(_tid);
return _socket_pair.client_sd;
}
int Platform_thread::server_sd()
{
client_sd();
return _socket_pair.server_sd;
}

View File

@ -56,12 +56,7 @@ void Ram_dataspace_factory::_export_ram_ds(Dataspace_component &ds)
}
void Ram_dataspace_factory::_revoke_ram_ds(Dataspace_component &ds)
{
int const fd = Capability_space::ipc_cap_data(ds.fd()).dst.socket;
if (fd != -1)
lx_close(fd);
}
void Ram_dataspace_factory::_revoke_ram_ds(Dataspace_component &) { }
void Ram_dataspace_factory::_clear_ds(Dataspace_component &) { }

View File

@ -56,8 +56,4 @@ Rom_session_component::Rom_session_component(Rom_fs &,
Rom_session_component::~Rom_session_component()
{
_ds_ep.dissolve(&_ds);
int const fd = Capability_space::ipc_cap_data(_ds.fd()).dst.socket;
if (fd != -1)
lx_close(fd);
}

View File

@ -0,0 +1,27 @@
/*
* \brief RPC capability factory
* \author Norman Feske
* \date 2016-01-19
*/
/*
* Copyright (C) 2016-2020 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* core includes */
#include <rpc_cap_factory.h>
using namespace Genode;
Native_capability Rpc_cap_factory::alloc(Native_capability)
{
return Native_capability();
}
void Rpc_cap_factory::free(Native_capability) { }

View File

@ -64,13 +64,13 @@ Dataspace_component::Dataspace_component(const char *args)
: _fname(_file_name(args)),
_size(_file_size()),
_addr(0),
_fd(lx_open(_fname.buf, O_RDONLY | LX_O_CLOEXEC, S_IRUSR | S_IXUSR)),
_cap(_fd_to_cap(lx_open(_fname.buf, O_RDONLY | LX_O_CLOEXEC, S_IRUSR | S_IXUSR))),
_writable(false),
_owner(0) { }
Dataspace_component::Dataspace_component(size_t size, addr_t, addr_t phys_addr,
Cache_attribute, bool, Dataspace_owner *_owner) :
_size(size), _addr(phys_addr), _fd(-1), _writable(false), _owner(_owner)
_size(size), _addr(phys_addr), _cap(), _writable(false), _owner(_owner)
{
warning("Should only be used for IOMEM and not within Linux.");
_fname.buf[0] = 0;

View File

@ -0,0 +1,281 @@
/*
* \brief Capability-space management for base-linux
* \author Norman Feske
* \date 2016-06-15
*
* On Linux, a capability is represented by a socket descriptor and an RPC
* object key. The thread ID respectively socket descriptor refer to the
* recipient of an RPC call (RPC destination).
*/
/*
* Copyright (C) 2016-2020 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__BASE__INTERNAL__CAPABILITY_SPACE_TPL_H_
#define _INCLUDE__BASE__INTERNAL__CAPABILITY_SPACE_TPL_H_
/* base includes */
#include <util/avl_tree.h>
#include <util/bit_allocator.h>
#include <base/mutex.h>
#include <base/log.h>
#include <util/construct_at.h>
/* base-internal includes */
#include <base/internal/capability_space.h>
#include <base/internal/rpc_destination.h>
#include <linux_syscalls.h>
namespace Genode { template <unsigned, typename> class Capability_space_tpl; }
/**
* Platform-specific supplement to the generic 'Capability_space' interface
*/
namespace Genode { namespace Capability_space {
/**
* Information needed to transfer capability via the kernel's IPC mechanism
*/
struct Ipc_cap_data
{
Rpc_destination dst;
Rpc_obj_key rpc_obj_key;
Ipc_cap_data(Rpc_destination dst, Rpc_obj_key rpc_obj_key)
: dst(dst), rpc_obj_key(rpc_obj_key) { }
void print(Output &out) const { Genode::print(out, dst, ",", rpc_obj_key); }
};
/**
* Retrieve IPC data for given capability
*/
Ipc_cap_data ipc_cap_data(Native_capability const &cap);
Native_capability lookup(Rpc_obj_key);
Native_capability import(Rpc_destination, Rpc_obj_key);
} }
/**
* Capability space template
*
* The capability space of core and non-core components differ in two ways.
*
* First, core must keep track of all capabilities of the system. Hence, its
* capability space must be dimensioned larger.
*
* Second, core has to maintain the information about the PD session that
* was used to allocate the capability to prevent misbehaving clients from
* freeing capabilities allocated from another component. This information
* is part of the core-specific 'Native_capability::Data' structure.
*/
template <unsigned NUM_CAPS, typename CAP_DATA>
class Genode::Capability_space_tpl : Noncopyable
{
private:
typedef CAP_DATA Data;
/**
* Supplement Native_capability::Data with the meta data needed to
* manage it in an AVL tree
*/
struct Tree_managed_data : Data, Avl_node<Tree_managed_data>
{
template <typename... ARGS>
Tree_managed_data(ARGS... args) : Data(args...) { }
Tree_managed_data() { }
bool higher(Tree_managed_data *data)
{
return data->rpc_obj_key().value() > this->rpc_obj_key().value();
}
Tree_managed_data *find_by_key(Rpc_obj_key key)
{
if (key.value() == this->rpc_obj_key().value()) return this;
Tree_managed_data *data =
this->child(key.value() > this->rpc_obj_key().value());
return data ? data->find_by_key(key) : nullptr;
}
};
Tree_managed_data _caps_data[NUM_CAPS];
Bit_allocator<NUM_CAPS> _alloc { };
Avl_tree<Tree_managed_data> _tree { };
Mutex mutable _mutex { };
/**
* Calculate index into _caps_data for capability data object
*/
unsigned _index(Data const &data) const
{
addr_t const offset = (addr_t)&data - (addr_t)_caps_data;
return offset / sizeof(_caps_data[0]);
}
Data *_lookup_unsynchronized(Rpc_obj_key key) const
{
/* omit lookup of reply capabilities as they are always foreign */
if (!key.valid())
return nullptr;
if (!_tree.first())
return nullptr;
return _tree.first()->find_by_key(key);
}
/**
* Create Genode capability
*
* The arguments are passed to the constructor of the
* 'Native_capability::Data' type.
*/
template <typename... ARGS>
Native_capability::Data &_create_capability_unsynchronized(ARGS &&... args)
{
addr_t const index = _alloc.alloc();
Tree_managed_data &data = _caps_data[index];
construct_at<Tree_managed_data>(&data, args...);
/*
* Register capability in the tree only if it refers to a valid
* object hosted locally within the component (not foreign).
*/
if (data.rpc_obj_key().valid() && !data.dst.foreign)
_tree.insert(&data);
return data;
}
public:
void dec_ref(Data &data)
{
Mutex::Guard guard(_mutex);
if (data.dec_ref() > 0)
return;
/*
* Reference count reached zero. Release the socket descriptors
* of the capability-space entry and mark the entry as free.
*/
if (data.rpc_obj_key().valid() && !data.dst.foreign)
_tree.remove(static_cast<Tree_managed_data *>(&data));
if (data.dst.socket.valid()) {
lx_close(data.dst.socket.value);
/*
* Close local socketpair end of a locally-implemented RPC
* object.
*/
if (!data.dst.foreign)
lx_close(data.rpc_obj_key().value());
}
int const index = _index(data);
_caps_data[index].dst = Rpc_destination::invalid();
_alloc.free(index);
data = Tree_managed_data();
}
void inc_ref(Data &data)
{
Mutex::Guard guard(_mutex);
if (data.inc_ref() == 255)
throw Native_capability::Reference_count_overflow();
}
Rpc_obj_key rpc_obj_key(Data const &data) const
{
return data.rpc_obj_key();
}
void print(Output &out, Data const &data) const
{
ipc_cap_data(data).print(out);
Genode::print(out, ",index=", _index(data));
}
Capability_space::Ipc_cap_data ipc_cap_data(Data const &data) const
{
return { data.dst, data.rpc_obj_key() };
}
Native_capability lookup(Rpc_obj_key)
{
/*
* This method is never called on base-linux. It merely exists
* for the compatiblity with the generic 'capability_space.cc'.
*/
struct Unreachable { };
throw Unreachable();
}
Native_capability import(Rpc_destination dst, Rpc_obj_key key)
{
Data *data_ptr = nullptr;
{
Mutex::Guard guard(_mutex);
data_ptr = _lookup_unsynchronized(key);
if (data_ptr && !data_ptr->dst.foreign) {
/*
* Compare if existing and incoming sockets refer to the
* same inode. If yes, they refer to an RPC object hosted
* in the local component. In this case, discard the
* incoming socket and keep using the original one.
*/
if (data_ptr->dst.socket.inode() == dst.socket.inode()) {
lx_close(dst.socket.value);
} else {
/* force occupation of new capability slot */
data_ptr = nullptr;
}
}
if (data_ptr == nullptr)
data_ptr = &_create_capability_unsynchronized(dst, key);
}
/* 'Native_capability' constructor aquires '_mutex' via 'inc_ref' */
return Native_capability(data_ptr);
}
};
namespace Genode { static inline Rpc_destination invalid_rpc_destination(); }
/* for compatiblity with generic 'capability_space.cc' */
static inline Genode::Rpc_destination Genode::invalid_rpc_destination()
{
return Rpc_destination::invalid();
}
#endif /* _INCLUDE__BASE__INTERNAL__CAPABILITY_SPACE_TPL_H_ */

View File

@ -27,7 +27,7 @@ namespace Genode {
*/
static inline bool local(Untyped_capability const &cap)
{
return Capability_space::ipc_cap_data(cap).dst.socket == -1;
return Capability_space::ipc_cap_data(cap).dst.socket.value == -1;
}
}
@ -54,7 +54,7 @@ class Genode::Local_capability
*/
static Capability<RPC_INTERFACE> local_cap(RPC_INTERFACE* ptr) {
Untyped_capability cap =
Capability_space::import(invalid_rpc_destination(),
Capability_space::import(Rpc_destination::invalid(),
Rpc_obj_key((long)ptr));
return reinterpret_cap_cast<RPC_INTERFACE>(cap); }

View File

@ -15,40 +15,99 @@
#define _INCLUDE__BASE__INTERNAL__NATIVE_THREAD_H_
#include <base/stdint.h>
#include <base/internal/server_socket_pair.h>
#include <base/native_capability.h>
#include <linux_syscalls.h>
namespace Genode { struct Native_thread; }
struct Genode::Native_thread
class Genode::Native_thread
{
/*
* Unfortunately, both - PID and TID - are needed for lx_tgkill()
*/
unsigned int tid = 0; /* Native thread ID type as returned by the
'clone' system call */
unsigned int pid = 0; /* process ID (resp. thread-group ID) */
private:
bool is_ipc_server = false;
/*
* Noncopyable
*/
Native_thread(Native_thread const &);
Native_thread &operator = (Native_thread const &);
/**
* Natively aligned memory location used in the lock implementation
*/
int futex_counter __attribute__((aligned(sizeof(Genode::addr_t)))) = 0;
public:
struct Meta_data;
/*
* Unfortunately, both - PID and TID - are needed for lx_tgkill()
*/
unsigned int tid = 0; /* Native thread ID type as returned by the
'clone' system call */
unsigned int pid = 0; /* process ID (resp. thread-group ID) */
/**
* Opaque pointer to additional thread-specific meta data
*
* This pointer is used by hybrid Linux/Genode programs to maintain
* POSIX-thread-related meta data. For non-hybrid Genode programs, it
* remains unused.
*/
Meta_data *meta_data = nullptr;
bool is_ipc_server = false;
Socket_pair socket_pair { };
/**
* Natively aligned memory location used in the lock implementation
*/
int futex_counter __attribute__((aligned(sizeof(Genode::addr_t)))) = 0;
Native_thread() { }
struct Meta_data;
/**
* Opaque pointer to additional thread-specific meta data
*
* This pointer is used by hybrid Linux/Genode programs to maintain
* POSIX-thread-related meta data. For non-hybrid Genode programs, it
* remains unused.
*/
Meta_data *meta_data = nullptr;
class Epoll
{
private:
Lx_socketpair _control { };
Lx_epoll_sd const _epoll;
void _add (Lx_sd);
void _remove(Lx_sd);
bool _rpc_ep_exited = false;
struct Control_function : Interface
{
virtual void execute() = 0;
};
/*
* Execute functor 'fn' in the context of the 'poll' method.
*/
template <typename FN>
void _exec_control(FN const &);
public:
Epoll();
~Epoll();
/**
* Wait for incoming RPC messages
*
* \return valid socket descriptor that matches the invoked
* RPC object
*/
Lx_sd poll();
Native_capability alloc_rpc_cap();
void free_rpc_cap(Native_capability);
/**
* Flag RPC entrypoint as no longer in charge of dispatching
*/
void rpc_ep_exited() { _rpc_ep_exited = true; }
} epoll { };
Native_thread() { }
};
#endif /* _INCLUDE__BASE__INTERNAL__NATIVE_THREAD_H_ */

View File

@ -15,27 +15,35 @@
#define _INCLUDE__BASE__INTERNAL__RPC_DESTINATION_H_
#include <base/output.h>
#include <base/internal/rpc_obj_key.h>
namespace Genode {
#include <linux_syscalls.h>
struct Rpc_destination
namespace Genode { struct Rpc_destination; }
struct Genode::Rpc_destination
{
Lx_sd socket;
/*
* Distinction between a capability referring to a locally implemented
* RPC object and a capability referring to an RPC object hosted in
* a different component.
*/
bool foreign = true;
Rpc_destination(Lx_sd socket) : socket(socket) { }
bool valid() const { return socket.valid(); }
static Rpc_destination invalid() { return Rpc_destination(Lx_sd::invalid()); }
void print(Output &out) const
{
int socket = -1;
explicit Rpc_destination(int socket) : socket(socket) { }
Rpc_destination() { }
};
static inline Rpc_destination invalid_rpc_destination()
{
return Rpc_destination();
Genode::print(out, "socket=", socket, ",foreign=", foreign);
}
static void print(Output &out, Rpc_destination const &dst)
{
Genode::print(out, "socket=", dst.socket);
}
}
};
#endif /* _INCLUDE__BASE__INTERNAL__RPC_DESTINATION_H_ */

View File

@ -1,44 +0,0 @@
/*
* \brief Socket pair used by RPC entrypoint
* \author Norman Feske
* \date 2016-03-25
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__BASE__INTERNAL__SERVER_SOCKET_PAIR_H_
#define _INCLUDE__BASE__INTERNAL__SERVER_SOCKET_PAIR_H_
namespace Genode {
struct Socket_pair
{
int client_sd = -1;
int server_sd = -1;
};
/*
* 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.
*/
Socket_pair 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(Socket_pair);
}
#endif /* _INCLUDE__BASE__INTERNAL__SERVER_SOCKET_PAIR_H_ */

View File

@ -1,150 +0,0 @@
/*
* \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-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__BASE__INTERNAL__SOCKET_DESCRIPTOR_REGISTRY_H_
#define _INCLUDE__BASE__INTERNAL__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 /* _INCLUDE__BASE__INTERNAL__SOCKET_DESCRIPTOR_REGISTRY_H_ */

View File

@ -26,12 +26,6 @@ struct Genode::Linux_native_cpu_client : Rpc_client<Linux_native_cpu>
void thread_id(Thread_capability thread, int pid, int tid) override {
call<Rpc_thread_id>(thread, pid, tid); }
Untyped_capability server_sd(Thread_capability thread) override {
return call<Rpc_server_sd>(thread); }
Untyped_capability client_sd(Thread_capability thread) override {
return call<Rpc_client_sd>(thread); }
};
#endif /* _INCLUDE__LINUX_NATIVE_CPU__CLIENT_H_ */

View File

@ -27,42 +27,14 @@ struct Genode::Linux_native_cpu : Cpu_session::Native_cpu
*/
virtual void thread_id(Thread_capability, int pid, int tid) = 0;
/*
* If a thread plays the role of an entrypoint, core creates a bound
* socket pair for the thread and passes both makes the socket
* descriptors of both ends available to the owner of the thread's
* CPU session via the 'server_sd' and 'client_sd' function.
*/
/**
* Request server-side socket descriptor
*
* The socket descriptor returned by this function is meant to be used
* exclusively by the server for receiving incoming requests. It should
* never leave the server process.
*/
virtual Untyped_capability server_sd(Thread_capability thread) = 0;
/**
* Request client-side socket descriptor
*
* The returned socket descriptor enables a client to send messages to
* the thread. It is already connected to the 'server_sd' descriptor.
* In contrast to 'server_sd', the 'client_sd' is expected to be passed
* around via capability delegations.
*/
virtual Untyped_capability client_sd(Thread_capability thread) = 0;
/*********************
** RPC declaration **
*********************/
GENODE_RPC(Rpc_thread_id, void, thread_id, Thread_capability, int, int);
GENODE_RPC(Rpc_server_sd, Untyped_capability, server_sd, Thread_capability);
GENODE_RPC(Rpc_client_sd, Untyped_capability, client_sd, Thread_capability);
GENODE_RPC_INTERFACE(Rpc_thread_id, Rpc_server_sd, Rpc_client_sd);
GENODE_RPC_INTERFACE(Rpc_thread_id);
};
#endif /* _INCLUDE__LINUX_NATIVE_CPU__LINUX_NATIVE_CPU_H_ */

View File

@ -2,11 +2,13 @@
* \brief Socket-based IPC implementation for Linux
* \author Norman Feske
* \author Christian Helmuth
* \author Stefan Thoeni
* \date 2011-10-11
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
* Copyright (C) 2019 gapfruit AG
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -17,13 +19,12 @@
#include <base/thread.h>
#include <base/blocking.h>
#include <base/env.h>
#include <base/sleep.h>
#include <linux_native_cpu/linux_native_cpu.h>
/* base-internal includes */
#include <base/internal/socket_descriptor_registry.h>
#include <base/internal/native_thread.h>
#include <base/internal/ipc_server.h>
#include <base/internal/server_socket_pair.h>
#include <base/internal/capability_space_tpl.h>
/* Linux includes */
@ -31,7 +32,6 @@
using namespace Genode;
namespace {
struct Pid
@ -56,11 +56,11 @@ namespace {
* long exception code
* ...call results...
*
* First data word of message, used to transfer the local name of the invoked
* object (when a client calls a server) or the exception code (when the server
* replies). This data word is never fetched from memory but transferred via
* the first short-IPC register. The 'protocol_word' is needed as a spacer
* between the header fields define above and the regular message payload..
* First data word of message, used to transfer the exception code (when the
* server replies). This data word is never fetched from memory but
* transferred via the first short-IPC register. The 'protocol_word' is needed
* as a spacer between the header fields defined above and the regular message
* payload.
*/
struct Protocol_header
{
@ -87,86 +87,17 @@ static_assert((int)Protocol_header::INVALID_BADGE != (int)Rpc_obj_key::INVALID,
"ambigious INVALID_BADGE");
/******************************
** 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_EAGAIN = 11,
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) {
raw("Error: could not parse tid number");
return -1;
}
return tid;
}
namespace {
/**
@ -217,9 +148,9 @@ namespace {
msghdr * msg() { return &_msg; }
void marshal_socket(int sd)
void marshal_socket(Lx_sd sd)
{
*((int *)CMSG_DATA((cmsghdr *)_cmsg_buf) + _num_sds) = sd;
*((int *)CMSG_DATA((cmsghdr *)_cmsg_buf) + _num_sds) = sd.value;
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&_msg);
if (cmsg) {
@ -236,9 +167,9 @@ namespace {
_msg.msg_controllen = cmsg->cmsg_len; /* actual cmsg length */
}
int socket_at_index(int index) const
Lx_sd socket_at_index(int index) const
{
return *((int *)CMSG_DATA((cmsghdr *)_cmsg_buf) + index);
return Lx_sd { *((int *)CMSG_DATA((cmsghdr *)_cmsg_buf) + index) };
}
unsigned num_sockets() const
@ -263,12 +194,25 @@ static void insert_sds_into_message(Message &msg,
Native_capability const &cap = snd_msgbuf.cap(i);
if (cap.valid()) {
auto remote_socket_of_capability = [] (Native_capability const &cap)
{
if (!cap.valid())
return Lx_sd::invalid();
Capability_space::Ipc_cap_data cap_data =
Capability_space::ipc_cap_data(cap);
msg.marshal_socket(cap_data.dst.socket);
header.badges[i] = cap_data.rpc_obj_key.value();
if (cap_data.dst.socket.value < 0)
return Lx_sd::invalid();
return cap_data.dst.socket;
};
Lx_sd const socket = remote_socket_of_capability(cap);
if (socket.valid()) {
msg.marshal_socket(socket);
header.badges[i] = Capability_space::ipc_cap_data(cap).rpc_obj_key.value();
} else {
header.badges[i] = Protocol_header::INVALID_BADGE;
}
@ -297,34 +241,14 @@ static void extract_sds_from_message(unsigned start_index,
continue;
}
int const sd = msg.socket_at_index(start_index + sd_cnt++);
int const id = lookup_tid_by_client_socket(sd);
Lx_sd const sd = msg.socket_at_index(start_index + sd_cnt++);
int const associated_sd = Genode::ep_sd_registry().try_associate(sd, id);
Rpc_destination const dst(sd);
Native_capability arg_cap = Capability_space::lookup(Rpc_obj_key(badge));
if (arg_cap.valid()) {
/*
* Discard the received selector and keep using the already
* present one.
*/
buf.insert(arg_cap);
} else {
buf.insert(Capability_space::import(Rpc_destination(associated_sd),
Rpc_obj_key(badge)));
}
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);
}
if (dst.valid())
buf.insert(Capability_space::import(dst, Rpc_obj_key(badge)));
else
buf.insert(Native_capability());
}
}
@ -332,7 +256,7 @@ static void extract_sds_from_message(unsigned start_index,
/**
* Send reply to client
*/
static inline void lx_reply(int reply_socket, Rpc_exception_code exception_code,
static inline void lx_reply(Lx_sd reply_socket, Rpc_exception_code exception_code,
Genode::Msgbuf_base &snd_msgbuf)
{
@ -348,13 +272,11 @@ static inline void lx_reply(int reply_socket, Rpc_exception_code exception_code,
int const 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);
if (ret >= 0 || ret == -LX_ECONNREFUSED)
return;
}
if (ret < 0)
raw("[", lx_gettid, "] lx_sendmsg failed with ", ret, " "
error(lx_getpid(), ":", lx_gettid(), " lx_sendmsg failed with ", ret, " "
"in lx_reply() reply_socket=", reply_socket);
}
@ -367,8 +289,13 @@ Rpc_exception_code Genode::ipc_call(Native_capability dst,
Msgbuf_base &snd_msgbuf, Msgbuf_base &rcv_msgbuf,
size_t)
{
if (!dst.valid()) {
error("attempt to call invalid capability, blocking forever");
sleep_forever();
}
Protocol_header &snd_header = snd_msgbuf.header<Protocol_header>();
snd_header.protocol_word = dst.local_name();
snd_header.protocol_word = 0;
Message snd_msg(snd_header.msg_start(),
sizeof(Protocol_header) + snd_msgbuf.data_size());
@ -378,49 +305,30 @@ Rpc_exception_code Genode::ipc_call(Native_capability dst,
*
* The reply channel will be closed when leaving the scope of 'lx_call'.
*/
struct Reply_channel
struct Reply_channel : Lx_socketpair
{
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) {
raw("[", lx_gettid(), "] lx_socketpair failed with ", 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]);
if (local.value != -1) lx_close(local.value);
if (remote.value != -1) lx_close(remote.value);
}
int local_socket() const { return sd[LOCAL_SOCKET]; }
int remote_socket() const { return sd[REMOTE_SOCKET]; }
} reply_channel;
/* assemble message */
/* marshal reply capability */
snd_msg.marshal_socket(reply_channel.remote_socket());
snd_msg.marshal_socket(reply_channel.remote);
/* marshal capabilities contained in 'snd_msgbuf' */
insert_sds_into_message(snd_msg, snd_header, snd_msgbuf);
int const dst_socket = Capability_space::ipc_cap_data(dst).dst.socket;
Lx_sd const dst_socket = Capability_space::ipc_cap_data(dst).dst.socket;
int const send_ret = lx_sendmsg(dst_socket, snd_msg.msg(), 0);
if (send_ret < 0) {
raw(Pid(), " lx_sendmsg to sd ", dst_socket,
error(lx_getpid(), ":", lx_gettid(), " lx_sendmsg to sd ", dst_socket,
" failed with ", send_ret, " in lx_call()");
for (;;);
throw Genode::Ipc_error();
sleep_forever();
}
/* receive reply */
@ -432,15 +340,15 @@ Rpc_exception_code Genode::ipc_call(Native_capability dst,
rcv_msg.accept_sockets(Message::MAX_SDS_PER_MSG);
rcv_msgbuf.reset();
int const recv_ret = lx_recvmsg(reply_channel.local_socket(), rcv_msg.msg(), 0);
int const recv_ret = lx_recvmsg(reply_channel.local, rcv_msg.msg(), 0);
/* system call got interrupted by a signal */
if (recv_ret == -LX_EINTR)
throw Genode::Blocking_canceled();
if (recv_ret < 0) {
raw("[", lx_getpid(), "] lx_recvmsg failed with ", recv_ret, " in lx_call()");
throw Genode::Ipc_error();
error(lx_getpid(), ":", lx_gettid(), " ipc_call failed to receive result (", recv_ret, ")");
sleep_forever();
}
extract_sds_from_message(0, rcv_msg, rcv_header, rcv_msgbuf);
@ -456,17 +364,17 @@ Rpc_exception_code Genode::ipc_call(Native_capability dst,
void Genode::ipc_reply(Native_capability caller, Rpc_exception_code exc,
Msgbuf_base &snd_msg)
{
int const reply_socket = Capability_space::ipc_cap_data(caller).dst.socket;
Lx_sd const reply_socket = Capability_space::ipc_cap_data(caller).dst.socket;
try { lx_reply(reply_socket, exc, snd_msg); } catch (Ipc_error) { }
}
Genode::Rpc_request Genode::ipc_reply_wait(Reply_capability const &last_caller,
Rpc_exception_code exc,
Msgbuf_base &reply_msg,
Msgbuf_base &request_msg,
Rpc_entrypoint::Native_context &)
Rpc_request Genode::ipc_reply_wait(Reply_capability const &last_caller,
Rpc_exception_code exc,
Msgbuf_base &reply_msg,
Msgbuf_base &request_msg,
Rpc_entrypoint::Native_context &)
{
/* when first called, there was no request yet */
if (last_caller.valid() && exc.value != Rpc_exception_code::INVALID_OBJECT)
@ -476,49 +384,48 @@ Genode::Rpc_request Genode::ipc_reply_wait(Reply_capability const &last_
* Block infinitely if called from the main thread. This may happen if the
* main thread calls 'sleep_forever()'.
*/
if (!Thread::myself()) {
Thread *myself_ptr = Thread::myself();
if (!myself_ptr) {
struct timespec ts = { 1000, 0 };
for (;;) lx_nanosleep(&ts, 0);
}
Native_thread::Epoll &epoll = myself_ptr->native_thread().epoll;
for (;;) {
Lx_sd const selected_sd = epoll.poll();
Protocol_header &header = request_msg.header<Protocol_header>();
Message msg(header.msg_start(), sizeof(Protocol_header) + request_msg.capacity());
msg.accept_sockets(Message::MAX_SDS_PER_MSG);
Native_thread &native_thread = Thread::myself()->native_thread();
request_msg.reset();
int const ret = lx_recvmsg(native_thread.socket_pair.server_sd, msg.msg(), 0);
int const ret = lx_recvmsg(selected_sd, msg.msg(), 0x40);
/* system call got interrupted by a signal */
if (ret == -LX_EINTR)
if (ret < 0)
continue;
if (ret < 0) {
raw("lx_recvmsg failed with ", ret, " in ipc_reply_wait, sd=",
native_thread.socket_pair.server_sd);
if (msg.num_sockets() == 0 || !msg.socket_at_index(0).valid()) {
warning("ipc_reply_wait: failed to obtain reply socket");
continue;
}
int const reply_socket = msg.socket_at_index(0);
unsigned long const badge = header.protocol_word;
Lx_sd const reply_socket = msg.socket_at_index(0);
/* start at offset 1 to skip the reply channel */
extract_sds_from_message(1, msg, header, request_msg);
return Rpc_request(Capability_space::import(Rpc_destination(reply_socket),
Rpc_obj_key()), badge);
Rpc_obj_key()), selected_sd.value);
}
}
Ipc_server::Ipc_server(Rpc_entrypoint::Native_context& _native_context)
Ipc_server::Ipc_server(Rpc_entrypoint::Native_context& native_context)
:
Native_capability(Capability_space::import(Rpc_destination(), Rpc_obj_key())),
_native_context(_native_context)
_native_context(native_context)
{
/*
* If 'thread' is 0, the constructor was called by the main thread. By
@ -531,21 +438,13 @@ Ipc_server::Ipc_server(Rpc_entrypoint::Native_context& _native_context)
Native_thread &native_thread = Thread::myself()->native_thread();
if (native_thread.is_ipc_server) {
Genode::raw("[", lx_gettid(), "] "
Genode::raw(lx_getpid(), ":", lx_gettid(),
" unexpected multiple instantiation of Ipc_server by one thread");
struct Ipc_server_multiple_instance { };
throw Ipc_server_multiple_instance();
}
Socket_pair const socket_pair = server_socket_pair();
native_thread.socket_pair = socket_pair;
native_thread.is_ipc_server = true;
/* override capability initialization */
*static_cast<Native_capability *>(this) =
Capability_space::import(Rpc_destination(socket_pair.client_sd),
Rpc_obj_key());
}
@ -560,9 +459,5 @@ Ipc_server::~Ipc_server()
*/
Native_thread &native_thread = Thread::myself()->native_thread();
Genode::ep_sd_registry().disassociate(native_thread.socket_pair.client_sd);
native_thread.is_ipc_server = false;
destroy_server_socket_pair(native_thread.socket_pair);
native_thread.socket_pair = Socket_pair();
}

View File

@ -0,0 +1,209 @@
/*
* \brief Native thread implementation for using epoll on base-linux
* \author Stefan Thoeni
* \author Norman Feske
* \date 2019-12-13
*/
/*
* Copyright (C) 2006-2020 Genode Labs GmbH
* Copyright (C) 2019 gapfruit AG
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <base/internal/native_thread.h>
#include <base/internal/capability_space_tpl.h>
#include <linux_syscalls.h>
using namespace Genode;
struct Epoll_error : Exception { };
Native_thread::Epoll::Epoll()
:
_epoll(lx_epoll_create())
{
_add(_control.local);
}
Native_thread::Epoll::~Epoll()
{
_remove(_control.local);
lx_close(_control.local.value);
lx_close(_control.remote.value);
lx_close(_epoll.value);
}
void Native_thread::Epoll::_add(Lx_sd sd)
{
epoll_event event;
event.events = EPOLLIN;
event.data.fd = sd.value;
int ret = lx_epoll_ctl(_epoll, EPOLL_CTL_ADD, sd, &event);
if (ret < 0) {
warning(lx_getpid(), ":", lx_gettid(), " lx_epoll_ctl add failed with ", ret);
throw Epoll_error();
}
}
void Native_thread::Epoll::_remove(Lx_sd sd)
{
epoll_event event;
event.events = EPOLLIN;
event.data.fd = sd.value;
int ret = lx_epoll_ctl(_epoll, EPOLL_CTL_DEL, sd, &event);
if (ret == -2) {
/* ignore file already closed */
} else if (ret == -9) {
/* ignore file already closed */
} else if (ret < 0) {
warning(lx_getpid(), ":", lx_gettid(), " lx_epoll_ctl remove failed with ", ret);
throw Epoll_error();
}
}
Lx_sd Native_thread::Epoll::poll()
{
for (;;) {
epoll_event events[1] { };
int const event_count = lx_epoll_wait(_epoll, events, 1, -1);
if ((event_count == 1) && (events[0].events == POLLIN)) {
Lx_sd const sd { events[0].data.fd };
if (!sd.valid())
continue;
/* dispatch control messages issued via '_exec_control' */
if (sd.value == _control.local.value) {
Control_function *control_function_ptr = nullptr;
struct iovec iovec { };
iovec.iov_base = &control_function_ptr;
iovec.iov_len = sizeof(control_function_ptr);
struct msghdr msg { };
msg.msg_iov = &iovec;
msg.msg_iovlen = 1;
int const ret = lx_recvmsg(sd, &msg, 0);
if (ret != sizeof(control_function_ptr) || !control_function_ptr) {
error("epoll interrupted by invalid control message");
continue;
}
control_function_ptr->execute();
struct msghdr ack { };
(void)lx_sendmsg(sd, &ack, 0);
continue;
}
return sd;
}
if (event_count > 1)
warning(lx_getpid(), ":", lx_gettid(), " too many events on epoll_wait");
}
}
template <typename FN>
void Native_thread::Epoll::_exec_control(FN const &fn)
{
Thread * const myself_ptr = Thread::myself();
/*
* If 'myself_ptr' is nullptr, the caller is the initial thread w/o
* a valid 'Thread' object associated yet. This thread is never polling.
*/
bool const myself_is_polling = (myself_ptr != nullptr)
&& (&myself_ptr->native_thread().epoll == this);
/*
* If caller runs in the context of the same thread that executes 'poll' we
* can perform the control function immediately because 'poll' cannot
* block at this time. If the RPC entrypoint has existed its dispatch
* loop, it also cannot poll anymore.
*/
if (myself_is_polling || _rpc_ep_exited) {
fn();
return;
}
/*
* If caller is a different thread than the polling thread, interrupt the
* polling with a control message, prompting the polling thread to
* execute the control function, and resume polling afterwards.
*/
struct Control_function_fn : Control_function
{
FN const &fn;
Control_function_fn(FN const &fn) : fn(fn) { }
void execute() override { fn(); }
} control_function_fn { fn };
/* send control message with pointer to control function as argument */
{
Control_function *control_function_ptr = &control_function_fn;
struct iovec iovec { };
iovec.iov_base = &control_function_ptr;
iovec.iov_len = sizeof(control_function_ptr);
struct msghdr msg { };
msg.msg_iov = &iovec;
msg.msg_iovlen = 1;
int const ret = lx_sendmsg(_control.remote, &msg, 0);
if (ret < 0) {
raw(lx_getpid(), ":", lx_gettid(), " _exec_control ",
_control.remote.value, " lx_sendmsg failed ", ret);
sleep_forever();
}
}
/* block for the completion of the control function */
{
struct msghdr ack { };
int const ret = lx_recvmsg(_control.remote, &ack, 0);
if (ret < 0)
warning("invalid acknowledgement for control message");
}
}
Native_capability Native_thread::Epoll::alloc_rpc_cap()
{
Lx_socketpair socketpair;
Rpc_destination dst(socketpair.remote);
dst.foreign = false;
_exec_control([&] () { _add(socketpair.local); });
return Capability_space::import(dst, Rpc_obj_key(socketpair.local.value));
}
void Native_thread::Epoll::free_rpc_cap(Native_capability cap)
{
int const local_socket = Capability_space::ipc_cap_data(cap).rpc_obj_key.value();
_exec_control([&] () { _remove(Lx_sd{local_socket}); });
}

View File

@ -0,0 +1,22 @@
/*
* \brief Platform dependant hook after binary ready
* \author Stefan Thoeni
* \date 2019-12-13
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019 gapfruit AG
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* base-internal includes */
#include <base/platform.h>
void binary_ready_hook_for_platform()
{
}

View File

@ -44,7 +44,7 @@ size_t Region_map_mmap::_dataspace_size(Dataspace_capability ds)
int Region_map_mmap::_dataspace_fd(Dataspace_capability ds)
{
Untyped_capability fd_cap = Linux_dataspace_client(ds).fd();
return Capability_space::ipc_cap_data(fd_cap).dst.socket;
return lx_dup(Capability_space::ipc_cap_data(fd_cap).dst.socket.value);
}
@ -138,7 +138,7 @@ static Parent_capability obtain_parent_cap()
long const local_name = get_env_ulong("parent_local_name");
Untyped_capability parent_cap =
Capability_space::import(Rpc_destination(PARENT_SOCKET_HANDLE),
Capability_space::import(Rpc_destination(Lx_sd{PARENT_SOCKET_HANDLE}),
Rpc_obj_key(local_name));
return reinterpret_cap_cast<Parent>(parent_cap);
@ -169,34 +169,3 @@ Platform_env::Platform_env()
native_cpu.thread_id(parent()->main_thread_cap(), lx_getpid(), lx_gettid());
}
/*****************************
** Support for IPC library **
*****************************/
namespace Genode {
Socket_pair server_socket_pair()
{
Linux_native_cpu_client native_cpu(env_deprecated()->cpu_session()->native_cpu());
Socket_pair socket_pair;
Thread *thread = Thread::myself();
if (thread) {
Untyped_capability server_cap = native_cpu.server_sd(thread->cap());
Untyped_capability client_cap = native_cpu.client_sd(thread->cap());
socket_pair.server_sd = Capability_space::ipc_cap_data(server_cap).dst.socket;
socket_pair.client_sd = Capability_space::ipc_cap_data(client_cap).dst.socket;
thread->native_thread().socket_pair = socket_pair;
}
return socket_pair;
}
void destroy_server_socket_pair(Socket_pair socket_pair)
{
/* close local file descriptor if it is valid */
if (socket_pair.server_sd != -1) lx_close(socket_pair.server_sd);
if (socket_pair.client_sd != -1) lx_close(socket_pair.client_sd);
}
}

View File

@ -0,0 +1,64 @@
/*
* \brief Back end of the RPC entrypoint
* \author Norman Feske
* \date 2016-01-19
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/env.h>
#include <base/rpc_server.h>
#include <deprecated/env.h>
/* base-internal includes */
#include <base/internal/native_thread.h>
using namespace Genode;
Native_capability Rpc_entrypoint::_alloc_rpc_cap(Pd_session& pd, Native_capability,
addr_t)
{
/* first we allocate a cap from core, to allow accounting of caps. */
for (;;) {
Ram_quota ram_upgrade { 0 };
Cap_quota cap_upgrade { 0 };
try { pd.alloc_rpc_cap(_cap); break; }
catch (Out_of_ram) { ram_upgrade = Ram_quota { 2*1024*sizeof(long) }; }
catch (Out_of_caps) { cap_upgrade = Cap_quota { 4 }; }
env_deprecated()->parent()->upgrade(Parent::Env::pd(),
String<100>("ram_quota=", ram_upgrade, ", "
"cap_quota=", cap_upgrade).string());
}
return Thread::native_thread().epoll.alloc_rpc_cap();
}
void Rpc_entrypoint::_free_rpc_cap(Pd_session& pd, Native_capability cap)
{
Native_thread::Epoll &epoll = Thread::native_thread().epoll;
/*
* Flag RPC entrypoint as exited to prevent 'free_rpc_cap' from issuing
* a remote control request.
*/
if (_exit_handler.exit)
epoll.rpc_ep_exited();
/*
* Perform the accounting of the PDs cap quota at core, to remain
* consistent with other kernel platforms.
*/
pd.free_rpc_cap(Native_capability());
epoll.free_rpc_cap(cap);
}

View File

@ -1,6 +1,7 @@
/*
* \brief Linux system-call bindings
* \author Norman Feske
* \author Stefan Thöni
* \date 2008-10-22
*
* This file is meant to be internally used by the framework. It is not public
@ -20,6 +21,7 @@
/*
* Copyright (C) 2008-2017 Genode Labs GmbH
* Copyright (C) 2019 gapfruit AG
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -36,6 +38,8 @@
#include <util/string.h>
#include <base/snprintf.h>
#include <base/log.h>
#include <base/sleep.h>
/*
* Resolve ambiguity between 'Genode::size_t' and the host's header's 'size_t'.
@ -51,7 +55,10 @@
#include <sys/syscall.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <sys/mman.h>
#include <sys/stat.h>
#undef size_t
@ -85,6 +92,12 @@ extern "C" int lx_clone(int (*fn)(void *), void *child_stack,
** General syscalls used by base-linux **
*****************************************/
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); }
inline int lx_write(int fd, const void *buf, Genode::size_t count)
{
return lx_syscall(SYS_write, fd, buf, count);
@ -97,6 +110,12 @@ inline int lx_close(int fd)
}
inline int lx_dup(int fd)
{
return lx_syscall(SYS_dup, fd);
}
inline int lx_dup2(int fd, int to)
{
return lx_syscall(SYS_dup2, fd, to);
@ -109,6 +128,39 @@ inline int lx_dup2(int fd, int to)
#include <linux/net.h>
struct Lx_sd
{
int value;
bool valid() const { return value >= 0; }
static Lx_sd invalid() { return Lx_sd{-1}; }
uint64_t inode() const
{
#ifdef __NR_fstat64
struct stat64 statbuf { };
(void)lx_syscall(SYS_fstat64, value, &statbuf);
#else
struct stat statbuf { };
(void)lx_syscall(SYS_fstat, value, &statbuf);
#endif /* __NR_fstat64 */
return statbuf.st_ino;
}
void print(Genode::Output &out) const
{
Genode::print(out, "socket=", value);
if (value >= 0)
Genode::print(out, ",inode=", inode());
}
};
struct Lx_epoll_sd { int value; };
#ifdef SYS_socketcall
inline int lx_socketcall(int call, long *args)
@ -124,26 +176,19 @@ inline int lx_socketpair(int domain, int type, int protocol, int sd[2])
}
inline int lx_sendmsg(int sockfd, const struct msghdr *msg, int flags)
inline int lx_sendmsg(Lx_sd sockfd, const struct msghdr *msg, int flags)
{
long args[3] = { sockfd, (long)msg, flags };
long args[3] = { sockfd.value, (long)msg, flags };
return lx_socketcall(SYS_SENDMSG, args);
}
inline int lx_recvmsg(int sockfd, struct msghdr *msg, int flags)
inline int lx_recvmsg(Lx_sd sockfd, struct msghdr *msg, int flags)
{
long args[3] = { sockfd, (long)msg, flags };
long args[3] = { sockfd.value, (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])
@ -152,21 +197,15 @@ inline int lx_socketpair(int domain, int type, int protocol, int sd[2])
}
inline int lx_sendmsg(int sockfd, const struct msghdr *msg, int flags)
inline int lx_sendmsg(Lx_sd sockfd, const struct msghdr *msg, int flags)
{
return lx_syscall(SYS_sendmsg, sockfd, msg, flags);
return lx_syscall(SYS_sendmsg, sockfd.value, msg, flags);
}
inline int lx_recvmsg(int sockfd, struct msghdr *msg, int flags)
inline int lx_recvmsg(Lx_sd 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);
return lx_syscall(SYS_recvmsg, sockfd.value, msg, flags);
}
/* TODO add missing socket system calls */
@ -174,6 +213,56 @@ inline int lx_getpeername(int sockfd, struct sockaddr *name, socklen_t *namelen)
#endif /* SYS_socketcall */
struct Lx_socketpair
{
Lx_sd local { -1 };
Lx_sd remote { -1 };
Lx_socketpair()
{
int sd[2];
sd[0] = -1; sd[1] = -1;
int const ret = lx_socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0, sd);
if (ret < 0) {
Genode::raw(lx_getpid(), ":", lx_gettid(), " lx_socketpair failed with ", ret);
Genode::sleep_forever();
}
local = Lx_sd { sd[0] };
remote = Lx_sd { sd[1] };
}
};
inline Lx_epoll_sd lx_epoll_create()
{
int const ret = lx_syscall(SYS_epoll_create, 1);
if (ret < 0) {
/*
* No recovery possible, just leave a diagnostic message and block
* forever.
*/
Genode::raw(lx_getpid(), ":", lx_gettid(), " lx_epoll_create failed with ", ret);
Genode::sleep_forever();
}
return Lx_epoll_sd { ret };
}
inline int lx_epoll_ctl(Lx_epoll_sd epoll, int op, Lx_sd fd, epoll_event *event)
{
return lx_syscall(SYS_epoll_ctl, epoll.value, op, fd.value, event);
}
inline int lx_epoll_wait(Lx_epoll_sd epoll, struct epoll_event *events,
int maxevents, int timeout)
{
return lx_syscall(SYS_epoll_wait, epoll.value, events, maxevents, timeout);
}
/*******************************************
** Functions used by the process library **
*******************************************/
@ -254,6 +343,7 @@ inline int lx_sigemptyset(sigset_t *set)
extern "C" void lx_restore_rt (void);
#endif
/**
* Simplified binding for sigaction system call
*/
@ -324,11 +414,6 @@ inline int lx_create_thread(void (*entry)(), void *stack, void *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 **
************************************/
@ -340,6 +425,7 @@ 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,