/* * \brief VirtualBox runtime (RT) * \author Norman Feske * \date 2013-08-20 */ /* * Copyright (C) 2013-2017 Genode Labs GmbH * * This file is distributed under the terms of the GNU General Public License * version 2. */ /* Genode includes */ #include #include #include /* VirtualBox includes */ #include #include #include #include #include #include #include #include "mm.h" #include "vmm.h" enum { MEMORY_MAX = 128 * 1024 * 1024, MEMORY_CACHED = 16 * 1024 * 1024, }; class Avl_ds : public Genode::Avl_node { private: Genode::Ram_dataspace_capability _ds; Genode::addr_t _virt; Genode::addr_t const _size; Genode::addr_t _used_size; bool _inuse = true; static Genode::addr_t _mem_allocated; static Genode::addr_t _mem_unused; static Genode::Avl_tree _unused_ds; static Genode::Avl_tree _runtime_ds; public: static Genode::addr_t hit; static Genode::addr_t hit_coarse; Avl_ds(Genode::Ram_dataspace_capability ds, void * virt, Genode::addr_t size) : _ds(ds), _virt(reinterpret_cast(virt)), _size(size), _used_size(_size) { _mem_allocated += _size; _runtime_ds.insert(this); } ~Avl_ds() { Assert (!_inuse); _unused_ds.remove(this); _mem_unused -= _size; _mem_allocated -= _size; genode_env().ram().free(_ds); Genode::log("free up ", _size, " ", _mem_allocated, "/", _mem_unused, " hit=", hit, "/", hit_coarse, " avail=", genode_env().pd().avail_ram()); } void unused() { _inuse = false; _runtime_ds.remove(this); _mem_unused += _size; _unused_ds.insert(this); } void used(Genode::addr_t size) { _inuse = true; _unused_ds.remove(this); _used_size = size; _mem_unused -= _size; _runtime_ds.insert(this); } bool higher(Avl_ds *e) { return _inuse ? e->_virt > _virt : e->_size > _size; } Avl_ds *find_virt(Genode::addr_t virt) { if (virt == _virt) return this; Avl_ds *obj = this->child(virt > _virt); return obj ? obj->find_virt(virt) : 0; } Avl_ds *find_coarse_match(Genode::addr_t size_min, Genode::addr_t size_max) { if (size_min <= _size && _size <= size_max) return this; Avl_ds *obj = this->child(size_max > _size); return obj ? obj->find_coarse_match(size_min, size_max) : 0; } Avl_ds *find_size(Genode::addr_t size, bool equal = true) { if (equal ? size == _size : size <= _size) return this; Avl_ds *obj = this->child(size > _size); return obj ? obj->find_size(size, equal) : 0; } static Avl_ds *find_match(Genode::addr_t size, bool coarse = false) { Avl_ds * head = _unused_ds.first(); if (!head) return head; return coarse ? head->find_coarse_match(size, size * 2) : head->find_size(size); } static Genode::addr_t max_size_at(void * p) { Avl_ds * ds_obj = Avl_ds::_runtime_ds.first(); if (ds_obj) ds_obj = ds_obj->find_virt(reinterpret_cast(p)); if (!ds_obj) return 0; return ds_obj->_size; } Genode::addr_t ds_virt() const { return _virt; } static void memory_freeup(Genode::addr_t const cb) { ::size_t cbx = cb * 4; while (_unused_ds.first() && cbx && (_mem_allocated + cb > MEMORY_MAX || _mem_unused + cb > MEMORY_CACHED || genode_env().pd().avail_ram().value < cb * 2 ) ) { Avl_ds * ds_free = _unused_ds.first()->find_size(cbx, false); if (!ds_free) { cbx = cbx / 2; continue; } destroy(vmm_heap(), ds_free); } } static void free_memory(void * pv, size_t cb) { if (cb % 0x1000) cb = (cb & ~0xFFFUL) + 0x1000UL; Avl_ds * ds_obj = Avl_ds::_runtime_ds.first(); if (ds_obj) ds_obj = ds_obj->find_virt(reinterpret_cast(pv)); if (ds_obj && ds_obj->_used_size == cb) ds_obj->unused(); else { Genode::error(__func__, " unknown memory region ", pv, "(", Genode::Hex(ds_obj ? ds_obj->ds_virt() : 0), ")+", Genode::Hex(cb), "(", Genode::Hex(ds_obj ? ds_obj->_size : 0), ")"); } } }; Genode::Avl_tree Avl_ds::_runtime_ds; Genode::Avl_tree Avl_ds::_unused_ds; static Genode::Mutex & mutex_ds() { static Genode::Mutex mutex { }; return mutex; } Genode::addr_t Avl_ds::hit = 0; Genode::addr_t Avl_ds::hit_coarse = 0; Genode::addr_t Avl_ds::_mem_allocated = 0; Genode::addr_t Avl_ds::_mem_unused = 0; static void *alloc_mem(size_t cb, const char *pszTag, bool executable = false) { /* using managed dataspace to have all addresses within a 1 << 31 bit range */ static Sub_rm_connection rt_memory(genode_env(), 2 * MEMORY_MAX); using namespace Genode; if (!cb) return nullptr; if (cb % 0x1000) cb = (cb & ~0xFFFUL) + 0x1000UL; Mutex::Guard guard(mutex_ds()); if (Avl_ds * ds_free = Avl_ds::find_match(cb)) { ds_free->used(cb); Avl_ds::hit++; return reinterpret_cast(ds_free->ds_virt()); } else if (Avl_ds * ds_free = Avl_ds::find_match(cb, true)) { ds_free->used(cb); Avl_ds::hit_coarse++; return reinterpret_cast(ds_free->ds_virt()); } /* check for memory freeup, give hint about required memory (cb) */ Avl_ds::memory_freeup(cb); try { Ram_dataspace_capability ds = genode_env().ram().alloc(cb); Assert(ds.valid()); Genode::size_t const whole_size = 0; Genode::off_t const offset = 0; bool const any_addr = false; void * any_local_addr = nullptr; void * local_addr = rt_memory.attach(ds, whole_size, offset, any_addr, any_local_addr, executable); Assert(local_addr); new (vmm_heap()) Avl_ds(ds, local_addr, cb); return local_addr; } catch (...) { Genode::error("Could not allocate RTMem* memory of size=", cb); return nullptr; } } /* * Called by the recompiler to allocate executable RAM */ void *RTMemExecAllocTag(size_t cb, const char *pszTag) { return alloc_mem(cb, pszTag, true); } void *RTMemPageAllocZTag(size_t cb, const char *pszTag) { /* * The RAM dataspace freshly allocated by 'RTMemExecAllocTag' is zeroed * already. */ void * addr = alloc_mem(cb, pszTag); if (addr) Genode::memset(addr, 0, cb); return addr; } void *RTMemPageAllocTag(size_t cb, const char *pszTag) { return alloc_mem(cb, pszTag); } void RTMemPageFree(void *pv, size_t cb) { Genode::Mutex::Guard guard(mutex_ds()); Avl_ds::free_memory(pv, cb); } /** * The tiny code generator (TCG) of the REM allocates quite a hugh amount * of individal TCG_CACHED_SIZE blocks. Using a dataspace per allocation * increases the cap count significantly (e.g. 9G RAM caused 2500 allocations). * Using a Slab for the known size avoids the cap issue. */ enum { TCG_CACHE = 4 * 1024 * 1024, TCG_CACHED_SIZE = 0x4000 }; typedef Genode::Bit_allocator Tcg_idx_allocator; struct Tcg_slab : Genode::List::Element { Tcg_idx_allocator _slots; Genode::addr_t _base { 0 }; bool _full { false }; Tcg_slab(void *memory) : _base(reinterpret_cast(memory)) { }; void * alloc() { unsigned idx = _slots.alloc(); return reinterpret_cast(_base + idx * TCG_CACHED_SIZE); } Genode::addr_t contains(Genode::addr_t const ptr) const { return _base <= ptr && ptr < _base + TCG_CACHED_SIZE; } }; static Genode::List list; void * RTMemTCGAlloc(size_t cb) { if (cb != TCG_CACHED_SIZE) return alloc_mem(cb, __func__); { Genode::Mutex::Guard guard(mutex_ds()); for (Tcg_slab * tcg = list.first(); tcg; tcg = tcg->next()) { if (tcg->_full) continue; try { return tcg->alloc(); } catch (Tcg_idx_allocator::Out_of_indices) { tcg->_full = true; /* try on another slab */ } } } Tcg_slab * tcg = new (vmm_heap()) Tcg_slab(alloc_mem(TCG_CACHE, __func__)); if (tcg && tcg->_base) { { Genode::Mutex::Guard guard(mutex_ds()); list.insert(tcg); } return RTMemTCGAlloc(cb); } Genode::error("no memory left for TCG"); if (tcg) destroy(vmm_heap(), tcg); return nullptr; } void * RTMemTCGAllocZ(size_t cb) { void * ptr = RTMemTCGAlloc(cb); if (ptr) Genode::memset(ptr, 0, cb); return ptr; } void RTMemTCGFree(void *pv) { Genode::Mutex::Guard guard(mutex_ds()); Genode::addr_t const ptr = reinterpret_cast(pv); for (Tcg_slab * tcg = list.first(); tcg; tcg = tcg->next()) { if (!tcg->contains(ptr)) continue; Genode::warning("could not free up TCG memory ", pv); return; } Avl_ds::free_memory(pv, Avl_ds::max_size_at(pv)); } void * RTMemTCGRealloc(void *ptr, size_t size) { if (!ptr && size) return RTMemTCGAllocZ(size); if (!size) { if (ptr) RTMemTCGFree(ptr); return nullptr; } Genode::addr_t max_size = 0; { Genode::Mutex::Guard guard(mutex_ds()); max_size = Avl_ds::max_size_at(ptr); if (!max_size) { Genode::error("bug - unknown pointer"); return nullptr; } if (size <= max_size) return ptr; } void * new_ptr = RTMemTCGAllocZ(size); if (!new_ptr) { Genode::error("no memory left ", size); return nullptr; } Genode::memcpy(new_ptr, ptr, max_size); RTMemTCGFree(ptr); return new_ptr; } #include uint32_t RTBldCfgVersionMajor(void) { return VBOX_VERSION_MAJOR; } uint32_t RTBldCfgVersionMinor(void) { return VBOX_VERSION_MINOR; } uint32_t RTBldCfgVersionBuild(void) { return VBOX_VERSION_BUILD; } uint32_t RTBldCfgRevision(void) { return ~0; } extern "C" DECLHIDDEN(int) rtProcInitExePath(char *pszPath, size_t cchPath) { Genode::copy_cstring(pszPath, "/virtualbox", cchPath); return 0; }