l4re-base-25.08.0

This commit is contained in:
2025-09-12 15:55:45 +02:00
commit d959eaab98
37938 changed files with 9382688 additions and 0 deletions

View File

@@ -0,0 +1,362 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/re/util/cap_alloc>
#include <l4/re/util/unique_cap>
#include <l4/re/consts>
#include <l4/re/env>
#include <l4/sys/cxx/ipc_server_loop>
#include <l4/sys/factory>
#include <l4/sys/task>
#include <l4/sys/thread>
#include <l4/sys/ipc_gate>
#include <l4/cxx/exceptions>
namespace L4Re { namespace Util {
/**
* A registry that manages server objects and their attached IPC gates for
* a single server loop for a specific thread.
*
* This class manages most of the setup of a server object. If necessary,
* an IPC gate is created, the specified thread is bound to the IPC gate.
* Incoming IPC is dispatched to the server object based on
* the label of the IPC gate.
*
* The object registry is also able to manage IRQ endpoints. They require a
* different method for the object creation. Otherwise they are handled in
* the same way as IPC gates: a server object is responsible to process
* the incoming interrupts.
*/
class Object_registry :
public L4::Basic_registry,
public L4::Registry_iface
{
/**
* Handler class for stale requests from servers that have been
* deregistered.
*/
struct Null_handler : L4::Epiface_t<Null_handler, L4::Kobject>
{};
protected:
L4::Cap<L4::Thread> _server;
L4::Cap<L4::Factory> _factory;
L4::Ipc_svr::Server_iface *_sif;
private:
Null_handler _null_handler;
public:
/**
* Create a registry for the main thread of the task using the default factory.
*
* \param sif Server loop interface.
*/
explicit
Object_registry(L4::Ipc_svr::Server_iface *sif)
: _server(L4Re::Env::env()->main_thread()),
_factory(L4Re::Env::env()->factory()),
_sif(sif)
{}
/**
* Create a registry for arbitrary threads.
*
* \param sif Server loop interface.
* \param server Capability to the thread that executes the server objects.
* \param factory Capability to a factory object capable of creating new
* IPC gates.
*/
Object_registry(L4::Ipc_svr::Server_iface *sif,
L4::Cap<L4::Thread> server,
L4::Cap<L4::Factory> factory)
: _server(server), _factory(factory), _sif(sif)
{}
private:
typedef L4::Ipc_svr::Server_iface Server_iface;
typedef Server_iface::Demand Demand;
L4::Cap<L4::Rcv_endpoint>
_register_ep(L4::Epiface *o, L4::Cap<L4::Rcv_endpoint> ep,
Demand const &demand)
{
int err = _sif->alloc_buffer_demand(demand);
if (err < 0)
return L4::Cap<L4::Rcv_endpoint>(err | L4_INVALID_CAP_BIT);
err = o->set_server(_sif, ep);
if (err < 0)
return L4::Cap<L4::Rcv_endpoint>(err | L4_INVALID_CAP_BIT);
l4_umword_t id = l4_umword_t(o);
err = l4_error(ep->bind_thread(_server, id));
if (err < 0)
return L4::Cap<L4::Rcv_endpoint>(err | L4_INVALID_CAP_BIT);
return ep;
}
L4::Cap<void> _register_ep(L4::Epiface *o, char const *service,
Demand const &demand)
{
L4::Cap<L4::Rcv_endpoint> cap = L4Re::Env::env()->get_cap<L4::Rcv_endpoint>(service);
if (!cap.is_valid())
return cap;
return _register_ep(o, cap, demand);
}
L4::Cap<void> _register_gate(L4::Epiface *o, Demand const &demand)
{
int err = _sif->alloc_buffer_demand(demand);
if (err < 0)
return L4::Cap<void>(err | L4_INVALID_CAP_BIT);
auto cap = L4Re::Util::make_unique_cap<L4::Kobject>();
if (!cap.is_valid())
return cap.get();
l4_umword_t id = l4_umword_t(o);
err = l4_error(_factory->create_gate(cap.get(), _server, id));
if (err < 0)
return L4::Cap<void>(err | L4_INVALID_CAP_BIT);
err = o->set_server(_sif, cap.get(), true);
if (err < 0)
return L4::Cap<void>(err | L4_INVALID_CAP_BIT);
return cap.release();
}
L4::Cap<L4::Irq> _register_irq(L4::Epiface *o,
Demand const &demand)
{
int err = _sif->alloc_buffer_demand(demand);
if (err < 0)
return L4::Cap<L4::Irq>(err | L4_INVALID_CAP_BIT);
auto cap = L4Re::Util::make_unique_cap<L4::Irq>();
if (!cap.is_valid())
return cap.get();
l4_umword_t id = l4_umword_t(o);
err = l4_error(_factory->create(cap.get()));
if (err < 0)
return L4::Cap<L4::Irq>(err | L4_INVALID_CAP_BIT);
err = o->set_server(_sif, cap.get(), true);
if (err < 0)
return L4::Cap<L4::Irq>(err | L4_INVALID_CAP_BIT);
err = l4_error(cap->bind_thread(_server, id));
if (err < 0)
return L4::Cap<L4::Irq>(err | L4_INVALID_CAP_BIT);
return cap.release();
}
static Demand _get_buffer_demand(L4::Epiface *o)
{ return o->get_buffer_demand(); }
template<typename T>
static Demand _get_buffer_demand(T *,
typename L4::Kobject_typeid<typename T::Interface>::Demand
d = typename L4::Kobject_typeid<typename T::Interface>::Demand())
{ return d; }
public:
/**
* Register a new server object to a pre-allocated receive endpoint.
*
* \param o Server object that handles IPC requests.
* \param service Name of a pre-allocated receive endpoint.
*
* \retval L4::Cap<void> The capability known as `service` on success.
* \retval L4::Cap<void>::Invalid No capability with the given name found.
*
* The interface must be freed with unregister_obj() by the caller
* to unbind the thread from the capability.
*/
L4::Cap<void> register_obj(L4::Epiface *o, char const *service) override
{
return _register_ep(o, service, _get_buffer_demand(o));
}
/**
* Register a new server object on a newly allocated capability.
*
* \param o Server object that handles IPC requests.
*
* \retval L4::Cap<void> A valid capability to a new IPC gate.
* \retval L4::Cap<void>::Invalid The allocation of the IPC gate
* has failed.
*
* The IPC gate will be allocated using the registry's factory. The
* caller must call unregister_obj() to free all resources.
*/
L4::Cap<void> register_obj(L4::Epiface *o) override
{
return _register_gate(o, _get_buffer_demand(o));
}
/**
* Register a handler for an interrupt.
*
* \param o Server object that handles IRQs.
*
* \retval L4::Cap<L4::Irq> Capability to a new IRQ object on success.
* \retval L4::Cap<L4::Irq>::Invalid The allocation of the IRQ has failed.
*
* The IRQ will be newly allocated using the registry's factory object. The
* caller must call unregister_obj() to free all resources.
*/
L4::Cap<L4::Irq> register_irq_obj(L4::Epiface *o) override
{
return _register_irq(o, _get_buffer_demand(o));
}
/**
* Register a handler for an already existing interrupt.
*
* \param o Server object that handles the IPC.
* \param ep Capability to a receive endpoint, may be a hardware or
* software interrupt or an IPC gate.
*
* \retval L4::Cap<L4::Rcv_endpoint> Capability `ep` on success.
* \retval L4::Cap<L4::Rcv_endpoint>::Invalid The IRQ attach operation has failed.
*
* The interface must be freed with unregister_obj() by the caller
* to unbind the thread from the capability.
*/
L4::Cap<L4::Rcv_endpoint>
register_obj(L4::Epiface *o, L4::Cap<L4::Rcv_endpoint> ep) override
{
return _register_ep(o, ep, _get_buffer_demand(o));
}
/**
* Remove a server object from the handler list.
*
* \param o Server object to unbind.
* \param unmap Specifies if the object capability shall be unmapped (true)
* or not. The default (true) is to unmap the capability.
*
* The capability used by the server object will be unmapped if `unmap` is
* true.
*/
void unregister_obj(L4::Epiface *o, bool unmap = true) override
{
L4::Epiface::Stored_cap c;
if (!o || !o->obj_cap().is_valid())
return;
c = o->obj_cap();
if (unmap)
L4::Cap<L4::Task>(L4Re::This_task)->unmap(c.fpage(), L4_FP_ALL_SPACES);
// make sure unhandled ipc ends up with the null handler
L4::Thread::Modify_senders todo;
todo.add(~3UL, reinterpret_cast<l4_umword_t>(o),
~0UL, reinterpret_cast<l4_umword_t>
(static_cast<L4::Epiface *>(&_null_handler)));
_server->modify_senders(todo);
// we use bit 4 to indicated an internally allocated cap
if (c.managed())
cap_alloc.free(c);
o->set_server(0, L4::Cap<void>::Invalid);
}
};
/**
* A server loop object which has a Object_registry included.
*/
template< typename LOOP_HOOKS = L4::Ipc_svr::Default_loop_hooks >
class Registry_server : public L4::Server<LOOP_HOOKS>
{
private:
typedef L4::Server<LOOP_HOOKS> Base;
Object_registry _registry;
public:
/**
* Create a new server loop object for the main thread of the task.
*
* \pre Must be called from the main thread or behaviour is undefined.
*/
Registry_server() : _registry(this)
{}
/**
* Create a new server loop object for an arbitrary thread and factory.
*
* \param server Capability to thread running the server loop.
* \param factory Capability to factory object used to create new IPC gates.
*
* \deprecated Note that this variant of the constructor is deprecated,
* please do not supply the UTCB pointer, it's not used.
*/
Registry_server(l4_utcb_t *, L4::Cap<L4::Thread> server,
L4::Cap<L4::Factory> factory) L4_DEPRECATED("Omit UTCB pointer argument")
: _registry(this, server, factory)
{}
/**
* Create a new server loop object for an arbitrary thread and factory.
*
* \param server Capability to thread running the server loop.
* \param factory Capability to factory object used to create new IPC gates.
*/
Registry_server(L4::Cap<L4::Thread> server,
L4::Cap<L4::Factory> factory)
: _registry(this, server, factory)
{}
/** Return registry of this server loop. */
Object_registry const *registry() const { return &_registry; }
/** Return registry of this server loop. */
Object_registry *registry() { return &_registry; }
/**
* Start the server loop.
*
* \param utcb The UTCB of the thread running the server loop, defaults to
* l4_utcb().
*/
void L4_NORETURN loop(l4_utcb_t *utcb = l4_utcb())
{ Base::template loop<L4::Runtime_error, Object_registry &>(_registry, utcb); }
/**
* Start the server loop with error printing.
*
* \tparam Printer The printer type.
* \param printer The printer object on which printf() is called.
* \param utcb The UTCB of the thread running the server loop, defaults
* to l4_utcb().
*/
template <typename Printer>
void L4_NORETURN loop_dbg(Printer printer, l4_utcb_t *utcb = l4_utcb())
{
Base::template loop_dbg<L4::Runtime_error, Object_registry &, Printer>
(_registry, printer, utcb);
}
};
}}