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:
parent
17bc7f08c9
commit
10ec896f99
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {};
|
||||
|
|
Loading…
Reference in New Issue