mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-20 09:46:20 +00:00
libc: fork, getpid, and wait4
This patch complements the C runtime with support for fork, getpid, and wait4 (and its cousin 'waitpid'). Fixes #3478
This commit is contained in:
parent
581785a48f
commit
bb5827b4e3
@ -20,6 +20,7 @@
|
||||
#include <os/path.h>
|
||||
#include <base/allocator.h>
|
||||
#include <base/id_space.h>
|
||||
#include <util/xml_generator.h>
|
||||
|
||||
/* libc includes */
|
||||
#include <stdlib.h>
|
||||
@ -115,6 +116,8 @@ namespace Libc {
|
||||
void preserve(int libc_fd);
|
||||
|
||||
File_descriptor *find_by_libc_fd(int libc_fd);
|
||||
|
||||
void generate_info(Genode::Xml_generator &);
|
||||
};
|
||||
|
||||
|
||||
|
@ -18,7 +18,7 @@ SRC_CC = atexit.cc dummies.cc rlimit.cc sysctl.cc \
|
||||
pread_pwrite.cc readv_writev.cc poll.cc \
|
||||
vfs_plugin.cc dynamic_linker.cc signal.cc \
|
||||
socket_operations.cc task.cc socket_fs_plugin.cc syscall.cc \
|
||||
getpwent.cc getrandom.cc
|
||||
getpwent.cc getrandom.cc fork.cc
|
||||
|
||||
#
|
||||
# Pthreads
|
||||
|
95
repos/libports/src/lib/libc/clone_session.h
Normal file
95
repos/libports/src/lib/libc/clone_session.h
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* \brief Session interface for fetching the content of cloned libc process
|
||||
* \author Norman Feske
|
||||
* \date 2019-08-14
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 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 _CLONE_SESSION_H_
|
||||
#define _CLONE_SESSION_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/rpc_server.h>
|
||||
#include <base/connection.h>
|
||||
#include <base/attached_dataspace.h>
|
||||
#include <util/misc_math.h>
|
||||
|
||||
/* libc includes */
|
||||
#include <string.h>
|
||||
|
||||
namespace Libc {
|
||||
struct Clone_session;
|
||||
struct Clone_connection;
|
||||
}
|
||||
|
||||
|
||||
struct Libc::Clone_session : Genode::Session
|
||||
{
|
||||
static const char *service_name() { return "Clone"; }
|
||||
|
||||
enum { BUFFER_SIZE = 512*1024UL,
|
||||
RAM_QUOTA = BUFFER_SIZE + 4096,
|
||||
CAP_QUOTA = 2 };
|
||||
|
||||
struct Memory_range
|
||||
{
|
||||
void *start;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
GENODE_RPC(Rpc_dataspace, Genode::Dataspace_capability, dataspace);
|
||||
GENODE_RPC(Rpc_memory_content, void, memory_content, Memory_range);
|
||||
|
||||
GENODE_RPC_INTERFACE(Rpc_dataspace, Rpc_memory_content);
|
||||
};
|
||||
|
||||
|
||||
struct Libc::Clone_connection : Genode::Connection<Clone_session>,
|
||||
Genode::Rpc_client<Clone_session>
|
||||
{
|
||||
Genode::Attached_dataspace const _buffer;
|
||||
|
||||
Clone_connection(Genode::Env &env)
|
||||
:
|
||||
Genode::Connection<Clone_session>(env,
|
||||
session(env.parent(),
|
||||
"ram_quota=%ld, cap_quota=%ld",
|
||||
RAM_QUOTA, CAP_QUOTA)),
|
||||
Genode::Rpc_client<Clone_session>(cap()),
|
||||
_buffer(env.rm(), call<Rpc_dataspace>())
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Obtain memory content from cloned address space
|
||||
*/
|
||||
void memory_content(void *dst, size_t const len)
|
||||
{
|
||||
size_t remaining = len;
|
||||
char *ptr = (char *)dst;
|
||||
|
||||
while (remaining > 0) {
|
||||
|
||||
size_t const chunk_len = Genode::min((size_t)BUFFER_SIZE, remaining);
|
||||
|
||||
/* instruct server to fill shared buffer */
|
||||
call<Rpc_memory_content>(Memory_range{ ptr, chunk_len });
|
||||
|
||||
/* copy-out data from shared buffer to local address space */
|
||||
::memcpy(ptr, _buffer.local_addr<char>(), chunk_len);
|
||||
|
||||
remaining -= chunk_len;
|
||||
ptr += chunk_len;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename OBJ>
|
||||
void object_content(OBJ &obj) { memory_content(&obj, sizeof(obj)); }
|
||||
};
|
||||
|
||||
#endif /* _CLONE_SESSION_H_ */
|
@ -110,7 +110,6 @@ DUMMY(int, -1, getifaddrs, (struct ifaddrs **))
|
||||
DUMMY(void, , freeifaddrs, (struct ifaddrs *ifp))
|
||||
DUMMY(char *, 0, _getlogin, (void))
|
||||
DUMMY(int , -1, getnameinfo, (const sockaddr *, socklen_t, char *, size_t, char *, size_t, int))
|
||||
DUMMY_SILENT(pid_t , -1, getpid, (void))
|
||||
DUMMY(struct servent *, 0, getservbyname, (const char *, const char *))
|
||||
DUMMY(int , -1, getsid, (pid_t))
|
||||
DUMMY(pid_t , -1, getppid, (void))
|
||||
@ -154,7 +153,6 @@ DUMMY(int , 0, utimes, (const char *, const timeval *))
|
||||
DUMMY(int, -1, semget, (key_t, int, int))
|
||||
DUMMY(int, -1, semop, (key_t, int, int))
|
||||
__SYS_DUMMY(int, -1, aio_suspend, (const struct aiocb * const[], int, const struct timespec *));
|
||||
__SYS_DUMMY(pid_t , -1, fork, (void))
|
||||
__SYS_DUMMY(int , -1, getfsstat, (struct statfs *, long, int))
|
||||
__SYS_DUMMY(int, -1, kevent, (int, const struct kevent*, int, struct kevent *, int, const struct timespec*));
|
||||
__SYS_DUMMY(void , , map_stacks_exec, (void));
|
||||
|
@ -21,6 +21,10 @@
|
||||
/* libc plugin interface */
|
||||
#include <libc-plugin/fd_alloc.h>
|
||||
|
||||
/* libc includes */
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace Libc;
|
||||
using namespace Genode;
|
||||
|
||||
@ -96,6 +100,37 @@ File_descriptor *File_descriptor_allocator::find_by_libc_fd(int libc_fd)
|
||||
}
|
||||
|
||||
|
||||
void File_descriptor_allocator::generate_info(Xml_generator &xml)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
_id_space.for_each<File_descriptor>([&] (File_descriptor &fd) {
|
||||
xml.node("fd", [&] () {
|
||||
|
||||
xml.attribute("id", fd.libc_fd);
|
||||
|
||||
if (fd.fd_path)
|
||||
xml.attribute("path", fd.fd_path);
|
||||
|
||||
if (fd.cloexec)
|
||||
xml.attribute("cloexec", "yes");
|
||||
|
||||
if (((fd.flags & O_ACCMODE) != O_WRONLY))
|
||||
xml.attribute("readable", "yes");
|
||||
|
||||
if (((fd.flags & O_ACCMODE) != O_RDONLY))
|
||||
xml.attribute("writeable", "yes");
|
||||
|
||||
if (fd.plugin) {
|
||||
::off_t const seek = fd.plugin->lseek(&fd, 0, SEEK_CUR);
|
||||
if (seek)
|
||||
xml.attribute("seek", seek);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/********************
|
||||
** Libc functions **
|
||||
********************/
|
||||
|
713
repos/libports/src/lib/libc/fork.cc
Normal file
713
repos/libports/src/lib/libc/fork.cc
Normal file
@ -0,0 +1,713 @@
|
||||
/*
|
||||
* \brief Libc fork mechanism
|
||||
* \author Norman Feske
|
||||
* \date 2019-08-13
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 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/log.h>
|
||||
#include <base/thread.h>
|
||||
#include <base/registry.h>
|
||||
#include <base/child.h>
|
||||
#include <base/session_object.h>
|
||||
#include <base/service.h>
|
||||
#include <base/shared_object.h>
|
||||
#include <base/attached_ram_dataspace.h>
|
||||
#include <util/retry.h>
|
||||
|
||||
/* libc includes */
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <libc-plugin/fd_alloc.h>
|
||||
|
||||
/* libc-internal includes */
|
||||
#include <task.h>
|
||||
#include <libc_init.h>
|
||||
#include <clone_session.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
static pid_t fork_result;
|
||||
|
||||
static Env *_env_ptr;
|
||||
static Allocator *_alloc_ptr;
|
||||
static Heap *_malloc_heap_ptr;
|
||||
static void *_user_stack_base_ptr;
|
||||
static size_t _user_stack_size;
|
||||
static int _pid;
|
||||
static int _pid_cnt;
|
||||
|
||||
static Libc::Config_accessor const *_config_accessor_ptr;
|
||||
|
||||
|
||||
void Libc::init_fork(Env &env, Config_accessor const &config_accessor,
|
||||
Allocator &alloc, Heap &malloc_heap, pid_t pid)
|
||||
{
|
||||
_env_ptr = &env;
|
||||
_alloc_ptr = &alloc;
|
||||
_malloc_heap_ptr = &malloc_heap;
|
||||
_config_accessor_ptr = &config_accessor;
|
||||
_pid = pid;
|
||||
}
|
||||
|
||||
|
||||
namespace Libc {
|
||||
struct Child_config;
|
||||
struct Parent_services;
|
||||
struct Local_rom_service;
|
||||
struct Local_rom_services;
|
||||
struct Local_clone_service;
|
||||
struct Forked_child_policy;
|
||||
struct Forked_child;
|
||||
|
||||
struct Child_ready : Interface
|
||||
{
|
||||
virtual void child_ready() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
struct Libc::Child_config
|
||||
{
|
||||
Constructible<Attached_ram_dataspace> _ds { };
|
||||
|
||||
Env &_env;
|
||||
|
||||
pid_t const _pid;
|
||||
|
||||
void _generate(Xml_generator &xml, Xml_node config);
|
||||
|
||||
Child_config(Env &env, Config_accessor const &config_accessor, pid_t pid)
|
||||
:
|
||||
_env(env), _pid(pid)
|
||||
{
|
||||
Xml_node const config = config_accessor.config();
|
||||
|
||||
size_t buffer_size = 4096;
|
||||
|
||||
retry<Xml_generator::Buffer_exceeded>(
|
||||
|
||||
[&] () {
|
||||
_ds.construct(env.ram(), env.rm(), buffer_size);
|
||||
|
||||
Xml_generator
|
||||
xml(_ds->local_addr<char>(), buffer_size, "config", [&] () {
|
||||
_generate(xml, config); });
|
||||
},
|
||||
|
||||
[&] () { buffer_size += 4096; }
|
||||
);
|
||||
}
|
||||
|
||||
Rom_dataspace_capability ds_cap() const
|
||||
{
|
||||
Dataspace_capability cap = _ds->cap();
|
||||
return static_cap_cast<Rom_dataspace>(cap);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Libc::Child_config::_generate(Xml_generator &xml, Xml_node config)
|
||||
{
|
||||
typedef String<30> Addr;
|
||||
|
||||
/*
|
||||
* Disarm the dynamic linker's sanity check for the
|
||||
* execution of static constructors for the forked child
|
||||
* because those constructors were executed by the parent
|
||||
* already.
|
||||
*/
|
||||
xml.attribute("ld_check_ctors", "no");
|
||||
|
||||
xml.node("libc", [&] () {
|
||||
|
||||
xml.attribute("pid", _pid);
|
||||
|
||||
typedef String<Vfs::MAX_PATH_LEN> Path;
|
||||
config.with_sub_node("libc", [&] (Xml_node node) {
|
||||
if (node.has_attribute("rtc"))
|
||||
xml.attribute("rtc", node.attribute_value("rtc", Path())); });
|
||||
|
||||
{
|
||||
char buf[Vfs::MAX_PATH_LEN] { };
|
||||
if (getcwd(buf, sizeof(buf)))
|
||||
xml.attribute("cwd", Path(Cstring(buf)));
|
||||
}
|
||||
|
||||
file_descriptor_allocator()->generate_info(xml);
|
||||
|
||||
auto gen_range_attr = [&] (auto at, auto size)
|
||||
{
|
||||
xml.attribute("at", Addr(at));
|
||||
xml.attribute("size", Addr(size));
|
||||
};
|
||||
|
||||
xml.attribute("cloned", "yes");
|
||||
xml.node("stack", [&] () {
|
||||
gen_range_attr(_user_stack_base_ptr, _user_stack_size); });
|
||||
|
||||
typedef Dynamic_linker::Object_info Info;
|
||||
Dynamic_linker::for_each_loaded_object(_env, [&] (Info const &info) {
|
||||
xml.node("rw", [&] () {
|
||||
xml.attribute("name", info.name);
|
||||
gen_range_attr(info.rw_start, info.rw_size); }); });
|
||||
|
||||
_malloc_heap_ptr->for_each_region([&] (void *start, size_t size) {
|
||||
xml.node("heap", [&] () {
|
||||
gen_range_attr(start, size); }); });
|
||||
});
|
||||
|
||||
xml.append("\n");
|
||||
|
||||
/* copy non-libc config as is */
|
||||
config.for_each_sub_node([&] (Xml_node node) {
|
||||
if (node.type() != "libc") {
|
||||
node.with_raw_node([&] (char const *start, size_t len) {
|
||||
xml.append("\t");
|
||||
xml.append(start, len);
|
||||
});
|
||||
xml.append("\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
class Libc::Parent_services : Noncopyable
|
||||
{
|
||||
private:
|
||||
|
||||
Env &_env;
|
||||
Allocator &_alloc;
|
||||
|
||||
typedef Registered<Parent_service> Registered_service;
|
||||
|
||||
Registry<Registered_service> _services { };
|
||||
|
||||
public:
|
||||
|
||||
Parent_services(Env &env, Allocator &alloc) : _env(env), _alloc(alloc) { }
|
||||
|
||||
~Parent_services()
|
||||
{
|
||||
_services.for_each([&] (Registered_service &service) {
|
||||
destroy(_alloc, &service); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Return parent service matching the specified name
|
||||
*/
|
||||
Service &matching_service(Service::Name const &name)
|
||||
{
|
||||
Service *service = nullptr;
|
||||
_services.for_each([&] (Parent_service &s) {
|
||||
if (!service && name == s.name())
|
||||
service = &s; });
|
||||
|
||||
if (service)
|
||||
return *service;
|
||||
|
||||
/* expand list of parent services on demand */
|
||||
return *new (_alloc) Registered_service(_services, _env, name);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Libc::Local_rom_service : Noncopyable
|
||||
{
|
||||
typedef Session_label Rom_name;
|
||||
|
||||
struct Session : Session_object<Rom_session>
|
||||
{
|
||||
Rom_dataspace_capability _ds;
|
||||
|
||||
static Session::Resources _resources()
|
||||
{
|
||||
return { .ram_quota = { 0 },
|
||||
.cap_quota = { Rom_session::CAP_QUOTA } };
|
||||
}
|
||||
|
||||
Session(Entrypoint &ep, Rom_name const &name, Rom_dataspace_capability ds)
|
||||
:
|
||||
Session_object<Rom_session>(ep.rpc_ep(), _resources(), name, Session::Diag()),
|
||||
_ds(ds)
|
||||
{ }
|
||||
|
||||
Rom_dataspace_capability dataspace() override { return _ds; }
|
||||
|
||||
void sigh(Signal_context_capability) override { }
|
||||
|
||||
} _session;
|
||||
|
||||
typedef Local_service<Session> Service;
|
||||
|
||||
Service::Single_session_factory _factory { _session };
|
||||
|
||||
Service service { _factory };
|
||||
|
||||
bool matches(Session_label const &label) const
|
||||
{
|
||||
return label.last_element() == _session.label();
|
||||
}
|
||||
|
||||
Local_rom_service(Entrypoint &ep, Rom_name const &name,
|
||||
Rom_dataspace_capability ds)
|
||||
: _session(ep, name, ds) { }
|
||||
|
||||
virtual ~Local_rom_service() { }
|
||||
};
|
||||
|
||||
|
||||
struct Libc::Local_rom_services : Noncopyable
|
||||
{
|
||||
Allocator &_alloc;
|
||||
|
||||
typedef Registered<Local_rom_service> Registered_service;
|
||||
|
||||
Registry<Registered_service> _services { };
|
||||
|
||||
Local_rom_services(Env &env, Entrypoint &fork_ep, Allocator &alloc)
|
||||
:
|
||||
_alloc(alloc)
|
||||
{
|
||||
typedef Dynamic_linker::Object_info Info;
|
||||
Dynamic_linker::for_each_loaded_object(env, [&] (Info const &info) {
|
||||
new (alloc)
|
||||
Registered_service(_services, fork_ep, info.name, info.ds_cap); });
|
||||
}
|
||||
|
||||
~Local_rom_services()
|
||||
{
|
||||
_services.for_each([&] (Registered_service &service) {
|
||||
destroy(_alloc, &service); });
|
||||
}
|
||||
|
||||
/*
|
||||
* \throw Service_denied
|
||||
*/
|
||||
Service &matching_service(Service::Name const &name, Session_label const &label)
|
||||
{
|
||||
if (name != Rom_session::service_name())
|
||||
throw Service_denied();
|
||||
|
||||
Service *service_ptr = nullptr;
|
||||
|
||||
_services.for_each([&] (Local_rom_service &service) {
|
||||
if (service.matches(label))
|
||||
service_ptr = &service.service; });
|
||||
|
||||
if (!service_ptr)
|
||||
throw Service_denied();
|
||||
|
||||
return *service_ptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Libc::Local_clone_service : Noncopyable
|
||||
{
|
||||
struct Session : Session_object<Clone_session, Session>
|
||||
{
|
||||
Attached_ram_dataspace _ds;
|
||||
|
||||
static Session::Resources _resources()
|
||||
{
|
||||
return { .ram_quota = { Clone_session::RAM_QUOTA },
|
||||
.cap_quota = { Clone_session::CAP_QUOTA } };
|
||||
}
|
||||
|
||||
Session(Env &env, Entrypoint &ep)
|
||||
:
|
||||
Session_object<Clone_session, Session>(ep.rpc_ep(), _resources(),
|
||||
"cloned", Session::Diag()),
|
||||
_ds(env.ram(), env.rm(), Clone_session::BUFFER_SIZE)
|
||||
{ }
|
||||
|
||||
Dataspace_capability dataspace() { return _ds.cap(); }
|
||||
|
||||
void memory_content(Memory_range range)
|
||||
{
|
||||
::memcpy(_ds.local_addr<void>(), range.start, range.size);
|
||||
}
|
||||
|
||||
} _session;
|
||||
|
||||
typedef Local_service<Session> Service;
|
||||
|
||||
Child_ready &_child_ready;
|
||||
|
||||
Io_signal_handler<Local_clone_service> _child_ready_handler;
|
||||
|
||||
void _handle_child_ready()
|
||||
{
|
||||
_child_ready.child_ready();
|
||||
|
||||
Libc::resume_all();
|
||||
}
|
||||
|
||||
struct Factory : Local_service<Session>::Factory
|
||||
{
|
||||
Session &_session;
|
||||
Signal_context_capability _started_sigh;
|
||||
|
||||
Factory(Session &session, Signal_context_capability started_sigh)
|
||||
: _session(session), _started_sigh(started_sigh) { }
|
||||
|
||||
Session &create(Args const &, Affinity) override { return _session; }
|
||||
|
||||
void upgrade(Session &, Args const &) override { }
|
||||
|
||||
void destroy(Session &) override { Signal_transmitter(_started_sigh).submit(); }
|
||||
|
||||
} _factory;
|
||||
|
||||
Service service { _factory };
|
||||
|
||||
Local_clone_service(Env &env, Entrypoint &ep, Child_ready &child_ready)
|
||||
:
|
||||
_session(env, ep), _child_ready(child_ready),
|
||||
_child_ready_handler(env.ep(), *this, &Local_clone_service::_handle_child_ready),
|
||||
_factory(_session, _child_ready_handler)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
struct Libc::Forked_child : Child_policy, Child_ready
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
pid_t const _pid;
|
||||
|
||||
enum class State { STARTING_UP, RUNNING, EXITED } _state { State::STARTING_UP };
|
||||
|
||||
int _exit_code = 0;
|
||||
|
||||
Name const _name { _pid };
|
||||
|
||||
/*
|
||||
* Signal handler triggered at the main entrypoint, waking up the libc
|
||||
* suspend mechanism.
|
||||
*/
|
||||
Io_signal_handler<Libc::Forked_child> _exit_handler {
|
||||
_env.ep(), *this, &Forked_child::_handle_exit };
|
||||
|
||||
void _handle_exit() { Libc::resume_all(); }
|
||||
|
||||
Libc::Child_config _child_config;
|
||||
|
||||
Parent_services &_parent_services;
|
||||
Local_rom_services &_local_rom_services;
|
||||
Local_clone_service _local_clone_service;
|
||||
Local_rom_service _config_rom_service;
|
||||
|
||||
struct Wait_fork_ready : Libc::Kernel_routine
|
||||
{
|
||||
Libc::Forked_child const &child;
|
||||
|
||||
Wait_fork_ready(Libc::Forked_child const &child) : child(child) { }
|
||||
|
||||
void execute_in_kernel() override
|
||||
{
|
||||
/* keep executing this kernel routine until child is running */
|
||||
if (!child.running())
|
||||
register_kernel_routine(*this);
|
||||
}
|
||||
} wait_fork_ready { *this };
|
||||
|
||||
pid_t pid() const { return _pid; }
|
||||
|
||||
bool running() const { return _state == State::RUNNING; }
|
||||
|
||||
bool exited() const { return _state == State::EXITED; }
|
||||
|
||||
int exit_code() const { return _exit_code; }
|
||||
|
||||
|
||||
/***************************
|
||||
** Child_ready interface **
|
||||
***************************/
|
||||
|
||||
void child_ready() override { _state = State::RUNNING; }
|
||||
|
||||
|
||||
/****************************
|
||||
** Child_policy interface **
|
||||
****************************/
|
||||
|
||||
Name name() const override { return _name; }
|
||||
|
||||
Binary_name binary_name() const override { return "binary"; }
|
||||
|
||||
Pd_session &ref_pd() override { return _env.pd(); }
|
||||
Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); }
|
||||
|
||||
void init(Pd_session &session, Pd_session_capability cap) override
|
||||
{
|
||||
session.ref_account(_env.pd_session_cap());
|
||||
|
||||
_env.pd().transfer_quota(cap, Ram_quota{2*1024*1024});
|
||||
_env.pd().transfer_quota(cap, Cap_quota{100});
|
||||
}
|
||||
|
||||
Route resolve_session_request(Service::Name const &name,
|
||||
Session_label const &label) override
|
||||
{
|
||||
Service *service_ptr = nullptr;
|
||||
if (_state == State::STARTING_UP && name == Clone_session::service_name())
|
||||
service_ptr = &_local_clone_service.service;
|
||||
|
||||
if (name == Rom_session::service_name()) {
|
||||
|
||||
try { service_ptr = &_local_rom_services.matching_service(name, label); }
|
||||
catch (...) { }
|
||||
|
||||
if (!service_ptr && label.last_element() == "config")
|
||||
service_ptr = &_config_rom_service.service;
|
||||
}
|
||||
|
||||
if (!service_ptr)
|
||||
service_ptr = &_parent_services.matching_service(name);
|
||||
|
||||
if (service_ptr)
|
||||
return Route { .service = *service_ptr,
|
||||
.label = label,
|
||||
.diag = Session::Diag() };
|
||||
|
||||
throw Service_denied();
|
||||
}
|
||||
|
||||
void resource_request(Parent::Resource_args const &args) override
|
||||
{
|
||||
Session::Resources resources = session_resources_from_args(args.string());
|
||||
|
||||
if (resources.ram_quota.value)
|
||||
_env.pd().transfer_quota(_child.pd_session_cap(), resources.ram_quota);
|
||||
|
||||
if (resources.cap_quota.value)
|
||||
_env.pd().transfer_quota(_child.pd_session_cap(), resources.cap_quota);
|
||||
|
||||
_child.notify_resource_avail();
|
||||
}
|
||||
|
||||
void exit(int code) override
|
||||
{
|
||||
_exit_code = code;
|
||||
_state = State::EXITED;
|
||||
|
||||
Signal_transmitter(_exit_handler).submit();
|
||||
}
|
||||
|
||||
Child _child;
|
||||
|
||||
Forked_child(Env &env,
|
||||
Entrypoint &fork_ep,
|
||||
Allocator &alloc,
|
||||
pid_t pid,
|
||||
Config_accessor const &config_accessor,
|
||||
Parent_services &parent_services,
|
||||
Local_rom_services &local_rom_services)
|
||||
:
|
||||
_env(env), _pid(pid),
|
||||
_child_config(env, config_accessor, pid),
|
||||
_parent_services(parent_services),
|
||||
_local_rom_services(local_rom_services),
|
||||
_local_clone_service(env, fork_ep, *this),
|
||||
_config_rom_service(fork_ep, "config", _child_config.ds_cap()),
|
||||
_child(env.rm(), fork_ep.rpc_ep(), *this)
|
||||
{ }
|
||||
|
||||
virtual ~Forked_child() { }
|
||||
};
|
||||
|
||||
|
||||
/* initialized on first call of 'fork_kernel_routine' */
|
||||
typedef Registry<Registered<Libc::Forked_child> > Forked_children;
|
||||
static Forked_children *_forked_children_ptr;
|
||||
|
||||
|
||||
static void fork_kernel_routine()
|
||||
{
|
||||
fork_result = 0;
|
||||
|
||||
if (!_env_ptr || !_alloc_ptr || !_config_accessor_ptr) {
|
||||
error("missing call of 'init_fork'");
|
||||
abort();
|
||||
}
|
||||
|
||||
Env &env = *_env_ptr;
|
||||
Allocator &alloc = *_alloc_ptr;
|
||||
|
||||
pid_t const child_pid = ++_pid_cnt;
|
||||
|
||||
enum { STACK_SIZE = 1024*16 };
|
||||
static Entrypoint fork_ep(env, STACK_SIZE, "fork_ep", Affinity::Location());
|
||||
|
||||
static Libc::Parent_services parent_services(env, alloc);
|
||||
|
||||
static Libc::Local_rom_services local_rom_services(env, fork_ep, alloc);
|
||||
|
||||
static Forked_children forked_children { };
|
||||
_forked_children_ptr = &forked_children;
|
||||
|
||||
Registered<Libc::Forked_child> &child = *new (alloc)
|
||||
Registered<Libc::Forked_child>(forked_children, env, fork_ep, alloc,
|
||||
child_pid, *_config_accessor_ptr,
|
||||
parent_services, local_rom_services);
|
||||
|
||||
fork_result = child_pid;
|
||||
|
||||
register_kernel_routine(child.wait_fork_ready);
|
||||
}
|
||||
|
||||
|
||||
/************
|
||||
** getpid **
|
||||
************/
|
||||
|
||||
/*
|
||||
* We provide weak symbols to allow 'libc_noux' to override them.
|
||||
*/
|
||||
|
||||
extern "C" pid_t __sys_fork(void) __attribute__((weak));
|
||||
extern "C" pid_t __sys_fork(void)
|
||||
{
|
||||
fork_result = -1;
|
||||
|
||||
/* obtain current stack info, which might have changed since the startup */
|
||||
Thread::Stack_info const mystack = Thread::mystack();
|
||||
_user_stack_base_ptr = (void *)mystack.base;
|
||||
_user_stack_size = mystack.top - mystack.base;
|
||||
|
||||
struct Fork_kernel_routine : Libc::Kernel_routine
|
||||
{
|
||||
void execute_in_kernel() override { fork_kernel_routine(); }
|
||||
|
||||
} kernel_routine { };
|
||||
|
||||
Libc::register_kernel_routine(kernel_routine);
|
||||
|
||||
struct Suspend_functor_impl : Libc::Suspend_functor
|
||||
{
|
||||
bool suspend() override { return false; }
|
||||
|
||||
} suspend_functor { };
|
||||
|
||||
Libc::suspend(suspend_functor, 0);
|
||||
|
||||
return fork_result;
|
||||
}
|
||||
|
||||
pid_t fork(void) __attribute__((weak, alias("__sys_fork")));
|
||||
|
||||
|
||||
/************
|
||||
** getpid **
|
||||
************/
|
||||
|
||||
extern "C" pid_t __sys_getpid(void) __attribute__((weak));
|
||||
extern "C" pid_t __sys_getpid(void) { return _pid; }
|
||||
|
||||
pid_t getpid(void) __attribute__((weak, alias("__sys_getpid")));
|
||||
|
||||
|
||||
/***********
|
||||
** wait4 **
|
||||
***********/
|
||||
|
||||
namespace Libc { struct Wait4_suspend_functor; }
|
||||
|
||||
struct Libc::Wait4_suspend_functor : Suspend_functor
|
||||
{
|
||||
Forked_children &_children;
|
||||
|
||||
pid_t const _pid;
|
||||
|
||||
Wait4_suspend_functor(pid_t pid, Forked_children &children)
|
||||
: _children(children), _pid(pid) { }
|
||||
|
||||
template <typename FN>
|
||||
bool with_exited_child(FN const &fn)
|
||||
{
|
||||
Registered<Forked_child> *child_ptr = nullptr;
|
||||
|
||||
_children.for_each([&] (Registered<Forked_child> &child) {
|
||||
|
||||
if (child_ptr || !child.exited())
|
||||
return;
|
||||
|
||||
if (_pid == child.pid() || _pid == -1)
|
||||
child_ptr = &child;
|
||||
});
|
||||
|
||||
if (!child_ptr)
|
||||
return false;
|
||||
|
||||
fn(*child_ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool suspend() override
|
||||
{
|
||||
bool const any_child_exited =
|
||||
with_exited_child([] (Forked_child const &) { });
|
||||
|
||||
return !any_child_exited;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
extern "C" pid_t __sys_wait4(pid_t, int *, int, rusage *) __attribute__((weak));
|
||||
extern "C" pid_t __sys_wait4(pid_t pid, int *status, int options, rusage *rusage)
|
||||
{
|
||||
pid_t result = -1;
|
||||
int exit_code = 0; /* code and status */
|
||||
|
||||
using namespace Genode;
|
||||
using namespace Libc;
|
||||
|
||||
if (!_forked_children_ptr) {
|
||||
errno = ECHILD;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Wait4_suspend_functor suspend_functor { pid, *_forked_children_ptr };
|
||||
|
||||
for (;;) {
|
||||
|
||||
suspend_functor.with_exited_child([&] (Registered<Forked_child> &child) {
|
||||
result = child.pid();
|
||||
exit_code = child.exit_code();
|
||||
destroy(*_alloc_ptr, &child);
|
||||
});
|
||||
|
||||
if (result >= 0 || (options & WNOHANG))
|
||||
break;
|
||||
|
||||
Libc::suspend(suspend_functor, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* The libc expects status information in bits 0..6 and the exit value
|
||||
* in bits 8..15 (according to 'wait.h').
|
||||
*
|
||||
* The status bits carry the information about the terminating signal.
|
||||
*/
|
||||
if (status)
|
||||
*status = ((exit_code >> 8) & 0x7f) | ((exit_code & 0xff) << 8);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
extern "C" pid_t wait4(pid_t, int *, int, rusage *) __attribute__((weak, alias("__sys_wait4")));
|
||||
|
@ -16,9 +16,12 @@
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/env.h>
|
||||
#include <base/allocator.h>
|
||||
#include <base/heap.h>
|
||||
#include <util/xml_node.h>
|
||||
|
||||
/* libc includes */
|
||||
#include <setjmp.h> /* for 'jmp_buf' type */
|
||||
|
||||
namespace Libc {
|
||||
|
||||
/**
|
||||
@ -41,16 +44,30 @@ namespace Libc {
|
||||
*/
|
||||
void libc_config_init(Genode::Xml_node node);
|
||||
|
||||
struct Clone_connection;
|
||||
|
||||
/**
|
||||
* Malloc allocator
|
||||
*/
|
||||
void init_malloc(Genode::Allocator &heap);
|
||||
void init_malloc_cloned(Clone_connection &);
|
||||
|
||||
/**
|
||||
* Allow thread.cc to access the 'Genode::Env' (needed for the
|
||||
* implementation of condition variables with timeout)
|
||||
*/
|
||||
void init_pthread_support(Genode::Env &env);
|
||||
|
||||
struct Config_accessor : Genode::Interface
|
||||
{
|
||||
virtual Genode::Xml_node config() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fork mechanism
|
||||
*/
|
||||
void init_fork(Genode::Env &, Config_accessor const &,
|
||||
Genode::Allocator &heap, Genode::Heap &malloc_heap, int pid);
|
||||
}
|
||||
|
||||
#endif /* _LIBC_INIT_H_ */
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include <base/env.h>
|
||||
#include <base/log.h>
|
||||
#include <base/slab.h>
|
||||
#include <util/construct_at.h>
|
||||
#include <util/reconstructible.h>
|
||||
#include <util/string.h>
|
||||
#include <util/misc_math.h>
|
||||
|
||||
@ -29,45 +29,51 @@ extern "C" {
|
||||
/* libc-internal includes */
|
||||
#include "libc_init.h"
|
||||
#include <base/internal/unmanaged_singleton.h>
|
||||
#include <clone_session.h>
|
||||
|
||||
|
||||
namespace Genode {
|
||||
namespace Libc {
|
||||
class Slab_alloc;
|
||||
class Malloc;
|
||||
|
||||
class Slab_alloc : public Slab
|
||||
{
|
||||
private:
|
||||
|
||||
size_t const _object_size;
|
||||
|
||||
size_t _calculate_block_size(size_t object_size)
|
||||
{
|
||||
size_t block_size = 16*object_size;
|
||||
return align_addr(block_size, 12);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Slab_alloc(size_t object_size, Allocator *backing_store)
|
||||
:
|
||||
Slab(object_size, _calculate_block_size(object_size), 0, backing_store),
|
||||
_object_size(object_size)
|
||||
{ }
|
||||
|
||||
void *alloc()
|
||||
{
|
||||
void *result;
|
||||
return (Slab::alloc(_object_size, &result) ? result : 0);
|
||||
}
|
||||
|
||||
void free(void *ptr) { Slab::free(ptr, _object_size); }
|
||||
};
|
||||
using namespace Genode;
|
||||
}
|
||||
|
||||
|
||||
class Libc::Slab_alloc : public Slab
|
||||
{
|
||||
private:
|
||||
|
||||
size_t const _object_size;
|
||||
|
||||
size_t _calculate_block_size(size_t object_size)
|
||||
{
|
||||
size_t block_size = 16*object_size;
|
||||
return align_addr(block_size, 12);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Slab_alloc(size_t object_size, Allocator &backing_store)
|
||||
:
|
||||
Slab(object_size, _calculate_block_size(object_size), 0, &backing_store),
|
||||
_object_size(object_size)
|
||||
{ }
|
||||
|
||||
void *alloc()
|
||||
{
|
||||
void *result;
|
||||
return (Slab::alloc(_object_size, &result) ? result : 0);
|
||||
}
|
||||
|
||||
void free(void *ptr) { Slab::free(ptr, _object_size); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Allocator that uses slabs for small objects sizes
|
||||
*/
|
||||
class Malloc
|
||||
class Libc::Malloc
|
||||
{
|
||||
private:
|
||||
|
||||
@ -112,9 +118,11 @@ class Malloc
|
||||
*/
|
||||
static constexpr size_t _room() { return sizeof(Metadata) + 15; }
|
||||
|
||||
Genode::Allocator &_backing_store; /* back-end allocator */
|
||||
Genode::Slab_alloc *_allocator[NUM_SLABS]; /* slab allocators */
|
||||
Genode::Lock _lock;
|
||||
Allocator &_backing_store; /* back-end allocator */
|
||||
|
||||
Constructible<Slab_alloc> _slabs[NUM_SLABS]; /* slab allocators */
|
||||
|
||||
Lock _lock;
|
||||
|
||||
unsigned _slab_log2(size_t size) const
|
||||
{
|
||||
@ -133,15 +141,13 @@ class Malloc
|
||||
|
||||
public:
|
||||
|
||||
Malloc(Genode::Allocator &backing_store) : _backing_store(backing_store)
|
||||
Malloc(Allocator &backing_store) : _backing_store(backing_store)
|
||||
{
|
||||
for (unsigned i = SLAB_START; i <= SLAB_STOP; i++) {
|
||||
_allocator[i - SLAB_START] =
|
||||
new (backing_store) Genode::Slab_alloc(1U << i, &backing_store);
|
||||
}
|
||||
for (unsigned i = SLAB_START; i <= SLAB_STOP; i++)
|
||||
_slabs[i - SLAB_START].construct(1U << i, backing_store);
|
||||
}
|
||||
|
||||
~Malloc() { Genode::warning(__func__, " unexpectedly called"); }
|
||||
~Malloc() { warning(__func__, " unexpectedly called"); }
|
||||
|
||||
/**
|
||||
* Allocator interface
|
||||
@ -149,7 +155,7 @@ class Malloc
|
||||
|
||||
void * alloc(size_t size)
|
||||
{
|
||||
Genode::Lock::Guard lock_guard(_lock);
|
||||
Lock::Guard lock_guard(_lock);
|
||||
|
||||
size_t const real_size = size + _room();
|
||||
unsigned const msb = _slab_log2(real_size);
|
||||
@ -160,7 +166,7 @@ class Malloc
|
||||
if (msb > SLAB_STOP)
|
||||
_backing_store.alloc(real_size, &alloc_addr);
|
||||
else
|
||||
alloc_addr = _allocator[msb - SLAB_START]->alloc();
|
||||
alloc_addr = _slabs[msb - SLAB_START]->alloc();
|
||||
|
||||
if (!alloc_addr) return nullptr;
|
||||
|
||||
@ -189,7 +195,7 @@ class Malloc
|
||||
|
||||
if (new_addr) {
|
||||
/* copy content from old block into new block */
|
||||
memcpy(new_addr, ptr, old_real_size - _room());
|
||||
::memcpy(new_addr, ptr, old_real_size - _room());
|
||||
|
||||
/* free old block */
|
||||
free(ptr);
|
||||
@ -200,7 +206,7 @@ class Malloc
|
||||
|
||||
void free(void *ptr)
|
||||
{
|
||||
Genode::Lock::Guard lock_guard(_lock);
|
||||
Lock::Guard lock_guard(_lock);
|
||||
|
||||
Metadata *md = (Metadata *)ptr - 1;
|
||||
|
||||
@ -212,13 +218,13 @@ class Malloc
|
||||
if (msb > SLAB_STOP) {
|
||||
_backing_store.free(alloc_addr, real_size);
|
||||
} else {
|
||||
_allocator[msb - SLAB_START]->free(alloc_addr);
|
||||
_slabs[msb - SLAB_START]->free(alloc_addr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static Malloc *mallocator;
|
||||
static Libc::Malloc *mallocator;
|
||||
|
||||
|
||||
extern "C" void *malloc(size_t size)
|
||||
@ -255,7 +261,26 @@ extern "C" void *realloc(void *ptr, size_t size)
|
||||
}
|
||||
|
||||
|
||||
static Genode::Constructible<Libc::Malloc> &constructible_malloc()
|
||||
{
|
||||
return *unmanaged_singleton<Genode::Constructible<Libc::Malloc> >();
|
||||
}
|
||||
|
||||
|
||||
void Libc::init_malloc(Genode::Allocator &heap)
|
||||
{
|
||||
mallocator = unmanaged_singleton<Malloc>(heap);
|
||||
|
||||
Genode::Constructible<Libc::Malloc> &_malloc = constructible_malloc();
|
||||
|
||||
_malloc.construct(heap);
|
||||
|
||||
mallocator = _malloc.operator->();
|
||||
}
|
||||
|
||||
|
||||
void Libc::init_malloc_cloned(Clone_connection &clone_connection)
|
||||
{
|
||||
clone_connection.object_content(constructible_malloc());
|
||||
|
||||
mallocator = constructible_malloc().operator->();
|
||||
}
|
||||
|
@ -63,25 +63,6 @@ extern "C" int __i386_libc_sigprocmask(int how, const sigset_t *set, sigset_t *o
|
||||
}
|
||||
|
||||
|
||||
extern "C" __attribute__((weak))
|
||||
pid_t wait4(pid_t, int *, int, struct rusage *)
|
||||
{
|
||||
Genode::warning(__func__, " not implemented");
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
extern "C"
|
||||
pid_t __sys_wait4(pid_t wpid, int *status, int options, struct rusage *rusage) {
|
||||
return wait4(wpid, status, options, rusage); }
|
||||
|
||||
|
||||
extern "C"
|
||||
pid_t _wait4(pid_t wpid, int *status, int options, struct rusage *rusage) {
|
||||
return wait4(wpid, status, options, rusage); }
|
||||
|
||||
|
||||
extern "C" pid_t wait(int *istat) {
|
||||
return wait4(WAIT_ANY, istat, 0, NULL); }
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
/* libc-internal includes */
|
||||
#include <internal/call_func.h>
|
||||
#include <base/internal/unmanaged_singleton.h>
|
||||
#include <clone_session.h>
|
||||
#include "vfs_plugin.h"
|
||||
#include "libc_init.h"
|
||||
#include "task.h"
|
||||
@ -39,6 +40,7 @@ extern char **environ;
|
||||
|
||||
namespace Libc {
|
||||
class Env_implementation;
|
||||
class Cloned_malloc_heap_range;
|
||||
class Kernel;
|
||||
class Pthreads;
|
||||
class Timer;
|
||||
@ -50,7 +52,7 @@ namespace Libc {
|
||||
}
|
||||
|
||||
|
||||
class Libc::Env_implementation : public Libc::Env
|
||||
class Libc::Env_implementation : public Libc::Env, public Config_accessor
|
||||
{
|
||||
private:
|
||||
|
||||
@ -104,6 +106,13 @@ class Libc::Env_implementation : public Libc::Env
|
||||
return _libc_config(); }
|
||||
|
||||
|
||||
/*************************************
|
||||
** Libc::Config_accessor interface **
|
||||
*************************************/
|
||||
|
||||
Xml_node config() const override { return _config.xml(); }
|
||||
|
||||
|
||||
/***************************
|
||||
** Genode::Env interface **
|
||||
***************************/
|
||||
@ -328,6 +337,40 @@ static void resumed_callback();
|
||||
static void suspended_callback();
|
||||
|
||||
|
||||
struct Libc::Cloned_malloc_heap_range
|
||||
{
|
||||
Genode::Ram_allocator &ram;
|
||||
Genode::Region_map &rm;
|
||||
|
||||
Genode::Ram_dataspace_capability ds;
|
||||
|
||||
size_t const size;
|
||||
addr_t const local_addr;
|
||||
|
||||
Cloned_malloc_heap_range(Genode::Ram_allocator &ram, Genode::Region_map &rm,
|
||||
void *start, size_t size)
|
||||
try :
|
||||
ram(ram), rm(rm), ds(ram.alloc(size)), size(size),
|
||||
local_addr(rm.attach_at(ds, (addr_t)start))
|
||||
{ }
|
||||
catch (Region_map::Region_conflict) {
|
||||
error("could not clone heap region ", Hex_range(local_addr, size));
|
||||
throw;
|
||||
}
|
||||
|
||||
void import_content(Clone_connection &clone_connection)
|
||||
{
|
||||
clone_connection.memory_content((void *)local_addr, size);
|
||||
}
|
||||
|
||||
virtual ~Cloned_malloc_heap_range()
|
||||
{
|
||||
rm.detach(local_addr);
|
||||
ram.free(ds);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Libc "kernel"
|
||||
*
|
||||
@ -343,12 +386,19 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Env &_env;
|
||||
Genode::Allocator &_heap;
|
||||
Genode::Env &_env;
|
||||
Genode::Allocator &_heap;
|
||||
|
||||
Genode::Constructible<Heap> _malloc_heap { };
|
||||
|
||||
Genode::Registry<Registered<Cloned_malloc_heap_range> > _cloned_heap_ranges { };
|
||||
|
||||
Env_implementation _libc_env { _env, _heap };
|
||||
Vfs_plugin _vfs { _libc_env, _heap, *this };
|
||||
|
||||
bool const _cloned = _libc_env.libc_config().attribute_value("cloned", false);
|
||||
pid_t const _pid = _libc_env.libc_config().attribute_value("pid", 0U);
|
||||
|
||||
Genode::Reconstructible<Genode::Io_signal_handler<Kernel>> _resume_main_handler {
|
||||
_env.ep(), *this, &Kernel::_resume_main };
|
||||
|
||||
@ -364,9 +414,21 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
|
||||
|
||||
addr_t _kernel_stack = Thread::mystack().top;
|
||||
|
||||
size_t _user_stack_size()
|
||||
{
|
||||
size_t size = Component::stack_size();
|
||||
if (!_cloned)
|
||||
return size;
|
||||
|
||||
_libc_env.libc_config().with_sub_node("stack", [&] (Xml_node stack) {
|
||||
size = stack.attribute_value("size", 0UL); });
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void *_user_stack = {
|
||||
_myself.alloc_secondary_stack(_myself.name().string(),
|
||||
Component::stack_size()) };
|
||||
_user_stack_size()) };
|
||||
|
||||
void (*_original_suspended_callback)() = nullptr;
|
||||
|
||||
@ -384,6 +446,8 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
|
||||
|
||||
Select_handler_base *_scheduled_select_handler = nullptr;
|
||||
|
||||
Kernel_routine *_kernel_routine = nullptr;
|
||||
|
||||
void _resume_main() { _resume_main_once = true; }
|
||||
|
||||
struct Timer_accessor : Libc::Timer_accessor
|
||||
@ -523,7 +587,7 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!check.suspend())
|
||||
if (!check.suspend() && !_kernel_routine)
|
||||
return 0;
|
||||
|
||||
if (timeout_ms > 0)
|
||||
@ -560,13 +624,26 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
|
||||
|
||||
void _init_file_descriptors();
|
||||
|
||||
void _clone_state_from_parent();
|
||||
|
||||
public:
|
||||
|
||||
Kernel(Genode::Env &env, Genode::Allocator &heap)
|
||||
: _env(env), _heap(heap)
|
||||
:
|
||||
_env(env), _heap(heap)
|
||||
{
|
||||
_env.ep().register_io_progress_handler(*this);
|
||||
|
||||
if (_cloned) {
|
||||
_clone_state_from_parent();
|
||||
|
||||
} else {
|
||||
_malloc_heap.construct(_env.ram(), _env.rm());
|
||||
init_malloc(*_malloc_heap);
|
||||
}
|
||||
|
||||
Libc::init_fork(_env, _libc_env, _heap, *_malloc_heap, _pid);
|
||||
|
||||
_init_file_descriptors();
|
||||
}
|
||||
|
||||
@ -593,8 +670,13 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
|
||||
/* save continuation of libc kernel (incl. current stack) */
|
||||
if (!_setjmp(_kernel_context)) {
|
||||
/* _setjmp() returned directly -> switch to user stack and call application code */
|
||||
_state = USER;
|
||||
call_func(_user_stack, (void *)_user_entry, (void *)this);
|
||||
|
||||
if (_cloned) {
|
||||
_switch_to_user();
|
||||
} else {
|
||||
_state = USER;
|
||||
call_func(_user_stack, (void *)_user_entry, (void *)this);
|
||||
}
|
||||
|
||||
/* never reached */
|
||||
}
|
||||
@ -602,6 +684,17 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
|
||||
/* _setjmp() returned after _longjmp() - user context suspended */
|
||||
|
||||
while ((!_app_returned) && (!_suspend_scheduled)) {
|
||||
|
||||
if (_kernel_routine) {
|
||||
Kernel_routine &routine = *_kernel_routine;
|
||||
|
||||
/* the 'kernel_routine' may install another kernel routine */
|
||||
_kernel_routine = nullptr;
|
||||
routine.execute_in_kernel();
|
||||
if (!_kernel_routine)
|
||||
_switch_to_user();
|
||||
}
|
||||
|
||||
if (_dispatch_pending_io_signals) {
|
||||
/* dispatch pending signals but don't block */
|
||||
while (_env.ep().dispatch_pending_io_signal()) ;
|
||||
@ -610,7 +703,7 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
|
||||
_env.ep().wait_and_dispatch_one_io_signal();
|
||||
}
|
||||
|
||||
if (_resume_main_once && !_setjmp(_kernel_context))
|
||||
if (!_kernel_routine && _resume_main_once && !_setjmp(_kernel_context))
|
||||
_switch_to_user();
|
||||
}
|
||||
|
||||
@ -819,6 +912,11 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
|
||||
Kernel::resume_all();
|
||||
}
|
||||
}
|
||||
|
||||
void register_kernel_routine(Kernel_routine &kernel_routine)
|
||||
{
|
||||
_kernel_routine = &kernel_routine;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -897,6 +995,79 @@ void Libc::Kernel::_init_file_descriptors()
|
||||
}
|
||||
|
||||
|
||||
void Libc::Kernel::_clone_state_from_parent()
|
||||
{
|
||||
struct Range { void *at; size_t size; };
|
||||
|
||||
auto range_attr = [&] (Xml_node node)
|
||||
{
|
||||
return Range {
|
||||
.at = (void *)node.attribute_value("at", 0UL),
|
||||
.size = node.attribute_value("size", 0UL)
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Allocate local memory for the backing store of the application heap,
|
||||
* mirrored from the parent.
|
||||
*
|
||||
* This step must precede the creation of the 'Clone_connection' because
|
||||
* the shared-memory buffer of the clone session may otherwise potentially
|
||||
* interfere with such a heap region.
|
||||
*/
|
||||
_libc_env.libc_config().for_each_sub_node("heap", [&] (Xml_node node) {
|
||||
Range const range = range_attr(node);
|
||||
new (_heap)
|
||||
Registered<Cloned_malloc_heap_range>(_cloned_heap_ranges,
|
||||
_env.ram(), _env.rm(),
|
||||
range.at, range.size); });
|
||||
|
||||
Clone_connection clone_connection(_env);
|
||||
|
||||
/* fetch heap content */
|
||||
_cloned_heap_ranges.for_each([&] (Cloned_malloc_heap_range &heap_range) {
|
||||
heap_range.import_content(clone_connection); });
|
||||
|
||||
/* fetch user contex of the parent's application */
|
||||
clone_connection.memory_content(&_user_context, sizeof(_user_context));
|
||||
_valid_user_context = true;
|
||||
|
||||
_libc_env.libc_config().for_each_sub_node([&] (Xml_node node) {
|
||||
|
||||
auto copy_from_parent = [&] (Range range)
|
||||
{
|
||||
clone_connection.memory_content(range.at, range.size);
|
||||
};
|
||||
|
||||
/* clone application stack */
|
||||
if (node.type() == "stack")
|
||||
copy_from_parent(range_attr(node));
|
||||
|
||||
/* clone RW segment of a shared library or the binary */
|
||||
if (node.type() == "rw") {
|
||||
typedef String<64> Name;
|
||||
Name const name = node.attribute_value("name", Name());
|
||||
|
||||
/*
|
||||
* The blacklisted segments are initialized via the
|
||||
* regular startup of the child.
|
||||
*/
|
||||
bool const blacklisted = (name == "ld.lib.so")
|
||||
|| (name == "libc.lib.so")
|
||||
|| (name == "libm.lib.so")
|
||||
|| (name == "posix.lib.so")
|
||||
|| (strcmp(name.string(), "vfs", 3) == 0);
|
||||
if (!blacklisted)
|
||||
copy_from_parent(range_attr(node));
|
||||
}
|
||||
});
|
||||
|
||||
/* import application-heap state from parent */
|
||||
clone_connection.object_content(_malloc_heap);
|
||||
init_malloc_cloned(clone_connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Libc kernel singleton
|
||||
*
|
||||
@ -1015,6 +1186,12 @@ void Libc::execute_in_application_context(Libc::Application_code &app_code)
|
||||
}
|
||||
|
||||
|
||||
void Libc::register_kernel_routine(Kernel_routine &kernel_routine)
|
||||
{
|
||||
kernel->register_kernel_routine(kernel_routine);
|
||||
}
|
||||
|
||||
|
||||
Genode::Xml_node Libc::libc_config()
|
||||
{
|
||||
return kernel->libc_env().libc_config();
|
||||
@ -1038,7 +1215,6 @@ void Component::construct(Genode::Env &env)
|
||||
*unmanaged_singleton<Genode::Heap>(env.ram(), env.rm());
|
||||
|
||||
/* pass Genode::Env to libc subsystems that depend on it */
|
||||
Libc::init_malloc(heap);
|
||||
Libc::init_mem_alloc(env);
|
||||
Libc::init_dl(env);
|
||||
Libc::sysctl_init(env);
|
||||
|
@ -81,6 +81,23 @@ namespace Libc {
|
||||
*/
|
||||
void schedule_select(Select_handler_base *);
|
||||
|
||||
struct Kernel_routine : Genode::Interface
|
||||
{
|
||||
virtual void execute_in_kernel() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Register routine to be called once on the next libc-kernel activation
|
||||
*
|
||||
* The specified routine is executed only once. For a repeated execution,
|
||||
* the routine must call 'register_kernel_routine' with itself as
|
||||
* argument.
|
||||
*
|
||||
* This mechanism is used by 'fork' to implement the blocking for the
|
||||
* startup of a new child and for 'wait4'.
|
||||
*/
|
||||
void register_kernel_routine(Kernel_routine &);
|
||||
|
||||
/**
|
||||
* Access libc configuration Xml_node.
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user