Nested Virtualization: hap-on-hap
PATCH 15/15 Nested Virtualization: hap-on-hap
Signed-off-by Christoph Egger <[email protected]>
Signature · 6 lines
---to satisfy European Law for business letters: Advanced Micro Devices GmbH Einsteinring 24, 85609 Dornach b. Muenchen Geschaeftsfuehrer: Andrew Bowd, Thomas M. McCoy, Giuliano Meroni Sitz: Dornach, Gemeinde Aschheim, Landkreis Muenchen Registergericht Muenchen, HRB Nr. 43632
# HG changeset patch
# User cegger
# Date 1275577555 -7200
Implemented Nested-on-Nested.
This allows the guest to run nested guest with hap.
xen/arch/x86/hvm/hvm.c +56 -3
@@ -984,12 +984,53 @@ void hvm_triple_fault(void)domain_shutdown (v->domain, SHUTDOWN_reboot); } -bool_t hvm_hap_nested_page_fault (unsigned long gfn) +bool_t hvm_hap_nested_page_fault (paddr_t gpa,struct cpu_user_regs *regs) {p2m_type_t p2mt;mfn_t mfn;struct vcpu *v = current;struct p2m_domain *p2m =p2m_get_hostp2m (v->domain); +unsigned long gfn = gpa >>PAGE_SHIFT ; +int rv; + +/* On Nested Virtualization, walk the guest page table. +* If this succeeds, all is fine. +* If this fails, inject a nested page fault into the guest. +*/ +if (nestedhvm_enabled (v->domain) + &&nestedhvm_vcpu_in_guestmode (v) + &&nestedhvm_paging_mode_hap (v) ) + { +enum nestedhvm_vmexits nsret; + +/* nested guest gpa == guest gva */ + rv =nestedhvm_hap_nested_page_fault (v, gpa); +switch (rv) { +case NESTEDHVM_PAGEFAULT_DONE : +return 1 ; +case NESTEDHVM_PAGEFAULT_ERROR : +return 0 ; +case NESTEDHVM_PAGEFAULT_INJECT : +break ; + } + +/* inject #VMEXIT(NPF) into guest. */ +VCPU_NESTEDHVM (v).nh_forcevmexit_exitcode =VMEXIT_NPF ; +VCPU_NESTEDHVM (v).nh_hostflags.fields.forcevmexit =1 ; + nsret =nestedhvm_vcpu_vmexit (v, regs,VMEXIT_NPF ); +VCPU_NESTEDHVM (v).nh_hostflags.fields.forcevmexit =0 ; +switch (nsret) { +case NESTEDHVM_VMEXIT_DONE : +case NESTEDHVM_VMEXIT_ERROR :/* L1 guest will crash L2 guest */ +return 1 ; +case NESTEDHVM_VMEXIT_HOST : +case NESTEDHVM_VMEXIT_CONTINUE : +case NESTEDHVM_VMEXIT_FATALERROR : +default : +gdprintk (XENLOG_ERR ,"unexpected nestedhvm error %i\n" , nsret); +return 0 ; + } + } mfn =gfn_to_mfn_guest (p2m, gfn, &p2mt);
@@ -1068,6 +1109,14 @@ int hvm_set_efer(uint64_t value)return X86EMUL_EXCEPTION ; } +if (nestedhvm_enabled (v->domain) && + ((value &EFER_SVME ) ==0 ) && + ((value ^ v->arch.hvm_vcpu.guest_efer) &EFER_SVME ) ) + { +/* Cleared EFER.SVME: Flush all nestedp2m tables */ +p2m_flush_nestedp2m (v->domain); + } + value |= v->arch.hvm_vcpu.guest_efer &EFER_LMA ; v->arch.hvm_vcpu.guest_efer = value;hvm_update_guest_efer (v);
@@ -1791,11 +1840,15 @@ static enum hvm_copy_result __hvm_copy( void *buf, paddr_t addr, int size, unsigned int flags, uint32_t pfec) { struct vcpu *curr = current; -struct p2m_domain *p2m =p2m_get_hostp2m (curr->domain); +struct p2m_domain *p2m;unsigned long gfn, mfn;p2m_type_t p2mt;char *p;int count, todo = size; +unsigned long cr3 = curr->arch.hvm_vcpu.guest_cr[3 ]; +const struct paging_mode *mode =paging_get_mode (curr); + + p2m =p2m_get_p2m (curr);while ( todo >0 ) {
@@ -1803,7 +1856,7 @@ static enum hvm_copy_result __hvm_copy(if ( flags & HVMCOPY_virt ) { - gfn =paging_gva_to_gfn (curr, addr, &pfec); + gfn =paging_p2m_ga_to_gfn (curr, p2m, mode, cr3, addr, &pfec);if ( gfn ==INVALID_GFN ) {if ( pfec == PFEC_page_paged )
xen/arch/x86/hvm/nestedhvm.c +1 -0
@@ -671,6 +671,7 @@ nestedhvm_vcpu_vmexit(struct vcpu *v, stenum nestedhvm_vmexits ret;VCPU_NESTEDHVM (v).nh_hostflags.fields.vmrun =1 ; +paging_update_nestedmode (v);if (nestedhvm_vcpu_in_guestmode (v)) { ret =nestedhvm_vmexit (v, regs, exit_reason);switch (ret) {
xen/arch/x86/hvm/svm/svm.c +16 -5
@@ -914,6 +914,12 @@ static int nsvm_vcpu_features(struct vcp *edx =0 ; +/* We require the host to use nested paging as +* nested-on-shadow is not supported. +*/ +if ( cpu_has_svm_npt &&paging_mode_hap (v->domain) ) + *edx |= (1U <<SVM_FEATURE_NPT ); +if ( cpu_has_svm_lbrv ) *edx |= (1U <<SVM_FEATURE_LBRV );#if 0 /* not yet implemented */
@@ -1265,6 +1271,10 @@ static int nsvm_vmcb_prepare4vmrun(struc/* Nested paging mode */ if (nestedhvm_paging_mode_hap (v)) {/* host nested paging + guest nested paging. */ + host_vmcb->np_enable =1 ; + + host_vmcb->h_cr3 = +pagetable_get_paddr (p2m_get_pagetable (p2m_get_nestedp2m (v, ns_vmcb->h_cr3)));/* hvm_set_cr3() below sets v->arch.hvm_vcpu.guest_cr[3] for us. */ rc =hvm_set_cr3 (ns_vmcb->cr3);
@@ -1588,14 +1598,15 @@ struct hvm_function_table * __init startreturn &svm_function_table; } -static void svm_do_nested_pgfault (paddr_t gpa) +static void svm_do_nested_pgfault (struct vcpu *v, +struct cpu_user_regs *regs,paddr_t gpa) {unsigned long gfn = gpa >>PAGE_SHIFT ;mfn_t mfn;p2m_type_t p2mt;struct p2m_domain *p2m; - p2m =p2m_get_hostp2m (current->domain); + p2m =p2m_get_p2m (v);if ( tb_init_done ) {
@@ -1613,14 +1624,14 @@ static void svm_do_nested_pgfault(paddr___trace_var (TRC_HVM_NPF ,0 ,sizeof (_d), (unsigned char *)&_d); } -if (hvm_hap_nested_page_fault (gfn) ) +if (hvm_hap_nested_page_fault (gpa, regs) )return ;/* Everything else is an error. */ mfn =gfn_to_mfn_guest (p2m, gfn, &p2mt);gdprintk (XENLOG_ERR ,"SVM violation gpa %#" PRIpaddr", mfn %#lx, type %i\n" , gpa,mfn_x (mfn), p2mt); -domain_crash (current->domain); +domain_crash (v->domain); }static void svm_fpu_dirty_intercept (void )
@@ -2499,7 +2510,7 @@ asmlinkage void svm_vmexit_handler(struccase VMEXIT_NPF :perfc_incra (svmexits,VMEXIT_NPF_PERFC ); regs->error_code = vmcb->exitinfo1; -svm_do_nested_pgfault (vmcb->exitinfo2); +svm_do_nested_pgfault (v, regs, vmcb->exitinfo2);break ;case VMEXIT_IRET :
xen/arch/x86/hvm/vmx/vmx.c +1 -1
@@ -2135,7 +2135,7 @@ static void ept_handle_violation(unsigne }if ( (qualification &EPT_GLA_VALID ) && -hvm_hap_nested_page_fault (gfn) ) +hvm_hap_nested_page_fault (gpa,guest_cpu_user_regs ()) )return ;/* Everything else is an error. */
xen/arch/x86/mm/hap/Makefile +1 -0
@@ -3,6 +3,7 @@ obj-y += guest_walk_2level.o obj-y += guest_walk_3level.o obj-y += guest_walk_4level.o obj-y += p2m-ept.o +obj-y += nested_hap.o guest_levels = $(subst level,,$(filter %level,$(subst ., ,$(subst _, ,$(1))))) guest_walk_defns = -DGUEST_PAGING_LEVELS=$(call guest_levels,$(1))
xen/arch/x86/mm/hap/guest_walk.c +23 -3
@@ -29,6 +29,9 @@#define _hap_gva_to_gfn (levels) hap_gva_to_gfn_##levels##_levels#define hap_gva_to_gfn (levels) _hap_gva_to_gfn(levels) +#define _hap_p2m_ga_to_gfn (levels) hap_p2m_ga_to_gfn_##levels##_levels +#define hap_p2m_ga_to_gfn (levels) _hap_p2m_ga_to_gfn(levels) +#if GUEST_PAGING_LEVELS <=CONFIG_PAGING_LEVELS #include <asm/guest_pt.h>
@@ -38,15 +41,23 @@ unsigned long hap_gva_to_gfn(GUEST_PAGIN struct vcpu *v, unsigned long gva, uint32_t *pfec) { unsigned long cr3; +struct p2m_domain *p2m =p2m_get_hostp2m (v->domain); + + cr3 = v->arch.hvm_vcpu.guest_cr[3 ]; +return hap_p2m_ga_to_gfn (GUEST_PAGING_LEVELS )(v, p2m, cr3, gva, pfec); +} + +unsigned long hap_p2m_ga_to_gfn (GUEST_PAGING_LEVELS )( +struct vcpu *v,struct p2m_domain *p2m,unsigned long cr3, +paddr_t ga,uint32_t *pfec) +{uint32_t missing;mfn_t top_mfn;void *top_map;p2m_type_t p2mt;walk_t gw; -struct p2m_domain *p2m =p2m_get_hostp2m (v->domain);/* Get the top-level table's MFN */ - cr3 = v->arch.hvm_vcpu.guest_cr[3 ]; top_mfn =gfn_to_mfn_unshare (p2m, cr3 >>PAGE_SHIFT , &p2mt,0 );if (p2m_is_paging (p2mt) ) {
@@ -72,7 +83,7 @@ unsigned long hap_gva_to_gfn(GUEST_PAGIN#if GUEST_PAGING_LEVELS ==3 top_map += (cr3 & ~(PAGE_MASK |31 ));#endif - missing =guest_walk_tables (v, p2m, gva, &gw, pfec[0 ], top_mfn, top_map); + missing =guest_walk_tables (v, p2m, ga, &gw, pfec[0 ], top_mfn, top_map);unmap_domain_page (top_map);/* Interpret the answer */
@@ -119,6 +130,15 @@ unsigned long hap_gva_to_gfn(GUEST_PAGIN return INVALID_GFN; } +unsigned long hap_p2m_ga_to_gfn (GUEST_PAGING_LEVELS )( +struct vcpu *v,struct p2m_domain *p2m,unsigned long cr3, +paddr_t ga,uint32_t *pfec) +{ +gdprintk (XENLOG_ERR , +"Guest paging level is greater than host paging level!\n" ); +domain_crash (v->domain); +return INVALID_GFN ; +}#endif
xen/arch/x86/mm/hap/hap.c +69 -6
162 lines not on this page · raw message
xen/arch/x86/mm/hap/nested_hap.c new file +421 -0
421 lines not on this page · raw message
xen/arch/x86/mm/hap/private.h +10 -0
@@ -30,4 +30,14 @@ unsigned long hap_gva_to_gfn_3_levels(stunsigned long hap_gva_to_gfn_4_levels (struct vcpu *v,unsigned long gva,uint32_t *pfec); +unsigned long hap_p2m_ga_to_gfn_2_levels (struct vcpu *v, +struct p2m_domain *p2m,unsigned long cr3, +paddr_t ga,uint32_t *pfec); +unsigned long hap_p2m_ga_to_gfn_3_levels (struct vcpu *v, +struct p2m_domain *p2m,unsigned long cr3, +paddr_t ga,uint32_t *pfec); +unsigned long hap_p2m_ga_to_gfn_4_levels (struct vcpu *v, +struct p2m_domain *p2m,unsigned long cr3, +paddr_t ga,uint32_t *pfec); +#endif /* __HAP_PRIVATE_H__ */
xen/arch/x86/mm/p2m.c +214 -4
290 lines not on this page · raw message
xen/arch/x86/mm/paging.c +38 -5
65 lines not on this page · raw message
xen/include/asm-x86/domain.h +7 -0
@@ -199,6 +199,8 @@ struct paging_domain {struct paging_vcpu {/* Pointers to mode-specific entry points. */ const struct paging_mode *mode; +/* Nested Virtualization: paging mode of nested guest */ +const struct paging_mode *nestedmode;/* HVM guest: last emulate was to a pagetable */ unsigned int last_write_was_pt:1 ;/* HVM guest: last write emulation succeeds */
@@ -214,6 +216,7 @@ struct paging_vcpu {#define MAX_CPUID_INPUT 40typedef xen_domctl_cpuid_t cpuid_input_t ; +#define MAX_NESTEDP2M 10struct p2m_domain ;struct time_scale {int shift;
@@ -247,6 +250,10 @@ struct arch_domainstruct paging_domain paging;struct p2m_domain *p2m; +/* nestedhvm: translate l2 guest physical to host physical */ +struct p2m_domain *nested_p2m[MAX_NESTEDP2M ]; +spinlock_t nested_p2m_lock; +/* NB. protected by d->event_lock and by irq_desc[irq].lock */ int *irq_pirq;int *pirq_irq;
xen/include/asm-x86/hvm/hvm.h +1 -1
@@ -354,7 +354,7 @@ static inline void hvm_set_info_guest(stint hvm_debug_op (struct vcpu *v,int32_t op); -bool_t hvm_hap_nested_page_fault (unsigned long gfn); +bool_t hvm_hap_nested_page_fault (paddr_t gpa,struct cpu_user_regs *regs);#define hvm_msr_tsc_aux (v) ({ \ struct domain *__d = (v)->domain; \
xen/include/asm-x86/p2m.h +29 -0
@@ -171,6 +171,7 @@ struct p2m_domain {pagetable_t phys_table;struct domain *domain;/* back pointer to domain */ +uint64_t cr3;/* to identify this p2m for re-use */ /* Pages used to construct the p2m */ struct page_list_head pages;
@@ -227,8 +228,26 @@ struct p2m_domain {/* get host p2m table */ #define p2m_get_hostp2m (d) ((d)->arch.p2m) +/* Get p2m table (re)usable for specified cr3. +* Automatically destroys and re-initializes a p2m if none found. +* If cr3 == 0 then v->arch.hvm_vcpu.guest_cr[3] is used. +*/ +struct p2m_domain *p2m_get_nestedp2m (struct vcpu *v,uint64_t cr3); + +/* If vcpu is in host mode then behaviour matches p2m_get_hostp2m(). +* If vcpu is in guest mode then behaviour matches p2m_get_nestedp2m(). +*/ +struct p2m_domain *p2m_get_p2m (struct vcpu *v); + +#define p2m_is_nestedp2m (p2m) ((p2m) != p2m_get_hostp2m((p2m->domain))) +#define p2m_get_pagetable (p2m) ((p2m)->phys_table) +/* Flushes specified p2m table */ +void p2m_flush (struct vcpu *v,struct p2m_domain *p2m); +/* Flushes all nested p2m tables */ +void p2m_flush_nestedp2m (struct domain *d); +/* * The P2M lock. This protects all updates to the p2m table. * Updates are expected to be safe against concurrent reads,
@@ -369,6 +388,9 @@ static inline unsigned long mfn_to_gfn(s/* Init the datastructures for later use by the p2m code */ int p2m_init (struct domain *d); +/* PTE flags for various types of p2m entry */ +unsigned long p2m_type_to_flags (p2m_type_t t); +/* Allocate a new p2m table for a domain. * * The alloc_page and free_page functions will be used to get memory to
@@ -379,6 +401,13 @@ int p2m_alloc_table(struct p2m_domain *pstruct page_info * (*alloc_page)(struct p2m_domain *p2m), void (*free_page)(struct p2m_domain *p2m, struct page_info *pg)); +/* Find the next level's P2M entry, checking for out-of-range gfn's... +* Returns NULL on error. +*/ +l1_pgentry_t * +p2m_find_entry (void *table,unsigned long *gfn_remainder, +unsigned long gfn,uint32_t shift,uint32_t max); +/* Return all the p2m resources to Xen. */ void p2m_teardown (struct p2m_domain *p2m);void p2m_final_teardown (struct domain *d);
xen/include/asm-x86/paging.h +42 -12
127 lines not on this page · raw message
_______________________________________________
Xen-devel mailing list
[email protected]
http://lists.xensource.com/xen-devel