Patchwork [RFC,12/12] target-xtensa: QOM'ify CPU

login
register
mail settings
Submitter Andreas Färber
Date March 14, 2012, 5:53 p.m.
Message ID <1331747617-7837-13-git-send-email-afaerber@suse.de>
Download mbox | patch
Permalink /patch/146700/
State New
Headers show

Comments

Andreas Färber - March 14, 2012, 5:53 p.m.
Let xtensa_cpu_list() enumerate CPU classes alphabetically.

Signed-off-by: Andreas Färber <afaerber@suse.de>
---
 Makefile.target              |    1 +
 gdbstub.c                    |   19 +++--
 hw/xtensa_pic.c              |   51 ++++++++----
 target-xtensa/core-dc232b.c  |    5 +-
 target-xtensa/core-fsf.c     |    5 +-
 target-xtensa/cpu-qom.h      |  186 ++++++++++++++++++++++++++++++++++++++++++
 target-xtensa/cpu.c          |   87 ++++++++++++++++++++
 target-xtensa/cpu.h          |  125 ++++------------------------
 target-xtensa/helper.c       |  151 ++++++++++++++++++++--------------
 target-xtensa/op_helper.c    |  104 ++++++++++++++++--------
 target-xtensa/overlay_tool.h |   28 +++++--
 target-xtensa/translate.c    |    9 ++-
 12 files changed, 527 insertions(+), 244 deletions(-)
 create mode 100644 target-xtensa/cpu-qom.h
 create mode 100644 target-xtensa/cpu.c
Max Filippov - March 15, 2012, 10:10 p.m.
> Let xtensa_cpu_list() enumerate CPU classes alphabetically.
>
> Signed-off-by: Andreas Färber<afaerber@suse.de>
> ---

[...]

> diff --git a/gdbstub.c b/gdbstub.c
> index f4e97f7..773e86f 100644
> --- a/gdbstub.c
> +++ b/gdbstub.c
> @@ -1570,14 +1570,17 @@ static int cpu_gdb_write_register(CPULM32State *env, uint8_t *mem_buf, int n)
>    * reset bit 0 in the 'flags' field of the registers definitions in the
>    * gdb/xtensa-config.c inside gdb source tree or inside gdb overlay.
>    */
> -#define NUM_CORE_REGS (env->config->gdb_regmap.num_regs)
> +#define NUM_CORE_REGS \
> +    (XTENSA_CPU_GET_CLASS(xtensa_env_get_cpu(env))->gdb_regmap.num_regs)
>   #define num_g_regs NUM_CORE_REGS
>
>   static int cpu_gdb_read_register(CPUXtensaState *env, uint8_t *mem_buf, int n)
>   {
> -    const XtensaGdbReg *reg = env->config->gdb_regmap.reg + n;
> +    XtensaCPU *cpu = xtensa_env_get_cpu(env);
> +    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);

*klass*

It's a bit strange to see patches that fix typos in comments and at the same time
to deliberately introduce this kind of misspelling. I'd suggest to call it what it is: cpu_class.

[...]

> diff --git a/target-xtensa/overlay_tool.h b/target-xtensa/overlay_tool.h
> index a3a5650..b46bca9 100644
> --- a/target-xtensa/overlay_tool.h
> +++ b/target-xtensa/overlay_tool.h
> @@ -291,16 +291,28 @@
>   #endif
>
>   #if (defined(TARGET_WORDS_BIGENDIAN) != 0) == (XCHAL_HAVE_BE != 0)
> -#define REGISTER_CORE(core) \
> -    static void __attribute__((constructor)) register_core(void) \
> +#define REGISTER_CORE(typename, class) \
> +    static void core_class_init(ObjectClass *klass, void *data) \
>       { \
> -        static XtensaConfigList node = { \
> -            .config =&core, \
> -        }; \
> -        xtensa_register_core(&node); \
> -    }
> +        /* XXX This is a really ugly but easy way to init the class... */ \
> +        memcpy((void *)klass + offsetof(XtensaCPUClass, options), \
> +               (void *)&(class) + offsetof(XtensaCPUClass, options), \
> +               sizeof(XtensaCPUClass) - offsetof(XtensaCPUClass, options)); \
> +    } \

- void pointer arithmetic is non-standard;
- (void *)&(class) + offsetof(XtensaCPUClass, options) looks suspicious, I don't think
   anything other than XtensaCPUClass instances should be passed here;

I'd suggest the following replacement:

         memcpy(&((XtensaCPUClass *)klass)->options, \
                &(class).options, \
                sizeof(XtensaCPUClass) - offsetof(XtensaCPUClass, options)); \

[...]

--
Thanks.
-- Max
Max Filippov - March 15, 2012, 11:10 p.m.
>> Let xtensa_cpu_list() enumerate CPU classes alphabetically.
>>
>> Signed-off-by: Andreas Färber<afaerber@suse.de>
>> ---

With this patch applied qemu aborts on unit tests with the following message:

qemu-system-xtensa -M sim -cpu dc232b -nographic -semihosting  -kernel ./test_b.tst
**
ERROR:/home/dumb/ws/m/awt/emu/xtensa/qemu/qom/object.c:156:type_get_parent: assertion failed: (type->parent_type != NULL)
make: *** [run-test_b.tst] Aborted


(gdb) bt
#0  0x00007ffff5f57285 in raise () from /lib64/libc.so.6
#1  0x00007ffff5f58b9b in abort () from /lib64/libc.so.6
#2  0x00007ffff771641d in g_assertion_message () from /lib64/libglib-2.0.so.0
#3  0x00007ffff7716942 in g_assertion_message_expr () from /lib64/libglib-2.0.so.0
#4  0x000055555565f23e in type_get_parent (type=0x55555625c8b0) at /home/dumb/ws/m/awt/emu/xtensa/qemu/qom/object.c:156
#5  0x000055555565f4ec in type_initialize (ti=0x55555625c8b0) at /home/dumb/ws/m/awt/emu/xtensa/qemu/qom/object.c:225
#6  0x00005555556602f4 in object_class_by_name (typename=0x7fffffffdc15 "dc232b") at 
/home/dumb/ws/m/awt/emu/xtensa/qemu/qom/object.c:543
#7  0x00005555556dd27b in cpu_xtensa_init (cpu_model=0x7fffffffdc15 "dc232b") at 
/home/dumb/ws/m/awt/emu/xtensa/qemu/target-xtensa/helper.c:85
#8  0x0000555555716703 in sim_init (ram_size=134217728, boot_device=0x7fffffffd6d0 "cad", kernel_filename=0x555556256060 
"./test_b.tst", kernel_cmdline=0x55555572aeb2 "", initrd_filename=0x0, cpu_model=
     0x7fffffffdc15 "dc232b") at /home/dumb/ws/m/awt/emu/xtensa/qemu/hw/xtensa_sim.c:55
#9  0x00005555557168e5 in xtensa_sim_init (ram_size=134217728, boot_device=0x7fffffffd6d0 "cad", 
kernel_filename=0x555556256060 "./test_b.tst", kernel_cmdline=0x55555572aeb2 "", initrd_filename=0x0, cpu_model=
     0x7fffffffdc15 "dc232b") at /home/dumb/ws/m/awt/emu/xtensa/qemu/hw/xtensa_sim.c:102
#10 0x000055555561f0bb in main (argc=9, argv=0x7fffffffd7f8, envp=0x7fffffffd848) at 
/home/dumb/ws/m/awt/emu/xtensa/qemu/vl.c:3496

(gdb) frame 4
#4  0x000055555565f23e in type_get_parent (type=0x55555625c8b0) at /home/dumb/ws/m/awt/emu/xtensa/qemu/qom/object.c:156
156             g_assert(type->parent_type != NULL);

(gdb) p *type
$6 = {name = 0x555556255fa0 "dc232b", class_size = 7848, instance_size = 75232, class_init = 0x5555556b4a6c 
<core_class_init>, class_finalize = 0, class_data = 0x0, instance_init = 0, instance_finalize = 0, abstract =
     false, parent = 0x555556255fc0 "xtensa-cpu", parent_type = 0x0, class = 0x555556264a90, num_interfaces = 0, 
interfaces = {{parent = 0x0, interface_initfn = 0, type = 0x0} <repeats 32 times>}}

Patch

diff --git a/Makefile.target b/Makefile.target
index 1c6ed12..3edbdfc 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -105,6 +105,7 @@  endif
 libobj-$(TARGET_SPARC) += int32_helper.o
 libobj-$(TARGET_SPARC64) += int64_helper.o
 libobj-$(TARGET_UNICORE32) += cpu.o
+libobj-$(TARGET_XTENSA) += cpu.o
 
 libobj-y += disas.o
 libobj-$(CONFIG_TCI_DIS) += tci-dis.o
diff --git a/gdbstub.c b/gdbstub.c
index f4e97f7..773e86f 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1570,14 +1570,17 @@  static int cpu_gdb_write_register(CPULM32State *env, uint8_t *mem_buf, int n)
  * reset bit 0 in the 'flags' field of the registers definitions in the
  * gdb/xtensa-config.c inside gdb source tree or inside gdb overlay.
  */
-#define NUM_CORE_REGS (env->config->gdb_regmap.num_regs)
+#define NUM_CORE_REGS \
+    (XTENSA_CPU_GET_CLASS(xtensa_env_get_cpu(env))->gdb_regmap.num_regs)
 #define num_g_regs NUM_CORE_REGS
 
 static int cpu_gdb_read_register(CPUXtensaState *env, uint8_t *mem_buf, int n)
 {
-    const XtensaGdbReg *reg = env->config->gdb_regmap.reg + n;
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+    const XtensaGdbReg *reg = klass->gdb_regmap.reg + n;
 
-    if (n < 0 || n >= env->config->gdb_regmap.num_regs) {
+    if (n < 0 || n >= klass->gdb_regmap.num_regs) {
         return 0;
     }
 
@@ -1588,7 +1591,7 @@  static int cpu_gdb_read_register(CPUXtensaState *env, uint8_t *mem_buf, int n)
 
     case 1: /*ar*/
         xtensa_sync_phys_from_window(env);
-        GET_REG32(env->phys_regs[(reg->targno & 0xff) % env->config->nareg]);
+        GET_REG32(env->phys_regs[(reg->targno & 0xff) % klass->nareg]);
         break;
 
     case 2: /*SR*/
@@ -1613,9 +1616,11 @@  static int cpu_gdb_read_register(CPUXtensaState *env, uint8_t *mem_buf, int n)
 static int cpu_gdb_write_register(CPUXtensaState *env, uint8_t *mem_buf, int n)
 {
     uint32_t tmp;
-    const XtensaGdbReg *reg = env->config->gdb_regmap.reg + n;
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+    const XtensaGdbReg *reg = klass->gdb_regmap.reg + n;
 
-    if (n < 0 || n >= env->config->gdb_regmap.num_regs) {
+    if (n < 0 || n >= klass->gdb_regmap.num_regs) {
         return 0;
     }
 
@@ -1627,7 +1632,7 @@  static int cpu_gdb_write_register(CPUXtensaState *env, uint8_t *mem_buf, int n)
         break;
 
     case 1: /*ar*/
-        env->phys_regs[(reg->targno & 0xff) % env->config->nareg] = tmp;
+        env->phys_regs[(reg->targno & 0xff) % klass->nareg] = tmp;
         xtensa_sync_window_from_phys(env);
         break;
 
diff --git a/hw/xtensa_pic.c b/hw/xtensa_pic.c
index 653ded6..3d398a1 100644
--- a/hw/xtensa_pic.c
+++ b/hw/xtensa_pic.c
@@ -31,13 +31,15 @@ 
 
 void xtensa_advance_ccount(CPUXtensaState *env, uint32_t d)
 {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
     uint32_t old_ccount = env->sregs[CCOUNT];
 
     env->sregs[CCOUNT] += d;
 
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) {
+    if (xtensa_option_enabled(klass, XTENSA_OPTION_TIMER_INTERRUPT)) {
         int i;
-        for (i = 0; i < env->config->nccompare; ++i) {
+        for (i = 0; i < klass->nccompare; ++i) {
             if (env->sregs[CCOMPARE + i] - old_ccount <= d) {
                 xtensa_timer_irq(env, i, 1);
             }
@@ -47,7 +49,9 @@  void xtensa_advance_ccount(CPUXtensaState *env, uint32_t d)
 
 void check_interrupts(CPUXtensaState *env)
 {
-    int minlevel = xtensa_get_cintlevel(env);
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+    int minlevel = xtensa_get_cintlevel(cpu);
     uint32_t int_set_enabled = env->sregs[INTSET] & env->sregs[INTENABLE];
     int level;
 
@@ -59,11 +63,11 @@  void check_interrupts(CPUXtensaState *env)
 
         xtensa_advance_ccount(env,
                 muldiv64(now - env->halt_clock,
-                    env->config->clock_freq_khz, 1000000));
+                    klass->clock_freq_khz, 1000000));
         env->halt_clock = now;
     }
-    for (level = env->config->nlevel; level > minlevel; --level) {
-        if (env->config->level_mask[level] & int_set_enabled) {
+    for (level = klass->nlevel; level > minlevel; --level) {
+        if (klass->level_mask[level] & int_set_enabled) {
             env->pending_irq_level = level;
             cpu_interrupt(env, CPU_INTERRUPT_HARD);
             qemu_log_mask(CPU_LOG_INT,
@@ -71,7 +75,7 @@  void check_interrupts(CPUXtensaState *env)
                     "pc = %08x, a0 = %08x, ps = %08x, "
                     "intset = %08x, intenable = %08x, "
                     "ccount = %08x\n",
-                    __func__, level, xtensa_get_cintlevel(env),
+                    __func__, level, xtensa_get_cintlevel(cpu),
                     env->pc, env->regs[0], env->sregs[PS],
                     env->sregs[INTSET], env->sregs[INTENABLE],
                     env->sregs[CCOUNT]);
@@ -85,15 +89,17 @@  void check_interrupts(CPUXtensaState *env)
 static void xtensa_set_irq(void *opaque, int irq, int active)
 {
     CPUXtensaState *env = opaque;
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
 
-    if (irq >= env->config->ninterrupt) {
+    if (irq >= klass->ninterrupt) {
         qemu_log("%s: bad IRQ %d\n", __func__, irq);
     } else {
         uint32_t irq_bit = 1 << irq;
 
         if (active) {
             env->sregs[INTSET] |= irq_bit;
-        } else if (env->config->interrupt[irq].inttype == INTTYPE_LEVEL) {
+        } else if (klass->interrupt[irq].inttype == INTTYPE_LEVEL) {
             env->sregs[INTSET] &= ~irq_bit;
         }
 
@@ -103,15 +109,20 @@  static void xtensa_set_irq(void *opaque, int irq, int active)
 
 void xtensa_timer_irq(CPUXtensaState *env, uint32_t id, uint32_t active)
 {
-    qemu_set_irq(env->irq_inputs[env->config->timerint[id]], active);
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+
+    qemu_set_irq(env->irq_inputs[klass->timerint[id]], active);
 }
 
 void xtensa_rearm_ccompare_timer(CPUXtensaState *env)
 {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
     int i;
     uint32_t wake_ccount = env->sregs[CCOUNT] - 1;
 
-    for (i = 0; i < env->config->nccompare; ++i) {
+    for (i = 0; i < klass->nccompare; ++i) {
         if (env->sregs[CCOMPARE + i] - env->sregs[CCOUNT] <
                 wake_ccount - env->sregs[CCOUNT]) {
             wake_ccount = env->sregs[CCOMPARE + i];
@@ -120,7 +131,7 @@  void xtensa_rearm_ccompare_timer(CPUXtensaState *env)
     env->wake_ccount = wake_ccount;
     qemu_mod_timer(env->ccompare_timer, env->halt_clock +
             muldiv64(wake_ccount - env->sregs[CCOUNT],
-                1000000, env->config->clock_freq_khz));
+                1000000, klass->clock_freq_khz));
 }
 
 static void xtensa_ccompare_cb(void *opaque)
@@ -139,10 +150,13 @@  static void xtensa_ccompare_cb(void *opaque)
 
 void xtensa_irq_init(CPUXtensaState *env)
 {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+
     env->irq_inputs = (void **)qemu_allocate_irqs(
-            xtensa_set_irq, env, env->config->ninterrupt);
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT) &&
-            env->config->nccompare > 0) {
+            xtensa_set_irq, env, klass->ninterrupt);
+    if (xtensa_option_enabled(klass, XTENSA_OPTION_TIMER_INTERRUPT) &&
+            klass->nccompare > 0) {
         env->ccompare_timer =
             qemu_new_timer_ns(vm_clock, &xtensa_ccompare_cb, env);
     }
@@ -150,8 +164,11 @@  void xtensa_irq_init(CPUXtensaState *env)
 
 void *xtensa_get_extint(CPUXtensaState *env, unsigned extint)
 {
-    if (extint < env->config->nextint) {
-        unsigned irq = env->config->extint[extint];
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+
+    if (extint < klass->nextint) {
+        unsigned irq = klass->extint[extint];
         return env->irq_inputs[irq];
     } else {
         qemu_log("%s: trying to acquire invalid external interrupt %d\n",
diff --git a/target-xtensa/core-dc232b.c b/target-xtensa/core-dc232b.c
index 7c03835..a62ca6a 100644
--- a/target-xtensa/core-dc232b.c
+++ b/target-xtensa/core-dc232b.c
@@ -6,8 +6,7 @@ 
 #include "core-dc232b/core-isa.h"
 #include "overlay_tool.h"
 
-static const XtensaConfig dc232b = {
-    .name = "dc232b",
+static const XtensaCPUClass dc232b = {
     .options = XTENSA_OPTIONS,
     .gdb_regmap = {
         .num_regs = 120,
@@ -25,4 +24,4 @@  static const XtensaConfig dc232b = {
     .clock_freq_khz = 10000,
 };
 
-REGISTER_CORE(dc232b)
+REGISTER_CORE("dc232b", dc232b)
diff --git a/target-xtensa/core-fsf.c b/target-xtensa/core-fsf.c
index c11d970..bcda18b 100644
--- a/target-xtensa/core-fsf.c
+++ b/target-xtensa/core-fsf.c
@@ -6,8 +6,7 @@ 
 #include "core-fsf/core-isa.h"
 #include "overlay_tool.h"
 
-static const XtensaConfig fsf = {
-    .name = "fsf",
+static const XtensaCPUClass fsf = {
     .options = XTENSA_OPTIONS,
     /* GDB for this core is not supported currently */
     .nareg = XCHAL_NUM_AREGS,
@@ -19,4 +18,4 @@  static const XtensaConfig fsf = {
     .clock_freq_khz = 10000,
 };
 
-REGISTER_CORE(fsf)
+REGISTER_CORE("fsf", fsf)
diff --git a/target-xtensa/cpu-qom.h b/target-xtensa/cpu-qom.h
new file mode 100644
index 0000000..e286458
--- /dev/null
+++ b/target-xtensa/cpu-qom.h
@@ -0,0 +1,186 @@ 
+/*
+ * QEMU Xtensa CPU
+ *
+ * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab.
+ * Copyright (c) 2012 SUSE LINUX Products GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the Open Source and Linux Lab nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef QEMU_XTENSA_CPU_QOM_H
+#define QEMU_XTENSA_CPU_QOM_H
+
+#include "qemu/cpu.h"
+#include "cpu.h"
+
+#define TYPE_XTENSA_CPU "xtensa-cpu"
+
+#define XTENSA_CPU_CLASS(klass) \
+    OBJECT_CLASS_CHECK(XtensaCPUClass, (klass), TYPE_XTENSA_CPU)
+#define XTENSA_CPU(obj) \
+    OBJECT_CHECK(XtensaCPU, (obj), TYPE_XTENSA_CPU)
+#define XTENSA_CPU_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(XtensaCPUClass, (obj), TYPE_XTENSA_CPU)
+
+typedef struct xtensa_tlb {
+    unsigned nways;
+    const unsigned way_size[10];
+    bool varway56;
+    unsigned nrefillentries;
+} xtensa_tlb;
+
+typedef struct XtensaGdbReg {
+    int targno;
+    int type;
+    int group;
+} XtensaGdbReg;
+
+typedef struct XtensaGdbRegmap {
+    int num_regs;
+    int num_core_regs;
+    /* PC + a + ar + sr + ur */
+    XtensaGdbReg reg[1 + 16 + 64 + 256 + 256];
+} XtensaGdbRegmap;
+
+/**
+ * XtensaCPUClass:
+ * @parent_reset: The parent class' reset handler.
+ *
+ * An Xtensa CPU model.
+ */
+typedef struct XtensaCPUClass {
+    /*< private >*/
+    CPUClass parent_class;
+    /*< public >*/
+
+    void (*parent_reset)(CPUState *cpu);
+
+    uint64_t options;
+    XtensaGdbRegmap gdb_regmap;
+    unsigned nareg;
+    int excm_level;
+    int ndepc;
+    uint32_t vecbase;
+    uint32_t exception_vector[EXC_MAX];
+    unsigned ninterrupt;
+    unsigned nlevel;
+    uint32_t interrupt_vector[MAX_NLEVEL + MAX_NNMI + 1];
+    uint32_t level_mask[MAX_NLEVEL + MAX_NNMI + 1];
+    uint32_t inttype_mask[INTTYPE_MAX];
+    struct {
+        uint32_t level;
+        interrupt_type inttype;
+    } interrupt[MAX_NINTERRUPT];
+    unsigned nccompare;
+    uint32_t timerint[MAX_NCCOMPARE];
+    unsigned nextint;
+    unsigned extint[MAX_NINTERRUPT];
+
+    unsigned debug_level;
+    unsigned nibreak;
+    unsigned ndbreak;
+
+    uint32_t clock_freq_khz;
+
+    xtensa_tlb itlb;
+    xtensa_tlb dtlb;
+} XtensaCPUClass;
+
+/**
+ * XtensaCPU:
+ * @env: Legacy CPU state.
+ *
+ * An Xtensa CPU.
+ */
+typedef struct XtensaCPU {
+    /*< private >*/
+    CPUState parent_obj;
+    /*< public >*/
+
+    CPUXtensaState env;
+} XtensaCPU;
+
+static inline XtensaCPU *xtensa_env_get_cpu(const CPUXtensaState *env)
+{
+    return XTENSA_CPU(container_of(env, XtensaCPU, env));
+}
+
+#define ENV_GET_CPU(e) CPU(xtensa_env_get_cpu(e))
+
+
+#define XTENSA_OPTION_BIT(opt) (((uint64_t)1) << (opt))
+
+static inline bool xtensa_option_bits_enabled(XtensaCPUClass *klass,
+                                              uint64_t opt)
+{
+    return (klass->options & opt) != 0;
+}
+
+static inline bool xtensa_option_enabled(XtensaCPUClass *klass, int opt)
+{
+    return xtensa_option_bits_enabled(klass, XTENSA_OPTION_BIT(opt));
+}
+
+static inline bool xtensa_cpu_option_enabled(XtensaCPU *cpu, int opt)
+{
+    return xtensa_option_enabled(XTENSA_CPU_GET_CLASS(cpu), opt);
+}
+
+static inline int xtensa_get_cintlevel(XtensaCPU *cpu)
+{
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+    CPUXtensaState *env = &cpu->env;
+    int level = (env->sregs[PS] & PS_INTLEVEL) >> PS_INTLEVEL_SHIFT;
+    if ((env->sregs[PS] & PS_EXCM) && klass->excm_level > level) {
+        level = klass->excm_level;
+    }
+    return level;
+}
+
+static inline int xtensa_get_debug_level(XtensaCPU *cpu)
+{
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+    return klass->debug_level;
+}
+
+static inline int xtensa_get_ring(XtensaCPU *cpu)
+{
+    if (xtensa_cpu_option_enabled(cpu, XTENSA_OPTION_MMU)) {
+        return (cpu->env.sregs[PS] & PS_RING) >> PS_RING_SHIFT;
+    } else {
+        return 0;
+    }
+}
+
+static inline int xtensa_get_cring(XtensaCPU *cpu)
+{
+    if (xtensa_cpu_option_enabled(cpu, XTENSA_OPTION_MMU) &&
+            (cpu->env.sregs[PS] & PS_EXCM) == 0) {
+        return (cpu->env.sregs[PS] & PS_RING) >> PS_RING_SHIFT;
+    } else {
+        return 0;
+    }
+}
+
+
+#endif
diff --git a/target-xtensa/cpu.c b/target-xtensa/cpu.c
new file mode 100644
index 0000000..54f4ffc
--- /dev/null
+++ b/target-xtensa/cpu.c
@@ -0,0 +1,87 @@ 
+/*
+ * QEMU Xtensa CPU
+ *
+ * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab.
+ * Copyright (c) 2012 SUSE LINUX Products GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the Open Source and Linux Lab nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cpu-qom.h"
+#include "qemu-common.h"
+
+static void xtensa_cpu_reset(CPUState *c)
+{
+    XtensaCPU *cpu = XTENSA_CPU(c);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+    CPUXtensaState *env = &cpu->env;
+
+    klass->parent_reset(c);
+
+    env->exception_taken = 0;
+    env->pc = klass->exception_vector[EXC_RESET];
+    env->sregs[LITBASE] &= ~1;
+    env->sregs[PS] = xtensa_option_enabled(klass,
+            XTENSA_OPTION_INTERRUPT) ? 0x1f : 0x10;
+    env->sregs[VECBASE] = klass->vecbase;
+    env->sregs[IBREAKENABLE] = 0;
+
+    env->pending_irq_level = 0;
+    reset_mmu(cpu);
+}
+
+static void xtensa_cpu_initfn(Object *obj)
+{
+    XtensaCPU *cpu = XTENSA_CPU(obj);
+    CPUXtensaState *env = &cpu->env;
+
+    cpu_exec_init(env);
+    xtensa_irq_init(env);
+}
+
+static void xtensa_cpu_class_init(ObjectClass *klass, void *data)
+{
+    CPUClass *cpu_class = CPU_CLASS(klass);
+    XtensaCPUClass *k = XTENSA_CPU_CLASS(klass);
+
+    k->parent_reset = cpu_class->reset;
+    cpu_class->reset = xtensa_cpu_reset;
+}
+
+static const TypeInfo xtensa_cpu_info = {
+    .name = TYPE_XTENSA_CPU,
+    .parent = TYPE_CPU,
+    .instance_size = sizeof(XtensaCPU),
+    .instance_init = xtensa_cpu_initfn,
+    .abstract = true,
+    .class_size = sizeof(XtensaCPUClass),
+    .class_init = xtensa_cpu_class_init,
+};
+
+static void xtensa_cpu_register_types(void)
+{
+    type_register_static(&xtensa_cpu_info);
+}
+
+type_init(xtensa_cpu_register_types)
diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h
index a7bcf52..1191dcf 100644
--- a/target-xtensa/cpu.h
+++ b/target-xtensa/cpu.h
@@ -260,66 +260,7 @@  typedef struct xtensa_tlb_entry {
     bool variable;
 } xtensa_tlb_entry;
 
-typedef struct xtensa_tlb {
-    unsigned nways;
-    const unsigned way_size[10];
-    bool varway56;
-    unsigned nrefillentries;
-} xtensa_tlb;
-
-typedef struct XtensaGdbReg {
-    int targno;
-    int type;
-    int group;
-} XtensaGdbReg;
-
-typedef struct XtensaGdbRegmap {
-    int num_regs;
-    int num_core_regs;
-    /* PC + a + ar + sr + ur */
-    XtensaGdbReg reg[1 + 16 + 64 + 256 + 256];
-} XtensaGdbRegmap;
-
-typedef struct XtensaConfig {
-    const char *name;
-    uint64_t options;
-    XtensaGdbRegmap gdb_regmap;
-    unsigned nareg;
-    int excm_level;
-    int ndepc;
-    uint32_t vecbase;
-    uint32_t exception_vector[EXC_MAX];
-    unsigned ninterrupt;
-    unsigned nlevel;
-    uint32_t interrupt_vector[MAX_NLEVEL + MAX_NNMI + 1];
-    uint32_t level_mask[MAX_NLEVEL + MAX_NNMI + 1];
-    uint32_t inttype_mask[INTTYPE_MAX];
-    struct {
-        uint32_t level;
-        interrupt_type inttype;
-    } interrupt[MAX_NINTERRUPT];
-    unsigned nccompare;
-    uint32_t timerint[MAX_NCCOMPARE];
-    unsigned nextint;
-    unsigned extint[MAX_NINTERRUPT];
-
-    unsigned debug_level;
-    unsigned nibreak;
-    unsigned ndbreak;
-
-    uint32_t clock_freq_khz;
-
-    xtensa_tlb itlb;
-    xtensa_tlb dtlb;
-} XtensaConfig;
-
-typedef struct XtensaConfigList {
-    const XtensaConfig *config;
-    struct XtensaConfigList *next;
-} XtensaConfigList;
-
 typedef struct CPUXtensaState {
-    const XtensaConfig *config;
     uint32_t regs[16];
     uint32_t pc;
     uint32_t sregs[256];
@@ -350,10 +291,17 @@  typedef struct CPUXtensaState {
 #define cpu_signal_handler cpu_xtensa_signal_handler
 #define cpu_list xtensa_cpu_list
 
+typedef struct XtensaCPU XtensaCPU;
+static inline XtensaCPU *xtensa_env_get_cpu(const CPUXtensaState *env);
+static inline bool xtensa_cpu_option_enabled(XtensaCPU *klass, int opt);
+static inline int xtensa_get_cintlevel(XtensaCPU *cpu);
+static inline int xtensa_get_debug_level(XtensaCPU *cpu);
+static inline int xtensa_get_ring(XtensaCPU *cpu);
+static inline int xtensa_get_cring(XtensaCPU *cpu);
+
 CPUXtensaState *cpu_xtensa_init(const char *cpu_model);
 void xtensa_translate_init(void);
 int cpu_xtensa_exec(CPUXtensaState *s);
-void xtensa_register_core(XtensaConfigList *node);
 void do_interrupt(CPUXtensaState *s);
 void check_interrupts(CPUXtensaState *s);
 void xtensa_irq_init(CPUXtensaState *env);
@@ -377,49 +325,9 @@  int xtensa_get_physical_addr(CPUXtensaState *env,
         uint32_t *paddr, uint32_t *page_size, unsigned *access);
 void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUXtensaState *env);
 void debug_exception_env(CPUXtensaState *new_env, uint32_t cause);
+void reset_mmu(XtensaCPU *cpu);
 
 
-#define XTENSA_OPTION_BIT(opt) (((uint64_t)1) << (opt))
-
-static inline bool xtensa_option_bits_enabled(const XtensaConfig *config,
-        uint64_t opt)
-{
-    return (config->options & opt) != 0;
-}
-
-static inline bool xtensa_option_enabled(const XtensaConfig *config, int opt)
-{
-    return xtensa_option_bits_enabled(config, XTENSA_OPTION_BIT(opt));
-}
-
-static inline int xtensa_get_cintlevel(const CPUXtensaState *env)
-{
-    int level = (env->sregs[PS] & PS_INTLEVEL) >> PS_INTLEVEL_SHIFT;
-    if ((env->sregs[PS] & PS_EXCM) && env->config->excm_level > level) {
-        level = env->config->excm_level;
-    }
-    return level;
-}
-
-static inline int xtensa_get_ring(const CPUXtensaState *env)
-{
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
-        return (env->sregs[PS] & PS_RING) >> PS_RING_SHIFT;
-    } else {
-        return 0;
-    }
-}
-
-static inline int xtensa_get_cring(const CPUXtensaState *env)
-{
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU) &&
-            (env->sregs[PS] & PS_EXCM) == 0) {
-        return (env->sregs[PS] & PS_RING) >> PS_RING_SHIFT;
-    } else {
-        return 0;
-    }
-}
-
 static inline xtensa_tlb_entry *xtensa_tlb_get_entry(CPUXtensaState *env,
         bool dtlb, unsigned wi, unsigned ei)
 {
@@ -436,7 +344,7 @@  static inline xtensa_tlb_entry *xtensa_tlb_get_entry(CPUXtensaState *env,
 
 static inline int cpu_mmu_index(CPUXtensaState *env)
 {
-    return xtensa_get_cring(env);
+    return xtensa_get_cring(xtensa_env_get_cpu(env));
 }
 
 #define XTENSA_TBFLAG_RING_MASK 0x3
@@ -448,28 +356,31 @@  static inline int cpu_mmu_index(CPUXtensaState *env)
 static inline void cpu_get_tb_cpu_state(CPUXtensaState *env, target_ulong *pc,
         target_ulong *cs_base, int *flags)
 {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+
     *pc = env->pc;
     *cs_base = 0;
     *flags = 0;
-    *flags |= xtensa_get_ring(env);
+    *flags |= xtensa_get_ring(cpu);
     if (env->sregs[PS] & PS_EXCM) {
         *flags |= XTENSA_TBFLAG_EXCM;
     }
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_EXTENDED_L32R) &&
+    if (xtensa_cpu_option_enabled(cpu, XTENSA_OPTION_EXTENDED_L32R) &&
             (env->sregs[LITBASE] & 1)) {
         *flags |= XTENSA_TBFLAG_LITBASE;
     }
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_DEBUG)) {
-        if (xtensa_get_cintlevel(env) < env->config->debug_level) {
+    if (xtensa_cpu_option_enabled(cpu, XTENSA_OPTION_DEBUG)) {
+        if (xtensa_get_cintlevel(cpu) < xtensa_get_debug_level(cpu)) {
             *flags |= XTENSA_TBFLAG_DEBUG;
         }
-        if (xtensa_get_cintlevel(env) < env->sregs[ICOUNTLEVEL]) {
+        if (xtensa_get_cintlevel(cpu) < env->sregs[ICOUNTLEVEL]) {
             *flags |= XTENSA_TBFLAG_ICOUNT;
         }
     }
 }
 
 #include "cpu-all.h"
+#include "cpu-qom.h"
 #include "exec-all.h"
 
 static inline int cpu_has_work(CPUXtensaState *env)
diff --git a/target-xtensa/helper.c b/target-xtensa/helper.c
index dab135c..3433228 100644
--- a/target-xtensa/helper.c
+++ b/target-xtensa/helper.c
@@ -33,35 +33,18 @@ 
 #include "hw/loader.h"
 #endif
 
-static void reset_mmu(CPUXtensaState *env);
-
 void cpu_state_reset(CPUXtensaState *env)
 {
-    env->exception_taken = 0;
-    env->pc = env->config->exception_vector[EXC_RESET];
-    env->sregs[LITBASE] &= ~1;
-    env->sregs[PS] = xtensa_option_enabled(env->config,
-            XTENSA_OPTION_INTERRUPT) ? 0x1f : 0x10;
-    env->sregs[VECBASE] = env->config->vecbase;
-    env->sregs[IBREAKENABLE] = 0;
-
-    env->pending_irq_level = 0;
-    reset_mmu(env);
+    cpu_reset(ENV_GET_CPU(env));
 }
 
-static struct XtensaConfigList *xtensa_cores;
-
-void xtensa_register_core(XtensaConfigList *node)
-{
-    node->next = xtensa_cores;
-    xtensa_cores = node;
-}
-
-static uint32_t check_hw_breakpoints(CPUXtensaState *env)
+static uint32_t check_hw_breakpoints(XtensaCPU *cpu)
 {
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+    CPUXtensaState *env = &cpu->env;
     unsigned i;
 
-    for (i = 0; i < env->config->ndbreak; ++i) {
+    for (i = 0; i < klass->ndbreak; ++i) {
         if (env->cpu_watchpoint[i] &&
                 env->cpu_watchpoint[i]->flags & BP_WATCHPOINT_HIT) {
             return DEBUGCAUSE_DB | (i << DEBUGCAUSE_DBNUM_SHIFT);
@@ -76,10 +59,11 @@  static void breakpoint_handler(CPUXtensaState *env)
 {
     if (env->watchpoint_hit) {
         if (env->watchpoint_hit->flags & BP_CPU) {
+            XtensaCPU *cpu = xtensa_env_get_cpu(env);
             uint32_t cause;
 
             env->watchpoint_hit = NULL;
-            cause = check_hw_breakpoints(env);
+            cause = check_hw_breakpoints(cpu);
             if (cause) {
                 debug_exception_env(env, cause);
             }
@@ -95,23 +79,14 @@  CPUXtensaState *cpu_xtensa_init(const char *cpu_model)
 {
     static int tcg_inited;
     static int debug_handler_inited;
+    XtensaCPU *cpu;
     CPUXtensaState *env;
-    const XtensaConfig *config = NULL;
-    XtensaConfigList *core = xtensa_cores;
 
-    for (; core; core = core->next)
-        if (strcmp(core->config->name, cpu_model) == 0) {
-            config = core->config;
-            break;
-        }
-
-    if (config == NULL) {
+    if (object_class_by_name(cpu_model) == NULL) {
         return NULL;
     }
-
-    env = g_malloc0(sizeof(*env));
-    env->config = config;
-    cpu_exec_init(env);
+    cpu = XTENSA_CPU(object_new(cpu_model));
+    env = &cpu->env;
 
     if (!tcg_inited) {
         tcg_inited = 1;
@@ -124,19 +99,48 @@  CPUXtensaState *cpu_xtensa_init(const char *cpu_model)
             cpu_set_debug_excp_handler(breakpoint_handler);
     }
 
-    xtensa_irq_init(env);
     qemu_init_vcpu(env);
     return env;
 }
 
 
+typedef struct XtensaCPUListState {
+    fprintf_function cpu_fprintf;
+    FILE *file;
+} XtensaCPUListState;
+
+/* Sort alphabetically. */
+static gint xtensa_cpu_list_compare(gconstpointer a, gconstpointer b)
+{
+    ObjectClass *class_a = (ObjectClass *)a;
+    ObjectClass *class_b = (ObjectClass *)b;
+
+    return strcasecmp(object_class_get_name(class_a),
+                      object_class_get_name(class_b));
+}
+
+static void xtensa_cpu_list_entry(gpointer data, gpointer user_data)
+{
+    ObjectClass *klass = data;
+    XtensaCPUListState *s = user_data;
+
+    (*s->cpu_fprintf)(s->file, "  %s\n",
+                      object_class_get_name(klass));
+}
+
 void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf)
 {
-    XtensaConfigList *core = xtensa_cores;
+    XtensaCPUListState s = {
+        .file = f,
+        .cpu_fprintf = cpu_fprintf,
+    };
+    GSList *list;
+
+    list = object_class_get_list(TYPE_XTENSA_CPU, false);
+    list = g_slist_sort(list, xtensa_cpu_list_compare);
     cpu_fprintf(f, "Available CPUs:\n");
-    for (; core; core = core->next) {
-        cpu_fprintf(f, "  %s\n", core->config->name);
-    }
+    g_slist_foreach(list, xtensa_cpu_list_entry, &s);
+    g_slist_free(list);
 }
 
 target_phys_addr_t cpu_get_phys_page_debug(CPUXtensaState *env, target_ulong addr)
@@ -158,9 +162,12 @@  target_phys_addr_t cpu_get_phys_page_debug(CPUXtensaState *env, target_ulong add
 
 static uint32_t relocated_vector(CPUXtensaState *env, uint32_t vector)
 {
-    if (xtensa_option_enabled(env->config,
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+
+    if (xtensa_option_enabled(klass,
                 XTENSA_OPTION_RELOCATABLE_VECTOR)) {
-        return vector - env->config->vecbase + env->sregs[VECBASE];
+        return vector - klass->vecbase + env->sregs[VECBASE];
     } else {
         return vector;
     }
@@ -174,11 +181,13 @@  static uint32_t relocated_vector(CPUXtensaState *env, uint32_t vector)
  */
 static void handle_interrupt(CPUXtensaState *env)
 {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
     int level = env->pending_irq_level;
 
-    if (level > xtensa_get_cintlevel(env) &&
-            level <= env->config->nlevel &&
-            (env->config->level_mask[level] &
+    if (level > xtensa_get_cintlevel(cpu) &&
+            level <= klass->nlevel &&
+            (klass->level_mask[level] &
              env->sregs[INTSET] &
              env->sregs[INTENABLE])) {
         if (level > 1) {
@@ -187,12 +196,12 @@  static void handle_interrupt(CPUXtensaState *env)
             env->sregs[PS] =
                 (env->sregs[PS] & ~PS_INTLEVEL) | level | PS_EXCM;
             env->pc = relocated_vector(env,
-                    env->config->interrupt_vector[level]);
+                    klass->interrupt_vector[level]);
         } else {
             env->sregs[EXCCAUSE] = LEVEL1_INTERRUPT_CAUSE;
 
             if (env->sregs[PS] & PS_EXCM) {
-                if (env->config->ndepc) {
+                if (klass->ndepc) {
                     env->sregs[DEPC] = env->pc;
                 } else {
                     env->sregs[EPC1] = env->pc;
@@ -211,13 +220,16 @@  static void handle_interrupt(CPUXtensaState *env)
 
 void do_interrupt(CPUXtensaState *env)
 {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+
     if (env->exception_index == EXC_IRQ) {
         qemu_log_mask(CPU_LOG_INT,
                 "%s(EXC_IRQ) level = %d, cintlevel = %d, "
                 "pc = %08x, a0 = %08x, ps = %08x, "
                 "intset = %08x, intenable = %08x, "
                 "ccount = %08x\n",
-                __func__, env->pending_irq_level, xtensa_get_cintlevel(env),
+                __func__, env->pending_irq_level, xtensa_get_cintlevel(cpu),
                 env->pc, env->regs[0], env->sregs[PS],
                 env->sregs[INTSET], env->sregs[INTENABLE],
                 env->sregs[CCOUNT]);
@@ -239,9 +251,9 @@  void do_interrupt(CPUXtensaState *env)
                 "pc = %08x, a0 = %08x, ps = %08x, ccount = %08x\n",
                 __func__, env->exception_index,
                 env->pc, env->regs[0], env->sregs[PS], env->sregs[CCOUNT]);
-        if (env->config->exception_vector[env->exception_index]) {
+        if (klass->exception_vector[env->exception_index]) {
             env->pc = relocated_vector(env,
-                    env->config->exception_vector[env->exception_index]);
+                    klass->exception_vector[env->exception_index]);
             env->exception_taken = 1;
         } else {
             qemu_log("%s(pc = %08x) bad exception_index: %d\n",
@@ -334,17 +346,20 @@  static void reset_tlb_region_way0(CPUXtensaState *env,
     }
 }
 
-static void reset_mmu(CPUXtensaState *env)
+void reset_mmu(XtensaCPU *cpu)
 {
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+    CPUXtensaState *env = &cpu->env;
+
+    if (xtensa_option_enabled(klass, XTENSA_OPTION_MMU)) {
         env->sregs[RASID] = 0x04030201;
         env->sregs[ITLBCFG] = 0;
         env->sregs[DTLBCFG] = 0;
         env->autorefill_idx = 0;
-        reset_tlb_mmu_all_ways(env, &env->config->itlb, env->itlb);
-        reset_tlb_mmu_all_ways(env, &env->config->dtlb, env->dtlb);
-        reset_tlb_mmu_ways56(env, &env->config->itlb, env->itlb);
-        reset_tlb_mmu_ways56(env, &env->config->dtlb, env->dtlb);
+        reset_tlb_mmu_all_ways(env, &klass->itlb, env->itlb);
+        reset_tlb_mmu_all_ways(env, &klass->dtlb, env->dtlb);
+        reset_tlb_mmu_ways56(env, &klass->itlb, env->itlb);
+        reset_tlb_mmu_ways56(env, &klass->dtlb, env->dtlb);
     } else {
         reset_tlb_region_way0(env, env->itlb);
         reset_tlb_region_way0(env, env->dtlb);
@@ -374,8 +389,10 @@  static unsigned get_ring(const CPUXtensaState *env, uint8_t asid)
 int xtensa_tlb_lookup(const CPUXtensaState *env, uint32_t addr, bool dtlb,
         uint32_t *pwi, uint32_t *pei, uint8_t *pring)
 {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
     const xtensa_tlb *tlb = dtlb ?
-        &env->config->dtlb : &env->config->itlb;
+        &klass->dtlb : &klass->itlb;
     const xtensa_tlb_entry (*entry)[MAX_TLB_WAY_SIZE] = dtlb ?
         env->dtlb : env->itlb;
 
@@ -567,10 +584,13 @@  int xtensa_get_physical_addr(CPUXtensaState *env,
         uint32_t vaddr, int is_write, int mmu_idx,
         uint32_t *paddr, uint32_t *page_size, unsigned *access)
 {
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+
+    if (xtensa_option_enabled(klass, XTENSA_OPTION_MMU)) {
         return get_physical_addr_mmu(env, vaddr, is_write, mmu_idx,
                 paddr, page_size, access);
-    } else if (xtensa_option_bits_enabled(env->config,
+    } else if (xtensa_option_bits_enabled(klass,
                 XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_PROTECTION) |
                 XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_TRANSLATION))) {
         return get_physical_addr_region(env, vaddr, is_write, mmu_idx,
@@ -586,11 +606,13 @@  int xtensa_get_physical_addr(CPUXtensaState *env,
 static void dump_tlb(FILE *f, fprintf_function cpu_fprintf,
         CPUXtensaState *env, bool dtlb)
 {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
     unsigned wi, ei;
     const xtensa_tlb *conf =
-        dtlb ? &env->config->dtlb : &env->config->itlb;
+        dtlb ? &klass->dtlb : &klass->itlb;
     unsigned (*attr_to_access)(uint32_t) =
-        xtensa_option_enabled(env->config, XTENSA_OPTION_MMU) ?
+        xtensa_cpu_option_enabled(cpu, XTENSA_OPTION_MMU) ?
         mmu_attr_to_access : region_attr_to_access;
 
     for (wi = 0; wi < conf->nways; ++wi) {
@@ -636,7 +658,10 @@  static void dump_tlb(FILE *f, fprintf_function cpu_fprintf,
 
 void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUXtensaState *env)
 {
-    if (xtensa_option_bits_enabled(env->config,
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+
+    if (xtensa_option_bits_enabled(klass,
                 XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_PROTECTION) |
                 XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_TRANSLATION) |
                 XTENSA_OPTION_BIT(XTENSA_OPTION_MMU))) {
diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c
index cdef0db..96d293f 100644
--- a/target-xtensa/op_helper.c
+++ b/target-xtensa/op_helper.c
@@ -62,8 +62,10 @@  static void do_restore_state(void *pc_ptr)
 static void do_unaligned_access(target_ulong addr, int is_write, int is_user,
         void *retaddr)
 {
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_UNALIGNED_EXCEPTION) &&
-            !xtensa_option_enabled(env->config, XTENSA_OPTION_HW_ALIGNMENT)) {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+
+    if (xtensa_cpu_option_enabled(cpu, XTENSA_OPTION_UNALIGNED_EXCEPTION) &&
+            !xtensa_cpu_option_enabled(cpu, XTENSA_OPTION_HW_ALIGNMENT)) {
         do_restore_state(retaddr);
         HELPER(exception_cause_vaddr)(
                 env->pc, LOAD_STORE_ALIGNMENT_CAUSE, addr);
@@ -107,11 +109,13 @@  void HELPER(exception)(uint32_t excp)
 
 void HELPER(exception_cause)(uint32_t pc, uint32_t cause)
 {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
     uint32_t vector;
 
     env->pc = pc;
     if (env->sregs[PS] & PS_EXCM) {
-        if (env->config->ndepc) {
+        if (klass->ndepc) {
             env->sregs[DEPC] = pc;
         } else {
             env->sregs[EPC1] = pc;
@@ -136,7 +140,9 @@  void HELPER(exception_cause_vaddr)(uint32_t pc, uint32_t cause, uint32_t vaddr)
 
 void debug_exception_env(CPUXtensaState *new_env, uint32_t cause)
 {
-    if (xtensa_get_cintlevel(new_env) < new_env->config->debug_level) {
+    XtensaCPU *new_cpu = xtensa_env_get_cpu(new_env);
+
+    if (xtensa_get_cintlevel(new_cpu) < xtensa_get_debug_level(new_cpu)) {
         env = new_env;
         HELPER(debug_exception)(env->pc, cause);
     }
@@ -144,7 +150,8 @@  void debug_exception_env(CPUXtensaState *new_env, uint32_t cause)
 
 void HELPER(debug_exception)(uint32_t pc, uint32_t cause)
 {
-    unsigned level = env->config->debug_level;
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    unsigned level = xtensa_get_debug_level(cpu);
 
     env->pc = pc;
     env->sregs[DEBUGCAUSE] = cause;
@@ -171,12 +178,15 @@  uint32_t HELPER(nsau)(uint32_t v)
 static void copy_window_from_phys(CPUXtensaState *env,
         uint32_t window, uint32_t phys, uint32_t n)
 {
-    assert(phys < env->config->nareg);
-    if (phys + n <= env->config->nareg) {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+
+    assert(phys < klass->nareg);
+    if (phys + n <= klass->nareg) {
         memcpy(env->regs + window, env->phys_regs + phys,
                 n * sizeof(uint32_t));
     } else {
-        uint32_t n1 = env->config->nareg - phys;
+        uint32_t n1 = klass->nareg - phys;
         memcpy(env->regs + window, env->phys_regs + phys,
                 n1 * sizeof(uint32_t));
         memcpy(env->regs + window + n1, env->phys_regs,
@@ -187,12 +197,15 @@  static void copy_window_from_phys(CPUXtensaState *env,
 static void copy_phys_from_window(CPUXtensaState *env,
         uint32_t phys, uint32_t window, uint32_t n)
 {
-    assert(phys < env->config->nareg);
-    if (phys + n <= env->config->nareg) {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+
+    assert(phys < klass->nareg);
+    if (phys + n <= klass->nareg) {
         memcpy(env->phys_regs + phys, env->regs + window,
                 n * sizeof(uint32_t));
     } else {
-        uint32_t n1 = env->config->nareg - phys;
+        uint32_t n1 = klass->nareg - phys;
         memcpy(env->phys_regs + phys, env->regs + window,
                 n1 * sizeof(uint32_t));
         memcpy(env->phys_regs, env->regs + window + n1,
@@ -203,7 +216,10 @@  static void copy_phys_from_window(CPUXtensaState *env,
 
 static inline unsigned windowbase_bound(unsigned a, const CPUXtensaState *env)
 {
-    return a & (env->config->nareg / 4 - 1);
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+
+    return a & (klass->nareg / 4 - 1);
 }
 
 static inline unsigned windowstart_bit(unsigned a, const CPUXtensaState *env)
@@ -382,6 +398,8 @@  void HELPER(dump_state)(void)
 
 void HELPER(waiti)(uint32_t pc, uint32_t intlevel)
 {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+
     env->pc = pc;
     env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) |
         (intlevel << PS_INTLEVEL_SHIFT);
@@ -393,7 +411,7 @@  void HELPER(waiti)(uint32_t pc, uint32_t intlevel)
 
     env->halt_clock = qemu_get_clock_ns(vm_clock);
     env->halted = 1;
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) {
+    if (xtensa_cpu_option_enabled(cpu, XTENSA_OPTION_TIMER_INTERRUPT)) {
         xtensa_rearm_ccompare_timer(env);
     }
     HELPER(exception)(EXCP_HLT);
@@ -447,10 +465,13 @@  static uint32_t get_page_size(const CPUXtensaState *env, bool dtlb, uint32_t way
  */
 uint32_t xtensa_tlb_get_addr_mask(const CPUXtensaState *env, bool dtlb, uint32_t way)
 {
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+
+    if (xtensa_cpu_option_enabled(cpu, XTENSA_OPTION_MMU)) {
         bool varway56 = dtlb ?
-            env->config->dtlb.varway56 :
-            env->config->itlb.varway56;
+            klass->dtlb.varway56 :
+            klass->itlb.varway56;
 
         switch (way) {
         case 4:
@@ -484,18 +505,21 @@  uint32_t xtensa_tlb_get_addr_mask(const CPUXtensaState *env, bool dtlb, uint32_t
  */
 static uint32_t get_vpn_mask(const CPUXtensaState *env, bool dtlb, uint32_t way)
 {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+
     if (way < 4) {
         bool is32 = (dtlb ?
-                env->config->dtlb.nrefillentries :
-                env->config->itlb.nrefillentries) == 32;
+                klass->dtlb.nrefillentries :
+                klass->itlb.nrefillentries) == 32;
         return is32 ? 0xffff8000 : 0xffffc000;
     } else if (way == 4) {
         return xtensa_tlb_get_addr_mask(env, dtlb, way) << 2;
     } else if (way <= 6) {
         uint32_t mask = xtensa_tlb_get_addr_mask(env, dtlb, way);
         bool varway56 = dtlb ?
-            env->config->dtlb.varway56 :
-            env->config->itlb.varway56;
+            klass->dtlb.varway56 :
+            klass->itlb.varway56;
 
         if (varway56) {
             return mask << (way == 5 ? 2 : 3);
@@ -514,9 +538,12 @@  static uint32_t get_vpn_mask(const CPUXtensaState *env, bool dtlb, uint32_t way)
 void split_tlb_entry_spec_way(const CPUXtensaState *env, uint32_t v, bool dtlb,
         uint32_t *vpn, uint32_t wi, uint32_t *ei)
 {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
+
     bool varway56 = dtlb ?
-        env->config->dtlb.varway56 :
-        env->config->itlb.varway56;
+        klass->dtlb.varway56 :
+        klass->itlb.varway56;
 
     if (!dtlb) {
         wi &= 7;
@@ -524,8 +551,8 @@  void split_tlb_entry_spec_way(const CPUXtensaState *env, uint32_t v, bool dtlb,
 
     if (wi < 4) {
         bool is32 = (dtlb ?
-                env->config->dtlb.nrefillentries :
-                env->config->itlb.nrefillentries) == 32;
+                klass->dtlb.nrefillentries :
+                klass->itlb.nrefillentries) == 32;
         *ei = (v >> 12) & (is32 ? 0x7 : 0x3);
     } else {
         switch (wi) {
@@ -569,7 +596,9 @@  void split_tlb_entry_spec_way(const CPUXtensaState *env, uint32_t v, bool dtlb,
 static void split_tlb_entry_spec(uint32_t v, bool dtlb,
         uint32_t *vpn, uint32_t *wi, uint32_t *ei)
 {
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+
+    if (xtensa_cpu_option_enabled(cpu, XTENSA_OPTION_MMU)) {
         *wi = v & (dtlb ? 0xf : 0x7);
         split_tlb_entry_spec_way(env, v, dtlb, vpn, *wi, ei);
     } else {
@@ -594,7 +623,9 @@  static xtensa_tlb_entry *get_tlb_entry(uint32_t v, bool dtlb, uint32_t *pwi)
 
 uint32_t HELPER(rtlb0)(uint32_t v, uint32_t dtlb)
 {
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+
+    if (xtensa_cpu_option_enabled(cpu, XTENSA_OPTION_MMU)) {
         uint32_t wi;
         const xtensa_tlb_entry *entry = get_tlb_entry(v, dtlb, &wi);
         return (entry->vaddr & get_vpn_mask(env, dtlb, wi)) | entry->asid;
@@ -611,7 +642,9 @@  uint32_t HELPER(rtlb1)(uint32_t v, uint32_t dtlb)
 
 void HELPER(itlb)(uint32_t v, uint32_t dtlb)
 {
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+
+    if (xtensa_cpu_option_enabled(cpu, XTENSA_OPTION_MMU)) {
         uint32_t wi;
         xtensa_tlb_entry *entry = get_tlb_entry(v, dtlb, &wi);
         if (entry->variable && entry->asid) {
@@ -623,7 +656,9 @@  void HELPER(itlb)(uint32_t v, uint32_t dtlb)
 
 uint32_t HELPER(ptlb)(uint32_t v, uint32_t dtlb)
 {
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+
+    if (xtensa_cpu_option_enabled(cpu, XTENSA_OPTION_MMU)) {
         uint32_t wi;
         uint32_t ei;
         uint8_t ring;
@@ -631,7 +666,7 @@  uint32_t HELPER(ptlb)(uint32_t v, uint32_t dtlb)
 
         switch (res) {
         case 0:
-            if (ring >= xtensa_get_ring(env)) {
+            if (ring >= xtensa_get_ring(cpu)) {
                 return (v & 0xfffff000) | wi | (dtlb ? 0x10 : 0x8);
             }
             break;
@@ -650,9 +685,10 @@  uint32_t HELPER(ptlb)(uint32_t v, uint32_t dtlb)
 void xtensa_tlb_set_entry(CPUXtensaState *env, bool dtlb,
         unsigned wi, unsigned ei, uint32_t vpn, uint32_t pte)
 {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
     xtensa_tlb_entry *entry = xtensa_tlb_get_entry(env, dtlb, wi, ei);
 
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
+    if (xtensa_cpu_option_enabled(cpu, XTENSA_OPTION_MMU)) {
         if (entry->variable) {
             if (entry->asid) {
                 tlb_flush_page(env, entry->vaddr);
@@ -667,7 +703,7 @@  void xtensa_tlb_set_entry(CPUXtensaState *env, bool dtlb,
         }
     } else {
         tlb_flush_page(env, entry->vaddr);
-        if (xtensa_option_enabled(env->config,
+        if (xtensa_cpu_option_enabled(cpu,
                     XTENSA_OPTION_REGION_TRANSLATION)) {
             entry->paddr = pte & REGION_PAGE_MASK;
         }
@@ -687,16 +723,18 @@  void HELPER(wtlb)(uint32_t p, uint32_t v, uint32_t dtlb)
 
 void HELPER(wsr_ibreakenable)(uint32_t v)
 {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
     uint32_t change = v ^ env->sregs[IBREAKENABLE];
     unsigned i;
 
-    for (i = 0; i < env->config->nibreak; ++i) {
+    for (i = 0; i < klass->nibreak; ++i) {
         if (change & (1 << i)) {
             tb_invalidate_phys_page_range(
                     env->sregs[IBREAKA + i], env->sregs[IBREAKA + i] + 1, 0);
         }
     }
-    env->sregs[IBREAKENABLE] = v & ((1 << env->config->nibreak) - 1);
+    env->sregs[IBREAKENABLE] = v & ((1 << klass->nibreak) - 1);
 }
 
 void HELPER(wsr_ibreaka)(uint32_t i, uint32_t v)
diff --git a/target-xtensa/overlay_tool.h b/target-xtensa/overlay_tool.h
index a3a5650..b46bca9 100644
--- a/target-xtensa/overlay_tool.h
+++ b/target-xtensa/overlay_tool.h
@@ -291,16 +291,28 @@ 
 #endif
 
 #if (defined(TARGET_WORDS_BIGENDIAN) != 0) == (XCHAL_HAVE_BE != 0)
-#define REGISTER_CORE(core) \
-    static void __attribute__((constructor)) register_core(void) \
+#define REGISTER_CORE(typename, class) \
+    static void core_class_init(ObjectClass *klass, void *data) \
     { \
-        static XtensaConfigList node = { \
-            .config = &core, \
-        }; \
-        xtensa_register_core(&node); \
-    }
+        /* XXX This is a really ugly but easy way to init the class... */ \
+        memcpy((void *)klass + offsetof(XtensaCPUClass, options), \
+               (void *)&(class) + offsetof(XtensaCPUClass, options), \
+               sizeof(XtensaCPUClass) - offsetof(XtensaCPUClass, options)); \
+    } \
+    static const TypeInfo core_info = { \
+        .name = (typename), \
+        .parent = TYPE_XTENSA_CPU, \
+        .instance_size = sizeof(XtensaCPU), \
+        .class_size = sizeof(XtensaCPUClass), \
+        .class_init = core_class_init, \
+    }; \
+    static void register_core_type(void) \
+    { \
+        type_register_static(&core_info); \
+    } \
+    type_init(register_core_type)
 #else
-#define REGISTER_CORE(core)
+#define REGISTER_CORE(name, core)
 #endif
 
 #define DEBUG_SECTION \
diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c
index e0ff72b..b05b83e 100644
--- a/target-xtensa/translate.c
+++ b/target-xtensa/translate.c
@@ -42,7 +42,7 @@ 
 #include "helpers.h"
 
 typedef struct DisasContext {
-    const XtensaConfig *config;
+    XtensaCPUClass *config;
     TranslationBlock *tb;
     uint32_t pc;
     uint32_t next_pc;
@@ -2524,6 +2524,7 @@  static void gen_ibreak_check(CPUXtensaState *env, DisasContext *dc)
 static void gen_intermediate_code_internal(
         CPUXtensaState *env, TranslationBlock *tb, int search_pc)
 {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
     DisasContext dc;
     int insn_count = 0;
     int j, lj = -1;
@@ -2537,7 +2538,7 @@  static void gen_intermediate_code_internal(
         max_insns = CF_COUNT_MASK;
     }
 
-    dc.config = env->config;
+    dc.config = XTENSA_CPU_GET_CLASS(cpu);
     dc.singlestep_enabled = env->singlestep_enabled;
     dc.tb = tb;
     dc.pc = pc_start;
@@ -2657,6 +2658,8 @@  void gen_intermediate_code_pc(CPUXtensaState *env, TranslationBlock *tb)
 void cpu_dump_state(CPUXtensaState *env, FILE *f, fprintf_function cpu_fprintf,
         int flags)
 {
+    XtensaCPU *cpu = xtensa_env_get_cpu(env);
+    XtensaCPUClass *klass = XTENSA_CPU_GET_CLASS(cpu);
     int i, j;
 
     cpu_fprintf(f, "PC=%08x\n\n", env->pc);
@@ -2686,7 +2689,7 @@  void cpu_dump_state(CPUXtensaState *env, FILE *f, fprintf_function cpu_fprintf,
 
     cpu_fprintf(f, "\n");
 
-    for (i = 0; i < env->config->nareg; ++i) {
+    for (i = 0; i < klass->nareg; ++i) {
         cpu_fprintf(f, "AR%02d=%08x%c", i, env->phys_regs[i],
                 (i % 4) == 3 ? '\n' : ' ');
     }