2015-02-06 17:29:30 +01:00
|
|
|
/*
|
2015-03-13 09:20:28 +01:00
|
|
|
* \brief x86_64 translation table definitions for core
|
|
|
|
* \author Adrian-Ken Rueegsegger
|
|
|
|
* \date 2015-02-06
|
2015-02-06 17:29:30 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2017-02-20 13:23:52 +01:00
|
|
|
* Copyright (C) 2015-2017 Genode Labs GmbH
|
2015-02-06 17:29:30 +01:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
2017-02-20 13:23:52 +01:00
|
|
|
* under the terms of the GNU Affero General Public License version 3.
|
2015-02-06 17:29:30 +01:00
|
|
|
*/
|
|
|
|
|
2016-01-20 20:52:51 +01:00
|
|
|
#ifndef _CORE__INCLUDE__SPEC__X86_64__TRANSLATION_TABLE_H_
|
|
|
|
#define _CORE__INCLUDE__SPEC__X86_64__TRANSLATION_TABLE_H_
|
2015-02-06 17:29:30 +01:00
|
|
|
|
2015-02-16 15:20:06 +01:00
|
|
|
/* Genode includes */
|
|
|
|
#include <util/misc_math.h>
|
|
|
|
#include <util/register.h>
|
2015-02-06 17:29:30 +01:00
|
|
|
#include <assert.h>
|
2015-02-16 15:20:06 +01:00
|
|
|
|
|
|
|
/* base-hw includes */
|
2015-06-25 15:53:39 +02:00
|
|
|
#include <util.h>
|
2015-02-16 15:20:06 +01:00
|
|
|
#include <page_flags.h>
|
2015-06-16 10:59:26 +02:00
|
|
|
#include <translation_table_allocator.h>
|
2015-02-06 17:29:30 +01:00
|
|
|
|
|
|
|
namespace Genode
|
|
|
|
{
|
2015-02-16 15:20:06 +01:00
|
|
|
|
2015-02-06 17:29:30 +01:00
|
|
|
/**
|
2015-02-16 15:20:06 +01:00
|
|
|
* IA-32e paging translates 48-bit linear addresses to 52-bit physical
|
|
|
|
* addresses. Translation structures are hierarchical and four levels
|
|
|
|
* deep.
|
|
|
|
*
|
|
|
|
* For detailed information refer to Intel SDM Vol. 3A, section 4.5.
|
2015-02-06 17:29:30 +01:00
|
|
|
*/
|
|
|
|
|
2015-02-16 15:20:06 +01:00
|
|
|
enum {
|
|
|
|
SIZE_LOG2_4KB = 12,
|
|
|
|
SIZE_LOG2_2MB = 21,
|
|
|
|
SIZE_LOG2_1GB = 30,
|
|
|
|
SIZE_LOG2_512GB = 39,
|
|
|
|
SIZE_LOG2_256TB = 48,
|
|
|
|
};
|
|
|
|
|
|
|
|
class Level_4_translation_table;
|
2015-03-20 12:22:34 +01:00
|
|
|
class Pml4_table;
|
2015-02-16 15:20:06 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* IA-32e page directory template.
|
|
|
|
*
|
|
|
|
* Page directories can refer to paging structures of the next higher level
|
|
|
|
* or directly map page frames by using large page mappings.
|
|
|
|
*
|
|
|
|
* \param PAGE_SIZE_LOG2 virtual address range size in log2
|
|
|
|
* of a single table entry
|
|
|
|
* \param SIZE_LOG2 virtual address range size in log2 of whole table
|
|
|
|
*/
|
|
|
|
template <typename ENTRY, unsigned PAGE_SIZE_LOG2, unsigned SIZE_LOG2>
|
|
|
|
class Page_directory;
|
|
|
|
|
|
|
|
using Level_3_translation_table =
|
|
|
|
Page_directory<Level_4_translation_table,
|
|
|
|
SIZE_LOG2_2MB, SIZE_LOG2_1GB>;
|
|
|
|
using Level_2_translation_table =
|
|
|
|
Page_directory<Level_3_translation_table,
|
|
|
|
SIZE_LOG2_1GB, SIZE_LOG2_512GB>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* IA-32e common descriptor.
|
|
|
|
*
|
|
|
|
* Table entry containing descriptor fields common to all four levels.
|
|
|
|
*/
|
|
|
|
struct Common_descriptor : Register<64>
|
|
|
|
{
|
|
|
|
struct P : Bitfield<0, 1> { }; /* present */
|
|
|
|
struct Rw : Bitfield<1, 1> { }; /* read/write */
|
|
|
|
struct Us : Bitfield<2, 1> { }; /* user/supervisor */
|
|
|
|
struct Pwt : Bitfield<3, 1> { }; /* write-through */
|
|
|
|
struct Pcd : Bitfield<4, 1> { }; /* cache disable */
|
|
|
|
struct A : Bitfield<5, 1> { }; /* accessed */
|
|
|
|
struct D : Bitfield<6, 1> { }; /* dirty */
|
|
|
|
struct Xd : Bitfield<63, 1> { }; /* execute-disable */
|
|
|
|
|
|
|
|
static bool present(access_t const v) { return P::get(v); }
|
|
|
|
|
|
|
|
static access_t create(Page_flags const &flags)
|
|
|
|
{
|
|
|
|
return P::bits(1)
|
|
|
|
| Rw::bits(flags.writeable)
|
|
|
|
| Us::bits(!flags.privileged)
|
|
|
|
| Xd::bits(!flags.executable);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return descriptor value with cleared accessed and dirty flags. These
|
|
|
|
* flags can be set by the MMU.
|
|
|
|
*/
|
|
|
|
static access_t clear_mmu_flags(access_t value)
|
|
|
|
{
|
|
|
|
A::clear(value);
|
|
|
|
D::clear(value);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Merge access rights of descriptor with given flags.
|
|
|
|
*/
|
|
|
|
static void merge_access_rights(access_t &desc,
|
|
|
|
Page_flags const &flags)
|
|
|
|
{
|
|
|
|
Rw::set(desc, Rw::get(desc) | flags.writeable);
|
|
|
|
Us::set(desc, Us::get(desc) | !flags.privileged);
|
|
|
|
Xd::set(desc, Xd::get(desc) & !flags.executable);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2015-02-06 17:29:30 +01:00
|
|
|
|
2015-02-16 15:20:06 +01:00
|
|
|
class Genode::Level_4_translation_table
|
2015-02-06 17:29:30 +01:00
|
|
|
{
|
2015-02-16 15:20:06 +01:00
|
|
|
private:
|
|
|
|
|
|
|
|
static constexpr size_t PAGE_SIZE_LOG2 = SIZE_LOG2_4KB;
|
|
|
|
static constexpr size_t SIZE_LOG2 = SIZE_LOG2_2MB;
|
2016-12-26 20:11:53 +01:00
|
|
|
static constexpr size_t MAX_ENTRIES = 1UL << (SIZE_LOG2-PAGE_SIZE_LOG2);
|
|
|
|
static constexpr size_t PAGE_SIZE = 1UL << PAGE_SIZE_LOG2;
|
|
|
|
static constexpr size_t PAGE_MASK = ~((1UL << PAGE_SIZE_LOG2) - 1);
|
2015-02-16 15:20:06 +01:00
|
|
|
|
|
|
|
class Misaligned {};
|
|
|
|
class Invalid_range {};
|
|
|
|
class Double_insertion {};
|
|
|
|
|
|
|
|
struct Descriptor : Common_descriptor
|
|
|
|
{
|
|
|
|
using Common = Common_descriptor;
|
|
|
|
|
|
|
|
struct Pat : Bitfield<7, 1> { }; /* page attribute table */
|
|
|
|
struct G : Bitfield<8, 1> { }; /* global */
|
|
|
|
struct Pa : Bitfield<12, 36> { }; /* physical address */
|
|
|
|
struct Mt : Bitset_3<Pwt, Pcd, Pat> { }; /* memory type */
|
|
|
|
|
|
|
|
static access_t create(Page_flags const &flags, addr_t const pa)
|
|
|
|
{
|
|
|
|
/* XXX: Set memory type depending on active PAT */
|
|
|
|
return Common::create(flags)
|
|
|
|
| G::bits(flags.global)
|
|
|
|
| Pa::masked(pa);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
typename Descriptor::access_t _entries[MAX_ENTRIES];
|
|
|
|
|
|
|
|
struct Insert_func
|
|
|
|
{
|
2015-06-16 10:59:26 +02:00
|
|
|
Page_flags const & flags;
|
|
|
|
Translation_table_allocator * alloc;
|
2015-02-16 15:20:06 +01:00
|
|
|
|
2015-03-20 12:22:34 +01:00
|
|
|
Insert_func(Page_flags const & flags,
|
2015-06-16 10:59:26 +02:00
|
|
|
Translation_table_allocator * alloc)
|
|
|
|
: flags(flags), alloc(alloc) { }
|
2015-02-16 15:20:06 +01:00
|
|
|
|
2015-03-20 12:22:34 +01:00
|
|
|
void operator () (addr_t const vo, addr_t const pa,
|
|
|
|
size_t const size,
|
|
|
|
Descriptor::access_t &desc)
|
|
|
|
{
|
|
|
|
if ((vo & ~PAGE_MASK) || (pa & ~PAGE_MASK) ||
|
|
|
|
size < PAGE_SIZE)
|
2015-02-16 15:20:06 +01:00
|
|
|
{
|
2015-03-20 12:22:34 +01:00
|
|
|
throw Invalid_range();
|
|
|
|
}
|
|
|
|
Descriptor::access_t table_entry =
|
|
|
|
Descriptor::create(flags, pa);
|
2015-02-16 15:20:06 +01:00
|
|
|
|
2015-03-20 12:22:34 +01:00
|
|
|
if (Descriptor::present(desc) &&
|
|
|
|
Descriptor::clear_mmu_flags(desc) != table_entry)
|
|
|
|
{
|
|
|
|
throw Double_insertion();
|
2015-02-16 15:20:06 +01:00
|
|
|
}
|
2015-03-20 12:22:34 +01:00
|
|
|
desc = table_entry;
|
|
|
|
}
|
2015-02-16 15:20:06 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct Remove_func
|
|
|
|
{
|
2015-06-16 10:59:26 +02:00
|
|
|
Translation_table_allocator * alloc;
|
2015-02-16 15:20:06 +01:00
|
|
|
|
2015-06-16 10:59:26 +02:00
|
|
|
Remove_func(Translation_table_allocator * alloc) : alloc(alloc) { }
|
2015-02-16 15:20:06 +01:00
|
|
|
|
2015-03-20 12:22:34 +01:00
|
|
|
void operator () (addr_t const vo, addr_t const pa,
|
|
|
|
size_t const size,
|
|
|
|
Descriptor::access_t &desc)
|
|
|
|
{ desc = 0; }
|
2015-02-16 15:20:06 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
template <typename FUNC>
|
|
|
|
void _range_op(addr_t vo, addr_t pa, size_t size, FUNC &&func)
|
|
|
|
{
|
|
|
|
for (size_t i = vo >> PAGE_SIZE_LOG2; size > 0;
|
|
|
|
i = vo >> PAGE_SIZE_LOG2) {
|
2016-12-26 20:11:53 +01:00
|
|
|
assert (i < MAX_ENTRIES);
|
2015-02-16 15:20:06 +01:00
|
|
|
addr_t end = (vo + PAGE_SIZE) & PAGE_MASK;
|
|
|
|
size_t sz = min(size, end-vo);
|
|
|
|
|
|
|
|
func(vo, pa, sz, _entries[i]);
|
|
|
|
|
|
|
|
/* check whether we wrap */
|
|
|
|
if (end < vo) return;
|
|
|
|
|
|
|
|
size = size - sz;
|
|
|
|
vo += sz;
|
|
|
|
pa += sz;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-06 17:29:30 +01:00
|
|
|
public:
|
|
|
|
|
2015-02-16 15:20:06 +01:00
|
|
|
static constexpr size_t MIN_PAGE_SIZE_LOG2 = SIZE_LOG2_4KB;
|
|
|
|
static constexpr size_t ALIGNM_LOG2 = SIZE_LOG2_4KB;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* IA-32e page table (Level 4)
|
|
|
|
*
|
|
|
|
* A page table consists of 512 entries that each maps a 4KB page
|
2015-03-20 12:22:34 +01:00
|
|
|
* frame. For further details refer to Intel SDM Vol. 3A, table 4-19.
|
2015-02-16 15:20:06 +01:00
|
|
|
*/
|
|
|
|
Level_4_translation_table()
|
|
|
|
{
|
2015-03-20 12:22:34 +01:00
|
|
|
if (!aligned(this, ALIGNM_LOG2)) throw Misaligned();
|
2015-02-16 15:20:06 +01:00
|
|
|
memset(&_entries, 0, sizeof(_entries));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns True if table does not contain any page mappings.
|
|
|
|
*/
|
|
|
|
bool empty()
|
|
|
|
{
|
|
|
|
for (unsigned i = 0; i < MAX_ENTRIES; i++)
|
|
|
|
if (Descriptor::present(_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
|
2015-06-16 10:59:26 +02:00
|
|
|
* \param alloc second level translation table allocator
|
2015-02-16 15:20:06 +01:00
|
|
|
*/
|
2015-03-20 12:22:34 +01:00
|
|
|
void insert_translation(addr_t vo, addr_t pa, size_t size,
|
2015-06-16 10:59:26 +02:00
|
|
|
Page_flags const & flags,
|
|
|
|
Translation_table_allocator * alloc)
|
2015-02-16 15:20:06 +01:00
|
|
|
{
|
2015-06-16 10:59:26 +02:00
|
|
|
this->_range_op(vo, pa, size, Insert_func(flags, alloc));
|
2015-02-16 15:20:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove translations that overlap with a given virtual region
|
|
|
|
*
|
|
|
|
* \param vo region offset within the tables virtual region
|
|
|
|
* \param size region size
|
2015-06-16 10:59:26 +02:00
|
|
|
* \param alloc second level translation table allocator
|
2015-02-16 15:20:06 +01:00
|
|
|
*/
|
2015-06-16 10:59:26 +02:00
|
|
|
void remove_translation(addr_t vo, size_t size,
|
|
|
|
Translation_table_allocator * alloc)
|
2015-02-16 15:20:06 +01:00
|
|
|
{
|
2015-06-16 10:59:26 +02:00
|
|
|
this->_range_op(vo, 0, size, Remove_func(alloc));
|
2015-02-16 15:20:06 +01:00
|
|
|
}
|
|
|
|
} __attribute__((aligned(1 << ALIGNM_LOG2)));
|
|
|
|
|
|
|
|
|
|
|
|
template <typename ENTRY, unsigned PAGE_SIZE_LOG2, unsigned SIZE_LOG2>
|
|
|
|
class Genode::Page_directory
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2016-12-26 20:11:53 +01:00
|
|
|
static constexpr size_t MAX_ENTRIES = 1UL << (SIZE_LOG2-PAGE_SIZE_LOG2);
|
|
|
|
static constexpr size_t PAGE_SIZE = 1UL << PAGE_SIZE_LOG2;
|
|
|
|
static constexpr size_t PAGE_MASK = ~((1UL << PAGE_SIZE_LOG2) - 1);
|
2015-02-16 15:20:06 +01:00
|
|
|
|
|
|
|
class Misaligned {};
|
|
|
|
class Invalid_range {};
|
|
|
|
class Double_insertion {};
|
|
|
|
|
|
|
|
struct Base_descriptor : Common_descriptor
|
|
|
|
{
|
|
|
|
using Common = Common_descriptor;
|
|
|
|
|
|
|
|
struct Ps : Common::template Bitfield<7, 1> { }; /* page size */
|
|
|
|
|
|
|
|
static bool maps_page(access_t const v) { return Ps::get(v); }
|
2015-02-06 17:29:30 +01:00
|
|
|
};
|
|
|
|
|
2015-02-16 15:20:06 +01:00
|
|
|
struct Page_descriptor : Base_descriptor
|
|
|
|
{
|
|
|
|
using Base = Base_descriptor;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Global attribute
|
|
|
|
*/
|
|
|
|
struct G : Base::template Bitfield<8, 1> { };
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Page attribute table
|
|
|
|
*/
|
|
|
|
struct Pat : Base::template Bitfield<12, 1> { };
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Physical address
|
|
|
|
*/
|
|
|
|
struct Pa : Base::template Bitfield<PAGE_SIZE_LOG2,
|
|
|
|
48 - PAGE_SIZE_LOG2> { };
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Memory type
|
|
|
|
*/
|
|
|
|
struct Mt : Base::template Bitset_3<Base::Pwt,
|
|
|
|
Base::Pcd, Pat> { };
|
|
|
|
|
|
|
|
static typename Base::access_t create(Page_flags const &flags,
|
|
|
|
addr_t const pa)
|
|
|
|
{
|
|
|
|
/* XXX: Set memory type depending on active PAT */
|
|
|
|
return Base::create(flags)
|
|
|
|
| Base::Ps::bits(1)
|
|
|
|
| G::bits(flags.global)
|
|
|
|
| Pa::masked(pa);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Table_descriptor : Base_descriptor
|
|
|
|
{
|
|
|
|
using Base = Base_descriptor;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Physical address
|
|
|
|
*/
|
|
|
|
struct Pa : Base::template Bitfield<12, 36> { };
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Memory types
|
|
|
|
*/
|
|
|
|
struct Mt : Base::template Bitset_2<Base::Pwt,
|
|
|
|
Base::Pcd> { };
|
|
|
|
|
|
|
|
static typename Base::access_t create(Page_flags const &flags,
|
|
|
|
addr_t const pa)
|
|
|
|
{
|
|
|
|
/* XXX: Set memory type depending on active PAT */
|
2015-03-20 12:22:34 +01:00
|
|
|
return Base::create(flags) | Pa::masked(pa);
|
2015-02-16 15:20:06 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
typename Base_descriptor::access_t _entries[MAX_ENTRIES];
|
|
|
|
|
|
|
|
struct Insert_func
|
|
|
|
{
|
2015-06-16 10:59:26 +02:00
|
|
|
Page_flags const & flags;
|
|
|
|
Translation_table_allocator * alloc;
|
2015-02-16 15:20:06 +01:00
|
|
|
|
|
|
|
Insert_func(Page_flags const & flags,
|
2015-06-16 10:59:26 +02:00
|
|
|
Translation_table_allocator * alloc)
|
|
|
|
: flags(flags), alloc(alloc) { }
|
2015-02-16 15:20:06 +01:00
|
|
|
|
2015-03-20 12:22:34 +01:00
|
|
|
void operator () (addr_t const vo, addr_t const pa,
|
2015-02-16 15:20:06 +01:00
|
|
|
size_t const size,
|
|
|
|
typename Base_descriptor::access_t &desc)
|
|
|
|
{
|
|
|
|
/* can we insert a large page mapping? */
|
|
|
|
if (!((vo & ~PAGE_MASK) || (pa & ~PAGE_MASK) ||
|
2015-03-20 12:22:34 +01:00
|
|
|
size < PAGE_SIZE))
|
|
|
|
{
|
2015-02-16 15:20:06 +01:00
|
|
|
typename Base_descriptor::access_t table_entry =
|
|
|
|
Page_descriptor::create(flags, pa);
|
|
|
|
|
|
|
|
if (Base_descriptor::present(desc) &&
|
2015-03-20 12:22:34 +01:00
|
|
|
Base_descriptor::clear_mmu_flags(desc) != table_entry)
|
|
|
|
{
|
2015-02-16 15:20:06 +01:00
|
|
|
throw Double_insertion();
|
2015-03-20 12:22:34 +01:00
|
|
|
}
|
2015-02-16 15:20:06 +01:00
|
|
|
|
|
|
|
desc = table_entry;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we need to use a next level table */
|
|
|
|
ENTRY *table;
|
|
|
|
if (!Base_descriptor::present(desc)) {
|
2015-06-16 10:59:26 +02:00
|
|
|
if (!alloc)
|
2015-02-16 15:20:06 +01:00
|
|
|
throw Allocator::Out_of_memory();
|
|
|
|
|
|
|
|
/* create and link next level table */
|
2015-06-16 10:59:26 +02:00
|
|
|
table = new (alloc) ENTRY();
|
|
|
|
ENTRY * phys_addr = (ENTRY*) alloc->phys_addr(table);
|
2015-03-20 12:22:34 +01:00
|
|
|
addr_t const pa = (addr_t)(phys_addr ? phys_addr : table);
|
2015-02-16 15:20:06 +01:00
|
|
|
desc = (typename Base_descriptor::access_t)
|
2015-03-20 12:22:34 +01:00
|
|
|
Table_descriptor::create(flags, pa);
|
|
|
|
|
2015-02-16 15:20:06 +01:00
|
|
|
} else if (Base_descriptor::maps_page(desc)) {
|
|
|
|
throw Double_insertion();
|
|
|
|
} else {
|
|
|
|
Base_descriptor::merge_access_rights(desc, flags);
|
|
|
|
ENTRY * phys_addr = (ENTRY*)
|
|
|
|
Table_descriptor::Pa::masked(desc);
|
2015-06-16 10:59:26 +02:00
|
|
|
table = (ENTRY*) alloc->virt_addr(phys_addr);
|
2015-02-16 15:20:06 +01:00
|
|
|
table = table ? table : (ENTRY*)phys_addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* insert translation */
|
|
|
|
table->insert_translation(vo - (vo & PAGE_MASK),
|
2015-06-16 10:59:26 +02:00
|
|
|
pa, size, flags, alloc);
|
2015-02-16 15:20:06 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Remove_func
|
|
|
|
{
|
2015-06-16 10:59:26 +02:00
|
|
|
Translation_table_allocator * alloc;
|
2015-02-16 15:20:06 +01:00
|
|
|
|
2015-06-16 10:59:26 +02:00
|
|
|
Remove_func(Translation_table_allocator * alloc) : alloc(alloc) { }
|
2015-02-16 15:20:06 +01:00
|
|
|
|
2015-03-20 12:22:34 +01:00
|
|
|
void operator () (addr_t const vo, addr_t const pa,
|
2015-02-16 15:20:06 +01:00
|
|
|
size_t const size,
|
|
|
|
typename Base_descriptor::access_t &desc)
|
|
|
|
{
|
|
|
|
if (Base_descriptor::present(desc)) {
|
|
|
|
if (Base_descriptor::maps_page(desc)) {
|
|
|
|
desc = 0;
|
|
|
|
} else {
|
|
|
|
/* use allocator to retrieve virt address of table */
|
|
|
|
ENTRY* phys_addr = (ENTRY*)
|
|
|
|
Table_descriptor::Pa::masked(desc);
|
2015-06-16 10:59:26 +02:00
|
|
|
ENTRY* table = (ENTRY*) alloc->virt_addr(phys_addr);
|
2015-02-16 15:20:06 +01:00
|
|
|
table = table ? table : (ENTRY*)phys_addr;
|
2015-03-20 12:22:34 +01:00
|
|
|
addr_t const table_vo = vo - (vo & PAGE_MASK);
|
2015-06-16 10:59:26 +02:00
|
|
|
table->remove_translation(table_vo, size, alloc);
|
2015-02-16 15:20:06 +01:00
|
|
|
if (table->empty()) {
|
2015-06-16 10:59:26 +02:00
|
|
|
destroy(alloc, table);
|
2015-02-16 15:20:06 +01:00
|
|
|
desc = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename FUNC>
|
|
|
|
void _range_op(addr_t vo, addr_t pa, size_t size, FUNC &&func)
|
|
|
|
{
|
|
|
|
for (size_t i = vo >> PAGE_SIZE_LOG2; size > 0;
|
2015-03-20 12:22:34 +01:00
|
|
|
i = vo >> PAGE_SIZE_LOG2)
|
|
|
|
{
|
2016-12-26 20:11:53 +01:00
|
|
|
assert (i < MAX_ENTRIES);
|
2015-02-16 15:20:06 +01:00
|
|
|
addr_t end = (vo + PAGE_SIZE) & PAGE_MASK;
|
|
|
|
size_t sz = min(size, end-vo);
|
|
|
|
|
|
|
|
func(vo, pa, sz, _entries[i]);
|
|
|
|
|
|
|
|
/* check whether we wrap */
|
|
|
|
if (end < vo) return;
|
|
|
|
|
|
|
|
size = size - sz;
|
|
|
|
vo += sz;
|
|
|
|
pa += sz;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
static constexpr size_t MIN_PAGE_SIZE_LOG2 = SIZE_LOG2_4KB;
|
|
|
|
static constexpr size_t ALIGNM_LOG2 = SIZE_LOG2_4KB;
|
|
|
|
|
|
|
|
Page_directory()
|
|
|
|
{
|
2015-03-20 12:22:34 +01:00
|
|
|
if (!aligned(this, ALIGNM_LOG2)) throw Misaligned();
|
2015-02-16 15:20:06 +01:00
|
|
|
memset(&_entries, 0, sizeof(_entries));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns True if table does not contain any page mappings.
|
|
|
|
*
|
|
|
|
* \return false if an entry is present, True otherwise
|
|
|
|
*/
|
|
|
|
bool empty()
|
|
|
|
{
|
|
|
|
for (unsigned i = 0; i < MAX_ENTRIES; i++)
|
|
|
|
if (Base_descriptor::present(_entries[i]))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
2015-02-06 17:29:30 +01:00
|
|
|
|
|
|
|
/**
|
2015-02-16 15:20:06 +01:00
|
|
|
* 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
|
2015-06-16 10:59:26 +02:00
|
|
|
* \param alloc second level translation table allocator
|
2015-02-06 17:29:30 +01:00
|
|
|
*/
|
2015-03-20 12:22:34 +01:00
|
|
|
void insert_translation(addr_t vo, addr_t pa, size_t size,
|
2015-06-16 10:59:26 +02:00
|
|
|
Page_flags const & flags,
|
|
|
|
Translation_table_allocator * alloc)
|
2015-02-16 15:20:06 +01:00
|
|
|
{
|
2015-06-16 10:59:26 +02:00
|
|
|
_range_op(vo, pa, size, Insert_func(flags, alloc));
|
2015-02-16 15:20:06 +01:00
|
|
|
}
|
2015-02-06 17:29:30 +01:00
|
|
|
|
|
|
|
/**
|
2015-02-16 15:20:06 +01:00
|
|
|
* Remove translations that overlap with a given virtual region
|
|
|
|
*
|
|
|
|
* \param vo region offset within the tables virtual region
|
|
|
|
* \param size region size
|
2015-06-16 10:59:26 +02:00
|
|
|
* \param alloc second level translation table allocator
|
2015-02-16 15:20:06 +01:00
|
|
|
*/
|
2015-06-16 10:59:26 +02:00
|
|
|
void remove_translation(addr_t vo, size_t size,
|
|
|
|
Translation_table_allocator * alloc)
|
2015-02-16 15:20:06 +01:00
|
|
|
{
|
2015-06-16 10:59:26 +02:00
|
|
|
_range_op(vo, 0, size, Remove_func(alloc));
|
2015-02-16 15:20:06 +01:00
|
|
|
}
|
|
|
|
} __attribute__((aligned(1 << ALIGNM_LOG2)));
|
|
|
|
|
|
|
|
|
2015-03-20 12:22:34 +01:00
|
|
|
class Genode::Pml4_table
|
2015-02-16 15:20:06 +01:00
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2016-12-26 20:11:53 +01:00
|
|
|
static constexpr size_t PAGE_SIZE_LOG2 = SIZE_LOG2_512GB;
|
|
|
|
static constexpr size_t SIZE_LOG2 = SIZE_LOG2_256TB;
|
2015-02-16 15:20:06 +01:00
|
|
|
static constexpr size_t MAX_ENTRIES = 512;
|
|
|
|
static constexpr size_t PAGE_SIZE = 1UL << PAGE_SIZE_LOG2;
|
|
|
|
static constexpr size_t PAGE_MASK = ~((1UL << PAGE_SIZE_LOG2) - 1);
|
|
|
|
|
|
|
|
class Misaligned {};
|
|
|
|
class Invalid_range {};
|
|
|
|
|
|
|
|
struct Descriptor : Common_descriptor
|
|
|
|
{
|
|
|
|
struct Pa : Bitfield<12, SIZE_LOG2> { }; /* physical address */
|
|
|
|
struct Mt : Bitset_2<Pwt, Pcd> { }; /* memory type */
|
|
|
|
|
|
|
|
static access_t create(Page_flags const &flags, addr_t const pa)
|
|
|
|
{
|
|
|
|
/* XXX: Set memory type depending on active PAT */
|
2015-03-20 12:22:34 +01:00
|
|
|
return Common_descriptor::create(flags) | Pa::masked(pa);
|
2015-02-16 15:20:06 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
typename Descriptor::access_t _entries[MAX_ENTRIES];
|
|
|
|
|
|
|
|
using ENTRY = Level_2_translation_table;
|
|
|
|
|
|
|
|
struct Insert_func
|
|
|
|
{
|
2015-06-16 10:59:26 +02:00
|
|
|
Page_flags const & flags;
|
|
|
|
Translation_table_allocator * alloc;
|
2015-02-16 15:20:06 +01:00
|
|
|
|
|
|
|
Insert_func(Page_flags const & flags,
|
2015-06-16 10:59:26 +02:00
|
|
|
Translation_table_allocator * alloc)
|
|
|
|
: flags(flags), alloc(alloc) { }
|
2015-02-16 15:20:06 +01:00
|
|
|
|
2015-03-20 12:22:34 +01:00
|
|
|
void operator () (addr_t const vo, addr_t const pa,
|
2015-02-16 15:20:06 +01:00
|
|
|
size_t const size,
|
|
|
|
Descriptor::access_t &desc)
|
|
|
|
{
|
|
|
|
/* we need to use a next level table */
|
|
|
|
ENTRY *table;
|
|
|
|
if (!Descriptor::present(desc)) {
|
2015-06-16 10:59:26 +02:00
|
|
|
if (!alloc)
|
2015-02-16 15:20:06 +01:00
|
|
|
throw Allocator::Out_of_memory();
|
|
|
|
|
|
|
|
/* create and link next level table */
|
2015-06-16 10:59:26 +02:00
|
|
|
table = new (alloc) ENTRY();
|
|
|
|
ENTRY * phys_addr = (ENTRY*) alloc->phys_addr(table);
|
2015-03-20 12:22:34 +01:00
|
|
|
addr_t const pa = (addr_t)(phys_addr ? phys_addr : table);
|
|
|
|
desc = Descriptor::create(flags, pa);
|
2015-02-16 15:20:06 +01:00
|
|
|
} else {
|
|
|
|
Descriptor::merge_access_rights(desc, flags);
|
|
|
|
ENTRY * phys_addr = (ENTRY*)
|
|
|
|
Descriptor::Pa::masked(desc);
|
2015-06-16 10:59:26 +02:00
|
|
|
table = (ENTRY*) alloc->virt_addr(phys_addr);
|
2015-02-16 15:20:06 +01:00
|
|
|
table = table ? table : (ENTRY*)phys_addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* insert translation */
|
2015-03-20 12:22:34 +01:00
|
|
|
addr_t const table_vo = vo - (vo & PAGE_MASK);
|
2015-06-16 10:59:26 +02:00
|
|
|
table->insert_translation(table_vo, pa, size, flags, alloc);
|
2015-02-16 15:20:06 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Remove_func
|
|
|
|
{
|
2015-06-16 10:59:26 +02:00
|
|
|
Translation_table_allocator * alloc;
|
2015-02-16 15:20:06 +01:00
|
|
|
|
2015-06-16 10:59:26 +02:00
|
|
|
Remove_func(Translation_table_allocator * alloc) : alloc(alloc) { }
|
2015-02-16 15:20:06 +01:00
|
|
|
|
2015-03-20 12:22:34 +01:00
|
|
|
void operator () (addr_t const vo, addr_t const pa,
|
2015-02-16 15:20:06 +01:00
|
|
|
size_t const size,
|
|
|
|
Descriptor::access_t &desc)
|
|
|
|
{
|
|
|
|
if (Descriptor::present(desc)) {
|
|
|
|
/* use allocator to retrieve virt address of table */
|
|
|
|
ENTRY* phys_addr = (ENTRY*)
|
|
|
|
Descriptor::Pa::masked(desc);
|
2015-06-16 10:59:26 +02:00
|
|
|
ENTRY* table = (ENTRY*) alloc->virt_addr(phys_addr);
|
2015-02-16 15:20:06 +01:00
|
|
|
table = table ? table : (ENTRY*)phys_addr;
|
2015-03-20 12:22:34 +01:00
|
|
|
addr_t const table_vo = vo - (vo & PAGE_MASK);
|
2015-06-16 10:59:26 +02:00
|
|
|
table->remove_translation(table_vo, size, alloc);
|
2015-02-16 15:20:06 +01:00
|
|
|
if (table->empty()) {
|
2015-06-16 10:59:26 +02:00
|
|
|
destroy(alloc, table);
|
2015-02-16 15:20:06 +01:00
|
|
|
desc = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename FUNC>
|
|
|
|
void _range_op(addr_t vo, addr_t pa, size_t size, FUNC &&func)
|
|
|
|
{
|
|
|
|
for (size_t i = vo >> PAGE_SIZE_LOG2; size > 0;
|
|
|
|
i = vo >> PAGE_SIZE_LOG2) {
|
2016-12-26 20:11:53 +01:00
|
|
|
assert (i < MAX_ENTRIES);
|
2015-02-16 15:20:06 +01:00
|
|
|
addr_t end = (vo + PAGE_SIZE) & PAGE_MASK;
|
|
|
|
size_t sz = min(size, end-vo);
|
|
|
|
|
|
|
|
func(vo, pa, sz, _entries[i]);
|
|
|
|
|
|
|
|
/* check whether we wrap */
|
|
|
|
if (end < vo) return;
|
|
|
|
|
|
|
|
size = size - sz;
|
|
|
|
vo += sz;
|
|
|
|
pa += sz;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-25 15:53:39 +02:00
|
|
|
protected:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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); }
|
|
|
|
|
2015-02-16 15:20:06 +01:00
|
|
|
public:
|
|
|
|
|
|
|
|
static constexpr size_t MIN_PAGE_SIZE_LOG2 = SIZE_LOG2_4KB;
|
|
|
|
static constexpr size_t ALIGNM_LOG2 = SIZE_LOG2_4KB;
|
|
|
|
|
2015-03-20 12:22:34 +01:00
|
|
|
Pml4_table()
|
2015-02-16 15:20:06 +01:00
|
|
|
{
|
2015-03-20 12:22:34 +01:00
|
|
|
if (!aligned(this, ALIGNM_LOG2)) throw Misaligned();
|
2015-02-16 15:20:06 +01:00
|
|
|
memset(&_entries, 0, sizeof(_entries));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns True if table does not contain any page mappings.
|
|
|
|
*
|
|
|
|
* \return false if an entry is present, True otherwise
|
2015-02-06 17:29:30 +01:00
|
|
|
*/
|
2015-02-16 15:20:06 +01:00
|
|
|
bool empty()
|
2015-02-06 17:29:30 +01:00
|
|
|
{
|
2015-02-16 15:20:06 +01:00
|
|
|
for (unsigned i = 0; i < MAX_ENTRIES; i++)
|
|
|
|
if (Descriptor::present(_entries[i]))
|
|
|
|
return false;
|
|
|
|
return true;
|
2015-02-06 17:29:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Insert translations into this table
|
|
|
|
*
|
2015-02-16 15:20:06 +01:00
|
|
|
* \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
|
2015-06-16 10:59:26 +02:00
|
|
|
* \param alloc second level translation table allocator
|
2015-02-06 17:29:30 +01:00
|
|
|
*/
|
2015-03-20 12:22:34 +01:00
|
|
|
void insert_translation(addr_t vo, addr_t pa, size_t size,
|
2015-06-16 10:59:26 +02:00
|
|
|
Page_flags const & flags,
|
|
|
|
Translation_table_allocator * alloc)
|
2015-02-06 17:29:30 +01:00
|
|
|
{
|
2015-06-16 10:59:26 +02:00
|
|
|
_range_op(vo, pa, size, Insert_func(flags, alloc));
|
2015-02-06 17:29:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove translations that overlap with a given virtual region
|
|
|
|
*
|
|
|
|
* \param vo region offset within the tables virtual region
|
|
|
|
* \param size region size
|
2015-06-16 10:59:26 +02:00
|
|
|
* \param alloc second level translation table allocator
|
2015-02-06 17:29:30 +01:00
|
|
|
*/
|
2015-06-16 10:59:26 +02:00
|
|
|
void remove_translation(addr_t vo, size_t size,
|
|
|
|
Translation_table_allocator * alloc)
|
2015-02-06 17:29:30 +01:00
|
|
|
{
|
2015-06-16 10:59:26 +02:00
|
|
|
_range_op(vo, 0, size, Remove_func(alloc));
|
2015-02-06 17:29:30 +01:00
|
|
|
}
|
2015-02-16 15:20:06 +01:00
|
|
|
} __attribute__((aligned(1 << ALIGNM_LOG2)));
|
2015-02-06 17:29:30 +01:00
|
|
|
|
2015-06-25 15:53:39 +02:00
|
|
|
|
2015-04-16 11:25:23 +02:00
|
|
|
namespace Genode {
|
2015-06-25 15:53:39 +02:00
|
|
|
class Translation_table;
|
|
|
|
}
|
|
|
|
|
|
|
|
class Genode::Translation_table : public Pml4_table
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
enum {
|
|
|
|
TABLE_LEVEL_X_SIZE_LOG2 = SIZE_LOG2_4KB,
|
|
|
|
CORE_VM_AREA_SIZE = 1024 * 1024 * 1024,
|
|
|
|
CORE_TRANS_TABLE_COUNT =
|
|
|
|
_count(CORE_VM_AREA_SIZE, SIZE_LOG2_512GB)
|
|
|
|
+ _count(CORE_VM_AREA_SIZE, SIZE_LOG2_1GB)
|
|
|
|
+ _count(CORE_VM_AREA_SIZE, SIZE_LOG2_2MB)
|
|
|
|
};
|
|
|
|
};
|
2015-04-16 11:25:23 +02:00
|
|
|
|
2016-01-20 20:52:51 +01:00
|
|
|
#endif /* _CORE__INCLUDE__SPEC__X86_64__TRANSLATION_TABLE_H_ */
|