/* * \brief Region map for l4lx support library. * \author Stefan Kalkowski * \date 2011-04-11 */ /* * Copyright (C) 2011-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. */ /* Genode includes */ #include #include #include #include /* L4lx includes */ #include namespace Fiasco { #include #include #include } using namespace L4lx; static const bool DEBUG_SEARCH = false; /* print information about the search for alternative address ranges */ Region* Region_manager::find_region(Genode::addr_t *addr, Genode::size_t *size) { Genode::Allocator_avl_base::Block *b = _find_by_address(*addr); if (!b) return 0; *addr = b->addr(); *size = b->size(); return b->used() ? metadata((void*)*addr) : 0; } void* Region_manager::attach(Genode::Dataspace_capability cap, const char* name) { /* Put it in the dataspace tree */ L4lx::Dataspace *ds = L4lx::Env::env()->dataspaces()->insert(name, cap); return attach(ds); } void* Region_manager::attach(Dataspace *ds) { void* addr = Genode::env()->rm_session()->attach(ds->cap()); alloc_addr(ds->size(), (Genode::addr_t)addr); metadata(addr, Region((Genode::addr_t)addr, ds->size(), ds)); return addr; } bool Region_manager::attach_at(Dataspace *ds, Genode::size_t size, Genode::size_t offset, void* addr) { Genode::Allocator_avl_base::Block *b = _find_by_address((Genode::addr_t)addr); /* If the region is already known, it should have been reserved before */ if (b && b->used()) { Region *r = metadata(addr); /* Sanity check */ if (!r || r->addr() != (Genode::addr_t)addr || r->size() != ds->size() || r->ds()) { return false; } /* We have to detach the dataspace placeholder */ Genode::env()->rm_session()->detach(addr); } else /* We have to reserve the area in our region map */ alloc_addr(ds->size(), (Genode::addr_t)addr); /* Now call Genode's region map to really attach the dataspace */ try { Genode::env()->rm_session()->attach(ds->cap(), size, offset, true, (Genode::addr_t) addr); } catch(...) { return false; } metadata(addr, Region((Genode::addr_t)addr, ds->size(), ds)); return true; } Region* Region_manager::reserve_range(Genode::size_t size, int align, Genode::addr_t start) { using namespace Genode; void* addr = 0; addr_t original_start = start; while (true) { Rm_connection *rmc = 0; try { /* * We attach a managed-dataspace as a placeholder to * Genode's region-map */ rmc = new (env()->heap()) Rm_connection(0, size); addr = start ? env()->rm_session()->attach_at(rmc->dataspace(), start) : env()->rm_session()->attach(rmc->dataspace()); //PDBG("attach done addr=%p!", addr); break; } catch(Rm_session::Attach_failed e) { destroy(env()->heap(), rmc); /* attach with pre-defined address failed, so search one */ if (start) { /* the original start address might have a different alignment */ addr_t aligned_start = align_addr(start, align); if (aligned_start != start) { if (DEBUG_SEARCH) PDBG("attach failed: start=%lx, trying %lx instead", start, aligned_start); start = aligned_start; } else { if (start <= ((addr_t)~0 - 2*(1 << align) + 1)) { if (DEBUG_SEARCH) PDBG("attach failed: start=%lx, trying %lx instead", start, start + (1 << align)); start += (1 << align); } else { PWRN("attach failed: start=%lx, size=0x%zx, align=%d", original_start, size, align); return 0; } } } else { PWRN("attach failed: start=0, size=0x%zx, align=%d", size, align); return 0; } } } /* * Mark the region reserved, in our region-map by setting the * dataspace reference to zero. */ alloc_addr(size, (addr_t)addr); Region reg((addr_t)addr, size, 0); metadata(addr, reg); return metadata(addr); } void Region_manager::reserve_range(Genode::addr_t addr, Genode::size_t size, const char *name) { Genode::Dataspace_capability cap; L4lx::Dataspace *ds = new (Genode::env()->heap()) L4lx::Single_dataspace(name, size, cap); L4lx::Env::env()->dataspaces()->insert(ds); alloc_addr(size, (Genode::addr_t)addr); metadata((void*)addr, Region(addr, size, ds)); } void Region_manager::dump() { Genode::addr_t addr = 0; Genode::printf("Region map:\n"); while (true) { Allocator_avl_base::Block *b = _find_by_address(addr); Region *r = metadata((void*)addr); if (!b) return; Genode::printf(" 0x%08lx - 0x%08lx ", b->addr(), b->addr() + b->size()); if (b->used()) Genode::printf("[%s]\n", (r && r->ds()) ? r->ds()->name() : "reserved"); else Genode::printf("[unused]\n"); addr = b->addr() + b->size(); } }; Mapping* Region_manager::_virt_to_phys(void *virt) { return _virt_tree.first() ? _virt_tree.first()->find_by_virt(virt) : 0; } Phys_mapping* Region_manager::_phys_to_virt(void *phys) { return _phys_tree.first() ? _phys_tree.first()->find_by_phys(phys) : 0; } void Region_manager::add_mapping(void *phys, void *virt, bool rw) { Mapping *m = _virt_to_phys(virt); if (!m) { m = new (Genode::env()->heap()) Mapping(virt, phys, rw); _virt_tree.insert(m); Phys_mapping *p = _phys_to_virt(phys); if (!p) { p = new (Genode::env()->heap()) Phys_mapping(phys); _phys_tree.insert(p); } p->mappings()->insert(m); } } void Region_manager::remove_mapping(void *virt) { using namespace Fiasco; l4_fpage_t fpage = l4_fpage((l4_addr_t)virt, L4_LOG2_PAGESIZE, L4_FPAGE_RW); l4_msgtag_t tag = l4_task_unmap(L4_BASE_TASK_CAP, fpage, L4_FP_ALL_SPACES); if (l4_error(tag)) PWRN("unmapping %p failed with error %ld!", virt, l4_error(tag)); Mapping *m = _virt_to_phys(virt); if (m) { _virt_tree.remove(m); Phys_mapping *p = _phys_to_virt(m->phys()); if (p) { p->mappings()->remove(m); if (!p->mappings()->first()) { _phys_tree.remove(p); Genode::destroy(Genode::env()->heap(), p); } } Genode::destroy(Genode::env()->heap(), m); } } void Region_manager::map(void *phys) { using namespace Fiasco; Phys_mapping *p = _phys_to_virt(phys); if (p) { Mapping *m = p->mappings()->first(); while (m) { if (!m->writeable()) l4_touch_ro(phys, L4_PAGESIZE); else l4_touch_rw(phys, L4_PAGESIZE); l4_fpage_t snd_fpage = m->writeable() ? l4_fpage((l4_addr_t)phys, L4_LOG2_PAGESIZE, L4_FPAGE_RW) : l4_fpage((l4_addr_t)phys, L4_LOG2_PAGESIZE, L4_FPAGE_RO); l4_msgtag_t tag = l4_task_map(L4_BASE_TASK_CAP, L4_BASE_TASK_CAP, snd_fpage, (l4_addr_t)m->virt()); if (l4_error(tag)) { PERR("mapping from %p to %p failed with error %ld!", phys, m->virt(), l4_error(tag)); } m = m->next(); } } } void* Region_manager::phys(void *virt) { Mapping *m = _virt_to_phys(virt); return m ? m->phys() : 0; }