/* * \brief Page table allocator * \author Stefan Kalkowski * \author Johannes Schlatow * \date 2015-06-10 */ /* * Copyright (C) 2015-2023 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 _INCLUDE__CPU__PAGE_TABLE_ALLOCATOR_H_ #define _INCLUDE__CPU__PAGE_TABLE_ALLOCATOR_H_ #include #include namespace Genode { template class Page_table_allocator; struct Out_of_tables {}; } template class Genode::Page_table_allocator { protected: using addr_t = Genode::addr_t; using size_t = Genode::size_t; addr_t const _virt_addr; addr_t const _phys_addr; size_t const _size; 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, size_t size) : _virt_addr(virt_addr), _phys_addr(phys_addr), _size(size) { } virtual ~Page_table_allocator() { } template void with_table(addr_t phys_addr, auto const &match_fn, auto const &no_match_fn) { static_assert((sizeof(TABLE) == TABLE_SIZE), "unexpected size"); if (phys_addr >= _phys_addr && phys_addr < _phys_addr + _size) match_fn(*(TABLE*)(_virt_addr + (phys_addr - _phys_addr))); else no_match_fn(); } template addr_t construct() { static_assert((sizeof(TABLE) == TABLE_SIZE), "unexpected size"); TABLE & table = *Genode::construct_at(_index(_alloc())); return _offset(table) + _phys_addr; } template void destruct(addr_t phys_addr) { static_assert((sizeof(TABLE) == TABLE_SIZE), "unexpected size"); with_table
(phys_addr, [&] (TABLE & table) { table.~TABLE(); _free((unsigned)(_offset(table) / sizeof(TABLE))); }, [&] { Genode::error("Trying to destruct foreign table at ", Genode::Hex(phys_addr)); }); } size_t size() const { return _size; } }; template template class Genode::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, COUNT * TABLE_SIZE) {} explicit Array(auto phys_addr) : _alloc(_tables, phys_addr((void*)_tables), COUNT * TABLE_SIZE) { } Page_table_allocator & alloc() { return _alloc; } }; template template class Genode::Page_table_allocator::Array::Allocator : public Genode::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 (unsigned)_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, size_t size) : Page_table_allocator((addr_t)tables, phys_addr, size) {} Allocator(addr_t phys_addr, addr_t virt_addr, size_t size) : Page_table_allocator(virt_addr, phys_addr, size), _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 /* _INCLUDE__CPU__PAGE_TABLE_ALLOCATOR_H_ */