/* * \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 #include #include /** * 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; 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) : 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 (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::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 _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 > { 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_ */