Merge "x86-kvm: only sync SREGS when doing address translation"
diff --git a/Makefile.target b/Makefile.target
index 5785b1c..aff7736 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -76,6 +76,7 @@
     goldfish_nand.c \
     goldfish_pipe.c \
     goldfish_tty.c \
+    goldfish_vmem.c \
     msmouse.c \
     pci.c \
     qdev.c \
diff --git a/hw/goldfish_device.c b/hw/goldfish_device.c
index e3dbfcb..8c9469e 100644
--- a/hw/goldfish_device.c
+++ b/hw/goldfish_device.c
@@ -12,10 +12,8 @@
 #include "qemu_file.h"
 #include "arm_pic.h"
 #include "goldfish_device.h"
+#include "goldfish_vmem.h"
 #include "android/utils/debug.h"
-#ifdef TARGET_I386
-#include "kvm.h"
-#endif
 
 #define PDEV_BUS_OP_DONE        (0x00)
 #define PDEV_BUS_OP_REMOVE_DEV  (0x04)
@@ -165,11 +163,7 @@
             break;
         case PDEV_BUS_GET_NAME:
             if(s->current) {
-#ifdef TARGET_I386
-                if(kvm_enabled())
-                    cpu_synchronize_state(cpu_single_env, 0);
-#endif
-                cpu_memory_rw_debug(cpu_single_env, value, (void*)s->current->name, strlen(s->current->name), 1);
+                safe_memory_rw_debug(cpu_single_env, value, (void*)s->current->name, strlen(s->current->name), 1);
             }
             break;
         default:
diff --git a/hw/goldfish_nand.c b/hw/goldfish_nand.c
index 55f77f6..9e40e2e 100644
--- a/hw/goldfish_nand.c
+++ b/hw/goldfish_nand.c
@@ -12,14 +12,11 @@
 #include "qemu_file.h"
 #include "goldfish_nand_reg.h"
 #include "goldfish_nand.h"
+#include "goldfish_vmem.h"
 #include "android/utils/tempfile.h"
 #include "qemu_debug.h"
 #include "android/android.h"
 
-#ifdef TARGET_I386
-#include "kvm.h"
-#endif
-
 #define  DEBUG  1
 #if DEBUG
 #  define  D(...)    VERBOSE_PRINT(init,__VA_ARGS__)
@@ -382,11 +379,7 @@
         if(!eof) {
             read_len = do_read(dev->fd, dev->data, read_len);
         }
-#ifdef TARGET_I386
-        if (kvm_enabled())
-            cpu_synchronize_state(cpu_single_env, 0);
-#endif
-        cpu_memory_rw_debug(cpu_single_env, data, dev->data, read_len, 1);
+        safe_memory_rw_debug(cpu_single_env, data, dev->data, read_len, 1);
         data += read_len;
         len -= read_len;
     }
@@ -405,11 +398,7 @@
     while(len > 0) {
         if(len < write_len)
             write_len = len;
-#ifdef TARGET_I386
-        if (kvm_enabled())
-                cpu_synchronize_state(cpu_single_env, 0);
-#endif
-        cpu_memory_rw_debug(cpu_single_env, data, dev->data, write_len, 0);
+        safe_memory_rw_debug(cpu_single_env, data, dev->data, write_len, 0);
         ret = do_write(dev->fd, dev->data, write_len);
         if(ret < write_len) {
             XLOG("nand_dev_write_file, write failed: %s\n", strerror(errno));
@@ -483,11 +472,7 @@
     case NAND_CMD_GET_DEV_NAME:
         if(size > dev->devname_len)
             size = dev->devname_len;
-#ifdef TARGET_I386
-        if (kvm_enabled())
-                cpu_synchronize_state(cpu_single_env, 0);
-#endif
-        cpu_memory_rw_debug(cpu_single_env, s->data, (uint8_t*)dev->devname, size, 1);
+        safe_memory_rw_debug(cpu_single_env, s->data, (uint8_t*)dev->devname, size, 1);
         return size;
     case NAND_CMD_READ_BATCH:
     case NAND_CMD_READ:
@@ -497,11 +482,7 @@
             size = dev->max_size - addr;
         if(dev->fd >= 0)
             return nand_dev_read_file(dev, s->data, addr, size);
-#ifdef TARGET_I386
-        if (kvm_enabled())
-                cpu_synchronize_state(cpu_single_env, 0);
-#endif
-        cpu_memory_rw_debug(cpu_single_env,s->data, &dev->data[addr], size, 1);
+        safe_memory_rw_debug(cpu_single_env,s->data, &dev->data[addr], size, 1);
         return size;
     case NAND_CMD_WRITE_BATCH:
     case NAND_CMD_WRITE:
@@ -513,11 +494,7 @@
             size = dev->max_size - addr;
         if(dev->fd >= 0)
             return nand_dev_write_file(dev, s->data, addr, size);
-#ifdef TARGET_I386
-        if (kvm_enabled())
-                cpu_synchronize_state(cpu_single_env, 0);
-#endif
-        cpu_memory_rw_debug(cpu_single_env,s->data, &dev->data[addr], size, 0);
+        safe_memory_rw_debug(cpu_single_env,s->data, &dev->data[addr], size, 0);
         return size;
     case NAND_CMD_ERASE_BATCH:
     case NAND_CMD_ERASE:
diff --git a/hw/goldfish_pipe.c b/hw/goldfish_pipe.c
index 276ac9a..9925538 100644
--- a/hw/goldfish_pipe.c
+++ b/hw/goldfish_pipe.c
@@ -13,10 +13,8 @@
 #include "android/utils/system.h"
 #include "hw/goldfish_pipe.h"
 #include "hw/goldfish_device.h"
+#include "hw/goldfish_vmem.h"
 #include "qemu-timer.h"
-#ifdef CONFIG_KVM
-#include "kvm.h"
-#endif
 
 #define  DEBUG 0
 
@@ -1015,12 +1013,7 @@
         uint32_t            address = dev->address;
         uint32_t            page    = address & TARGET_PAGE_MASK;
         target_phys_addr_t  phys;
-#ifdef CONFIG_KVM
-        if(kvm_enabled()) {
-            cpu_synchronize_state(env, 0);
-        }
-#endif
-        phys = cpu_get_phys_page_debug(env, page);
+        phys = safe_get_phys_page_debug(env, page);
         buffer.data = qemu_get_ram_ptr(phys) + (address - page);
         buffer.size = dev->size;
         dev->status = pipe->funcs->recvBuffers(pipe->opaque, &buffer, 1);
@@ -1035,12 +1028,7 @@
         uint32_t            address = dev->address;
         uint32_t            page    = address & TARGET_PAGE_MASK;
         target_phys_addr_t  phys;
-#ifdef CONFIG_KVM
-        if(kvm_enabled()) {
-            cpu_synchronize_state(env, 0);
-        }
-#endif
-        phys = cpu_get_phys_page_debug(env, page);
+        phys = safe_get_phys_page_debug(env, page);
         buffer.data = qemu_get_ram_ptr(phys) + (address - page);
         buffer.size = dev->size;
         dev->status = pipe->funcs->sendBuffers(pipe->opaque, &buffer, 1);
diff --git a/hw/goldfish_switch.c b/hw/goldfish_switch.c
index 99a9379..a244cbc 100644
--- a/hw/goldfish_switch.c
+++ b/hw/goldfish_switch.c
@@ -11,6 +11,7 @@
 */
 #include "qemu_file.h"
 #include "goldfish_device.h"
+#include "goldfish_vmem.h"
 
 enum {
     SW_NAME_LEN     = 0x00,
@@ -93,7 +94,7 @@
 
     switch(offset) {
         case SW_NAME_PTR:
-            cpu_memory_rw_debug(cpu_single_env, value, (void*)s->name, strlen(s->name), 1);
+            safe_memory_rw_debug(cpu_single_env, value, (void*)s->name, strlen(s->name), 1);
             break;
 
         case SW_STATE:
diff --git a/hw/goldfish_trace.c b/hw/goldfish_trace.c
index 9eeb2a4..b384cd9 100644
--- a/hw/goldfish_trace.c
+++ b/hw/goldfish_trace.c
@@ -15,6 +15,7 @@
  */
 #include "qemu_file.h"
 #include "goldfish_trace.h"
+#include "goldfish_vmem.h"
 #include "sysemu.h"
 #include "android-trace.h"
 #ifdef CONFIG_MEMCHECK
@@ -145,7 +146,7 @@
         cmdlen = value;
         break;
     case TRACE_DEV_REG_CMDLINE:         // execve, process cmdline
-        cpu_memory_rw_debug(cpu_single_env, value, (uint8_t*)exec_arg, cmdlen, 0);
+        safe_memory_rw_debug(cpu_single_env, value, (uint8_t*)exec_arg, cmdlen, 0);
         if (trace_filename != NULL) {
             trace_execve(exec_arg, cmdlen);
         }
diff --git a/hw/goldfish_tty.c b/hw/goldfish_tty.c
index fd8eca8..d989900 100644
--- a/hw/goldfish_tty.c
+++ b/hw/goldfish_tty.c
@@ -12,10 +12,7 @@
 #include "qemu_file.h"
 #include "qemu-char.h"
 #include "goldfish_device.h"
-
-#ifdef TARGET_I386
-#include "kvm.h"
-#endif
+#include "goldfish_vmem.h"
 
 enum {
     TTY_PUT_CHAR       = 0x00,
@@ -130,11 +127,7 @@
                             if (to_write > len)
                                 to_write = len;
 
-#ifdef TARGET_I386
-                            if (kvm_enabled())
-                                cpu_synchronize_state(cpu_single_env, 0);
-#endif
-                            cpu_memory_rw_debug(cpu_single_env, buf, (uint8_t*)temp, to_write, 0);
+                            safe_memory_rw_debug(cpu_single_env, buf, (uint8_t*)temp, to_write, 0);
                             qemu_chr_write(s->cs, (const uint8_t*)temp, to_write);
                             buf += to_write;
                             len -= to_write;
@@ -146,11 +139,7 @@
                 case TTY_CMD_READ_BUFFER:
                     if(s->ptr_len > s->data_count)
                         cpu_abort (cpu_single_env, "goldfish_tty_write: reading more data than available %d %d\n", s->ptr_len, s->data_count);
-#ifdef TARGET_I386
-                    if (kvm_enabled())
-                        cpu_synchronize_state(cpu_single_env, 0);
-#endif
-                    cpu_memory_rw_debug(cpu_single_env,s->ptr, s->data, s->ptr_len,1);
+                    safe_memory_rw_debug(cpu_single_env,s->ptr, s->data, s->ptr_len,1);
                     //printf("goldfish_tty_write: read %d bytes to %x\n", s->ptr_len, s->ptr);
                     if(s->data_count > s->ptr_len)
                         memmove(s->data, s->data + s->ptr_len, s->data_count - s->ptr_len);
diff --git a/hw/goldfish_vmem.c b/hw/goldfish_vmem.c
new file mode 100644
index 0000000..fa88365
--- /dev/null
+++ b/hw/goldfish_vmem.c
@@ -0,0 +1,49 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "hw.h"
+#include "goldfish_vmem.h"
+#ifdef TARGET_I386
+#include "kvm.h"
+#endif
+
+
+// Both safe_memory_rw_debug and safe_get_phys_page_debug need to translate
+// virtual addresses to physical addresses. When running on KVM we need to
+// pull the cr registers and hflags from the VCPU. These functions wrap the
+// calls to kvm_get_sregs to pull these registers over when necessary.
+//
+// Note: we do not call the cpu_synchronize_state function because that pulls
+// all the VCPU registers. That equates to 4 ioctls on the KVM virtual device
+// and on AMD some of those ioctls (in particular KVM_GET_MSRS) are 10 to 100x
+// slower than on Intel chips.
+
+int safe_memory_rw_debug(CPUState *env, target_ulong addr, uint8_t *buf,
+                         int len, int is_write)
+{
+#ifdef TARGET_I386
+    if (kvm_enabled()) {
+        kvm_get_sregs(env);
+    }
+#endif
+    return cpu_memory_rw_debug(env, addr, buf, len, is_write);
+}
+
+target_phys_addr_t safe_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+#ifdef TARGET_I386
+    if (kvm_enabled()) {
+        kvm_get_sregs(env);
+    }
+#endif
+    return cpu_get_phys_page_debug(env, addr);
+}
+
diff --git a/hw/goldfish_vmem.h b/hw/goldfish_vmem.h
new file mode 100644
index 0000000..1e55246
--- /dev/null
+++ b/hw/goldfish_vmem.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef GOLDFISH_VMEM_H
+#define GOLDFISH_VMEM_H
+
+// Call these functions instead of cpu_memory_rw_debug and
+// cpu_get_phys_page_debug to ensure virtual address translation always works
+// properly, and efficently, under KVM.
+
+int safe_memory_rw_debug(CPUState *env, target_ulong addr, uint8_t *buf,
+                         int len, int is_write);
+
+target_phys_addr_t safe_get_phys_page_debug(CPUState *env, target_ulong addr);
+
+
+#endif  /* GOLDFISH_VMEM_H */
diff --git a/kvm.h b/kvm.h
index 2ef89c7..5e95655 100644
--- a/kvm.h
+++ b/kvm.h
@@ -152,4 +152,7 @@
 #endif
 }
 
+int kvm_get_sregs(CPUState *env);
+
+
 #endif
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 07efac0..00e01fe 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -493,7 +493,7 @@
     return 0;
 }
 
-static int kvm_get_sregs(CPUState *env)
+int kvm_get_sregs(CPUState *env)
 {
     struct kvm_sregs sregs;
     uint32_t hflags;