From 9ebfc3adb189c60bc8a8323148c9b027c5d5f250 Mon Sep 17 00:00:00 2001 From: Shuo A Liu Date: Thu, 27 May 2021 13:34:55 +0800 Subject: [PATCH] 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 Signed-off-by: Zide Chen Signed-off-by: Shuo A Liu Reviewed-by: Jason Chen CJ Acked-by: Eddie Dong --- hypervisor/arch/x86/guest/vept.c | 119 +++++++++++++++++++ hypervisor/include/arch/x86/asm/guest/vept.h | 22 ++++ 2 files changed, 141 insertions(+) diff --git a/hypervisor/arch/x86/guest/vept.c b/hypervisor/arch/x86/guest/vept.c index fea24fd22..6a25a4f2c 100644 --- a/hypervisor/arch/x86/guest/vept.c +++ b/hypervisor/arch/x86/guest/vept.c @@ -11,8 +11,14 @@ #include #include #include +#include #include +#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); } diff --git a/hypervisor/include/arch/x86/asm/guest/vept.h b/hypervisor/include/arch/x86/asm/guest/vept.h index 70c7ddf7e..fd7363c0b 100644 --- a/hypervisor/include/arch/x86/asm/guest/vept.h +++ b/hypervisor/include/arch/x86/asm/guest/vept.h @@ -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) {};