memory hotadd 5/7: Sync changes to mapping changes caused by memory
authorKeir Fraser <keir.fraser@citrix.com>
Fri, 11 Dec 2009 08:56:50 +0000 (08:56 +0000)
committerKeir Fraser <keir.fraser@citrix.com>
Fri, 11 Dec 2009 08:56:50 +0000 (08:56 +0000)
hotplug in page fault handler.

In compact guest situation, the compat m2p table is copied, not
directly mapped in L3, so we have to sync it.  Direct mapping range
may changes, and we need sync it with guest's table.

Signed-off-by: Jiang, Yunhong <yunhong.jiang@intel.com>
xen/arch/x86/traps.c
xen/arch/x86/x86_64/mm.c
xen/include/asm-x86/mm.h

index 69877207256759e4ea6377711b2d28aa9391ed17..946b3403404601dbd2ecd0dfa8a04ff32bc277bc 100644 (file)
@@ -1230,6 +1230,10 @@ static int fixup_page_fault(unsigned long addr, struct cpu_user_regs *regs)
         return ret;
     }
 
+    if ( !(regs->error_code & PFEC_page_present) &&
+          (pagefault_by_memadd(addr, regs)) )
+        return handle_memadd_fault(addr, regs);
+
     if ( unlikely(IN_HYPERVISOR_RANGE(addr)) )
     {
         if ( !(regs->error_code & PFEC_reserved_bit) &&
index 89ba4cbd8dbf09063c9fb373792ef394005353d5..085d3b205b4ad7541cb4572bbb20b5c343b63c84 100644 (file)
@@ -1182,6 +1182,87 @@ int check_descriptor(const struct domain *dom, struct desc_struct *d)
     return 0;
 }
 
+int pagefault_by_memadd(unsigned long addr, struct cpu_user_regs *regs)
+{
+    struct domain *d = current->domain;
+
+    if (guest_mode(regs) &&
+        is_pv_32bit_domain(d) &&
+        ((addr >= HYPERVISOR_COMPAT_VIRT_START(d)) &&
+             (addr < MACH2PHYS_COMPAT_VIRT_END)) )
+            return 1;
+    return 0;
+}
+
+int handle_memadd_fault(unsigned long addr, struct cpu_user_regs *regs)
+{
+    struct domain *d = current->domain;
+    l4_pgentry_t *pl4e = NULL;
+    l4_pgentry_t l4e;
+    l3_pgentry_t  *pl3e = NULL;
+    l3_pgentry_t l3e;
+    l2_pgentry_t *pl2e = NULL;
+    l2_pgentry_t l2e, idle_l2e;
+    unsigned long mfn, idle_index;
+    int ret = 0;
+
+    if (!is_pv_32on64_domain(d))
+        return 0;
+
+    if ((addr < HYPERVISOR_COMPAT_VIRT_START(d)) ||
+             (addr > MACH2PHYS_COMPAT_VIRT_END) )
+        return 0;
+
+    mfn = (read_cr3()) >> PAGE_SHIFT;
+
+    pl4e = map_domain_page(mfn);
+
+    l4e = pl4e[addr];
+
+    if (!(l4e_get_flags(l4e) & _PAGE_PRESENT))
+        goto unmap;
+
+    mfn = l4e_get_pfn(l4e);
+    /* We don't need get page type here since it is current CR3 */
+    pl3e = map_domain_page(mfn);
+
+    l3e = pl3e[3];
+
+    if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) )
+        goto unmap;
+
+    mfn = l3e_get_pfn(l3e);
+    pl2e = map_domain_page(mfn);
+
+    l2e = pl2e[l2_table_offset(addr)];
+
+    if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT))
+        goto unmap;
+
+    idle_index = (l2_table_offset(addr) -
+                        COMPAT_L2_PAGETABLE_FIRST_XEN_SLOT(d))/
+                  sizeof(l2_pgentry_t);
+    idle_l2e = compat_idle_pg_table_l2[idle_index];
+    if (!(l2e_get_flags(idle_l2e) & _PAGE_PRESENT))
+        goto unmap;
+
+    memcpy(&pl2e[l2_table_offset(addr)],
+            &compat_idle_pg_table_l2[idle_index],
+            sizeof(l2_pgentry_t));
+
+    ret = EXCRET_fault_fixed;
+
+unmap:
+    if ( pl4e )
+        unmap_domain_page(pl4e);
+    if ( pl3e )
+        unmap_domain_page(pl3e);
+    if ( pl2e )
+        unmap_domain_page(pl2e);
+
+    return ret;
+}
+
 void domain_set_alloc_bitsize(struct domain *d)
 {
     if ( !is_pv_32on64_domain(d) ||
index e2134d4f0729e910c2a4f36c1bf5a49f14eaf1ce..b259c486e5761d54ed55c5b4e177f21995e4406a 100644 (file)
@@ -476,6 +476,21 @@ int  ptwr_do_page_fault(struct vcpu *, unsigned long,
 
 int audit_adjust_pgtables(struct domain *d, int dir, int noisy);
 
+#ifdef CONFIG_X86_64
+extern int pagefault_by_memadd(unsigned long addr, struct cpu_user_regs *regs);
+extern int handle_memadd_fault(unsigned long addr, struct cpu_user_regs *regs);
+#else
+int pagefault_by_memadd(unsigned long addr, struct cpu_user_regs *regs)
+{
+    return 0;
+}
+
+int handle_memadd_fault(unsigned long addr, struct cpu_user_regs *regs)
+{
+    return 0;
+}
+#endif
+
 #ifndef NDEBUG
 
 #define AUDIT_SHADOW_ALREADY_LOCKED ( 1u << 0 )