mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-19 15:43:56 +00:00
Clean up base-library structure
This patch moves the base library from src/base to src/lib/base, flattens the library-internal directory structure, and moves the common parts of the library-description files to base/lib/mk/base.inc and base/lib/mk/base-common.inc. Furthermore, the patch fixes a few cosmetic issues (whitespace and comments only) that I encountered while browsing the result. Fixes #1952
This commit is contained in:
114
repos/base-linux/src/lib/base/child_process.cc
Normal file
114
repos/base-linux/src/lib/base/child_process.cc
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* \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/env.h>
|
||||
#include <base/child.h>
|
||||
#include <base/printf.h>
|
||||
#include <linux_native_pd/client.h>
|
||||
|
||||
/* base-internal includes */
|
||||
#include <linux_syscalls.h>
|
||||
#include <base/internal/elf.h>
|
||||
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
Child::Process::Initial_thread::Initial_thread(Cpu_session &cpu,
|
||||
Pd_session_capability pd,
|
||||
char const *name)
|
||||
:
|
||||
cpu(cpu),
|
||||
cap(cpu.create_thread(pd, Cpu_session::DEFAULT_WEIGHT, name))
|
||||
{ }
|
||||
|
||||
|
||||
Child::Process::Initial_thread::~Initial_thread() { }
|
||||
|
||||
|
||||
/*
|
||||
* On Linux, the ELF loading is performed by the kernel
|
||||
*/
|
||||
Child::Process::Loaded_executable::Loaded_executable(Dataspace_capability,
|
||||
Dataspace_capability,
|
||||
Ram_session &,
|
||||
Region_map &,
|
||||
Region_map &,
|
||||
Parent_capability) { }
|
||||
|
||||
|
||||
Child::Process::Process(Dataspace_capability elf_ds,
|
||||
Dataspace_capability ldso_ds,
|
||||
Pd_session_capability pd_cap,
|
||||
Pd_session &pd,
|
||||
Ram_session &ram,
|
||||
Cpu_session &cpu,
|
||||
Region_map &local_rm,
|
||||
Region_map &remote_rm,
|
||||
Parent_capability parent_cap,
|
||||
char const *name)
|
||||
:
|
||||
initial_thread(cpu, pd_cap, name),
|
||||
loaded_executable(elf_ds, ldso_ds, ram, local_rm, remote_rm, parent_cap)
|
||||
{
|
||||
/* skip loading when called during fork */
|
||||
if (!elf_ds.valid())
|
||||
return;
|
||||
|
||||
/* attach ELF locally */
|
||||
addr_t elf_addr;
|
||||
try { elf_addr = local_rm.attach(elf_ds); }
|
||||
catch (Region_map::Attach_failed) {
|
||||
PERR("local attach of ELF executable failed"); throw; }
|
||||
|
||||
/* setup ELF object and read program entry pointer */
|
||||
Elf_binary elf(elf_addr);
|
||||
if (!elf.valid())
|
||||
throw Invalid_executable();
|
||||
|
||||
bool const dynamically_linked = elf.is_dynamically_linked();
|
||||
|
||||
local_rm.detach(elf_addr);
|
||||
|
||||
/*
|
||||
* If the specified executable is a dynamically linked program, we load
|
||||
* the dynamic linker instead.
|
||||
*/
|
||||
if (dynamically_linked) {
|
||||
|
||||
if (!ldso_ds.valid()) {
|
||||
PERR("attempt to start dynamic executable without dynamic linker");
|
||||
throw Missing_dynamic_linker();
|
||||
}
|
||||
|
||||
elf_ds = ldso_ds;
|
||||
}
|
||||
|
||||
pd.assign_parent(parent_cap);
|
||||
|
||||
Linux_native_pd_client
|
||||
lx_pd(static_cap_cast<Linux_native_pd>(pd.native_pd()));
|
||||
|
||||
lx_pd.start(elf_ds);
|
||||
}
|
||||
|
||||
|
||||
Child::Process::~Process() { }
|
21
repos/base-linux/src/lib/base/cpu/arm/cache.cc
Normal file
21
repos/base-linux/src/lib/base/cpu/arm/cache.cc
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* \brief Implementation of the cache operations
|
||||
* \author Christian Prochaska
|
||||
* \date 2014-05-13
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#include <linux_syscalls.h>
|
||||
|
||||
#include <cpu/cache.h>
|
||||
|
||||
void Genode::cache_coherent(Genode::addr_t addr, Genode::size_t size)
|
||||
{
|
||||
lx_syscall(__ARM_NR_cacheflush, addr, addr + size, 0);
|
||||
}
|
57
repos/base-linux/src/lib/base/debug.cc
Normal file
57
repos/base-linux/src/lib/base/debug.cc
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* \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 */
|
||||
}
|
24
repos/base-linux/src/lib/base/env_reinit.cc
Normal file
24
repos/base-linux/src/lib/base/env_reinit.cc
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* \brief Environment reinitialization
|
||||
* \author Norman Feske
|
||||
* \date 2016-04-29
|
||||
*
|
||||
* Support functions for implementing fork on Noux, which is not supported on
|
||||
* Linux.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
/* base-internal includes */
|
||||
#include <base/internal/platform_env.h>
|
||||
|
||||
|
||||
void Genode::Platform_env_base::reinit(Native_capability::Dst, long) { }
|
||||
|
||||
|
||||
void Genode::Platform_env_base::reinit_main_thread(Capability<Region_map> &) { }
|
521
repos/base-linux/src/lib/base/ipc.cc
Normal file
521
repos/base-linux/src/lib/base/ipc.cc
Normal file
@ -0,0 +1,521 @@
|
||||
/*
|
||||
* \brief Socket-based IPC implementation for Linux
|
||||
* \author Norman Feske
|
||||
* \author Christian Helmuth
|
||||
* \date 2011-10-11
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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_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>
|
||||
|
||||
/* Linux includes */
|
||||
#include <linux_syscalls.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
/*
|
||||
* The request message layout is:
|
||||
*
|
||||
* long local_name;
|
||||
* ...call arguments, starting with the opcode...
|
||||
*
|
||||
* Response messages look like this:
|
||||
*
|
||||
* 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..
|
||||
*/
|
||||
struct Protocol_header
|
||||
{
|
||||
/* badge of invoked object (on call) / exception code (on reply) */
|
||||
unsigned long protocol_word;
|
||||
|
||||
size_t num_caps;
|
||||
|
||||
/* badges of the transferred capability arguments */
|
||||
unsigned long badges[Msgbuf_base::MAX_CAPS_PER_MSG];
|
||||
|
||||
enum { INVALID_BADGE = ~0UL };
|
||||
|
||||
void *msg_start() { return &protocol_word; }
|
||||
};
|
||||
|
||||
|
||||
/******************************
|
||||
** 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 + 1 };
|
||||
|
||||
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 */
|
||||
|
||||
|
||||
static void insert_sds_into_message(Message &msg,
|
||||
Protocol_header &header,
|
||||
Genode::Msgbuf_base &snd_msgbuf)
|
||||
{
|
||||
for (unsigned i = 0; i < snd_msgbuf.used_caps(); i++) {
|
||||
|
||||
Native_capability const &cap = snd_msgbuf.cap(i);
|
||||
|
||||
if (cap.valid()) {
|
||||
msg.marshal_socket(cap.dst().socket);
|
||||
header.badges[i] = cap.local_name();
|
||||
} else {
|
||||
header.badges[i] = Protocol_header::INVALID_BADGE;
|
||||
}
|
||||
|
||||
}
|
||||
header.num_caps = snd_msgbuf.used_caps();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Utility: Extract socket desriptors from SCM message into 'Genode::Msgbuf'
|
||||
*/
|
||||
static void extract_sds_from_message(unsigned start_index,
|
||||
Message const &msg,
|
||||
Protocol_header const &header,
|
||||
Genode::Msgbuf_base &buf)
|
||||
{
|
||||
unsigned sd_cnt = 0;
|
||||
for (unsigned i = 0; i < min(header.num_caps, Msgbuf_base::MAX_CAPS_PER_MSG); i++) {
|
||||
|
||||
unsigned long const badge = header.badges[i];
|
||||
|
||||
/* an invalid capabity was transferred */
|
||||
if (badge == Protocol_header::INVALID_BADGE) {
|
||||
buf.insert(Native_capability());
|
||||
continue;
|
||||
}
|
||||
|
||||
int const sd = msg.socket_at_index(start_index + sd_cnt++);
|
||||
int const id = lookup_tid_by_client_socket(sd);
|
||||
|
||||
int const associated_sd = Genode::ep_sd_registry()->try_associate(sd, id);
|
||||
|
||||
buf.insert(Native_capability(Cap_dst_policy::Dst(associated_sd), 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send reply to client
|
||||
*/
|
||||
static inline void lx_reply(int reply_socket, Rpc_exception_code exception_code,
|
||||
Genode::Msgbuf_base &snd_msgbuf)
|
||||
{
|
||||
|
||||
Protocol_header &header = snd_msgbuf.header<Protocol_header>();
|
||||
|
||||
header.protocol_word = exception_code.value;
|
||||
|
||||
Message msg(header.msg_start(), sizeof(Protocol_header) + snd_msgbuf.data_size());
|
||||
|
||||
/* marshall capabilities to be transferred to the client */
|
||||
insert_sds_into_message(msg, header, snd_msgbuf);
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
PRAW("[%d] lx_sendmsg failed with %d in lx_reply() reply_socket=%d", lx_gettid(), ret, reply_socket);
|
||||
}
|
||||
|
||||
|
||||
/****************
|
||||
** IPC client **
|
||||
****************/
|
||||
|
||||
Rpc_exception_code Genode::ipc_call(Native_capability dst,
|
||||
Msgbuf_base &snd_msgbuf, Msgbuf_base &rcv_msgbuf,
|
||||
size_t)
|
||||
{
|
||||
Protocol_header &snd_header = snd_msgbuf.header<Protocol_header>();
|
||||
snd_header.protocol_word = dst.local_name();
|
||||
|
||||
Message snd_msg(snd_header.msg_start(),
|
||||
sizeof(Protocol_header) + snd_msgbuf.data_size());
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
snd_msg.marshal_socket(reply_channel.remote_socket());
|
||||
|
||||
/* marshal capabilities contained in 'snd_msgbuf' */
|
||||
insert_sds_into_message(snd_msg, snd_header, snd_msgbuf);
|
||||
|
||||
int const send_ret = lx_sendmsg(dst.dst().socket, snd_msg.msg(), 0);
|
||||
if (send_ret < 0) {
|
||||
PRAW("[%d] lx_sendmsg to sd %d failed with %d in lx_call()",
|
||||
lx_getpid(), dst.dst().socket, send_ret);
|
||||
throw Genode::Ipc_error();
|
||||
}
|
||||
|
||||
/* receive reply */
|
||||
Protocol_header &rcv_header = rcv_msgbuf.header<Protocol_header>();
|
||||
rcv_header.protocol_word = 0;
|
||||
|
||||
Message rcv_msg(rcv_header.msg_start(),
|
||||
sizeof(Protocol_header) + rcv_msgbuf.capacity());
|
||||
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);
|
||||
|
||||
/* system call got interrupted by a signal */
|
||||
if (recv_ret == -LX_EINTR)
|
||||
throw Genode::Blocking_canceled();
|
||||
|
||||
if (recv_ret < 0) {
|
||||
PRAW("[%d] lx_recvmsg failed with %d in lx_call()", lx_getpid(), recv_ret);
|
||||
throw Genode::Ipc_error();
|
||||
}
|
||||
|
||||
extract_sds_from_message(0, rcv_msg, rcv_header, rcv_msgbuf);
|
||||
|
||||
return Rpc_exception_code(rcv_header.protocol_word);
|
||||
}
|
||||
|
||||
|
||||
/****************
|
||||
** IPC server **
|
||||
****************/
|
||||
|
||||
void Genode::ipc_reply(Native_capability caller, Rpc_exception_code exc,
|
||||
Msgbuf_base &snd_msg)
|
||||
{
|
||||
try { lx_reply(caller.dst().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)
|
||||
{
|
||||
/* when first called, there was no request yet */
|
||||
if (last_caller.valid() && exc.value != Rpc_exception_code::INVALID_OBJECT)
|
||||
lx_reply(last_caller.dst().socket, exc, reply_msg);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
|
||||
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_base::myself()->native_thread();
|
||||
|
||||
request_msg.reset();
|
||||
int const ret = lx_recvmsg(native_thread.socket_pair.server_sd, msg.msg(), 0);
|
||||
|
||||
/* system call got interrupted by a signal */
|
||||
if (ret == -LX_EINTR)
|
||||
continue;
|
||||
|
||||
if (ret < 0) {
|
||||
PRAW("lx_recvmsg failed with %d in ipc_reply_wait, sd=%d",
|
||||
ret, native_thread.socket_pair.server_sd);
|
||||
continue;
|
||||
}
|
||||
|
||||
int const reply_socket = msg.socket_at_index(0);
|
||||
unsigned long const badge = header.protocol_word;
|
||||
|
||||
/* start at offset 1 to skip the reply channel */
|
||||
extract_sds_from_message(1, msg, header, request_msg);
|
||||
|
||||
typedef Native_capability::Dst Dst;
|
||||
return Rpc_request(Native_capability(Dst(reply_socket), ~0UL), badge);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ipc_server::Ipc_server()
|
||||
:
|
||||
Native_capability(Dst(-1), 0)
|
||||
{
|
||||
/*
|
||||
* 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_base::myself())
|
||||
return;
|
||||
|
||||
Native_thread &native_thread = Thread_base::myself()->native_thread();
|
||||
|
||||
if (native_thread.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();
|
||||
}
|
||||
|
||||
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) =
|
||||
Native_capability(Native_capability::Dst(socket_pair.client_sd), 0);
|
||||
}
|
||||
|
||||
|
||||
Ipc_server::~Ipc_server()
|
||||
{
|
||||
if (!Thread_base::myself())
|
||||
return;
|
||||
|
||||
/*
|
||||
* Reset thread role to non-server such that we can enter 'sleep_forever'
|
||||
* without getting a warning.
|
||||
*/
|
||||
Native_thread &native_thread = Thread_base::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();
|
||||
}
|
198
repos/base-linux/src/lib/base/platform_env.cc
Normal file
198
repos/base-linux/src/lib/base/platform_env.cc
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* \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>
|
||||
|
||||
/* base-internal includes */
|
||||
#include <base/internal/platform_env.h>
|
||||
#include <base/internal/native_thread.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
/****************************************************
|
||||
** Support for Platform_env_base::Rm_session_mmap **
|
||||
****************************************************/
|
||||
|
||||
Genode::size_t
|
||||
Platform_env_base::Region_map_mmap::_dataspace_size(Dataspace_capability ds)
|
||||
{
|
||||
if (ds.valid())
|
||||
return Dataspace_client(ds).size();
|
||||
|
||||
return Local_capability<Dataspace>::deref(ds)->size();
|
||||
}
|
||||
|
||||
|
||||
int Platform_env_base::Region_map_mmap::_dataspace_fd(Dataspace_capability ds)
|
||||
{
|
||||
return Linux_dataspace_client(ds).fd().dst().socket;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Platform_env_base::Region_map_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)
|
||||
{
|
||||
Local_rm_session *session = new (_alloc) Local_rm_session(_alloc);
|
||||
|
||||
return Local_capability<Session>::local_cap(session);
|
||||
}
|
||||
|
||||
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> rm = static_cap_cast<Rm_session>(session);
|
||||
|
||||
destroy(_alloc, Local_capability<Rm_session>::deref(rm));
|
||||
}
|
||||
|
||||
|
||||
Platform_env::Local_parent::Local_parent(Parent_capability parent_cap,
|
||||
Emergency_ram_reserve &reserve,
|
||||
Allocator &alloc)
|
||||
:
|
||||
Expanding_parent_client(parent_cap, reserve), _alloc(alloc)
|
||||
{ }
|
||||
|
||||
|
||||
/******************
|
||||
** 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, _heap);
|
||||
return local_parent;
|
||||
}
|
||||
|
||||
|
||||
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()))
|
||||
{
|
||||
/* attach stack area to local address space */
|
||||
_local_pd_session._address_space.attach_at(_local_pd_session._stack_area.dataspace(),
|
||||
stack_area_virtual_base(),
|
||||
stack_area_virtual_size());
|
||||
|
||||
env_stack_area_region_map = &_local_pd_session._stack_area;
|
||||
env_stack_area_ram_session = ram_session();
|
||||
|
||||
/* register TID and PID of the main thread at core */
|
||||
Linux_native_cpu_client native_cpu(cpu_session()->native_cpu());
|
||||
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()->cpu_session()->native_cpu());
|
||||
|
||||
Socket_pair socket_pair;
|
||||
|
||||
Thread_base *thread = Thread_base::myself();
|
||||
if (thread) {
|
||||
socket_pair.server_sd = native_cpu.server_sd(thread->cap()).dst().socket;
|
||||
socket_pair.client_sd = native_cpu.client_sd(thread->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);
|
||||
}
|
||||
}
|
69
repos/base-linux/src/lib/base/region_map_client.cc
Normal file
69
repos/base-linux/src/lib/base/region_map_client.cc
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* \brief Pseudo region map client stub targeting the process-local implementation
|
||||
* \author Norman Feske
|
||||
* \date 2011-11-21
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2016 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 <region_map/client.h>
|
||||
|
||||
/* base-internal includes */
|
||||
#include <base/internal/local_capability.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
/**
|
||||
* Return pointer to locally implemented region map
|
||||
*
|
||||
* \throw Local_interface::Non_local_capability
|
||||
*/
|
||||
static Region_map *_local(Capability<Region_map> cap)
|
||||
{
|
||||
return Local_capability<Region_map>::deref(cap);
|
||||
}
|
||||
|
||||
|
||||
Region_map_client::Region_map_client(Capability<Region_map> session)
|
||||
: Rpc_client<Region_map>(session) { }
|
||||
|
||||
|
||||
Region_map::Local_addr
|
||||
Region_map_client::attach(Dataspace_capability ds, size_t size,
|
||||
off_t offset, bool use_local_addr,
|
||||
Region_map::Local_addr local_addr,
|
||||
bool executable)
|
||||
{
|
||||
return _local(*this)->attach(ds, size, offset, use_local_addr,
|
||||
local_addr, executable);
|
||||
}
|
||||
|
||||
|
||||
void Region_map_client::detach(Local_addr local_addr) {
|
||||
return _local(*this)->detach(local_addr); }
|
||||
|
||||
|
||||
void Region_map_client::fault_handler(Signal_context_capability /*handler*/)
|
||||
{
|
||||
/*
|
||||
* On Linux, page faults are never reflected to the user land. They
|
||||
* are always handled by the kernel. If a segmentation fault
|
||||
* occurs, this condition is being reflected as a CPU exception
|
||||
* to the handler registered via 'Cpu_session::exception_handler'.
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
Region_map::State Region_map_client::state() { return _local(*this)->state(); }
|
||||
|
||||
|
||||
Dataspace_capability Region_map_client::dataspace() {
|
||||
return _local(*this)->dataspace(); }
|
||||
|
364
repos/base-linux/src/lib/base/region_map_mmap.cc
Normal file
364
repos/base-linux/src/lib/base/region_map_mmap.cc
Normal file
@ -0,0 +1,364 @@
|
||||
/*
|
||||
* \brief Implementation of Linux-specific local region map
|
||||
* \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 stack 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 stack 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 stack 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>
|
||||
|
||||
/* base-internal includes */
|
||||
#include <base/internal/local_capability.h>
|
||||
#include <base/internal/platform_env.h>
|
||||
#include <base/internal/stack_area.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
static bool is_sub_rm_session(Dataspace_capability ds)
|
||||
{
|
||||
if (ds.valid())
|
||||
return false;
|
||||
|
||||
return Local_capability<Dataspace>::deref(ds) != 0;
|
||||
}
|
||||
|
||||
|
||||
addr_t Platform_env_base::Region_map_mmap::_reserve_local(bool use_local_addr,
|
||||
addr_t local_addr,
|
||||
Genode::size_t size)
|
||||
{
|
||||
/* special handling for stack area */
|
||||
if (use_local_addr
|
||||
&& local_addr == stack_area_virtual_base()
|
||||
&& size == stack_area_virtual_size()) {
|
||||
|
||||
/*
|
||||
* On the first request to reserve the stack 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_stack_area();
|
||||
reserve_stack_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 Region_map::Region_conflict();
|
||||
}
|
||||
|
||||
return (addr_t) addr_out;
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
Platform_env_base::Region_map_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 Region_map::Region_conflict();
|
||||
}
|
||||
|
||||
return addr_out;
|
||||
}
|
||||
|
||||
|
||||
void Platform_env::Region_map_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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Region_map::Local_addr
|
||||
Platform_env::Region_map_mmap::attach(Dataspace_capability ds,
|
||||
size_t size, off_t offset,
|
||||
bool use_local_addr,
|
||||
Region_map::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("Region_map_mmap::attach: attaching w/o local addr not supported\n");
|
||||
throw Out_of_metadata();
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
PERR("Region_map_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("Region_map_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("Region_map_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 = Local_capability<Dataspace>::deref(ds);
|
||||
|
||||
Region_map_mmap *rm = dynamic_cast<Region_map_mmap *>(ds_if);
|
||||
|
||||
if (!rm)
|
||||
throw Invalid_dataspace();
|
||||
|
||||
/*
|
||||
* Case 2.1
|
||||
*
|
||||
* Detect if sub RM session is already attached
|
||||
*/
|
||||
if (rm->_base) {
|
||||
PERR("Region_map_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_registry::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::Region_map_mmap::detach(Region_map::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 = Local_capability<Dataspace>::deref(region.dataspace());
|
||||
Region_map_mmap *rm = dynamic_cast<Region_map_mmap *>(ds_if);
|
||||
if (rm)
|
||||
rm->_base = 0;
|
||||
}
|
||||
}
|
43
repos/base-linux/src/lib/base/rm_session_client.cc
Normal file
43
repos/base-linux/src/lib/base/rm_session_client.cc
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* \brief Pseudo RM session client stub targeting the process-local implementation
|
||||
* \author Norman Feske
|
||||
* \date 2011-11-21
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2016 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <rm_session/client.h>
|
||||
|
||||
/* base-internal includes */
|
||||
#include <base/internal/local_capability.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
/**
|
||||
* Return pointer to locally implemented RM session
|
||||
*
|
||||
* \throw Local_interface::Non_local_capability
|
||||
*/
|
||||
static Rm_session *_local(Capability<Rm_session> cap)
|
||||
{
|
||||
return Local_capability<Rm_session>::deref(cap);
|
||||
}
|
||||
|
||||
|
||||
Rm_session_client::Rm_session_client(Capability<Rm_session> session)
|
||||
: Rpc_client<Rm_session>(session) { }
|
||||
|
||||
|
||||
Capability<Region_map> Rm_session_client::create(size_t size) {
|
||||
return _local(*this)->create(size); }
|
||||
|
||||
|
||||
void Rm_session_client::destroy(Capability<Region_map> cap) {
|
||||
_local(*this)->destroy(cap); }
|
111
repos/base-linux/src/lib/base/thread_env.cc
Normal file
111
repos/base-linux/src/lib/base/thread_env.cc
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* \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>
|
||||
|
||||
#include <linux_syscalls.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))));
|
||||
|
||||
/**
|
||||
* Genode console hook
|
||||
*/
|
||||
extern "C" int stdout_write(char const *);
|
||||
|
||||
/*
|
||||
* Core lacks the hook, so provide a base-linux specific weak implementation
|
||||
*/
|
||||
extern "C" __attribute__((weak)) int stdout_write(char const *s)
|
||||
{
|
||||
return raw_write_str(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal handler for exceptions like segmentation faults
|
||||
*/
|
||||
void exception_signal_handler(int signum)
|
||||
{
|
||||
char const *reason = nullptr;
|
||||
|
||||
switch (signum) {
|
||||
case LX_SIGILL: reason = "Illegal instruction"; break;
|
||||
case LX_SIGBUS: reason = "Bad memory access"; break;
|
||||
case LX_SIGFPE: reason = "Floating point exception"; break;
|
||||
case LX_SIGSEGV: reason = "Segmentation fault"; break;
|
||||
|
||||
default: /* unexpected signal */ return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't use Genode::printf() as the exception may have occurred in the
|
||||
* Genode console library itself, which uses a mutex. Therefore, we use
|
||||
* Genode::snprintf() and call the console hook directly to minimize
|
||||
* overlaps with other code paths.
|
||||
*/
|
||||
static char msg[128];
|
||||
snprintf(msg, sizeof(msg),
|
||||
ESC_ERR "%s (signum=%d), see Linux kernel log for details" ESC_END "\n",
|
||||
reason, signum);
|
||||
stdout_write(msg);
|
||||
|
||||
/*
|
||||
* We reset the signal handler to SIG_DFL and trigger exception again,
|
||||
* i.e., terminate the process.
|
||||
*/
|
||||
lx_sigaction(signum, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void lx_exception_signal_handlers()
|
||||
{
|
||||
lx_sigaction(LX_SIGILL, exception_signal_handler);
|
||||
lx_sigaction(LX_SIGBUS, exception_signal_handler);
|
||||
lx_sigaction(LX_SIGFPE, exception_signal_handler);
|
||||
lx_sigaction(LX_SIGSEGV, exception_signal_handler);
|
||||
}
|
||||
|
||||
|
||||
/*****************************
|
||||
** 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];
|
||||
|
||||
lx_exception_signal_handlers();
|
||||
}
|
156
repos/base-linux/src/lib/base/thread_linux.cc
Normal file
156
repos/base-linux/src/lib/base/thread_linux.cc
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* \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_native_cpu/client.h>
|
||||
|
||||
/* base-internal includes */
|
||||
#include <base/internal/stack.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); }
|
||||
|
||||
|
||||
static char signal_stack[0x2000] __attribute__((aligned(0x1000)));
|
||||
|
||||
void Thread_base::_thread_start()
|
||||
{
|
||||
lx_sigaltstack(signal_stack, sizeof(signal_stack));
|
||||
|
||||
/*
|
||||
* 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_native_cpu_client native_cpu(thread->_cpu_session->native_cpu());
|
||||
native_cpu.thread_id(thread->cap(), thread->native_thread().pid, thread->native_thread().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(size_t weight, 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(env()->pd_session_cap(),
|
||||
weight, _stack->name().string());
|
||||
return;
|
||||
}
|
||||
/* adjust initial object state for main threads */
|
||||
native_thread().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(native_thread().pid, native_thread().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;
|
||||
}
|
||||
|
||||
native_thread().tid = lx_create_thread(Thread_base::_thread_start, stack_top(), this);
|
||||
native_thread().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