diff mbox

[qom-next,57/59] cpu: Introduce mandatory tlb_flush callback

Message ID 1337742502-28565-58-git-send-email-afaerber@suse.de
State New
Headers show

Commit Message

Andreas Färber May 23, 2012, 3:08 a.m. UTC
Signed-off-by: Andreas Färber <afaerber@suse.de>
---
 include/qemu/cpu.h          |   12 ++++++++++++
 qom/cpu.c                   |    9 +++++++++
 target-alpha/cpu.c          |   16 ++++++++++++++++
 target-arm/cpu.c            |   10 ++++++++++
 target-cris/cpu.c           |   10 ++++++++++
 target-i386/cpu.c           |   10 ++++++++++
 target-lm32/cpu.c           |   10 ++++++++++
 target-m68k/cpu.c           |   10 ++++++++++
 target-microblaze/cpu.c     |   10 ++++++++++
 target-mips/cpu.c           |   10 ++++++++++
 target-ppc/translate_init.c |   10 ++++++++++
 target-s390x/cpu.c          |   10 ++++++++++
 target-sh4/cpu.c            |   10 ++++++++++
 target-sparc/cpu.c          |   10 ++++++++++
 target-unicore32/cpu.c      |   16 ++++++++++++++++
 target-xtensa/cpu.c         |   10 ++++++++++
 16 files changed, 173 insertions(+), 0 deletions(-)

Comments

Alexander Graf May 30, 2012, 7:53 a.m. UTC | #1
Mind to elaborate (in the patch description maybe) why we need a CPU specific TLB flush callback that merely calls the globally visible tlb_flush? :)

Alex

On 23.05.2012, at 05:08, Andreas Färber wrote:

> Signed-off-by: Andreas Färber <afaerber@suse.de>
陳韋任 May 30, 2012, 8:48 a.m. UTC | #2
On Wed, May 30, 2012 at 09:53:20AM +0200, Alexander Graf wrote:
> Mind to elaborate (in the patch description maybe) why we need a CPU specific TLB flush callback that merely calls the globally visible tlb_flush? :)

  For future extension? ;)

Regards,
chenwj
Alexander Graf May 30, 2012, 9:08 a.m. UTC | #3
On 30.05.2012, at 10:48, 陳韋任 (Wei-Ren Chen) wrote:

> On Wed, May 30, 2012 at 09:53:20AM +0200, Alexander Graf wrote:
>> Mind to elaborate (in the patch description maybe) why we need a CPU specific TLB flush callback that merely calls the globally visible tlb_flush? :)
> 
>  For future extension? ;)

Oh, I can guess too :). Just want to see it in the patch description.


Alex
Peter Maydell May 30, 2012, 9:28 a.m. UTC | #4
On 23 May 2012 04:08, Andreas Färber <afaerber@suse.de> wrote:
> +void cpu_tlb_flush(CPUState *cpu, bool flush_global)
> +{
> +    CPUClass *cc = CPU_GET_CLASS(cpu);
> +
> +    g_assert(cc->tlb_flush != NULL);
> +
> +    cc->tlb_flush(cpu, flush_global);
> +}

This needs to be able to call tlb_flush() itself
rather than having to have every single subclass of CPUState
implement an identical tlb_flush method. You could do this
if there was a CPU_GET_ENV()...

> 16 files changed, 173 insertions(+), 0 deletions(-)

...which is a lot of extra code to be inserting to do nothing
that's specific to a particular target.

-- PMM
Andreas Färber May 30, 2012, 9:58 a.m. UTC | #5
Am 30.05.2012 11:28, schrieb Peter Maydell:
> On 23 May 2012 04:08, Andreas Färber <afaerber@suse.de> wrote:
>> +void cpu_tlb_flush(CPUState *cpu, bool flush_global)
>> +{
>> +    CPUClass *cc = CPU_GET_CLASS(cpu);
>> +
>> +    g_assert(cc->tlb_flush != NULL);
>> +
>> +    cc->tlb_flush(cpu, flush_global);
>> +}
> 
> This needs to be able to call tlb_flush() itself
> rather than having to have every single subclass of CPUState
> implement an identical tlb_flush method. You could do this
> if there was a CPU_GET_ENV()...

Which is exactly the point: CPUState does not know about the
target-specific "env". And CPU_GET_ENV() is just plain wrong
conceptually because it adds yet another cpu.h dependency.

What we could do is have a void *envp field in CPUState that points to
env but then we'd still need the new function as a wrapper.

For the TLB the plan was to split the TLB off from CPUState mid-term,
see the sun4v-softmmu discussion and the one on part 3. The problem
there is that it contains both targer_phys_addr_t and target_ulong, so
some Makefile fiddling will be required to make it work, so I'd like to
defer that. We're already seeing two new softmmu targets on the list
plus my RL78 that I'm still rebasing onto QOM, so I'd like to move
forward with a subset of all possible improvements before I have to
touch and get acks for even more targets irrelevant to SUSE's business.
We can trim patches from the back if need be.

>> 16 files changed, 173 insertions(+), 0 deletions(-)
> 
> ...which is a lot of extra code to be inserting to do nothing
> that's specific to a particular target.

You're forgetting, env is in fact specific to the particular target.

There's a separation between old code using env and new, clean code:
Just like Anthony doesn't want old concepts rewritten with the new type
(cf. object_realize() discussion) I don't want the old cpu.h #define
mess leaking into code that I'm redesigning specifically to get rid of
that target-*/cpu.h dependency in favor of a single qemu/cpu.h.
qom/cpu.c is by definition not compiled per target so it cannot contain
any target-specific code.

Andreas
Andreas Färber May 30, 2012, 4:57 p.m. UTC | #6
Am 30.05.2012 09:53, schrieb Alexander Graf:
> Mind to elaborate (in the patch description maybe) why we need a CPU specific TLB flush callback that merely calls the globally visible tlb_flush? :)

Ouch! That's one patch description I forgot to fill in. :-/

The issue is that env is defined in X86CPU, PowerPCCPU, etc. and its
parent, CPUState, does not know about it. Thus, a function passed
CPUState *cpu cannot call functions expecting CPUArchState *env, here
tlb_flush(), unless it is target-specific (e.g., cpu_has_work()).

I've previously structured my series in such a way that all references
to env were removed first before the argument type was changed, cf. (iv)
in [1]. VMState restricts my artistic freedom there by needing matching
types of which the offsets are calculated and which is passed as opaque
to its callbacks. So the alternative to finding some indirect way to
call tlb_flush() from there would be to postpone patch 59 until the TLB
is QOM'ified. But since Anthony had suggested to make "halted" a QOM
property for controlling the vCPU, I did not want to wait forever. ;)

To make it clear, this patch (or an alternative thereof) is only needed
as band-aid for patch 59. It does not provide any value on its own.

Long-term I was thinking of keeping cpu_tlb_flush() as wrapper that
forwards to a TLBClass::flush() or so, so the function in qom/cpu.c
wouldn't have been a complete waste.

Andreas

[1] https://lists.gnu.org/archive/html/qemu-devel/2012-05/msg01286.html
Paul Brook May 31, 2012, 6:53 p.m. UTC | #7
> >> +void cpu_tlb_flush(CPUState *cpu, bool flush_global)
> >> +{
> >> +    CPUClass *cc = CPU_GET_CLASS(cpu);
> >> +
> >> +    g_assert(cc->tlb_flush != NULL);
> >> +
> >> +    cc->tlb_flush(cpu, flush_global);
> >> +}
> > 
> > This needs to be able to call tlb_flush() itself
> > rather than having to have every single subclass of CPUState
> > implement an identical tlb_flush method. You could do this
> > if there was a CPU_GET_ENV()...
> 
> Which is exactly the point: CPUState does not know about the
> target-specific "env". And CPU_GET_ENV() is just plain wrong
> conceptually because it adds yet another cpu.h dependency.

Maybe so, but having every single taget implement its own copy of the exact 
same target independent wrapper seems even more wrong.

> There's a separation between old code using env and new, clean code:
> Just like Anthony doesn't want old concepts rewritten with the new type
> (cf. object_realize() discussion) I don't want the old cpu.h #define
> mess leaking into code that I'm redesigning specifically to get rid of
> that target-*/cpu.h dependency in favor of a single qemu/cpu.h.
> qom/cpu.c is by definition not compiled per target so it cannot contain
> any target-specific code.

At minimum it should be clearly documented[1] that this is a transitional 
hack, and how it should be removed.  There have already been two posts in this 
thread suggesting this is a feature, implying that this operation is somehow 
target specific.  I think the opposite is true:  This is a target agnostic 
detail of the TCG implementation, and implementing architecturally defined 
MMU/TLB behavior here is activley wrong.

Paul

[1] In the code, not the commit message.  Commit logs are not documentation.  
Commit logs are transient information valid only when the patch is applied.  
After that point they become archeological evidence, and you should not expect 
subsequent developers to be aware of them.
Blue Swirl June 2, 2012, 7:40 p.m. UTC | #8
On Thu, May 31, 2012 at 6:53 PM, Paul Brook <paul@codesourcery.com> wrote:
>> >> +void cpu_tlb_flush(CPUState *cpu, bool flush_global)
>> >> +{
>> >> +    CPUClass *cc = CPU_GET_CLASS(cpu);
>> >> +
>> >> +    g_assert(cc->tlb_flush != NULL);
>> >> +
>> >> +    cc->tlb_flush(cpu, flush_global);
>> >> +}
>> >
>> > This needs to be able to call tlb_flush() itself
>> > rather than having to have every single subclass of CPUState
>> > implement an identical tlb_flush method. You could do this
>> > if there was a CPU_GET_ENV()...
>>
>> Which is exactly the point: CPUState does not know about the
>> target-specific "env". And CPU_GET_ENV() is just plain wrong
>> conceptually because it adds yet another cpu.h dependency.
>
> Maybe so, but having every single taget implement its own copy of the exact
> same target independent wrapper seems even more wrong.
>
>> There's a separation between old code using env and new, clean code:
>> Just like Anthony doesn't want old concepts rewritten with the new type
>> (cf. object_realize() discussion) I don't want the old cpu.h #define
>> mess leaking into code that I'm redesigning specifically to get rid of
>> that target-*/cpu.h dependency in favor of a single qemu/cpu.h.
>> qom/cpu.c is by definition not compiled per target so it cannot contain
>> any target-specific code.
>
> At minimum it should be clearly documented[1] that this is a transitional
> hack, and how it should be removed.  There have already been two posts in this
> thread suggesting this is a feature, implying that this operation is somehow
> target specific.  I think the opposite is true:  This is a target agnostic
> detail of the TCG implementation, and implementing architecturally defined
> MMU/TLB behavior here is activley wrong.

The advantage of making the TLB more target specific (it already
depends on sizes of both target_ulong and target_phys_addr_t) is that
we can push some run time MMU model dependent code to translation
time.

The translator currently generates calls to qemu_ld, which generates a
TLB lookup with TCG. In the miss case, the TCG helper calls tlb_fill
and then cpu_xxx_handle_mmu_fault(). Most of those functions have
conditional code for current MMU mode, data access vs code access,
switch() based on MMU type etc.

With changes to TCG TLB handling, the translator could specify the
function to be called on TLB miss (or different TLB functions can be
generated with softmmu templates). Then the conditional code can be
pushed to translator.

In some TLB based MMU cases, we could predict or even calculate in
advance the MMU TLB index and other parameters but there's no way to
pass those to the miss/fault handler now.

Taking x86 as an example, we could have different functions for each case of:
- paging disabled
- x86_64 long mode enabled
- PAE enabled
- maybe permuted with the above, PSE enabled

PPC (or later Sparc64) MMU models do not change during execution, so
getting rid of the run time switches would be nice.

In some cases the QEMU TLB and target MMU TLBs could be combined,
provided that the target MMU is reasonable. For example UltraSPARC-IIi
I/D MMU TLBs have only 64 (though variable size) entries, whereas the
QEMU TLB can have many more, so I probably would not consider that
case.

Some MMUs have a notion of contexts (e.g. mapped to PID or thread ID),
but currently a TLB flush is needed when the context is changed which
can be suboptimal. To solve that, we'd need to change the TLB access
to target specific code to consider both context and address when
calculating the index.

In general, the interface between generated code and the TCG helper
should be more optimized.

>
> Paul
>
> [1] In the code, not the commit message.  Commit logs are not documentation.
> Commit logs are transient information valid only when the patch is applied.
> After that point they become archeological evidence, and you should not expect
> subsequent developers to be aware of them.
diff mbox

Patch

diff --git a/include/qemu/cpu.h b/include/qemu/cpu.h
index 61b7698..7d03369 100644
--- a/include/qemu/cpu.h
+++ b/include/qemu/cpu.h
@@ -41,6 +41,7 @@  typedef struct CPUState CPUState;
 /**
  * CPUClass:
  * @reset: Callback to reset the #CPUState to its initial state.
+ * @tlb_flush: Callback to flush the TLB.
  *
  * Represents a CPU family or model.
  */
@@ -50,6 +51,8 @@  typedef struct CPUClass {
     /*< public >*/
 
     void (*reset)(CPUState *cpu);
+
+    void (*tlb_flush)(CPUState *cpu, bool flush_global);
 } CPUClass;
 
 /**
@@ -88,6 +91,15 @@  struct CPUState {
 void cpu_reset(CPUState *cpu);
 
 /**
+ * cpu_tlb_flush:
+ * @cpu: The CPU whose TLB is to be flushed.
+ * @flush_global: Whether to flush TLB entries marked as global.
+ *
+ * Flushes the TLB of the CPU.
+ */
+void cpu_tlb_flush(CPUState *cpu, bool flush_global);
+
+/**
  * qemu_cpu_has_work:
  * @cpu: The vCPU to check.
  *
diff --git a/qom/cpu.c b/qom/cpu.c
index 5b36046..729f4cf 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -34,6 +34,15 @@  static void cpu_common_reset(CPUState *cpu)
 {
 }
 
+void cpu_tlb_flush(CPUState *cpu, bool flush_global)
+{
+    CPUClass *cc = CPU_GET_CLASS(cpu);
+
+    g_assert(cc->tlb_flush != NULL);
+
+    cc->tlb_flush(cpu, flush_global);
+}
+
 static void cpu_class_init(ObjectClass *klass, void *data)
 {
     CPUClass *k = CPU_CLASS(klass);
diff --git a/target-alpha/cpu.c b/target-alpha/cpu.c
index 11a19eb..d20f367 100644
--- a/target-alpha/cpu.c
+++ b/target-alpha/cpu.c
@@ -23,6 +23,14 @@ 
 #include "qemu-common.h"
 
 
+/* CPUClass::tlb_flush() */
+static void alpha_cpu_tlb_flush(CPUState *c, bool flush_global)
+{
+    AlphaCPU *cpu = ALPHA_CPU(c);
+
+    tlb_flush(&cpu->env, flush_global);
+}
+
 static void alpha_cpu_initfn(Object *obj)
 {
     AlphaCPU *cpu = ALPHA_CPU(obj);
@@ -41,6 +49,13 @@  static void alpha_cpu_initfn(Object *obj)
     env->fen = 1;
 }
 
+static void alpha_cpu_class_init(ObjectClass *oc, void *data)
+{
+    CPUClass *cc = CPU_CLASS(oc);
+
+    cc->tlb_flush = alpha_cpu_tlb_flush;
+}
+
 static const TypeInfo alpha_cpu_type_info = {
     .name = TYPE_ALPHA_CPU,
     .parent = TYPE_CPU,
@@ -48,6 +63,7 @@  static const TypeInfo alpha_cpu_type_info = {
     .instance_init = alpha_cpu_initfn,
     .abstract = false,
     .class_size = sizeof(AlphaCPUClass),
+    .class_init = alpha_cpu_class_init,
 };
 
 static void alpha_cpu_register_types(void)
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 7eb323a..abcf158 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -120,6 +120,14 @@  static void arm_cpu_reset(CPUState *s)
     tb_flush(env);
 }
 
+/* CPUClass::tlb_flush() */
+static void arm_cpu_tlb_flush(CPUState *c, bool flush_global)
+{
+    ARMCPU *cpu = ARM_CPU(c);
+
+    tlb_flush(&cpu->env, flush_global);
+}
+
 static inline void set_feature(CPUARMState *env, int feature)
 {
     env->features |= 1u << feature;
@@ -637,6 +645,8 @@  static void arm_cpu_class_init(ObjectClass *oc, void *data)
 
     acc->parent_reset = cc->reset;
     cc->reset = arm_cpu_reset;
+
+    cc->tlb_flush = arm_cpu_tlb_flush;
 }
 
 static void cpu_register(const ARMCPUInfo *info)
diff --git a/target-cris/cpu.c b/target-cris/cpu.c
index c596609..167ba30 100644
--- a/target-cris/cpu.c
+++ b/target-cris/cpu.c
@@ -55,6 +55,14 @@  static void cris_cpu_reset(CPUState *s)
 #endif
 }
 
+/* CPUClass::tlb_flush() */
+static void cris_cpu_tlb_flush(CPUState *c, bool flush_global)
+{
+    CRISCPU *cpu = CRIS_CPU(c);
+
+    tlb_flush(&cpu->env, flush_global);
+}
+
 static void cris_cpu_initfn(Object *obj)
 {
     CRISCPU *cpu = CRIS_CPU(obj);
@@ -70,6 +78,8 @@  static void cris_cpu_class_init(ObjectClass *oc, void *data)
 
     ccc->parent_reset = cc->reset;
     cc->reset = cris_cpu_reset;
+
+    cc->tlb_flush = cris_cpu_tlb_flush;
 }
 
 static const TypeInfo cris_cpu_type_info = {
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 89b4ac7..c8e6b80 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -1706,6 +1706,14 @@  static void x86_cpu_reset(CPUState *s)
     cpu_watchpoint_remove_all(env, BP_CPU);
 }
 
+/* CPUClass::tlb_flush() */
+static void x86_cpu_tlb_flush(CPUState *c, bool flush_global)
+{
+    X86CPU *cpu = X86_CPU(c);
+
+    tlb_flush(&cpu->env, flush_global);
+}
+
 static void mce_init(X86CPU *cpu)
 {
     CPUX86State *cenv = &cpu->env;
@@ -1772,6 +1780,8 @@  static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
 
     xcc->parent_reset = cc->reset;
     cc->reset = x86_cpu_reset;
+
+    cc->tlb_flush = x86_cpu_tlb_flush;
 }
 
 static const TypeInfo x86_cpu_type_info = {
diff --git a/target-lm32/cpu.c b/target-lm32/cpu.c
index caa4834..a58369e 100644
--- a/target-lm32/cpu.c
+++ b/target-lm32/cpu.c
@@ -42,6 +42,14 @@  static void lm32_cpu_reset(CPUState *s)
     memset(env, 0, offsetof(CPULM32State, breakpoints));
 }
 
+/* CPUClass::tlb_flush() */
+static void lm32_cpu_tlb_flush(CPUState *c, bool flush_global)
+{
+    LM32CPU *cpu = LM32_CPU(c);
+
+    tlb_flush(&cpu->env, flush_global);
+}
+
 static void lm32_cpu_initfn(Object *obj)
 {
     LM32CPU *cpu = LM32_CPU(obj);
@@ -61,6 +69,8 @@  static void lm32_cpu_class_init(ObjectClass *oc, void *data)
 
     lcc->parent_reset = cc->reset;
     cc->reset = lm32_cpu_reset;
+
+    cc->tlb_flush = lm32_cpu_tlb_flush;
 }
 
 static const TypeInfo lm32_cpu_type_info = {
diff --git a/target-m68k/cpu.c b/target-m68k/cpu.c
index 3e70bb0..e1daeff 100644
--- a/target-m68k/cpu.c
+++ b/target-m68k/cpu.c
@@ -53,6 +53,14 @@  static void m68k_cpu_reset(CPUState *s)
     tlb_flush(env, 1);
 }
 
+/* CPUClass::tlb_flush() */
+static void m68k_cpu_tlb_flush(CPUState *c, bool flush_global)
+{
+    M68kCPU *cpu = M68K_CPU(c);
+
+    tlb_flush(&cpu->env, flush_global);
+}
+
 /* CPU models */
 
 static void m5206_cpu_initfn(Object *obj)
@@ -134,6 +142,8 @@  static void m68k_cpu_class_init(ObjectClass *c, void *data)
 
     mcc->parent_reset = cc->reset;
     cc->reset = m68k_cpu_reset;
+
+    cc->tlb_flush = m68k_cpu_tlb_flush;
 }
 
 static void register_cpu_type(const M68kCPUInfo *info)
diff --git a/target-microblaze/cpu.c b/target-microblaze/cpu.c
index 9c3b74e..a850d8f 100644
--- a/target-microblaze/cpu.c
+++ b/target-microblaze/cpu.c
@@ -83,6 +83,14 @@  static void mb_cpu_reset(CPUState *s)
 #endif
 }
 
+/* CPUClass::tlb_flush() */
+static void mb_cpu_tlb_flush(CPUState *c, bool flush_global)
+{
+    MicroBlazeCPU *cpu = MICROBLAZE_CPU(c);
+
+    tlb_flush(&cpu->env, flush_global);
+}
+
 static void mb_cpu_initfn(Object *obj)
 {
     MicroBlazeCPU *cpu = MICROBLAZE_CPU(obj);
@@ -100,6 +108,8 @@  static void mb_cpu_class_init(ObjectClass *oc, void *data)
 
     mcc->parent_reset = cc->reset;
     cc->reset = mb_cpu_reset;
+
+    cc->tlb_flush = mb_cpu_tlb_flush;
 }
 
 static const TypeInfo mb_cpu_type_info = {
diff --git a/target-mips/cpu.c b/target-mips/cpu.c
index 0044062..7e51a2b 100644
--- a/target-mips/cpu.c
+++ b/target-mips/cpu.c
@@ -34,6 +34,14 @@  static void mips_cpu_reset(CPUState *s)
     cpu_state_reset(env);
 }
 
+/* CPUClass::tlb_flush() */
+static void mips_cpu_tlb_flush(CPUState *c, bool flush_global)
+{
+    MIPSCPU *cpu = MIPS_CPU(c);
+
+    tlb_flush(&cpu->env, flush_global);
+}
+
 static void mips_cpu_initfn(Object *obj)
 {
     MIPSCPU *cpu = MIPS_CPU(obj);
@@ -49,6 +57,8 @@  static void mips_cpu_class_init(ObjectClass *c, void *data)
 
     mcc->parent_reset = cc->reset;
     cc->reset = mips_cpu_reset;
+
+    cc->tlb_flush = mips_cpu_tlb_flush;
 }
 
 static const TypeInfo mips_cpu_type_info = {
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index 6f61175..5bebd84 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -10266,6 +10266,14 @@  static void ppc_cpu_reset(CPUState *s)
     tlb_flush(env, 1);
 }
 
+/* CPUClass::tlb_flush() */
+static void ppc_cpu_tlb_flush(CPUState *c, bool flush_global)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(c);
+
+    tlb_flush(&cpu->env, flush_global);
+}
+
 static void ppc_cpu_initfn(Object *obj)
 {
     PowerPCCPU *cpu = POWERPC_CPU(obj);
@@ -10281,6 +10289,8 @@  static void ppc_cpu_class_init(ObjectClass *oc, void *data)
 
     pcc->parent_reset = cc->reset;
     cc->reset = ppc_cpu_reset;
+
+    cc->tlb_flush = ppc_cpu_tlb_flush;
 }
 
 static const TypeInfo ppc_cpu_type_info = {
diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c
index 619b202..b87e307 100644
--- a/target-s390x/cpu.c
+++ b/target-s390x/cpu.c
@@ -45,6 +45,14 @@  static void s390_cpu_reset(CPUState *s)
     s390_add_running_cpu(env);
 }
 
+/* CPUClass::tlb_flush() */
+static void s390_cpu_tlb_flush(CPUState *c, bool flush_global)
+{
+    S390CPU *cpu = S390_CPU(c);
+
+    tlb_flush(&cpu->env, flush_global);
+}
+
 static void s390_cpu_initfn(Object *obj)
 {
     S390CPU *cpu = S390_CPU(obj);
@@ -76,6 +84,8 @@  static void s390_cpu_class_init(ObjectClass *oc, void *data)
 
     scc->parent_reset = cc->reset;
     cc->reset = s390_cpu_reset;
+
+    cc->tlb_flush = s390_cpu_tlb_flush;
 }
 
 static const TypeInfo s390_cpu_type_info = {
diff --git a/target-sh4/cpu.c b/target-sh4/cpu.c
index a1a177f..a0727bc 100644
--- a/target-sh4/cpu.c
+++ b/target-sh4/cpu.c
@@ -53,6 +53,14 @@  static void superh_cpu_reset(CPUState *s)
     set_default_nan_mode(1, &env->fp_status);
 }
 
+/* CPUClass::tlb_flush() */
+static void superh_cpu_tlb_flush(CPUState *c, bool flush_global)
+{
+    SuperHCPU *cpu = SUPERH_CPU(c);
+
+    tlb_flush(&cpu->env, flush_global);
+}
+
 static void superh_cpu_initfn(Object *obj)
 {
     SuperHCPU *cpu = SUPERH_CPU(obj);
@@ -70,6 +78,8 @@  static void superh_cpu_class_init(ObjectClass *oc, void *data)
 
     scc->parent_reset = cc->reset;
     cc->reset = superh_cpu_reset;
+
+    cc->tlb_flush = superh_cpu_tlb_flush;
 }
 
 static const TypeInfo superh_cpu_type_info = {
diff --git a/target-sparc/cpu.c b/target-sparc/cpu.c
index f7c004c..7216b42 100644
--- a/target-sparc/cpu.c
+++ b/target-sparc/cpu.c
@@ -74,6 +74,14 @@  static void sparc_cpu_reset(CPUState *s)
     env->cache_control = 0;
 }
 
+/* CPUClass::tlb_flush() */
+static void sparc_cpu_tlb_flush(CPUState *c, bool flush_global)
+{
+    SPARCCPU *cpu = SPARC_CPU(c);
+
+    tlb_flush(&cpu->env, flush_global);
+}
+
 static int cpu_sparc_register(CPUSPARCState *env, const char *cpu_model)
 {
     sparc_def_t def1, *def = &def1;
@@ -875,6 +883,8 @@  static void sparc_cpu_class_init(ObjectClass *oc, void *data)
 
     scc->parent_reset = cc->reset;
     cc->reset = sparc_cpu_reset;
+
+    cc->tlb_flush = sparc_cpu_tlb_flush;
 }
 
 static const TypeInfo sparc_cpu_type_info = {
diff --git a/target-unicore32/cpu.c b/target-unicore32/cpu.c
index 5467728..cc00bc2 100644
--- a/target-unicore32/cpu.c
+++ b/target-unicore32/cpu.c
@@ -20,6 +20,14 @@  static inline void set_feature(CPUUniCore32State *env, int feature)
     env->features |= feature;
 }
 
+/* CPUClass::tlb_flush() */
+static void uc32_cpu_tlb_flush(CPUState *c, bool flush_global)
+{
+    UniCore32CPU *cpu = UNICORE32_CPU(c);
+
+    tlb_flush(&cpu->env, flush_global);
+}
+
 /* CPU models */
 
 typedef struct UniCore32CPUInfo {
@@ -71,6 +79,13 @@  static void uc32_cpu_initfn(Object *obj)
     tlb_flush(env, 1);
 }
 
+static void uc32_cpu_class_init(ObjectClass *oc, void *data)
+{
+    CPUClass *cc = CPU_CLASS(oc);
+
+    cc->tlb_flush = uc32_cpu_tlb_flush;
+}
+
 static void uc32_register_cpu_type(const UniCore32CPUInfo *info)
 {
     TypeInfo type_info = {
@@ -89,6 +104,7 @@  static const TypeInfo uc32_cpu_type_info = {
     .instance_init = uc32_cpu_initfn,
     .abstract = true,
     .class_size = sizeof(UniCore32CPUClass),
+    .class_init = uc32_cpu_class_init,
 };
 
 static void uc32_cpu_register_types(void)
diff --git a/target-xtensa/cpu.c b/target-xtensa/cpu.c
index 9d01983..59671c1 100644
--- a/target-xtensa/cpu.c
+++ b/target-xtensa/cpu.c
@@ -53,6 +53,14 @@  static void xtensa_cpu_reset(CPUState *s)
     reset_mmu(env);
 }
 
+/* CPUClass::tlb_flush() */
+static void xtensa_cpu_tlb_flush(CPUState *c, bool flush_global)
+{
+    XtensaCPU *cpu = XTENSA_CPU(c);
+
+    tlb_flush(&cpu->env, flush_global);
+}
+
 static void xtensa_cpu_initfn(Object *obj)
 {
     XtensaCPU *cpu = XTENSA_CPU(obj);
@@ -68,6 +76,8 @@  static void xtensa_cpu_class_init(ObjectClass *oc, void *data)
 
     xcc->parent_reset = cc->reset;
     cc->reset = xtensa_cpu_reset;
+
+    cc->tlb_flush = xtensa_cpu_tlb_flush;
 }
 
 static const TypeInfo xtensa_cpu_type_info = {