mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-20 04:33:55 +00:00
hv: nested: audit guest EPT mapping during shadow EPT entries setup
generate_shadow_ept_entry() didn't verify the correctness of the requested guest EPT mapping. That might leak host memory access to L2 VM. To simplify the implementation of the guest EPT audit, hide capabilities 'map 2-Mbyte page' and 'map 1-Gbyte page' from L1 VM. In addition, minimize the attribute bits of EPT entry when create a shadow EPT entry. Also, for invalid requested mapping address, reflect the EPT_VIOLATION to L1 VM. Here, we have some TODOs: 1) Enable large page support in generate_shadow_ept_entry() 2) Evaluate if need to emulate the invalid GPA access of L2 in HV directly. 3) Minimize EPT entry attributes. Tracked-On: #5923 Signed-off-by: Shuo A Liu <shuo.a.liu@intel.com> Reviewed-by: Jason Chen CJ <jason.cj.chen@intel.com> Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
parent
fd81937bd5
commit
da288a670b
@ -166,7 +166,7 @@ void init_vmx_msrs(struct acrn_vcpu *vcpu)
|
|||||||
* Hide 5 level EPT capability
|
* Hide 5 level EPT capability
|
||||||
* Hide accessed and dirty flags for EPT
|
* Hide accessed and dirty flags for EPT
|
||||||
*/
|
*/
|
||||||
msr_value &= ~(VMX_EPT_PAGE_WALK_5 | VMX_EPT_AD);
|
msr_value &= ~(VMX_EPT_PAGE_WALK_5 | VMX_EPT_AD | VMX_EPT_2MB_PAGE | VMX_EPT_1GB_PAGE);
|
||||||
vcpu_set_guest_msr(vcpu, MSR_IA32_VMX_EPT_VPID_CAP, msr_value);
|
vcpu_set_guest_msr(vcpu, MSR_IA32_VMX_EPT_VPID_CAP, msr_value);
|
||||||
|
|
||||||
/* For now passthru the value from physical MSR to L1 guest */
|
/* For now passthru the value from physical MSR to L1 guest */
|
||||||
|
@ -207,33 +207,82 @@ void put_nept_desc(uint64_t guest_eptp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t get_leaf_entry(uint64_t gpa, uint64_t *eptp, enum _page_table_level *level)
|
||||||
|
{
|
||||||
|
enum _page_table_level pt_level = IA32E_PML4;
|
||||||
|
uint16_t offset;
|
||||||
|
uint64_t ept_entry = 0UL;
|
||||||
|
uint64_t *p_ept_entry = eptp;
|
||||||
|
|
||||||
|
while (pt_level <= IA32E_PT) {
|
||||||
|
offset = PAGING_ENTRY_OFFSET(gpa, pt_level);
|
||||||
|
ept_entry = p_ept_entry[offset];
|
||||||
|
|
||||||
|
if (is_present_ept_entry(ept_entry)) {
|
||||||
|
if (is_leaf_ept_entry(ept_entry, pt_level)) {
|
||||||
|
*level = pt_level;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ept_entry = 0UL;
|
||||||
|
pr_err("%s, GPA[%llx] is invalid!", __func__, gpa);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_ept_entry = (uint64_t *)(ept_entry & EPT_ENTRY_PFN_MASK);
|
||||||
|
pt_level += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ept_entry;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Shadow a guest EPT entry
|
* @brief Shadow a guest EPT entry
|
||||||
* @pre vcpu != NULL
|
* @pre vcpu != NULL
|
||||||
*/
|
*/
|
||||||
static uint64_t generate_shadow_ept_entry(struct acrn_vcpu *vcpu, uint64_t guest_ept_entry,
|
static uint64_t generate_shadow_ept_entry(struct acrn_vcpu *vcpu, uint64_t guest_ept_entry,
|
||||||
enum _page_table_level guest_level)
|
enum _page_table_level guest_ept_level)
|
||||||
{
|
{
|
||||||
uint64_t shadow_ept_entry;
|
uint64_t shadow_ept_entry = 0UL;
|
||||||
|
uint64_t ept_entry;
|
||||||
|
enum _page_table_level ept_level;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Clone a shadow EPT entry w/o physical address bits from guest EPT entry
|
* Create a shadow EPT entry
|
||||||
* TODO:
|
* We only support 4K page for guest EPT. So it's simple to create a shadow EPT entry
|
||||||
* Before the cloning, host EPT mapping audit is a necessary.
|
* for it. The rules are:
|
||||||
|
* > Find the host EPT leaf entry of address in ept_entry[M-1:12], named as ept_entry
|
||||||
|
* > Minimize the attribute bits (according to ept_entry and guest_ept_entry) and
|
||||||
|
* set in shadow EPT entry shadow_ept_entry.
|
||||||
|
* > Set the HPA of guest_ept_entry[M-1:12] to shadow_ept_entry.
|
||||||
*/
|
*/
|
||||||
shadow_ept_entry = guest_ept_entry & ~EPT_ENTRY_PFN_MASK;
|
if (is_leaf_ept_entry(guest_ept_entry, guest_ept_level)) {
|
||||||
if (is_leaf_ept_entry(guest_ept_entry, guest_level)) {
|
ASSERT(guest_ept_level == IA32E_PT, "Only support 4K page for guest EPT!");
|
||||||
/*
|
ept_entry = get_leaf_entry((guest_ept_entry & EPT_ENTRY_PFN_MASK), get_ept_entry(vcpu->vm), &ept_level);
|
||||||
* Use guest EPT entry HPA in shadow EPT entry
|
if (ept_entry != 0UL) {
|
||||||
*/
|
/*
|
||||||
shadow_ept_entry |= gpa2hpa(vcpu->vm, (guest_ept_entry & EPT_ENTRY_PFN_MASK));
|
* TODO:
|
||||||
|
* Now, take guest EPT entry attributes directly. We need take care
|
||||||
|
* of memory type, permission bits, reserved bits when we merge EPT
|
||||||
|
* entry and guest EPT entry.
|
||||||
|
*
|
||||||
|
* Just keep the code skeleton here for extend.
|
||||||
|
*/
|
||||||
|
shadow_ept_entry = guest_ept_entry & ~EPT_ENTRY_PFN_MASK;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the address.
|
||||||
|
* gpa2hpa() should be successful as ept_entry already be found.
|
||||||
|
*/
|
||||||
|
shadow_ept_entry |= gpa2hpa(vcpu->vm, (guest_ept_entry & EPT_ENTRY_PFN_MASK));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Use a HPA of a new page in shadow EPT entry */
|
/* Use a HPA of a new page in shadow EPT entry */
|
||||||
|
shadow_ept_entry = guest_ept_entry & ~EPT_ENTRY_PFN_MASK;
|
||||||
shadow_ept_entry |= hva2hpa((void *)alloc_page(&sept_page_pool)) & EPT_ENTRY_PFN_MASK;
|
shadow_ept_entry |= hva2hpa((void *)alloc_page(&sept_page_pool)) & EPT_ENTRY_PFN_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return shadow_ept_entry;
|
return shadow_ept_entry;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -378,6 +427,15 @@ bool handle_l2_ept_violation(struct acrn_vcpu *vcpu)
|
|||||||
/* Create a shadow EPT entry */
|
/* Create a shadow EPT entry */
|
||||||
shadow_ept_entry = generate_shadow_ept_entry(vcpu, guest_ept_entry, pt_level);
|
shadow_ept_entry = generate_shadow_ept_entry(vcpu, guest_ept_entry, pt_level);
|
||||||
p_shadow_ept_page[offset] = shadow_ept_entry;
|
p_shadow_ept_page[offset] = shadow_ept_entry;
|
||||||
|
if (shadow_ept_entry == 0UL) {
|
||||||
|
/*
|
||||||
|
* TODO:
|
||||||
|
* For invalid GPA in guest EPT entries, now reflect the violation to L1 VM.
|
||||||
|
* Need to revisit this and evaluate if need to emulate the invalid GPA
|
||||||
|
* access of L2 in HV directly.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Shadow EPT entry exists */
|
/* Shadow EPT entry exists */
|
||||||
|
Loading…
Reference in New Issue
Block a user