EPT: 1GB large page support.

Alloc 1GB large page for EPT if possible. It also contains the logic
to split large page into small ones (2M or 4K).

Signed-off-by: Dongxiao Xu <dongxiao.xu@intel.com>
Signed-off-by: Xiaohui Xin <xiaohui.xin@intel.com>
Acked-by: Tim Deegan <Tim.Deegan@citrix.com>
This commit is contained in:
Keir Fraser committed 2010-04-06 07:14:56 +01:00
1 parent 453c7b6442
commit 2dcbce9769
6 files changed
+131 -94

No files matched your search

+5
View File
@@ -966,6 +966,11 @@ bool_t hvm_hap_nested_page_fault(unsigned long gfn)
/* Spurious fault? PoD and log-dirty also take this path. */
if ( p2m_is_ram(p2mt) )
{
/*
* Page log dirty is always done with order 0. If this mfn resides in
* a large page, we do not change other pages type within that large
* page.
*/
paging_mark_dirty(current->domain, mfn_x(mfn));
p2m_change_type(current->domain, gfn, p2m_ram_logdirty, p2m_ram_rw);
return 1;
+16
View File
@@ -64,6 +64,7 @@ u32 vmx_cpu_based_exec_control __read_mostly;
u32 vmx_secondary_exec_control __read_mostly;
u32 vmx_vmexit_control __read_mostly;
u32 vmx_vmentry_control __read_mostly;
u8 vmx_ept_super_page_level_limit __read_mostly;
bool_t cpu_has_vmx_ins_outs_instr_info __read_mostly;
static DEFINE_PER_CPU_READ_MOSTLY(struct vmcs_struct *, host_vmcs);
@@ -183,6 +184,21 @@ static void vmx_init_vmcs_config(void)
_vmx_secondary_exec_control &=
~(SECONDARY_EXEC_ENABLE_EPT |
SECONDARY_EXEC_UNRESTRICTED_GUEST);
if ( _vmx_secondary_exec_control & SECONDARY_EXEC_ENABLE_EPT )
{
uint64_t cap;
rdmsrl(MSR_IA32_VMX_EPT_VPID_CAP, cap);
if ( cap & VMX_EPT_SUPER_PAGE_1G )
{
vmx_ept_super_page_level_limit = 2;
printk("EPT support 1G super page.\n");
}
else if ( cap & VMX_EPT_SUPER_PAGE_2M )
{
vmx_ept_super_page_level_limit = 1;
printk("EPT support 2M super page.\n");
}
}
}
if ( (_vmx_secondary_exec_control & SECONDARY_EXEC_PAUSE_LOOP_EXITING) &&
+2 -1
View File
@@ -1446,7 +1446,8 @@ void start_vmx(void)
if ( cpu_has_vmx_ept )
vmx_function_table.hap_supported = 1;
vmx_function_table.hap_1gb_pgtb = 0;
vmx_function_table.hap_1gb_pgtb = ( vmx_ept_super_page_level_limit == 2 ) ?
1 : 0;
setup_vmcs_dump();
+100 -93
View File
@@ -25,6 +25,7 @@
#include <asm/domain.h>
#include <asm/p2m.h>
#include <asm/hvm/vmx/vmx.h>
#include <asm/hvm/vmx/vmcs.h>
#include <xen/iommu.h>
#include <asm/mtrr.h>
#include <asm/hvm/cacheattr.h>
@@ -167,6 +168,61 @@ static int ept_next_level(struct domain *d, bool_t read_only,
}
}
/* It's super page before and we should break down it now. */
static int ept_split_large_page(struct domain *d,
ept_entry_t **table, u32 *index,
unsigned long gfn, int level)
{
ept_entry_t *prev_table = *table;
ept_entry_t *split_table = NULL;
ept_entry_t *split_entry = NULL;
ept_entry_t *ept_entry = (*table) + (*index);
ept_entry_t temp_ept_entry;
unsigned long s_gfn, s_mfn;
unsigned long offset, trunk;
int i;
/* alloc new page for new ept middle level entry which is
* before a leaf super entry
*/
if ( !ept_set_middle_entry(d, &temp_ept_entry) )
return 0;
/* split the super page to small next level pages */
split_table = map_domain_page(temp_ept_entry.mfn);
offset = gfn & ((1UL << (level * EPT_TABLE_ORDER)) - 1);
trunk = (1UL << ((level-1) * EPT_TABLE_ORDER));
for ( i = 0; i < (1UL << EPT_TABLE_ORDER); i++ )
{
s_gfn = gfn - offset + i * trunk;
s_mfn = ept_entry->mfn + i * trunk;
split_entry = split_table + i;
split_entry->emt = ept_entry->emt;
split_entry->ipat = ept_entry->ipat;
split_entry->sp_avail = (level > 1) ? 1 : 0;
split_entry->mfn = s_mfn;
split_entry->avail1 = ept_entry->avail1;
split_entry->avail2 = 0;
/* last step */
split_entry->r = split_entry->w = split_entry->x = 1;
ept_p2m_type_to_flags(split_entry, ept_entry->avail1);
}
*ept_entry = temp_ept_entry;
*index = offset / trunk;
*table = split_table;
unmap_domain_page(prev_table);
return 1;
}
/*
* ept_set_entry() computes 'need_modify_vtd_table' for itself,
* by observing whether any gfn->mfn translations are modified.
@@ -183,14 +239,12 @@ ept_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn,
int i;
int rv = 0;
int ret = 0;
int split_level = 0;
int walk_level = order / EPT_TABLE_ORDER;
int direct_mmio = (p2mt == p2m_mmio_direct);
uint8_t ipat = 0;
int need_modify_vtd_table = 1;
/* We only support 4k and 2m pages now */
BUG_ON(order && order != EPT_TABLE_ORDER);
if ( order != 0 )
if ( (gfn & ((1UL << order) - 1)) )
return 1;
@@ -208,16 +262,16 @@ ept_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn,
break;
}
/* If order == 9, we should never get SUPERPAGE or PoD.
* If order == 0, we should only get POD if we have a POD superpage.
/* If order == 0, we should only get POD if we have a POD superpage.
* If i > walk_level, we need to split the page; otherwise,
* just behave as normal. */
ASSERT(order == 0 || ret == GUEST_TABLE_NORMAL_PAGE);
ASSERT(ret != GUEST_TABLE_POD_PAGE || i != walk_level);
index = gfn_remainder >> ( i ? (i * EPT_TABLE_ORDER): order);
offset = (gfn_remainder & ( ((1 << (i*EPT_TABLE_ORDER)) - 1)));
split_level = i;
ept_entry = table + index;
if ( i == walk_level )
@@ -231,25 +285,10 @@ ept_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn,
ept_entry->ipat = ipat;
ept_entry->sp_avail = order ? 1 : 0;
if ( ret == GUEST_TABLE_SUPER_PAGE )
{
if ( ept_entry->mfn == (mfn_x(mfn) - offset) )
need_modify_vtd_table = 0;
else
ept_entry->mfn = mfn_x(mfn) - offset;
if ( (ept_entry->avail1 == p2m_ram_logdirty)
&& (p2mt == p2m_ram_rw) )
for ( i = 0; i < 512; i++ )
paging_mark_dirty(d, mfn_x(mfn) - offset + i);
}
if ( ept_entry->mfn == mfn_x(mfn) )
need_modify_vtd_table = 0;
else
{
if ( ept_entry->mfn == mfn_x(mfn) )
need_modify_vtd_table = 0;
else
ept_entry->mfn = mfn_x(mfn);
}
ept_entry->mfn = mfn_x(mfn);
ept_entry->avail1 = p2mt;
ept_entry->avail2 = 0;
@@ -261,51 +300,22 @@ ept_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn,
}
else
{
/*
* It's super page before, now set one of the 4k pages, so
* we should split the 2m page to 4k pages now.
*/
/* Pointers to / into new (split) middle-level table */
ept_entry_t *split_table = NULL;
ept_entry_t *split_ept_entry = NULL;
/* Info about old (superpage) table */
unsigned long super_mfn = ept_entry->mfn;
p2m_type_t super_p2mt = ept_entry->avail1;
/* The new l2 entry which we'll write after we've build the new l1 table */
ept_entry_t l2_ept_entry;
/*
* Allocate new page for new ept middle level entry which is
* before a leaf super entry
*/
if ( !ept_set_middle_entry(d, &l2_ept_entry) )
goto out;
/* Split the super page before to 4k pages */
split_table = map_domain_page(l2_ept_entry.mfn);
offset = gfn & ((1 << EPT_TABLE_ORDER) - 1);
for ( i = 0; i < 512; i++ )
int num = order / EPT_TABLE_ORDER;
int level;
ept_entry_t *split_ept_entry;
if ( num >= cpu_vmx_ept_super_page_level_limit )
num = cpu_vmx_ept_super_page_level_limit;
for ( level = split_level; level > num ; level-- )
{
split_ept_entry = split_table + i;
split_ept_entry->emt = epte_get_entry_emt(d, gfn - offset + i,
_mfn(super_mfn + i),
&ipat, direct_mmio);
split_ept_entry->ipat = ipat;
split_ept_entry->sp_avail = 0;
/* Don't increment mfn if it's a PoD mfn */
if ( super_p2mt != p2m_populate_on_demand )
split_ept_entry->mfn = super_mfn + i;
else
split_ept_entry->mfn = super_mfn;
split_ept_entry->avail1 = super_p2mt;
split_ept_entry->avail2 = 0;
ept_p2m_type_to_flags(split_ept_entry, super_p2mt);
rv = ept_split_large_page(d, &table, &index, gfn, level);
if ( !rv )
goto out;
}
/* Set the destinated 4k page as normal */
split_ept_entry = split_table + offset;
split_ept_entry = table + index;
split_ept_entry->avail1 = p2mt;
ept_p2m_type_to_flags(split_ept_entry, p2mt);
split_ept_entry->emt = epte_get_entry_emt(d, gfn, mfn, &ipat,
direct_mmio);
split_ept_entry->ipat = ipat;
@@ -314,12 +324,6 @@ ept_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn,
need_modify_vtd_table = 0;
else
split_ept_entry->mfn = mfn_x(mfn);
split_ept_entry->avail1 = p2mt;
ept_p2m_type_to_flags(split_ept_entry, p2mt);
unmap_domain_page(split_table);
*ept_entry = l2_ept_entry;
}
/* Track the highest gfn for which we have ever had a valid mapping */
@@ -336,7 +340,7 @@ out:
ept_sync_domain(d);
/* Now the p2m table is not shared with vt-d page table */
if ( iommu_enabled && need_iommu(d) && need_modify_vtd_table )
if ( rv && iommu_enabled && need_iommu(d) && need_modify_vtd_table )
{
if ( p2mt == p2m_ram_rw )
{
@@ -459,7 +463,7 @@ out:
/* WARNING: Only caller doesn't care about PoD pages. So this function will
* always return 0 for PoD pages, not populate them. If that becomes necessary,
* pass a p2m_query_t type along to distinguish. */
static ept_entry_t ept_get_entry_content(struct domain *d, unsigned long gfn)
static ept_entry_t ept_get_entry_content(struct domain *d, unsigned long gfn, int *level)
{
ept_entry_t *table =
map_domain_page(mfn_x(pagetable_get_mfn(d->arch.phys_table)));
@@ -487,6 +491,7 @@ static ept_entry_t ept_get_entry_content(struct domain *d, unsigned long gfn)
index = gfn_remainder >> (i * EPT_TABLE_ORDER);
ept_entry = table + index;
content = *ept_entry;
*level = i;
out:
unmap_domain_page(table);
@@ -579,7 +584,10 @@ void ept_change_entry_emt_with_range(struct domain *d, unsigned long start_gfn,
p2m_lock(d->arch.p2m);
for ( gfn = start_gfn; gfn <= end_gfn; gfn++ )
{
e = ept_get_entry_content(d, gfn);
int level = 0;
uint64_t trunk = 0;
e = ept_get_entry_content(d, gfn, &level);
if ( !p2m_has_emt(e.avail1) )
continue;
@@ -588,25 +596,24 @@ void ept_change_entry_emt_with_range(struct domain *d, unsigned long start_gfn,
if ( e.sp_avail )
{
if ( !(gfn & ((1 << EPT_TABLE_ORDER) - 1)) &&
((gfn + 0x1FF) <= end_gfn) )
while ( level )
{
/*
* gfn assigned with 2M, and the end covers more than 2m areas.
* Set emt for super page.
*/
order = EPT_TABLE_ORDER;
if ( need_modify_ept_entry(d, gfn, mfn, e.ipat, e.emt, e.avail1) )
ept_set_entry(d, gfn, mfn, order, e.avail1);
gfn += 0x1FF;
}
else
{
/* Change emt for partial entries of the 2m area. */
if ( need_modify_ept_entry(d, gfn, mfn, e.ipat, e.emt, e.avail1) )
ept_set_entry(d, gfn, mfn, order, e.avail1);
gfn = ((gfn >> EPT_TABLE_ORDER) << EPT_TABLE_ORDER) + 0x1FF;
}
trunk = (1UL << (level * EPT_TABLE_ORDER)) - 1;
if ( !(gfn & trunk) && (gfn + trunk <= end_gfn) )
{
/* gfn assigned with 2M or 1G, and the end covers more than
* the super page areas.
* Set emt for super page.
*/
order = level * EPT_TABLE_ORDER;
if ( need_modify_ept_entry(d, gfn, mfn,
e.ipat, e.emt, e.avail1) )
ept_set_entry(d, gfn, mfn, order, e.avail1);
gfn += trunk;
break;
}
level--;
}
}
else /* gfn assigned with 4k */
{
+7
View File
@@ -176,6 +176,11 @@ extern u32 vmx_secondary_exec_control;
extern bool_t cpu_has_vmx_ins_outs_instr_info;
extern u8 vmx_ept_super_page_level_limit;
#define VMX_EPT_SUPER_PAGE_2M 0x00010000
#define VMX_EPT_SUPER_PAGE_1G 0x00020000
#define cpu_has_wbinvd_exiting \
(vmx_secondary_exec_control & SECONDARY_EXEC_WBINVD_EXITING)
#define cpu_has_vmx_virtualize_apic_accesses \
@@ -203,6 +208,8 @@ extern bool_t cpu_has_vmx_ins_outs_instr_info;
SECONDARY_EXEC_UNRESTRICTED_GUEST)
#define cpu_has_vmx_ple \
(vmx_secondary_exec_control & SECONDARY_EXEC_PAUSE_LOOP_EXITING)
#define cpu_vmx_ept_super_page_level_limit \
vmx_ept_super_page_level_limit
/* GUEST_INTERRUPTIBILITY_INFO flags. */
#define VMX_INTR_SHADOW_STI 0x00000001
+1
View File
@@ -166,6 +166,7 @@
#define MSR_IA32_VMX_CR4_FIXED0 0x488
#define MSR_IA32_VMX_CR4_FIXED1 0x489
#define MSR_IA32_VMX_PROCBASED_CTLS2 0x48b
#define MSR_IA32_VMX_EPT_VPID_CAP 0x48c
#define MSR_IA32_VMX_TRUE_PINBASED_CTLS 0x48d
#define MSR_IA32_VMX_TRUE_PROCBASED_CTLS 0x48e
#define MSR_IA32_VMX_TRUE_EXIT_CTLS 0x48f
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.