KVM: Correctly handle writes crossing a page boundary
authorAvi Kivity <avi@qumranet.com>
Sun, 22 Jul 2007 15:48:54 +0000 (18:48 +0300)
committerAvi Kivity <avi@qumranet.com>
Wed, 25 Jul 2007 11:29:17 +0000 (14:29 +0300)
Writes that are contiguous in virtual memory may not be contiguous in
physical memory; so split writes that straddle a page boundary.

Thanks to Aurelien for reporting the bug, patient testing, and a fix
to this very patch.

Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
Signed-off-by: Avi Kivity <avi@qumranet.com>
drivers/kvm/kvm_main.c

index bcbe683..a0a3fdd 100644 (file)
@@ -1078,10 +1078,10 @@ static int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa,
        return 1;
 }
 
-static int emulator_write_emulated(unsigned long addr,
-                                  const void *val,
-                                  unsigned int bytes,
-                                  struct x86_emulate_ctxt *ctxt)
+static int emulator_write_emulated_onepage(unsigned long addr,
+                                          const void *val,
+                                          unsigned int bytes,
+                                          struct x86_emulate_ctxt *ctxt)
 {
        struct kvm_vcpu      *vcpu = ctxt->vcpu;
        struct kvm_io_device *mmio_dev;
@@ -1113,6 +1113,26 @@ static int emulator_write_emulated(unsigned long addr,
        return X86EMUL_CONTINUE;
 }
 
+static int emulator_write_emulated(unsigned long addr,
+                                  const void *val,
+                                  unsigned int bytes,
+                                  struct x86_emulate_ctxt *ctxt)
+{
+       /* Crossing a page boundary? */
+       if (((addr + bytes - 1) ^ addr) & PAGE_MASK) {
+               int rc, now;
+
+               now = -addr & ~PAGE_MASK;
+               rc = emulator_write_emulated_onepage(addr, val, now, ctxt);
+               if (rc != X86EMUL_CONTINUE)
+                       return rc;
+               addr += now;
+               val += now;
+               bytes -= now;
+       }
+       return emulator_write_emulated_onepage(addr, val, bytes, ctxt);
+}
+
 static int emulator_cmpxchg_emulated(unsigned long addr,
                                     const void *old,
                                     const void *new,