hv: nested: Introduce shadow EPT infrastructure

To shadow guest EPT, the hypervisor needs construct a shadow EPT for each
guest EPT. The key to associate a shadow EPT and a guest EPT is the EPTP
(EPT pointer). This patch provides following structure to do the association.

	struct nept_desc {
	       /*
	        * A shadow EPTP.
	        * The format is same with 'EPT pointer' in VMCS.
	        * Its PML4 address field is a HVA of the hypervisor.
	        */
	       uint64_t shadow_eptp;
	       /*
	        * An guest EPTP configured by L1 VM.
	        * The format is same with 'EPT pointer' in VMCS.
	        * Its PML4 address field is a GPA of the L1 VM.
	        */
	       uint64_t guest_eptp;
	       uint32_t ref_count;
	};

Due to lack of dynamic memory allocation of the hypervisor, a array
nept_bucket of type 'struct nept_desc' is introduced to store those
association information. A guest EPT might be shared between different
L2 vCPUs, so this patch provides several functions to handle the
reference of the structure.

Interface get_shadow_eptp() also is introduced. To find the shadow EPTP
of a specified guest EPTP.

Tracked-On: #5923
Signed-off-by: Sainath Grandhi <sainath.grandhi@intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
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:
Shuo A Liu 2021-05-27 13:34:55 +08:00 committed by Xie, Nanlin
parent 17bc7f08c9
commit 10ec896f99
2 changed files with 141 additions and 0 deletions

View File

@ -11,8 +11,14 @@
#include <asm/guest/vm.h>
#include <asm/guest/vmexit.h>
#include <asm/guest/ept.h>
#include <asm/guest/vept.h>
#include <asm/guest/nested.h>
#define VETP_LOG_LEVEL LOG_DEBUG
#define CONFIG_MAX_GUEST_EPT_NUM 4
static struct nept_desc nept_desc_bucket[CONFIG_MAX_GUEST_EPT_NUM];
static spinlock_t nept_desc_bucket_lock;
/*
* For simplicity, total platform RAM size is considered to calculate the
* memory needed for shadow page tables. This is not an accurate upper bound.
@ -43,6 +49,117 @@ void reserve_buffer_for_sept_pages(void)
sept_pages = (struct page *)page_base;
}
/*
* @brief Convert a guest EPTP to the associated nept_desc.
* @return struct nept_desc * if existed.
* @return NULL if non-existed.
*/
static struct nept_desc *find_nept_desc(uint64_t guest_eptp)
{
uint32_t i;
struct nept_desc *desc = NULL;
if (guest_eptp) {
spinlock_obtain(&nept_desc_bucket_lock);
for (i = 0L; i < CONFIG_MAX_GUEST_EPT_NUM; i++) {
/* Find an existed nept_desc of the guest EPTP */
if (nept_desc_bucket[i].guest_eptp == guest_eptp) {
desc = &nept_desc_bucket[i];
break;
}
}
spinlock_release(&nept_desc_bucket_lock);
}
return desc;
}
/*
* @brief Convert a guest EPTP to a shadow EPTP.
* @return 0 if non-existed.
*/
uint64_t get_shadow_eptp(uint64_t guest_eptp)
{
struct nept_desc *desc = NULL;
desc = find_nept_desc(guest_eptp);
return (desc != NULL) ? hva2hpa((void *)desc->shadow_eptp) : 0UL;
}
/*
* @brief Get a nept_desc to cache a guest EPTP
*
* If there is already an existed nept_desc associated with given guest_eptp,
* increase its ref_count and return it. If there is not existed nept_desc
* for guest_eptp, create one and initialize it.
*
* @return a nept_desc which associate the guest EPTP with a shadow EPTP
*/
struct nept_desc *get_nept_desc(uint64_t guest_eptp)
{
uint32_t i;
struct nept_desc *desc = NULL;
if (guest_eptp != 0UL) {
spinlock_obtain(&nept_desc_bucket_lock);
for (i = 0L; i < CONFIG_MAX_GUEST_EPT_NUM; i++) {
/* Find an existed nept_desc of the guest EPTP address bits */
if (nept_desc_bucket[i].guest_eptp == guest_eptp) {
desc = &nept_desc_bucket[i];
desc->ref_count++;
break;
}
/* Get the first empty nept_desc for the guest EPTP */
if (!desc && (nept_desc_bucket[i].ref_count == 0UL)) {
desc = &nept_desc_bucket[i];
}
}
ASSERT(desc != NULL, "Get nept_desc failed!");
/* A new nept_desc, initialize it */
if (desc->shadow_eptp == 0UL) {
desc->shadow_eptp = (uint64_t)alloc_page(&sept_page_pool) | (guest_eptp & ~PAGE_MASK);
desc->guest_eptp = guest_eptp;
desc->ref_count = 1UL;
dev_dbg(VETP_LOG_LEVEL, "[%s], nept_desc[%llx] ref[%d] shadow_eptp[%llx] guest_eptp[%llx]",
__func__, desc, desc->ref_count, desc->shadow_eptp, desc->guest_eptp);
}
spinlock_release(&nept_desc_bucket_lock);
}
return desc;
}
/*
* @brief Put a nept_desc who associate with a guest_eptp
*
* If ref_count of the nept_desc, then release all resources used by it.
*/
void put_nept_desc(uint64_t guest_eptp)
{
struct nept_desc *desc = NULL;
if (guest_eptp != 0UL) {
desc = find_nept_desc(guest_eptp);
spinlock_obtain(&nept_desc_bucket_lock);
if (desc) {
desc->ref_count--;
if (desc->ref_count == 0UL) {
dev_dbg(VETP_LOG_LEVEL, "[%s], nept_desc[%llx] ref[%d] shadow_eptp[%llx] guest_eptp[%llx]",
__func__, desc, desc->ref_count, desc->shadow_eptp, desc->guest_eptp);
free_page(&sept_page_pool, (struct page *)(desc->shadow_eptp & PAGE_MASK));
/* Flush the hardware TLB */
invept((void *)(desc->shadow_eptp & PAGE_MASK));
desc->shadow_eptp = 0UL;
desc->guest_eptp = 0UL;
}
}
spinlock_release(&nept_desc_bucket_lock);
}
}
/**
* @pre vcpu != NULL
*/
@ -75,4 +192,6 @@ void init_vept(void)
spinlock_init(&sept_page_pool.lock);
memset((void *)sept_page_pool.bitmap, 0, sept_page_pool.bitmap_size * sizeof(uint64_t));
sept_page_pool.last_hint_id = 0UL;
spinlock_init(&nept_desc_bucket_lock);
}

View File

@ -7,8 +7,30 @@
#define VEPT_H
#ifdef CONFIG_NVMX_ENABLED
/*
* A descriptor to store info of nested EPT
*/
struct nept_desc {
/*
* A shadow EPTP.
* The format is same with 'EPT pointer' in VMCS.
* Its PML4 address field is a HVA of the hypervisor.
*/
uint64_t shadow_eptp;
/*
* An guest EPTP configured by L1 VM.
* The format is same with 'EPT pointer' in VMCS.
* Its PML4 address field is a GPA of the L1 VM.
*/
uint64_t guest_eptp;
uint32_t ref_count;
};
void reserve_buffer_for_sept_pages(void);
void init_vept(void);
uint64_t get_shadow_eptp(uint64_t guest_eptp);
struct nept_desc *get_nept_desc(uint64_t guest_eptp);
void put_nept_desc(uint64_t guest_eptp);
int32_t invept_vmexit_handler(struct acrn_vcpu *vcpu);
#else
static inline void reserve_buffer_for_sept_pages(void) {};