genode/repos/base-hw/src/include/hw/spec/riscv/page_table.h

410 lines
10 KiB
C
Raw Normal View History

/*
* \brief RISCV Sv39 page table format
* \author Sebastian Sumpf
* \date 2015-08-04
*/
/*
* 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__SPEC__RISCV__PAGE_TABLE_H_
#define _SRC__LIB__HW__SPEC__RISCV__PAGE_TABLE_H_
#include <base/log.h>
#include <hw/page_flags.h>
#include <hw/page_table_allocator.h>
#include <util/misc_math.h>
#include <util/register.h>
namespace Sv39
{
using namespace Genode;
enum {
SIZE_LOG2_4K = 12,
SIZE_LOG2_2M = 21,
SIZE_LOG2_1G = 30,
SIZE_LOG2_512G = 39,
};
struct None { };
template <typename ENTRY, unsigned BLOCK_SIZE_LOG2, unsigned SIZE_LOG2>
class Level_x_translation_table;
using Level_3_translation_table =
Level_x_translation_table<None, SIZE_LOG2_4K, SIZE_LOG2_2M>;
using Level_2_translation_table =
Level_x_translation_table<Level_3_translation_table, SIZE_LOG2_2M, SIZE_LOG2_1G>;
using Level_1_translation_table =
Level_x_translation_table<Level_2_translation_table, SIZE_LOG2_1G, SIZE_LOG2_512G>;
struct Descriptor;
struct Table_descriptor;
struct Block_descriptor;
}
struct Sv39::Descriptor : Register<64>
{
enum Descriptor_type { INVALID, TABLE, BLOCK };
struct V : Bitfield<0, 1> { }; /* present */
struct R : Bitfield<1, 1> { }; /* read */
struct W : Bitfield<2, 1> { }; /* write */
struct X : Bitfield<3, 1> { }; /* executable */
struct U : Bitfield<4, 1> { }; /* user */
struct G : Bitfield<5, 1> { }; /* global */
struct Perm : Bitfield<0, 5> { };
struct Type : Bitfield<1, 3>
{
enum {
POINTER = 0
};
};
struct Ppn : Bitfield<10, 38> { }; /* physical address 10 bit aligned */
struct Base : Bitfield<12, 38> { }; /* physical address page aligned */
static access_t permission_bits(Hw::Page_flags const &f)
{
access_t rights = 0;
R::set(rights, 1);
if (f.writeable)
W::set(rights, 1);
if (f.executable)
X::set(rights, 1);
if (!f.privileged)
U::set(rights, 1);
if (f.global)
G::set(rights, 1);
return rights;
}
static Descriptor_type type(access_t const v)
{
if (!V::get(v)) return INVALID;
if (Type::get(v) == Type::POINTER)
return TABLE;
return BLOCK;
}
static bool valid(access_t const v) {
return V::get(v); }
};
struct Sv39::Table_descriptor : Descriptor
{
static access_t create(void * const pa)
{
access_t base = Base::get((access_t)pa);
access_t desc = 0;
Ppn::set(desc, base);
Type::set(desc, Type::POINTER);
V::set(desc, 1);
return desc;
}
};
struct Sv39::Block_descriptor : Descriptor
{
static access_t create(Hw::Page_flags const &f, addr_t const pa)
{
access_t base = Base::get(pa);
access_t desc = 0;
Ppn::set(desc, base);
Perm::set(desc, permission_bits(f));
V::set(desc, 1);
return desc;
}
};
template <typename ENTRY, unsigned BLOCK_SIZE_LOG2, unsigned SIZE_LOG2>
class Sv39::Level_x_translation_table
{
private:
bool _aligned(addr_t const a, size_t const alignm_log2) {
return a == ((a >> alignm_log2) << alignm_log2); }
void _translation_added(addr_t, size_t);
public:
static constexpr size_t MIN_PAGE_SIZE_LOG2 = SIZE_LOG2_4K;
static constexpr size_t ALIGNM_LOG2 = SIZE_LOG2_4K;
static constexpr size_t MAX_ENTRIES = 1 << (SIZE_LOG2 - BLOCK_SIZE_LOG2);
static constexpr size_t BLOCK_SIZE = 1 << BLOCK_SIZE_LOG2;
static constexpr size_t BLOCK_MASK = ~(BLOCK_SIZE - 1);
static constexpr size_t VM_MASK = (1UL<< SIZE_LOG2_512G) - 1;
class Misaligned { };
class Invalid_range { };
class Double_insertion { };
using Allocator = Hw::Page_table_allocator<4096>;
protected:
typename Descriptor::access_t _entries[MAX_ENTRIES];
/*
* Return how many entries of an alignment fit into region
*/
static constexpr size_t _count(size_t region, size_t alignment) {
return align_addr<size_t>(region, alignment) / (1UL << alignment); }
template <typename FUNC>
void _range_op(addr_t vo, addr_t pa, size_t size, FUNC &&func)
{
/* sanity check vo bits 38 to 63 must be equal */
addr_t sanity = vo >> 38;
if (sanity != 0 && sanity != 0x3ffffff) {
Genode::error("invalid virtual address: ", vo);
throw Invalid_range();
}
/* clear bits 39 - 63 */
vo &= VM_MASK;
for (size_t i = vo >> BLOCK_SIZE_LOG2; size > 0;
i = vo >> BLOCK_SIZE_LOG2) {
addr_t end = (vo + BLOCK_SIZE) & BLOCK_MASK;
size_t sz = min(size, end-vo);
func(vo, pa, sz, _entries[i]);
/* flush cached table entry address */
_translation_added((addr_t)&_entries[i], sz);
/* check whether we wrap */
if (end < vo) return;
size = size - sz;
vo += sz;
pa += sz;
}
}
template <typename E>
struct Insert_func
{
Hw::Page_flags const & flags;
Allocator & alloc;
Insert_func(Hw::Page_flags const & flags, Allocator & alloc)
: flags(flags), alloc(alloc) { }
void operator () (addr_t const vo,
addr_t const pa,
size_t const size,
typename Descriptor::access_t &desc)
{
using Td = Table_descriptor;
/* can we insert a whole block? */
if (!((vo & ~BLOCK_MASK) || (pa & ~BLOCK_MASK) || size < BLOCK_SIZE)) {
typename Descriptor::access_t blk_desc =
Block_descriptor::create(flags, pa);
if (Descriptor::valid(desc) && desc != blk_desc)
throw Double_insertion();
desc = blk_desc;
return;
}
/* we need to use a next level table */
switch (Descriptor::type(desc)) {
case Descriptor::INVALID: /* no entry */
{
/* create and link next level table */
E & table = alloc.construct<E>();
desc = Td::create((void*)alloc.phys_addr(table));
}
[[fallthrough]];
case Descriptor::TABLE: /* table already available */
{
/* use allocator to retrieve virt address of table */
E & table = alloc.virt_addr<E>(Td::Base::bits(Td::Ppn::get(desc)));
table.insert_translation(vo - (vo & BLOCK_MASK),
pa, size, flags, alloc);
break;
}
case Descriptor::BLOCK: /* there is already a block */
throw Double_insertion();
};
}
};
template <typename E>
struct Remove_func
{
Allocator & alloc;
Remove_func(Allocator & alloc) : alloc(alloc) { }
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
void operator () (addr_t const vo,
addr_t const /* pa */,
size_t const size,
typename Descriptor::access_t &desc)
{
using Td = Table_descriptor;
switch (Descriptor::type(desc)) {
case Descriptor::TABLE:
{
/* use allocator to retrieve virt address of table */
E & table = alloc.virt_addr<E>(Td::Base::bits(Td::Ppn::get(desc)));
table.remove_translation(vo - (vo & BLOCK_MASK), size, alloc);
if (!table.empty()) break;
alloc.destruct<E>(table);
}
[[fallthrough]];
case Descriptor::BLOCK: [[fallthrough]];
case Descriptor::INVALID:
desc = 0;
}
}
};
public:
Level_x_translation_table()
{
if (!_aligned((addr_t)this, ALIGNM_LOG2)) {
Genode::warning("misaligned address");
throw Misaligned();
}
memset(&_entries, 0, sizeof(_entries));
}
bool empty()
{
for (unsigned i = 0; i < MAX_ENTRIES; i++)
if (Descriptor::valid(_entries[i]))
return false;
return true;
}
/**
* Insert translations into this table
*
* \param vo offset of the virtual region represented
* by the translation within the virtual
* region represented by this table
* \param pa base of the physical backing store
* \param size size of the translated region
* \param flags mapping flags
* \param alloc level allocator
*/
void insert_translation(addr_t vo, addr_t pa, size_t size,
Hw::Page_flags const & flags,
Allocator & alloc )
{
_range_op(vo, pa, size, Insert_func<ENTRY>(flags, alloc));
}
/**
* Remove translations that overlap with a given virtual region
*
* \param vo region offset within the tables virtual region
* \param size region size
* \param alloc level allocator
*/
void remove_translation(addr_t vo, size_t size, Allocator & alloc)
{
_range_op(vo, 0, size, Remove_func<ENTRY>(alloc));
}
} __attribute__((aligned(1 << ALIGNM_LOG2)));
namespace Sv39 {
/**
* Insert/Remove functor specialization for level 3
*/
template <> template <>
struct Level_3_translation_table::Insert_func<None>
{
Hw::Page_flags const & flags;
Insert_func(Hw::Page_flags const & flags, Allocator &)
: flags(flags) { }
void operator () (addr_t const vo,
addr_t const pa,
size_t const size,
Descriptor::access_t &desc)
{
if ((vo & ~BLOCK_MASK) || (pa & ~BLOCK_MASK) ||
size < BLOCK_SIZE) {
Genode::warning("invalid range");
throw Invalid_range();
}
Descriptor::access_t blk_desc =
Block_descriptor::create(flags, pa);
if (Descriptor::valid(desc) && desc != blk_desc)
throw Double_insertion();
desc = blk_desc;
}
};
template <> template <>
struct Level_3_translation_table::Remove_func<None>
{
Remove_func(Allocator &) { }
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
void operator () (addr_t /* vo */,
addr_t /* pa */,
size_t /* size */,
Descriptor::access_t &desc) {
desc = 0; }
};
}
namespace Hw {
struct Page_table : Sv39::Level_1_translation_table
{
enum {
TABLE_LEVEL_X_SIZE_LOG2 = Sv39::SIZE_LOG2_4K,
CORE_VM_AREA_SIZE = 128 * 1024 * 1024,
CORE_TRANS_TABLE_COUNT =
_count(CORE_VM_AREA_SIZE, Sv39::SIZE_LOG2_1G) +
_count(CORE_VM_AREA_SIZE, Sv39::SIZE_LOG2_2M),
};
Page_table() : Sv39::Level_1_translation_table() {}
Page_table(Page_table & kernel_table)
: Sv39::Level_1_translation_table() {
static unsigned first = (0xffffffc000000000UL & VM_MASK) >> Sv39::SIZE_LOG2_1G;
for (unsigned i = first; i < MAX_ENTRIES; i++)
_entries[i] = kernel_table._entries[i]; }
};
}
#endif /* _SRC__LIB__HW__SPEC__RISCV__PAGE_TABLE_H_ */