genode/gems/src/server/d3m/block_service.h

323 lines
8.3 KiB
C++

/*
* \brief D3m block service
* \author Norman Feske
* \author Christian Helmuth
* \date 2012-01-25
*/
/*
* Copyright (C) 2012-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.
*/
#ifndef _BLOCK_SERVICE_H_
#define _BLOCK_SERVICE_H_
/* Genode includes */
#include <util/list.h>
#include <block_session/capability.h>
#include <root/component.h>
/**
* Facility to probe a block session for the presence of a specific file
*
* The 'Iso9660_boot_probe' utility is used to select an iso9660 formatted
* block device to boot from by checking for the presence of a magic file.
*/
class Iso9660_boot_probe
{
/**
* Policy for iso9660 server when executed as slave service
*
* The policy supplies a predefined block root capability to the iso9660
* server.
*/
struct Iso9660_policy : Genode::Slave_policy
{
Genode::Lock &_annouce_lock;
Genode::Capability<Block::Session> _block_session;
Genode::Root_capability _rom_root;
/**
* Pseudo service, handing out a predefined session capability
*/
struct Proxy_service : Genode::Service
{
Genode::Session_capability _session;
Proxy_service(Genode::Session_capability session)
: Genode::Service("proxy"), _session(session) { }
Genode::Session_capability session(char const *,
Genode::Affinity const &)
{ return _session; }
void upgrade(Genode::Session_capability session, const char *) { }
} _block_proxy_service;
Iso9660_policy(Genode::Rpc_entrypoint &entrypoint,
Genode::Lock &annouce_lock,
Genode::Capability<Block::Session> block_session)
:
Genode::Slave_policy("iso9660", entrypoint),
_annouce_lock(annouce_lock),
_block_session(block_session),
_block_proxy_service(block_session)
{ }
Genode::Root_capability rom_root() { return _rom_root; }
/****************************
** Slave_policy interface **
****************************/
const char **_permitted_services() const
{
static const char *permitted_services[] = {
"CAP", "RM", "LOG", "SIGNAL", 0 };
return permitted_services;
};
Genode::Service *resolve_session_request(const char *service_name,
const char *args)
{
if (Genode::strcmp(service_name, "Block") == 0)
return &_block_proxy_service;
return Genode::Slave_policy::resolve_session_request(service_name, args);
}
bool announce_service(char const *service_name,
Genode::Root_capability root,
Genode::Allocator *alloc,
Genode::Server *server)
{
if (Genode::strcmp(service_name, "ROM") != 0)
return false;
_rom_root = root;
_annouce_lock.unlock();
return true;
}
};
Genode::Root_capability _block_root;
Block::Session_capability _block_session;
Genode::Lock _rom_announce_lock;
Genode::Cap_connection _cap;
enum { STACK_SIZE = 2*4096 };
Genode::Rpc_entrypoint _entrypoint;
Iso9660_policy _iso9660_policy;
Genode::Slave _iso9660_slave;
/**
* RAM quota to assign to the iso9660 service
*/
enum { ISO9660_RAM_QUOTA = 8*1024*1024 };
/**
* Obtain block session from specified root interface
*/
Block::Session_capability _init_session()
{
char const *args = "ram_quota=140K, tx_buf_size=128K";
Genode::Root_client root(_block_root);
return Genode::static_cap_cast<Block::Session>
(root.session(args, Genode::Affinity()));
}
/**
* Private constructor
*
* This constructor is used by the 'probe' function below.
*/
Iso9660_boot_probe(Genode::Root_capability root, char const *boot_tag_name)
:
_block_root(root),
_block_session(_init_session()),
_rom_announce_lock(Genode::Lock::LOCKED),
_entrypoint(&_cap, STACK_SIZE, "iso9660_slave"),
_iso9660_policy(_entrypoint, _rom_announce_lock, _block_session),
_iso9660_slave(_entrypoint, _iso9660_policy, ISO9660_RAM_QUOTA)
{
/* wait until the iso9660 server announces the ROM service */
_rom_announce_lock.lock();
/* try to open file with filename 'boot_tag_name' */
Genode::Root_client rom_root(_iso9660_policy.rom_root());
char args[Genode::Root::Session_args::MAX_SIZE];
Genode::snprintf(args, sizeof(args), "ram_quota=4K, filename=\"%s\"",
boot_tag_name);
rom_root.session(args, Genode::Affinity());
}
public:
/**
* Probe block service for the presence of a boot tag file
*
* \param root root capability of block service to probe
* \param boot_tag_name name of the file to probe for
*
* \return true if the specified tag file exists at the block session
*/
static bool probe(Genode::Root_capability root,
char const *boot_tag_name)
{
/*
* In the process of creating an 'Iso9660_boot_probe', many steps
* can fail. For example, the binary of the iso9660 server may be
* missing, the block service may not contain an iso9660 file
* system, or the file may be missing. Only if all steps succeed,
* we report the probing to have succeeded.
*/
try {
/*
* Do not allocate the boot probe at the stack because this
* object may too large for the stack size of the context
* calling the probe function. We use the heap instead to
* create and subsequently destroy the boot probe.
*/
Iso9660_boot_probe *boot_probe = new (Genode::env()->heap())
Iso9660_boot_probe(root, boot_tag_name);
Genode::destroy(Genode::env()->heap(), boot_probe);
return true;
} catch (...) { }
PWRN("Could not probe file at iso9660 ROM service");
return false;
}
~Iso9660_boot_probe()
{
/* close session at block service */
Genode::Root_client(_block_root).close(_block_session);
}
};
namespace Block {
class Driver : public Genode::List<Driver>::Element
{
private:
char const *_name;
Genode::Root_capability _root;
public:
Driver() : _name(0) { }
void init(char const *name, Genode::Root_capability root)
{
_name = name;
_root = root;
}
char const *name() const { return _name; }
Genode::Root_capability root() const { return _root; }
};
class Driver_registry
{
private:
Genode::Lock _lock;
Genode::List<Driver> _drivers;
Genode::Semaphore _retry_probing_sem;
public:
void add_driver(Driver *driver)
{
Genode::Lock::Guard guard(_lock);
_drivers.insert(driver);
PDBG("\ninsert new driver %s", driver->name());
_retry_probing_sem.up();
}
/**
* Return root capability of boot device
*
* If the boot device is not available yet, block until a matching
* driver becomes available.
*/
Genode::Root_capability root()
{
for (;;) {
{
Genode::Lock::Guard guard(_lock);
while (_drivers.first()) {
Driver *driver = _drivers.first();
PDBG("\nprobing driver %s", driver->name());
if (!Iso9660_boot_probe::probe(driver->root(),
"libc.lib.so")) {
PWRN("probing failed, not using %s as boot device",
driver->name());
_drivers.remove(driver);
break;
}
PINF("found boot medium via %s", driver->name());
return driver->root();
}
}
/*
* Block until another driver becomes known via
* 'add_driver'
*/
_retry_probing_sem.down();
};
}
};
class Root : public Genode::Rpc_object<Genode::Typed_root<Session> >
{
private:
Driver_registry &_driver_registry;
public:
Root(Driver_registry &driver_registry)
: _driver_registry(driver_registry) { }
Genode::Session_capability session(Genode::Root::Session_args const &args,
Genode::Affinity const &affinity)
{
PDBG("\nsession requested args=\"%s\"", args.string());
Genode::Root_capability root = _driver_registry.root();
return Genode::Root_client(root).session(args, affinity);
}
void upgrade(Genode::Session_capability,
Genode::Root::Upgrade_args const &) { }
void close(Genode::Session_capability session)
{
Genode::Root_capability root = _driver_registry.root();
Genode::Root_client(root).close(session);
}
};
}
#endif /* _BLOCK_SERVICE_H_ */