qom/cpu: Add copy method

Add a copy method to the QOM CPUClass, which creates a
new CPU object as a copy of an existing one. This replaces
the direct call to cpu_copy() made in linux-user when
handling creating a new pthread; the motivation is to
allow target-specific CPU subclasses to override the
copy method if there is something in their CPUState which
cannot be handled with a shallow copy.

In particular we will use this to avoid leaking the
hashtable used for ARM's new cp15 implementation.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/cpu-all.h b/cpu-all.h
index 028528f..1b39fc9 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -332,7 +332,6 @@
 int page_check_range(target_ulong start, target_ulong len, int flags);
 #endif
 
-CPUArchState *cpu_copy(CPUArchState *env);
 CPUArchState *qemu_get_cpu(int cpu);
 
 #define CPU_DUMP_CODE 0x00010000
diff --git a/exec.c b/exec.c
index 0607c9b..f567a38 100644
--- a/exec.c
+++ b/exec.c
@@ -1876,8 +1876,13 @@
     abort();
 }
 
-CPUArchState *cpu_copy(CPUArchState *env)
+/* Note that this function is suitable for use as the CPUClass copy
+ * callback if your CPUArchState has no members which are unsuitable
+ * for simple shallow copy via memcpy().
+ */
+CPUState *cpu_default_copy(CPUState *oldcpu)
 {
+    CPUArchState *env = CPU_GET_ENV(oldcpu);
     CPUArchState *new_env = cpu_init(env->cpu_model_str);
     CPUArchState *next_cpu = new_env->next_cpu;
     int cpu_index = new_env->cpu_index;
@@ -1907,7 +1912,7 @@
     }
 #endif
 
-    return new_env;
+    return ENV_GET_CPU(new_env);
 }
 
 #if !defined(CONFIG_USER_ONLY)
diff --git a/include/qemu/cpu.h b/include/qemu/cpu.h
index 78b65b3..e24d116 100644
--- a/include/qemu/cpu.h
+++ b/include/qemu/cpu.h
@@ -40,6 +40,7 @@
 /**
  * CPUClass:
  * @reset: Callback to reset the #CPUState to its initial state.
+ * @copy: Callback to create a new #CPUState as a copy of this one.
  *
  * Represents a CPU family or model.
  */
@@ -49,6 +50,7 @@
     /*< public >*/
 
     void (*reset)(CPUState *cpu);
+    CPUState *(*copy)(CPUState *cpu);
 } CPUClass;
 
 /**
@@ -71,5 +73,19 @@
  */
 void cpu_reset(CPUState *cpu);
 
+/**
+ * cpu_copy:
+ * @cpu: The CPU whose state is to be copied to create a new CPU state.
+ */
+CPUState *cpu_copy(CPUState *cpu);
+
+/**
+ * cpu_default_copy:
+ * An implementation of the cpu_copy method suitable for
+ * use if the CPUClass subclass:
+ *  (a) provides CPU_GET_ENV/ENV_GET_CPU macros in its cpu.h
+ *  (b) has only members which can be safely shallow copied.
+ */
+CPUState *cpu_default_copy(CPUState *oldcpu);
 
 #endif
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 20d2a74..bdae327 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -4260,7 +4260,7 @@
         ts = g_malloc0(sizeof(TaskState));
         init_task_state(ts);
         /* we create a new CPU instance. */
-        new_env = cpu_copy(env);
+        new_env = CPU_GET_ENV(cpu_copy(ENV_GET_CPU(env)));
 #if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC)
         cpu_state_reset(new_env);
 #endif
diff --git a/qom/cpu.c b/qom/cpu.c
index 5b36046..5d8a477 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -30,6 +30,12 @@
     }
 }
 
+CPUState *cpu_copy(CPUState *cpu)
+{
+    CPUClass *klass = CPU_GET_CLASS(cpu);
+    return (*klass->copy)(cpu);
+}
+
 static void cpu_common_reset(CPUState *cpu)
 {
 }
@@ -39,6 +45,7 @@
     CPUClass *k = CPU_CLASS(klass);
 
     k->reset = cpu_common_reset;
+    k->copy = cpu_default_copy;
 }
 
 static TypeInfo cpu_type_info = {
diff --git a/target-alpha/cpu-qom.h b/target-alpha/cpu-qom.h
index 6b4ca6d..5de9a48 100644
--- a/target-alpha/cpu-qom.h
+++ b/target-alpha/cpu-qom.h
@@ -66,6 +66,6 @@
 }
 
 #define ENV_GET_CPU(e) CPU(alpha_env_get_cpu(e))
-
+#define CPU_GET_ENV(c) (&ALPHA_CPU(c)->env)
 
 #endif
diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
index a61c68d..d9cf6d1 100644
--- a/target-arm/cpu-qom.h
+++ b/target-arm/cpu-qom.h
@@ -102,6 +102,7 @@
 }
 
 #define ENV_GET_CPU(e) CPU(arm_env_get_cpu(e))
+#define CPU_GET_ENV(c) (&ARM_CPU(c)->env)
 
 void arm_cpu_realize(ARMCPU *cpu);
 
diff --git a/target-cris/cpu-qom.h b/target-cris/cpu-qom.h
index d0e5f04..a5ee514 100644
--- a/target-cris/cpu-qom.h
+++ b/target-cris/cpu-qom.h
@@ -65,6 +65,7 @@
 }
 
 #define ENV_GET_CPU(e) CPU(cris_env_get_cpu(e))
+#define CPU_GET_ENV(c) (&CRIS_CPU(c)->env)
 
 
 #endif
diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h
index 40635c4..c223d54 100644
--- a/target-i386/cpu-qom.h
+++ b/target-i386/cpu-qom.h
@@ -70,6 +70,7 @@
 }
 
 #define ENV_GET_CPU(e) CPU(x86_env_get_cpu(e))
+#define CPU_GET_ENV(c) (&X86_CPU(c)->env)
 
 
 #endif
diff --git a/target-lm32/cpu-qom.h b/target-lm32/cpu-qom.h
index 4ae2edd..7a6f8e8 100644
--- a/target-lm32/cpu-qom.h
+++ b/target-lm32/cpu-qom.h
@@ -66,6 +66,7 @@
 }
 
 #define ENV_GET_CPU(e) CPU(lm32_env_get_cpu(e))
+#define CPU_GET_ENV(c) (&LM32_CPU(c)->env)
 
 
 #endif
diff --git a/target-m68k/cpu-qom.h b/target-m68k/cpu-qom.h
index 805786b..d007cfe 100644
--- a/target-m68k/cpu-qom.h
+++ b/target-m68k/cpu-qom.h
@@ -65,6 +65,7 @@
 }
 
 #define ENV_GET_CPU(e) CPU(m68k_env_get_cpu(e))
+#define CPU_GET_ENV(c) (&M68K_CPU(c)->env)
 
 
 #endif
diff --git a/target-microblaze/cpu-qom.h b/target-microblaze/cpu-qom.h
index 4b23303..c4a5f04 100644
--- a/target-microblaze/cpu-qom.h
+++ b/target-microblaze/cpu-qom.h
@@ -65,6 +65,7 @@
 }
 
 #define ENV_GET_CPU(e) CPU(mb_env_get_cpu(e))
+#define CPU_GET_ENV(c) (&MICROBLAZE_CPU(c)->env)
 
 
 #endif
diff --git a/target-mips/cpu-qom.h b/target-mips/cpu-qom.h
index 6e22371..796c4e5 100644
--- a/target-mips/cpu-qom.h
+++ b/target-mips/cpu-qom.h
@@ -69,6 +69,7 @@
 }
 
 #define ENV_GET_CPU(e) CPU(mips_env_get_cpu(e))
+#define CPU_GET_ENV(c) (&MIPS_CPU(c)->env)
 
 
 #endif
diff --git a/target-ppc/cpu-qom.h b/target-ppc/cpu-qom.h
index fef6f95..e2c4450 100644
--- a/target-ppc/cpu-qom.h
+++ b/target-ppc/cpu-qom.h
@@ -72,6 +72,7 @@
 }
 
 #define ENV_GET_CPU(e) CPU(ppc_env_get_cpu(e))
+#define CPU_GET_ENV(c) (&POWERPC_CPU(c)->env)
 
 
 #endif
diff --git a/target-s390x/cpu-qom.h b/target-s390x/cpu-qom.h
index 6fa55a8..d5779c8 100644
--- a/target-s390x/cpu-qom.h
+++ b/target-s390x/cpu-qom.h
@@ -66,6 +66,7 @@
 }
 
 #define ENV_GET_CPU(e) CPU(s390_env_get_cpu(e))
+#define CPU_GET_ENV(c) (&S390_CPU(c)->env)
 
 
 #endif
diff --git a/target-sh4/cpu-qom.h b/target-sh4/cpu-qom.h
index c41164a..6a9b57e 100644
--- a/target-sh4/cpu-qom.h
+++ b/target-sh4/cpu-qom.h
@@ -65,6 +65,7 @@
 }
 
 #define ENV_GET_CPU(e) CPU(sh_env_get_cpu(e))
+#define CPU_GET_ENV(c) (&SUPERH_CPU(c)->env)
 
 
 #endif
diff --git a/target-sparc/cpu-qom.h b/target-sparc/cpu-qom.h
index 3d3ac0f..2362ece 100644
--- a/target-sparc/cpu-qom.h
+++ b/target-sparc/cpu-qom.h
@@ -70,6 +70,7 @@
 }
 
 #define ENV_GET_CPU(e) CPU(sparc_env_get_cpu(e))
+#define CPU_GET_ENV(c) (&SPARC_CPU(c)->env)
 
 
 #endif
diff --git a/target-unicore32/cpu-qom.h b/target-unicore32/cpu-qom.h
index 342d85e..3e653d7 100644
--- a/target-unicore32/cpu-qom.h
+++ b/target-unicore32/cpu-qom.h
@@ -54,6 +54,7 @@
 }
 
 #define ENV_GET_CPU(e) CPU(uc32_env_get_cpu(e))
+#define CPU_GET_ENV(c) (&UNICORE32_CPU(c)->env)
 
 
 #endif
diff --git a/target-xtensa/cpu-qom.h b/target-xtensa/cpu-qom.h
index 1fd2f27..826483e 100644
--- a/target-xtensa/cpu-qom.h
+++ b/target-xtensa/cpu-qom.h
@@ -75,6 +75,7 @@
 }
 
 #define ENV_GET_CPU(e) CPU(xtensa_env_get_cpu(e))
+#define CPU_GET_ENV(c) (&XTENSA_CPU(c)->env)
 
 
 #endif