Skip to contentSkip to conversation

Nested Virtualization: hap-on-hap

PATCH In review Christoph Egger 1 participant 1 message Jun 3, 2010

lore
Share & tools
lore
#0 lore raw

PATCH 15/15 Nested Virtualization: hap-on-hap

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
diff -r b0fb7e2d339e -r 547efe297601 xen/arch/x86/hvm/hvm.c
@@ -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
diff -r b0fb7e2d339e -r 547efe297601 xen/arch/x86/hvm/nestedhvm.c
@@ -671,6 +671,7 @@ nestedhvm_vcpu_vmexit(struct vcpu *v, st
 	enum 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
diff -r b0fb7e2d339e -r 547efe297601 xen/arch/x86/hvm/svm/svm.c
@@ -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 start
     return &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(struc
     case 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
diff -r b0fb7e2d339e -r 547efe297601 xen/arch/x86/hvm/vmx/vmx.c
@@ -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
diff -r b0fb7e2d339e -r 547efe297601 xen/arch/x86/mm/hap/Makefile
@@ -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
diff -r b0fb7e2d339e -r 547efe297601 xen/arch/x86/mm/hap/guest_walk.c
@@ -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
diff -r b0fb7e2d339e -r 547efe297601 xen/arch/x86/mm/hap/private.h
@@ -30,4 +30,14 @@ unsigned long hap_gva_to_gfn_3_levels(st
 unsigned 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
diff -r b0fb7e2d339e -r 547efe297601 xen/include/asm-x86/domain.h
@@ -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 40
 typedef xen_domctl_cpuid_t cpuid_input_t;
 
+#define MAX_NESTEDP2M 10
 struct p2m_domain;
 struct time_scale {
     int shift;
@@ -247,6 +250,10 @@ struct arch_domain
     struct 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
diff -r b0fb7e2d339e -r 547efe297601 xen/include/asm-x86/hvm/hvm.h
@@ -354,7 +354,7 @@ static inline void hvm_set_info_guest(st
 
 int 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
diff -r b0fb7e2d339e -r 547efe297601 xen/include/asm-x86/p2m.h
@@ -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 *p
                struct 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