/* * \brief Linux-specific environment * \author Norman Feske * \author Christian Helmuth * \date 2006-07-28 */ /* * Copyright (C) 2006-2012 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 _INCLUDE__BASE__PLATFORM_ENV_H_ #define _INCLUDE__BASE__PLATFORM_ENV_H_ #include #include #include #include #include #include #include #include #include #include #include #include namespace Genode { class Platform_env : public Env { private: /************************** ** Local region manager ** **************************/ class Region { private: addr_t _start; off_t _offset; Dataspace_capability _ds; size_t _size; /** * Return offset of first byte after the region */ addr_t _end() const { return _start + _size; } public: Region() : _start(0), _offset(0), _size(0) { } Region(addr_t start, off_t offset, Dataspace_capability ds, size_t size) : _start(start), _offset(offset), _ds(ds), _size(size) { } bool used() const { return _size > 0; } addr_t start() const { return _start; } off_t offset() const { return _offset; } size_t size() const { return _size; } Dataspace_capability dataspace() const { return _ds; } bool intersects(Region const &r) const { return (r.start() < _end()) && (_start < r._end()); } }; /** * Meta data about dataspaces attached to an RM session */ class Region_map { public: enum { MAX_REGIONS = 4096 }; private: Region _map[MAX_REGIONS]; bool _id_valid(int id) const { return (id >= 0 && id < MAX_REGIONS); } public: /** * Add region to region map * * \return region ID, or * -1 if out of metadata, or * -2 if region conflicts existing region */ int add_region(Region const ®ion) { /* * Check for region conflicts */ for (int i = 0; i < MAX_REGIONS; i++) { if (_map[i].intersects(region)) return -2; } /* * Allocate new region metadata */ int i; for (i = 0; i < MAX_REGIONS; i++) if (!_map[i].used()) break; if (i == MAX_REGIONS) { PERR("maximum number of %d regions reached", MAX_REGIONS); return -1; } _map[i] = region; return i; } Region region(int id) const { return _id_valid(id) ? _map[id] : Region(); } Region lookup(addr_t start) { for (int i = 0; i < MAX_REGIONS; i++) if (_map[i].start() == start) return _map[i]; return Region(); } void remove_region(addr_t start) { for (int i = 0; i < MAX_REGIONS; i++) if (_map[i].start() == start) _map[i] = Region(); } }; /* * On Linux, we use a local region manager session * that attaches dataspaces via mmap to the local * address space. */ class Rm_session_mmap : public Local_interface, public Rm_session, public Dataspace { private: Lock _lock; /* protect '_rmap' */ Region_map _rmap; bool const _sub_rm; /* false if RM session is root */ size_t const _size; /** * Base offset of the RM session * * For a normal RM session (the one that comes with the * 'env()', this value is zero. If the RM session is * used as nested dataspace, '_base' contains the address * where the managed dataspace is attached in the root RM * session. * * Note that a managed dataspace cannot be attached more * than once. Furthermore, managed dataspace cannot be * attached to another managed dataspace. The nested * dataspace emulation is solely implemented to support * the common use case of managed dataspaces as mechanism * to reserve parts of the local address space from being * populated by the 'env()->rm_session()'. (i.e., for the * context area, or for the placement of consecutive * shared-library segments) */ addr_t _base; bool _is_attached() const { return _base > 0; } void _add_to_rmap(Region const &); public: Rm_session_mmap(bool sub_rm, size_t size = ~0) : _sub_rm(sub_rm), _size(size), _base(0) { } ~Rm_session_mmap() { /* detach sub RM session when destructed */ if (_sub_rm && _is_attached()) env()->rm_session()->detach((void *)_base); } /************************************** ** Region manager session interface ** **************************************/ Local_addr attach(Dataspace_capability ds, size_t size, off_t, bool, Local_addr); void detach(Local_addr local_addr); Pager_capability add_client(Thread_capability thread) { return Pager_capability(); } void fault_handler(Signal_context_capability handler) { } State state() { return State(); } /************************* ** Dataspace interface ** *************************/ size_t size() { return _size; } addr_t phys_addr() { return 0; } bool writable() { return true; } /** * Return pseudo dataspace capability of the RM session * * The capability returned by this function is only usable * as argument to 'Rm_session_mmap::attach'. It is not a * real capability. */ Dataspace_capability dataspace() { return Local_interface::capability(this); } }; class Expanding_ram_session_client : public Ram_session_client { Ram_session_capability _cap; public: Expanding_ram_session_client(Ram_session_capability cap) : Ram_session_client(cap), _cap(cap) { } Ram_dataspace_capability alloc(size_t size) { bool try_again; do { try_again = false; try { return Ram_session_client::alloc(size); } catch (Ram_session::Out_of_metadata) { /* give up if the error occurred a second time */ if (try_again) break; PINF("upgrade quota donation for Env::RAM session"); env()->parent()->upgrade(_cap, "ram_quota=8K"); try_again = true; } } while (try_again); return Ram_dataspace_capability(); } }; /** * Local interceptor of parent requests * * On Linux, we need to intercept calls to the parent interface to * implement the RM service locally. This particular service is * used for creating managed dataspaces, which allow the * reservation of parts of the local address space from being * automatically managed by the 'env()->rm_session()'. * * All requests that do not refer to the RM service are passed * through the real parent interface. */ class Local_parent : public Parent_client { public: /********************** ** Parent interface ** **********************/ Session_capability session(Service_name const &, Session_args const &); void close(Session_capability); /** * Constructor * * \param parent_cap real parent capability used to * promote requests to non-local * services */ Local_parent(Parent_capability parent_cap); }; /************************************* ** Linux-specific helper functions ** *************************************/ /** * Read Unix environment variable as long value */ static unsigned long _get_env_ulong(const char *key); Parent_capability _parent_cap() { long tid = _get_env_ulong("parent_tid"); long local_name = _get_env_ulong("parent_local_name"); /* produce typed capability manually */ return reinterpret_cap_cast(Native_capability(tid, local_name)); } /******************************* ** Platform-specific members ** *******************************/ Local_parent _parent; Ram_session_capability _ram_session_cap; Expanding_ram_session_client _ram_session_client; Cpu_session_client _cpu_session_client; Rm_session_mmap _rm_session_mmap; Heap _heap; public: /** * Standard constructor */ Platform_env() : _parent(_parent_cap()), _ram_session_cap(static_cap_cast(parent()->session("Env::ram_session", ""))), _ram_session_client(_ram_session_cap), _cpu_session_client(static_cap_cast(parent()->session("Env::cpu_session", ""))), _rm_session_mmap(false), _heap(&_ram_session_client, &_rm_session_mmap) { } /** * Destructor */ ~Platform_env() { parent()->exit(0); } /** * Reload parent capability and reinitialize environment resources */ void reload_parent_cap(Capability) { /* not supported on Linux */ } /******************* ** Env interface ** *******************/ Parent *parent() { return &_parent; } Ram_session *ram_session() { return &_ram_session_client; } Ram_session_capability ram_session_cap() { return _ram_session_cap; } Rm_session *rm_session() { return &_rm_session_mmap; } Heap *heap() { return &_heap; } Cpu_session *cpu_session() { return &_cpu_session_client; } Pd_session *pd_session() { return 0; } }; } #endif /* _INCLUDE__BASE__PLATFORM_ENV_H_ */