mirror of
https://github.com/genodelabs/genode.git
synced 2025-03-11 06:54:18 +00:00
Propagate new parent cap to forked process
This patch removes the hardcoded parent cap for the new child by introducing a facility called 'poke' similar to copy-to-user. This change makes the fork mechanism kernel-agnostic. The 'noux_fork' test works on L4/Fiasco, OKL4, NOVA, Fiasco.OC, and L4ka::Pistachio. Linux is not supported yet.
This commit is contained in:
parent
da5b0ff316
commit
cbd4830977
@ -15,11 +15,13 @@ set build_components {
|
||||
server/terminal
|
||||
}
|
||||
lappend build_components noux-pkg/bash
|
||||
lappend build_components noux-pkg/coreutils
|
||||
lappend build_components test/libports/ncurses
|
||||
|
||||
build $build_components
|
||||
|
||||
exec tar cfv bin/bash.tar -h -C bin/bash .
|
||||
exec tar rfv bin/bash.tar -h -C bin/coreutils .
|
||||
|
||||
create_boot_directory
|
||||
|
||||
|
@ -33,42 +33,39 @@
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
|
||||
|
||||
void *operator new (size_t, void *ptr) { return ptr; }
|
||||
|
||||
|
||||
class Noux_connection
|
||||
{
|
||||
private:
|
||||
|
||||
Noux::Connection *_connection;
|
||||
Noux::Connection _connection;
|
||||
Noux::Sysio *_sysio;
|
||||
|
||||
void _connect()
|
||||
Noux::Sysio *_obtain_sysio()
|
||||
{
|
||||
_connection = new (Genode::env()->heap()) Noux::Connection;
|
||||
_sysio = Genode::env()->rm_session()->attach(_connection->sysio_dataspace());
|
||||
}
|
||||
|
||||
void _disconnect()
|
||||
{
|
||||
Genode::env()->rm_session()->detach(_sysio);
|
||||
Genode::destroy(Genode::env()->heap(), _connection);
|
||||
return Genode::env()->rm_session()->attach(_connection.sysio_dataspace());
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Noux_connection() { _connect(); }
|
||||
Noux_connection() : _sysio(_obtain_sysio()) { }
|
||||
|
||||
void reconnect() {
|
||||
_disconnect(); _connect();
|
||||
PDBG("reconnected to Noux cap=%lx", _connection->cap().local_name());
|
||||
PDBG("getpid returns %d", getpid());
|
||||
void reconnect()
|
||||
{
|
||||
new (&_connection) Noux_connection;
|
||||
Genode::env()->rm_session()->detach(_sysio);
|
||||
_sysio = _obtain_sysio();
|
||||
}
|
||||
|
||||
Noux::Session *session() { return _connection; }
|
||||
Noux::Sysio *sysio() { return _sysio; }
|
||||
Noux::Session *session() { return &_connection; }
|
||||
Noux::Sysio *sysio() { return _sysio; }
|
||||
};
|
||||
|
||||
|
||||
@ -320,31 +317,26 @@ extern "C" int select(int nfds, fd_set *readfds, fd_set *writefds,
|
||||
return out_fds.total_fds();
|
||||
}
|
||||
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
#include <base/platform_env.h>
|
||||
|
||||
|
||||
static jmp_buf fork_jmp_buf;
|
||||
static Genode::Capability<Genode::Parent> new_parent_cap;
|
||||
|
||||
extern "C" void stdout_reconnect(); /* provided by 'log_console.cc' */
|
||||
|
||||
|
||||
/*
|
||||
* The new process created via fork will start its execution here.
|
||||
*/
|
||||
extern "C" void fork_trampoline()
|
||||
{
|
||||
/*
|
||||
* XXX remove hard-coded parent cap
|
||||
*/
|
||||
Okl4::L4_ThreadId_t tid;
|
||||
tid.raw = 0x210001UL;
|
||||
Genode::Native_capability cap(tid, 0x107UL);
|
||||
new_parent_cap = Genode::reinterpret_cap_cast<Genode::Parent>(cap);
|
||||
|
||||
static_cast<Genode::Platform_env *>(Genode::env())->reload_parent_cap(new_parent_cap);
|
||||
|
||||
stdout_reconnect();
|
||||
noux_connection()->reconnect();
|
||||
|
||||
longjmp(fork_jmp_buf, 1);
|
||||
}
|
||||
|
||||
@ -357,8 +349,9 @@ extern "C" pid_t fork(void)
|
||||
|
||||
if (setjmp(fork_jmp_buf)) {
|
||||
|
||||
/* got here via longjmp from 'fork_trampoline' */
|
||||
noux_connection()->reconnect();
|
||||
/*
|
||||
* We got here via longjmp from 'fork_trampoline'.
|
||||
*/
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
@ -384,6 +377,51 @@ extern "C" pid_t getpid(void)
|
||||
}
|
||||
|
||||
|
||||
extern "C" pid_t _wait4(pid_t pid, int *status, int options,
|
||||
struct rusage *rusage)
|
||||
{
|
||||
/*
|
||||
* XXX dummy to accomodate the 'reap_zombie_children' function in bash
|
||||
*/
|
||||
if (options & WNOHANG) {
|
||||
PWRN("_wait4 dummy called (with WNOHANG) - not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
PDBG("_wait4 (pid=%d, options=0x%x) called, waiting forever...",
|
||||
pid, options);
|
||||
|
||||
for (;;);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/********************
|
||||
** Time functions **
|
||||
********************/
|
||||
|
||||
/*
|
||||
* The default implementations as provided by the libc rely on a dedicated
|
||||
* thread. But on Noux, no thread other than the main thread is allowed. For
|
||||
* this reason, we need to override the default implementations here.
|
||||
*/
|
||||
|
||||
extern "C" int clock_gettime(clockid_t clk_id, struct timespec *tp)
|
||||
{
|
||||
PDBG("clock_gettime called - not implemented");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
extern "C" int gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
PDBG("gettimeofdaye called - not implemented");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/*********************
|
||||
** File operations **
|
||||
*********************/
|
||||
|
@ -163,6 +163,10 @@ namespace Noux {
|
||||
virtual void destroy(Dataspace_capability) = 0;
|
||||
};
|
||||
|
||||
|
||||
class Dataspace_registry;
|
||||
|
||||
|
||||
class Dataspace_info : public Object_pool<Dataspace_info>::Entry
|
||||
{
|
||||
private:
|
||||
@ -185,7 +189,30 @@ namespace Noux {
|
||||
size_t size() const { return _size; }
|
||||
Dataspace_capability ds_cap() const { return _ds_cap; }
|
||||
|
||||
virtual Dataspace_capability fork(Ram_session_capability ram) = 0;
|
||||
/**
|
||||
* Create shadow copy of dataspace
|
||||
*
|
||||
* \param ds_registry registry for keeping track of
|
||||
* the new dataspace
|
||||
* \param ep entrypoint used to serve the RPC
|
||||
* interface of the new dataspace
|
||||
* (used if the dataspace is a sub
|
||||
* RM session)
|
||||
* \return capability for the new dataspace
|
||||
*/
|
||||
virtual Dataspace_capability fork(Ram_session_capability ram,
|
||||
Dataspace_destroyer &destroyer,
|
||||
Dataspace_registry &ds_registry,
|
||||
Rpc_entrypoint &ep) = 0;
|
||||
|
||||
/**
|
||||
* Write data sequence into dataspace
|
||||
*
|
||||
* \param dst_offset destination offset within dataspace
|
||||
* \param src data source buffer
|
||||
* \param len length of source buffer in bytes
|
||||
*/
|
||||
virtual void poke(addr_t dst_offset, void const *src, size_t len) = 0;
|
||||
|
||||
Dataspace_destroyer &destroyer() { return _destroyer; }
|
||||
};
|
||||
@ -223,7 +250,10 @@ namespace Noux {
|
||||
Dataspace_destroyer &destroyer)
|
||||
: Dataspace_info(ds_cap, destroyer) { }
|
||||
|
||||
Dataspace_capability fork(Ram_session_capability ram)
|
||||
Dataspace_capability fork(Ram_session_capability ram,
|
||||
Dataspace_destroyer &destroyer,
|
||||
Dataspace_registry &,
|
||||
Rpc_entrypoint &)
|
||||
{
|
||||
size_t const size = Dataspace_client(ds_cap()).size();
|
||||
|
||||
@ -232,7 +262,7 @@ namespace Noux {
|
||||
try {
|
||||
dst_ds = Ram_session_client(ram).alloc(size);
|
||||
} catch (...) {
|
||||
return Ram_dataspace_capability();
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
void *src = 0;
|
||||
@ -253,11 +283,29 @@ namespace Noux {
|
||||
|
||||
if (!src || !dst) {
|
||||
Ram_session_client(ram).free(dst_ds);
|
||||
return Ram_dataspace_capability();
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
|
||||
return dst_ds;
|
||||
}
|
||||
|
||||
void poke(addr_t dst_offset, void const *src, size_t len)
|
||||
{
|
||||
if ((dst_offset >= size()) || (dst_offset + len > size())) {
|
||||
PERR("illegal attemt to write beyond dataspace boundary");
|
||||
return;
|
||||
}
|
||||
|
||||
char *dst = 0;
|
||||
try {
|
||||
dst = Genode::env()->rm_session()->attach(ds_cap());
|
||||
} catch (...) { }
|
||||
|
||||
if (src && dst)
|
||||
memcpy(dst + dst_offset, src, len);
|
||||
|
||||
if (dst) Genode::env()->rm_session()->detach(dst);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -295,7 +343,7 @@ namespace Noux {
|
||||
** Dataspace_destroyer interface **
|
||||
***********************************/
|
||||
|
||||
/* XXX not yet used */
|
||||
/* XXX not used yet */
|
||||
void destroy(Dataspace_capability ds)
|
||||
{
|
||||
free(static_cap_cast<Ram_dataspace>(ds));
|
||||
@ -409,8 +457,19 @@ namespace Noux {
|
||||
* the copies of RAM dataspaces
|
||||
*/
|
||||
void replay(Ram_session_capability dst_ram,
|
||||
Rm_session_capability dst_rm)
|
||||
Rm_session_capability dst_rm,
|
||||
Dataspace_registry &ds_registry,
|
||||
Rpc_entrypoint &ep)
|
||||
{
|
||||
/*
|
||||
* XXX remove this
|
||||
*/
|
||||
struct Dummy_destroyer : Dataspace_destroyer
|
||||
{
|
||||
void destroy(Dataspace_capability) { }
|
||||
};
|
||||
static Dummy_destroyer dummy_destroyer;
|
||||
|
||||
Region *curr = _regions.first();
|
||||
for (; curr; curr = curr->next()) {
|
||||
|
||||
@ -419,7 +478,15 @@ namespace Noux {
|
||||
Dataspace_info *info = _ds_registry.lookup_info(curr->ds);
|
||||
|
||||
if (info) {
|
||||
ds = info->fork(dst_ram);
|
||||
/*
|
||||
* XXX using 'this' as destroyer may be false,
|
||||
* the destroyer should better correspond to
|
||||
* dst_ram.
|
||||
*/
|
||||
ds = info->fork(dst_ram,
|
||||
dummy_destroyer,
|
||||
ds_registry,
|
||||
ep);
|
||||
if (!ds.valid())
|
||||
PERR("replay: Error while forking dataspace");
|
||||
|
||||
@ -443,7 +510,7 @@ namespace Noux {
|
||||
* detect unexpected dataspaces.
|
||||
*/
|
||||
ds = curr->ds;
|
||||
PWRN("replay: unknown dataspace type");
|
||||
PWRN("replay: unknown dataspace type, assume ROM");
|
||||
}
|
||||
|
||||
Rm_session_client(dst_rm).attach(ds, curr->size,
|
||||
@ -453,6 +520,38 @@ namespace Noux {
|
||||
}
|
||||
}
|
||||
|
||||
void poke(addr_t dst_addr, void const *src, size_t len)
|
||||
{
|
||||
Region *region = _lookup_region_by_addr(dst_addr);
|
||||
if (!region) {
|
||||
PERR("poke: no region at 0x%lx", dst_addr);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test if start and end address occupied by the object
|
||||
* type refers to the same region.
|
||||
*/
|
||||
if (region != _lookup_region_by_addr(dst_addr + len - 1)) {
|
||||
PERR("attempt to write beyond region boundary");
|
||||
return;
|
||||
}
|
||||
|
||||
Dataspace_info *info = _ds_registry.lookup_info(region->ds);
|
||||
if (!info) {
|
||||
PERR("attempt to write to unknown dataspace type");
|
||||
for (;;);
|
||||
return;
|
||||
}
|
||||
|
||||
if (region->offset) {
|
||||
PERR("poke: writing to region with offset is not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
info->poke(dst_addr - region->local_addr, src, len);
|
||||
}
|
||||
|
||||
|
||||
/**************************
|
||||
** RM session interface **
|
||||
@ -535,20 +634,30 @@ namespace Noux {
|
||||
_sub_rm(sub_rm)
|
||||
{ }
|
||||
|
||||
Dataspace_capability fork(Ram_session_capability ram)
|
||||
Dataspace_capability fork(Ram_session_capability ram,
|
||||
Dataspace_destroyer &destoyer,
|
||||
Dataspace_registry &ds_registry,
|
||||
Rpc_entrypoint &ep)
|
||||
{
|
||||
Rm_connection *new_sub_rm = new Rm_connection(0, size());
|
||||
Local_rm_session *new_sub_rm =
|
||||
new Local_rm_session(ds_registry, 0, size());
|
||||
|
||||
/*
|
||||
* XXX We are leaking the 'Rm_connection' because of
|
||||
* keeping only the dataspace.
|
||||
*/
|
||||
Rm_session_capability rm_cap = ep.manage(new_sub_rm);
|
||||
|
||||
_sub_rm.replay(ram, new_sub_rm->cap());
|
||||
_sub_rm.replay(ram, rm_cap, ds_registry, ep);
|
||||
|
||||
Dataspace_capability ds_cap = new_sub_rm->dataspace();
|
||||
ds_registry.insert(new Sub_rm_dataspace_info(*new_sub_rm));
|
||||
|
||||
return ds_cap;
|
||||
return new_sub_rm->dataspace();
|
||||
}
|
||||
|
||||
void poke(addr_t dst_offset, void const *src, size_t len)
|
||||
{
|
||||
if ((dst_offset >= size()) || (dst_offset + len > size())) {
|
||||
PERR("illegal attemt to write beyond RM boundary");
|
||||
return;
|
||||
}
|
||||
_sub_rm.poke(dst_offset, src, len);
|
||||
}
|
||||
};
|
||||
|
||||
@ -573,6 +682,7 @@ namespace Noux {
|
||||
*/
|
||||
if (main_thread.valid()) {
|
||||
PWRN("Invalid attempt to create a thread besides main");
|
||||
while (1);
|
||||
return Thread_capability();
|
||||
}
|
||||
main_thread = cpu.create_thread(name);
|
||||
@ -804,7 +914,7 @@ namespace Noux {
|
||||
_args(ARGS_DS_SIZE, args),
|
||||
_env(env),
|
||||
_vfs(vfs),
|
||||
_binary_ds(forked ? Dataspace_capability()
|
||||
_binary_ds(forked ? Dataspace_capability()
|
||||
: vfs->dataspace_from_file(name)),
|
||||
_child(_binary_ds, _resources.ram.cap(), _resources.cpu.cap(),
|
||||
_resources.rm.cap(), &_entrypoint, this),
|
||||
@ -821,11 +931,6 @@ namespace Noux {
|
||||
{
|
||||
_args.dump();
|
||||
strncpy(_name, name, sizeof(_name));
|
||||
|
||||
Native_capability cap = _child.parent_cap();
|
||||
|
||||
PINF("PID %d has parent cap %lx,%lx",
|
||||
_pid, cap.tid().raw, cap.local_name());
|
||||
}
|
||||
|
||||
~Child()
|
||||
@ -837,17 +942,21 @@ namespace Noux {
|
||||
|
||||
void start() { _entrypoint.activate(); }
|
||||
|
||||
void start_forked_main_thread(addr_t ip, addr_t sp, addr_t /* parent_cap_addr */)
|
||||
void start_forked_main_thread(addr_t ip, addr_t sp, addr_t parent_cap_addr)
|
||||
{
|
||||
/*
|
||||
* XXX poke parent_cap_addr into child's address space
|
||||
*/
|
||||
/* poke parent_cap_addr into child's address space */
|
||||
Parent_capability const cap = _child.parent_cap();
|
||||
_resources.rm.poke(parent_cap_addr, &cap, sizeof(cap));
|
||||
|
||||
/* start execution of new main thread at supplied trampoline */
|
||||
_resources.cpu.start_main_thread(ip, sp);
|
||||
}
|
||||
|
||||
Ram_session_capability ram() const { return _resources.ram.cap(); }
|
||||
Rm_session_capability rm() const { return _resources.rm.cap(); }
|
||||
|
||||
Resources::Dataspace_registry &ds_registry() { return _resources.ds_registry; }
|
||||
|
||||
|
||||
/****************************
|
||||
** Child_policy interface **
|
||||
|
@ -284,7 +284,12 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
||||
int new_pid = pid_allocator()->alloc();
|
||||
char const *env = "PWD=\"/\"";
|
||||
|
||||
Child *child = new Child("borked" /*name()*/,
|
||||
/*
|
||||
* XXX To ease debugging, it would be useful to generate a
|
||||
* unique name that includes the PID instead of just
|
||||
* reusing the name of the parent.
|
||||
*/
|
||||
Child *child = new Child(name(),
|
||||
new_pid,
|
||||
_sig_rec,
|
||||
_vfs,
|
||||
@ -298,7 +303,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
||||
_assign_io_channels_to(child);
|
||||
|
||||
/* copy our address space into the new child */
|
||||
_resources.rm.replay(child->ram(), child->rm());
|
||||
_resources.rm.replay(child->ram(), child->rm(),
|
||||
child->ds_registry(), _resources.ep);
|
||||
|
||||
/* start executing the main thread of the new process */
|
||||
child->start_forked_main_thread(ip, sp, parent_cap_addr);
|
||||
|
Loading…
x
Reference in New Issue
Block a user