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

582 lines
15 KiB
C
Raw Normal View History

/*
* \brief Standard ARM v7 2-level page table format
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2012-02-22
*/
/*
* Copyright (C) 2012-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__ARM__PAGE_TABLE_H_
#define _SRC__LIB__HW__SPEC__ARM__PAGE_TABLE_H_
#include <hw/util.h>
#include <hw/assert.h>
#include <hw/page_flags.h>
#include <hw/page_table_allocator.h>
#include <util/register.h>
namespace Hw { class Page_table; }
class Hw::Page_table
{
private:
template <unsigned long B>
using Register = Genode::Register<B>;
template <typename T, typename U>
using Bitset_2 = Genode::Bitset_2<T,U>;
class Descriptor_base
{
protected:
/**
* Return TEX value for device-memory
*/
static constexpr unsigned _device_tex();
/**
* Return whether this is a SMP system
*/
static constexpr bool _smp();
/**
* Return descriptor value according to physical address 'pa'
*/
template <typename T>
static typename T::access_t _create(Page_flags const & f,
addr_t pa)
{
using namespace Genode;
typename T::access_t v = T::Pa::masked(pa);
T::S::set(v, _smp());
T::Ng::set(v, !f.global);
T::Xn::set(v, !f.executable);
if (f.type == DEVICE) {
T::Tex::set(v, _device_tex());
} else {
switch (f.cacheable) {
case CACHED: T::Tex::set(v, 5); [[fallthrough]];
case WRITE_COMBINED: T::B::set(v, 1); break;
case UNCACHED: T::Tex::set(v, 1); break;
}
}
if (f.writeable) if (f.privileged) T::Ap::set(v, 1);
else T::Ap::set(v, 3);
else if (f.privileged) T::Ap::set(v, 5);
else T::Ap::set(v, 2);
return v;
}
};
class Page_table_level_2
{
public:
enum {
SIZE_LOG2 = 10,
SIZE = 1 << SIZE_LOG2,
ALIGNM_LOG2 = SIZE_LOG2,
};
/**
* Common descriptor structure
*/
struct Descriptor : Register<32>, Descriptor_base
{
enum Type { FAULT, SMALL_PAGE };
enum {
VIRT_SIZE_LOG2 = 12,
VIRT_SIZE = 1 << VIRT_SIZE_LOG2,
VIRT_OFFSET_MASK = (1 << VIRT_SIZE_LOG2) - 1,
VIRT_BASE_MASK = ~(VIRT_OFFSET_MASK),
};
struct Type_0 : Bitfield<0, 2> { };
struct Type_1 : Bitfield<1, 1> { };
/**
* Get descriptor type of 'v'
*/
static Type type(access_t const v)
{
access_t const t0 = Type_0::get(v);
if (t0 == 0) { return FAULT; }
access_t const t1 = Type_1::get(v);
if (t1 == 1) { return SMALL_PAGE; }
return FAULT;
}
/**
* At descriptor value 'v' set type to 't'
*/
static void type(access_t & v, Type const t)
{
switch (t) {
case FAULT: Type_0::set(v, 0); return;
case SMALL_PAGE: Type_1::set(v, 1); return; }
}
static void invalidate(access_t & v) { type(v, FAULT); }
static bool valid(access_t & v) {
return type(v) != FAULT; }
};
/**
* Small page descriptor structure
*/
struct Small_page : Descriptor
{
struct Xn : Bitfield<0, 1> { }; /* execute never */
struct B : Bitfield<2, 1> { }; /* mem region attr. */
struct Ap_0 : Bitfield<4, 2> { }; /* access permission */
struct Tex : Bitfield<6, 3> { }; /* mem region attr. */
struct Ap_1 : Bitfield<9, 1> { }; /* access permission */
struct S : Bitfield<10, 1> { }; /* shareable bit */
struct Ng : Bitfield<11, 1> { }; /* not global bit */
struct Pa : Bitfield<12, 20> { }; /* physical base */
struct Ap : Bitset_2<Ap_0, Ap_1> { }; /* access permission */
/**
* Return page descriptor for physical address 'pa' and 'flags'
*/
static access_t create(Page_flags const & flags,
addr_t const pa)
{
access_t v = _create<Small_page>(flags, pa);
Descriptor::type(v, Descriptor::SMALL_PAGE);
return v;
}
};
private:
static constexpr size_t cnt =
SIZE / sizeof(Descriptor::access_t);
Descriptor::access_t _entries[cnt];
enum {
MAX_INDEX = sizeof(_entries) / sizeof(_entries[0]) - 1
};
/**
* Get entry index by virtual offset
*
* \param i is overridden with the index if call returns 0
* \param vo virtual offset relative to the virtual table base
*
* \retval 0 on success
* \retval <0 translation failed
*/
bool _index_by_vo(unsigned & i, addr_t const vo) const
{
if (vo > max_virt_offset()) return false;
i = vo >> Descriptor::VIRT_SIZE_LOG2;
return true;
}
public:
Page_table_level_2()
{
assert(aligned(this, ALIGNM_LOG2));
Genode::memset(&_entries, 0, sizeof(_entries));
}
/**
* Maximum virtual offset that can be translated by this table
*/
static addr_t max_virt_offset()
{
return (MAX_INDEX << Descriptor::VIRT_SIZE_LOG2)
+ (Descriptor::VIRT_SIZE - 1);
}
/**
* Insert one atomic translation 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
*/
void insert_translation(addr_t vo,
addr_t pa,
size_t size,
Page_flags const & flags)
{
constexpr size_t sz = Descriptor::VIRT_SIZE;
for (unsigned i; (size > 0) && _index_by_vo(i, vo);
size = (size < sz) ? 0 : size - sz,
vo += sz, pa += sz)
{
/* compose new descriptor value */
Small_page::access_t const e =
Small_page::create(flags, pa);
/* check if it is a good idea to override the entry */
assert(!Descriptor::valid(_entries[i]) ||
_entries[i] == e);
/* override entry */
_entries[i] = e;
}
}
/**
* Remove translations that overlap with a given virtual region
*
* \param vo region offset within the tables virtual region
* \param size region size
*/
void remove_translation(addr_t vo, size_t size)
{
constexpr size_t sz = Descriptor::VIRT_SIZE;
for (unsigned i; (size > 0) && _index_by_vo(i, vo);
size = (size < sz) ? 0 : size - sz, vo += sz)
{
switch (Descriptor::type(_entries[i])) {
case Descriptor::SMALL_PAGE:
Descriptor::invalidate(_entries[i]);
default: ;
}
}
}
/**
* Does this table solely contain invalid entries
*/
bool empty()
{
for (unsigned i = 0; i <= MAX_INDEX; i++) {
if (Descriptor::valid(_entries[i])) return false; }
return true;
}
} __attribute__((aligned(1<<Page_table_level_2::ALIGNM_LOG2)));
public:
using Allocator = Hw::Page_table_allocator<Page_table_level_2::SIZE>;
enum {
SIZE_LOG2 = 14,
SIZE = 1 << SIZE_LOG2,
ALIGNM_LOG2 = SIZE_LOG2,
MAX_PAGE_SIZE_LOG2 = 20,
MIN_PAGE_SIZE_LOG2 = 12,
TABLE_LEVEL_X_VIRT_SIZE = 1 << MAX_PAGE_SIZE_LOG2,
TABLE_LEVEL_X_SIZE_LOG2 = MIN_PAGE_SIZE_LOG2,
CORE_VM_AREA_SIZE = 1024 * 1024 * 1024,
CORE_TRANS_TABLE_COUNT = CORE_VM_AREA_SIZE / TABLE_LEVEL_X_VIRT_SIZE,
};
/**
* A first level translation descriptor
*/
struct Descriptor : Register<32>, Descriptor_base
{
enum Type { FAULT, PAGE_TABLE, SECTION };
enum {
VIRT_SIZE_LOG2 = 20,
VIRT_SIZE = 1 << VIRT_SIZE_LOG2,
VIRT_OFFSET_MASK = (1 << VIRT_SIZE_LOG2) - 1,
VIRT_BASE_MASK = ~VIRT_OFFSET_MASK,
};
struct Type_0 : Bitfield<0, 2> { };
struct Type_1_0 : Bitfield<1, 1> { };
struct Type_1_1 : Bitfield<18, 1> { };
struct Type_1 : Bitset_2<Type_1_0, Type_1_1> { };
/**
* Get descriptor type of 'v'
*/
static Type type(access_t const v)
{
switch (Type_0::get(v)) {
case 0: return FAULT;
case 1: return PAGE_TABLE; }
if (Type_1::get(v) == 1) { return SECTION; }
return FAULT;
}
/**
* Set descriptor type of 'v'
*/
static void type(access_t & v, Type const t)
{
switch (t) {
case FAULT: Type_0::set(v, 0); return;
case PAGE_TABLE: Type_0::set(v, 1); return;
case SECTION: Type_1::set(v, 1); return; }
}
static void invalidate(access_t & v) { type(v, FAULT); }
static bool valid(access_t & v) { return type(v) != FAULT; }
static inline Type align(addr_t vo, addr_t pa, size_t size)
{
return ((vo & VIRT_OFFSET_MASK) || (pa & VIRT_OFFSET_MASK) ||
size < VIRT_SIZE) ? PAGE_TABLE : SECTION;
}
};
/**
* Link to a second level translation table
*/
struct Page_table_descriptor : Descriptor
{
struct Domain : Bitfield<5, 4> { }; /* domain */
struct Pa : Bitfield<10, 22> { }; /* physical base */
/**
* Return descriptor value for page table 'pt
*/
static access_t create(addr_t pt)
{
access_t v = Pa::masked(pt);
Descriptor::type(v, Descriptor::PAGE_TABLE);
return v;
}
};
/**
* Section translation descriptor
*/
struct Section : Descriptor
{
struct B : Bitfield<2, 1> { }; /* mem. region attr. */
struct Xn : Bitfield<4, 1> { }; /* execute never bit */
struct Ap_0 : Bitfield<10, 2> { }; /* access permission */
struct Tex : Bitfield<12, 3> { }; /* mem. region attr. */
struct Ap_1 : Bitfield<15, 1> { }; /* access permission */
struct S : Bitfield<16, 1> { }; /* shared */
struct Ng : Bitfield<17, 1> { }; /* not global */
struct Pa : Bitfield<20, 12> { }; /* physical base */
struct Ap : Bitset_2<Ap_0, Ap_1> { }; /* access permission */
/**
* Return section descriptor for physical address 'pa' and 'flags'
*/
static access_t create(Page_flags const & flags, addr_t const pa)
{
access_t v = _create<Section>(flags, pa);
Descriptor::type(v, Descriptor::SECTION);
return v;
}
};
protected:
/* table payload, must be the first member of this class */
Descriptor::access_t _entries[SIZE/sizeof(Descriptor::access_t)];
enum { MAX_INDEX = sizeof(_entries) / sizeof(_entries[0]) - 1 };
static inline void _translation_added(addr_t, size_t);
/**
* Try to get entry index in 'i' for virtual offset 'vo!
*
* \return whether it was successful
*/
bool _index_by_vo(unsigned & i, addr_t const vo) const
{
if (vo > max_virt_offset()) return false;
i = vo >> Descriptor::VIRT_SIZE_LOG2;
return true;
}
/**
* Insert a second level translation at the given entry index
*
* \param i the related index
* \param vo virtual start address of range
* \param pa physical start address of range
* \param size size of range
* \param flags mapping flags
* \param alloc second level translation table allocator
*/
void _insert_second_level(unsigned i, addr_t const vo, addr_t const pa,
size_t const size, Page_flags const & flags,
Allocator & alloc)
{
if (i > MAX_INDEX)
return;
using Pt = Page_table_level_2;
using Ptd = Page_table_descriptor;
switch (Descriptor::type(_entries[i])) {
case Descriptor::FAULT:
{
/* create and link page table */
Pt & pt = alloc.construct<Pt>();
_entries[i] = Ptd::create(alloc.phys_addr(pt));
_translation_added((addr_t)&_entries[i], sizeof(Ptd));
}
[[fallthrough]];
case Descriptor::PAGE_TABLE:
{
Pt & pt = alloc.virt_addr<Pt>(Ptd::Pa::masked(_entries[i]));
pt.insert_translation(vo - Section::Pa::masked(vo), pa, size, flags);
_translation_added((addr_t)&pt, sizeof(Page_table_level_2));
break;
}
default: assert(0);
}
}
public:
Page_table()
{
assert(aligned(this, ALIGNM_LOG2));
Genode::memset(&_entries, 0, sizeof(_entries));
}
/**
* On ARM we do not need to copy top-level kernel entries
* because the virtual-memory kernel part is hold in a separate table
*/
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
explicit Page_table(Page_table &) : Page_table() { }
/**
* Maximum virtual offset that can be translated by this table
*/
static addr_t max_virt_offset()
{
constexpr addr_t base = MAX_INDEX << Descriptor::VIRT_SIZE_LOG2;
return base + (Descriptor::VIRT_SIZE - 1);
}
/**
* Insert translations into this table
*
* \param vo offset of virt. transl. region in virt. table region
* \param pa base of physical backing store
* \param size size of translated region
* \param f mapping flags
* \param alloc second level translation table allocator
*/
void insert_translation(addr_t vo, addr_t pa, size_t size,
Page_flags const & f, Allocator & alloc)
{
/* check sanity */
assert(!(vo & Page_table_level_2::Descriptor::VIRT_OFFSET_MASK) &&
size >= Page_table_level_2::Descriptor::VIRT_SIZE);
for (unsigned i; (size > 0) && _index_by_vo (i, vo);) {
addr_t const ve = (vo + Descriptor::VIRT_SIZE);
addr_t const end = ve & Descriptor::VIRT_BASE_MASK;
/* decide granularity of entry that can be inserted */
switch (Descriptor::align(vo, pa, size)) {
case Descriptor::SECTION:
{
/* compose new entry */
Section::access_t const e = Section::create(f, pa);
/* override entry */
if (_entries[i] == e) { break; }
assert(!Descriptor::valid(_entries[i]));
_entries[i] = e;
/* some CPUs need to act on changed translations */
_translation_added((addr_t)&_entries[i], sizeof(Section));
break;
}
default:
_insert_second_level(i, vo, pa, Genode::min(size, end - vo), f,
alloc);
};
/* check whether we wrap */
if (end < vo) { return; }
size_t sz = end - vo;
size = (size > sz) ? size - sz : 0;
vo += sz;
pa += sz;
}
}
/**
* Remove translations that overlap with a given virtual region
*
* \param vo region offset within the tables virtual region
* \param size region size
* \param alloc second level translation table allocator
*/
void remove_translation(addr_t vo, size_t size, Allocator & alloc)
{
using Pt = Page_table_level_2;
/* check sanity */
assert(vo <= (vo + size));
for (unsigned i; (size > 0) && _index_by_vo(i, vo);) {
constexpr addr_t dbm = Descriptor::VIRT_BASE_MASK;
addr_t const end = (vo + Descriptor::VIRT_SIZE) & dbm;
switch (Descriptor::type(_entries[i])) {
case Descriptor::PAGE_TABLE:
{
using Ptd = Page_table_descriptor;
Pt & pt =
alloc.virt_addr<Pt>(Ptd::Pa::masked(_entries[i]));
addr_t const pt_vo = vo - Section::Pa::masked(vo);
pt.remove_translation(pt_vo, Genode::min(size, end-vo));
if (pt.empty()) {
Descriptor::invalidate(_entries[i]);
alloc.destruct<Pt>(pt);
}
break;
}
default: Descriptor::invalidate(_entries[i]); }
/* check whether we wrap */
if (end < vo) return;
size_t sz = end - vo;
size = (size > sz) ? size - sz : 0;
vo += sz;
}
}
} __attribute__((aligned(1<<Page_table::ALIGNM_LOG2)));
#endif /* _SRC__LIB__HW__SPEC__ARM__PAGE_TABLE_H_ */