/* * \brief Page table allocator * \author Stefan Kalkowski * \date 2015-06-10 */ /* * Copyright (C) 2015-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ #ifndef _SRC__LIB__HW__PAGE_TABLE_ALLOCATOR_H_ #define _SRC__LIB__HW__PAGE_TABLE_ALLOCATOR_H_ #include #include namespace Hw { template class Page_table_allocator; struct Out_of_tables {}; } template class Hw::Page_table_allocator { protected: using addr_t = Genode::addr_t; addr_t const _virt_addr; addr_t const _phys_addr; template addr_t _offset(TABLE & table) { return (addr_t)&table - _virt_addr; } void * _index(unsigned idx) { return (void*)(_virt_addr + TABLE_SIZE*idx); } virtual unsigned _alloc() = 0; virtual void _free(unsigned idx) = 0; public: template class Array; Page_table_allocator(addr_t virt_addr, addr_t phys_addr) : _virt_addr(virt_addr), _phys_addr(phys_addr) { } virtual ~Page_table_allocator() { } template addr_t phys_addr(TABLE & table) { static_assert((sizeof(TABLE) == TABLE_SIZE), "unexpected size"); return _offset(table) + _phys_addr; } template TABLE & virt_addr(addr_t phys_addr) { static_assert((sizeof(TABLE) == TABLE_SIZE), "unexpected size"); return *(TABLE*)(_virt_addr + (phys_addr - _phys_addr)); } template TABLE & construct() { static_assert((sizeof(TABLE) == TABLE_SIZE), "unexpected size"); return *Genode::construct_at(_index(_alloc())); } template void destruct(TABLE & table) { static_assert((sizeof(TABLE) == TABLE_SIZE), "unexpected size"); table.~TABLE(); _free(_offset(table) / sizeof(TABLE)); } }; template template class Hw::Page_table_allocator::Array { public: class Allocator; private: struct Table { Genode::uint8_t data[TABLE_SIZE]; }; Table _tables[COUNT]; Allocator _alloc; public: Array() : _alloc((Table*)&_tables, (addr_t)&_tables) {} template explicit Array(T phys_addr) : _alloc(_tables, phys_addr((void*)_tables)) { } Page_table_allocator & alloc() { return _alloc; } }; template template class Hw::Page_table_allocator::Array::Allocator : public Hw::Page_table_allocator { private: using Bit_allocator = Genode::Bit_allocator; using Array = Page_table_allocator::Array; Bit_allocator _free_tables { }; unsigned _alloc() override { try { return _free_tables.alloc(); } catch (typename Bit_allocator::Out_of_indices&) {} throw Out_of_tables(); } void _free(unsigned idx) override { _free_tables.free(idx); } public: Allocator(Table * tables, addr_t phys_addr) : Page_table_allocator((addr_t)tables, phys_addr) {} Allocator(addr_t phys_addr, addr_t virt_addr) : Page_table_allocator(virt_addr, phys_addr), _free_tables(static_cast(&reinterpret_cast(virt_addr)->alloc())->_free_tables) { static_assert(!__is_polymorphic(Bit_allocator), "base class needs to be non-virtual"); } }; #endif /* _SRC__LIB__HW__PAGE_TABLE_ALLOCATOR_H_ */