mirror of
https://github.com/genodelabs/genode.git
synced 2025-03-22 12:06:00 +00:00
hw: abstract nested paging on x86
The initial SVM implementation (correctly) just used another standard x86_64 page table for nested paging. The EPT implementation is for Intel VMX only. Since we don't know the underlying virtualization technology at compile time, we need to pick the correct page table implementation at runtime. Add add a AMD-compatible HPT page table implementation using the same base implementation and (more importantly) allocator as the EPT implementation. Add a Vm_page_table implementation that determines the used virtualization technology at runtime and internally defers insert and remove operations to the correct page table implementation. Issue #5218
This commit is contained in:
parent
221d0c6c48
commit
b83b53d3b2
@ -1,4 +1,5 @@
|
||||
/*
|
||||
*
|
||||
* \brief Board with PC virtualization support
|
||||
* \author Benjamin Lamowski
|
||||
* \date 2022-10-14
|
||||
@ -21,7 +22,7 @@
|
||||
#include <cpu.h>
|
||||
#include <cpu/vcpu_state_virtualization.h>
|
||||
#include <hw/spec/x86_64/x86_64.h>
|
||||
#include <spec/x86_64/virtualization/ept.h>
|
||||
#include <spec/x86_64/virtualization/vm_page_table.h>
|
||||
#include <spec/x86_64/virtualization/svm.h>
|
||||
#include <spec/x86_64/virtualization/vmx.h>
|
||||
|
||||
@ -29,10 +30,6 @@ using Genode::addr_t;
|
||||
using Genode::uint64_t;
|
||||
|
||||
namespace Board {
|
||||
|
||||
using Vm_page_table = Hw::Ept;
|
||||
using Vm_page_table_array =
|
||||
Vm_page_table::Allocator::Array<Kernel::DEFAULT_TRANSLATION_TABLE_MAX>;
|
||||
struct Vcpu_context;
|
||||
using Vcpu_data = Genode::Vcpu_data;
|
||||
using Vcpu_state = Genode::Vcpu_state;
|
||||
|
229
repos/base-hw/src/core/spec/x86_64/virtualization/hpt.h
Normal file
229
repos/base-hw/src/core/spec/x86_64/virtualization/hpt.h
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* \brief x86_64 AMD nested page table definitions
|
||||
* \author Benjamin Lamowski
|
||||
* \date 2024-05-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2024 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 _CORE__SPEC__PC__VIRTUALIZATION__HPT_H_
|
||||
#define _CORE__SPEC__PC__VIRTUALIZATION__HPT_H_
|
||||
|
||||
#include <cpu/page_table_allocator.h>
|
||||
#include <page_table/page_table_base.h>
|
||||
#include <util/register.h>
|
||||
|
||||
namespace Hw {
|
||||
using namespace Genode;
|
||||
|
||||
class Hpt;
|
||||
|
||||
/**
|
||||
* IA-32e common descriptor.
|
||||
*
|
||||
* Table entry containing descriptor fields common to all four levels.
|
||||
*/
|
||||
struct Hpt_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 or PAT defined */
|
||||
struct Pcd : Bitfield<4, 1> { }; /* cache disable or PAT defined */
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
template <unsigned _PAGE_SIZE_LOG2, unsigned _SIZE_LOG2>
|
||||
struct Pml4_table_descriptor : Hpt_common_descriptor
|
||||
{
|
||||
static constexpr size_t SIZE_LOG2 = _SIZE_LOG2;
|
||||
static constexpr size_t PAGE_SIZE_LOG2 = _PAGE_SIZE_LOG2;
|
||||
|
||||
struct Pa : Bitfield<12, SIZE_LOG2> { }; /* physical address */
|
||||
|
||||
static access_t create(addr_t const pa)
|
||||
{
|
||||
/* XXX: Set memory type depending on active PAT */
|
||||
static Page_flags flags { RW, EXEC, USER, NO_GLOBAL,
|
||||
RAM, CACHED };
|
||||
return Hpt_common_descriptor::create(flags) | Pa::masked(pa);
|
||||
}
|
||||
};
|
||||
|
||||
struct Hpt_page_directory_base_descriptor : Hpt_common_descriptor
|
||||
{
|
||||
using Common = Hpt_common_descriptor;
|
||||
|
||||
struct Ps : Common::template Bitfield<7, 1> { }; /* page size */
|
||||
|
||||
static bool maps_page(access_t const v) { return Ps::get(v); }
|
||||
};
|
||||
|
||||
template <unsigned _PAGE_SIZE_LOG2>
|
||||
struct Hpt_page_directory_descriptor : Hpt_page_directory_base_descriptor
|
||||
{
|
||||
static constexpr size_t PAGE_SIZE_LOG2 = _PAGE_SIZE_LOG2;
|
||||
|
||||
struct Page;
|
||||
struct Table;
|
||||
};
|
||||
|
||||
|
||||
template <unsigned _PAGE_SIZE_LOG2>
|
||||
struct Page_table_entry_descriptor : Hpt_common_descriptor
|
||||
{
|
||||
using Common = Hpt_common_descriptor;
|
||||
|
||||
static constexpr size_t PAGE_SIZE_LOG2 = _PAGE_SIZE_LOG2;
|
||||
|
||||
struct Pat : Bitfield<7, 1> { }; /* page attribute table */
|
||||
struct G : Bitfield<8, 1> { }; /* global */
|
||||
struct Pa : Bitfield<12, 36> { }; /* physical address */
|
||||
|
||||
static access_t create(Page_flags const &flags, addr_t const pa)
|
||||
{
|
||||
bool const wc = flags.cacheable == Cache::WRITE_COMBINED;
|
||||
|
||||
return Common::create(flags)
|
||||
| G::bits(flags.global)
|
||||
| Pa::masked(pa)
|
||||
| Pwt::bits(wc ? 1 : 0);
|
||||
}
|
||||
};
|
||||
|
||||
struct Level1_translation_table
|
||||
:
|
||||
Genode::Final_table<Page_table_entry_descriptor<Genode::SIZE_LOG2_4KB>>
|
||||
{ } __attribute__((aligned(1 << ALIGNM_LOG2)));
|
||||
|
||||
struct Pd
|
||||
:
|
||||
Genode::Page_directory<Level1_translation_table,
|
||||
Hpt_page_directory_descriptor<SIZE_LOG2_2MB>>
|
||||
{ } __attribute__((aligned(1 << ALIGNM_LOG2)));
|
||||
|
||||
struct Pdpt
|
||||
:
|
||||
Genode::Page_directory<Pd,
|
||||
Hpt_page_directory_descriptor<SIZE_LOG2_1GB>>
|
||||
{ } __attribute__((aligned(1 << ALIGNM_LOG2)));
|
||||
|
||||
struct Pml4_adapted_table
|
||||
:
|
||||
Genode::Pml4_table<Pdpt,
|
||||
Pml4_table_descriptor<SIZE_LOG2_512GB, Genode::SIZE_LOG2_256TB>>
|
||||
{ } __attribute__((aligned(1 << ALIGNM_LOG2)));
|
||||
}
|
||||
|
||||
|
||||
template <unsigned _PAGE_SIZE_LOG2>
|
||||
struct Hw::Hpt_page_directory_descriptor<_PAGE_SIZE_LOG2>::Table
|
||||
: Hpt_page_directory_base_descriptor
|
||||
{
|
||||
using Base = Hpt_page_directory_base_descriptor;
|
||||
|
||||
/**
|
||||
* Physical address
|
||||
*/
|
||||
struct Pa : Base::template Bitfield<12, 36> { };
|
||||
|
||||
/**
|
||||
* Memory types
|
||||
*/
|
||||
static typename Base::access_t create(addr_t const pa)
|
||||
{
|
||||
/* XXX: Set memory type depending on active PAT */
|
||||
static Page_flags flags { RW, EXEC, USER, NO_GLOBAL,
|
||||
RAM, CACHED };
|
||||
return Base::create(flags) | Pa::masked(pa);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <unsigned _PAGE_SIZE_LOG2>
|
||||
struct Hw::Hpt_page_directory_descriptor<_PAGE_SIZE_LOG2>::Page
|
||||
: Hpt_page_directory_base_descriptor
|
||||
{
|
||||
using Base = Hpt_page_directory_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> { };
|
||||
|
||||
static typename Base::access_t create(Page_flags const &flags,
|
||||
addr_t const pa)
|
||||
{
|
||||
bool const wc = flags.cacheable == Cache::WRITE_COMBINED;
|
||||
|
||||
return Base::create(flags)
|
||||
| Base::Ps::bits(1)
|
||||
| G::bits(flags.global)
|
||||
| Pa::masked(pa)
|
||||
| Base::Pwt::bits(wc ? 1 : 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Hw::Hpt : public Pml4_adapted_table
|
||||
{
|
||||
public:
|
||||
using Allocator = Genode::Page_table_allocator<1UL << SIZE_LOG2_4KB>;
|
||||
using Pml4_adapted_table::Pml4_adapted_table;
|
||||
|
||||
bool lookup_rw_translation(addr_t const, addr_t &, Allocator &)
|
||||
{
|
||||
raw(__func__, " not implemented yet");
|
||||
return false;
|
||||
}
|
||||
|
||||
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)
|
||||
};
|
||||
};
|
||||
|
||||
#endif /* _CORE__SPEC__PC__VIRTUALIZATION__HPT_H_ */
|
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* \brief VM page table abstraction between VMX and SVM for x86
|
||||
* \author Benjamin Lamowski
|
||||
* \date 2024-04-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2024 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 _CORE__SPEC__PC__VIRTUALIZATION__VM_PAGE_TABLE_H_
|
||||
#define _CORE__SPEC__PC__VIRTUALIZATION__VM_PAGE_TABLE_H_
|
||||
|
||||
#include <base/log.h>
|
||||
#include <util/construct_at.h>
|
||||
#include <spec/x86_64/virtualization/ept.h>
|
||||
#include <spec/x86_64/virtualization/hpt.h>
|
||||
|
||||
namespace Board {
|
||||
using namespace Genode;
|
||||
|
||||
struct Vm_page_table
|
||||
{
|
||||
/* Both Ept and Hpt need to actually use this allocator */
|
||||
using Allocator = Genode::Page_table_allocator<1UL << SIZE_LOG2_4KB>;
|
||||
|
||||
template <class T, class U>
|
||||
struct is_same {
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct is_same <T, T> {
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
static_assert(is_same<Allocator, Hw::Ept::Allocator>::value,
|
||||
"Ept uses different allocator");
|
||||
static_assert(is_same<Allocator, Hw::Hpt::Allocator>::value,
|
||||
"Hpt uses different allocator");
|
||||
|
||||
static constexpr size_t ALIGNM_LOG2 = Hw::SIZE_LOG2_4KB;
|
||||
|
||||
enum Virt_type {
|
||||
VIRT_TYPE_NONE,
|
||||
VIRT_TYPE_VMX,
|
||||
VIRT_TYPE_SVM
|
||||
};
|
||||
|
||||
union {
|
||||
Hw::Ept ept;
|
||||
Hw::Hpt hpt;
|
||||
};
|
||||
|
||||
void insert_translation(addr_t vo,
|
||||
addr_t pa,
|
||||
size_t size,
|
||||
Page_flags const & flags,
|
||||
Allocator & alloc)
|
||||
{
|
||||
if (virt_type() == VIRT_TYPE_VMX)
|
||||
ept.insert_translation(vo, pa, size, flags, alloc);
|
||||
else if (virt_type() == VIRT_TYPE_SVM)
|
||||
hpt.insert_translation(vo, pa, size, flags, alloc);
|
||||
}
|
||||
|
||||
void remove_translation(addr_t vo, size_t size, Allocator & alloc)
|
||||
{
|
||||
if (virt_type() == VIRT_TYPE_VMX)
|
||||
ept.remove_translation(vo, size, alloc);
|
||||
else if (virt_type() == VIRT_TYPE_SVM)
|
||||
hpt.remove_translation(vo, size, alloc);
|
||||
}
|
||||
|
||||
static Virt_type virt_type() {
|
||||
static Virt_type virt_type { VIRT_TYPE_NONE };
|
||||
|
||||
if (virt_type == VIRT_TYPE_NONE) {
|
||||
if (Hw::Virtualization_support::has_vmx())
|
||||
virt_type = VIRT_TYPE_VMX;
|
||||
else if (Hw::Virtualization_support::has_svm())
|
||||
virt_type = VIRT_TYPE_SVM;
|
||||
else
|
||||
error("Failed to detect Virtualization technology");
|
||||
}
|
||||
|
||||
return virt_type;
|
||||
}
|
||||
|
||||
Vm_page_table()
|
||||
{
|
||||
if (virt_type() == VIRT_TYPE_VMX)
|
||||
Genode::construct_at<Hw::Ept>(this);
|
||||
else if (virt_type() == VIRT_TYPE_SVM)
|
||||
Genode::construct_at<Hw::Hpt>(this);
|
||||
}
|
||||
};
|
||||
|
||||
using Vm_page_table_array =
|
||||
Vm_page_table::Allocator::Array<Kernel::DEFAULT_TRANSLATION_TABLE_MAX>;
|
||||
};
|
||||
|
||||
#endif /* _CORE__SPEC__PC__VIRTUALIZATION__VM_PAGE_TABLE_H_ */
|
Loading…
x
Reference in New Issue
Block a user