diff mbox

[RFC,v4,02/20] target-arm: Introduce QOM ARMCPUClass

Message ID 1331398436-20761-3-git-send-email-afaerber@suse.de
State New
Headers show

Commit Message

Andreas Färber March 10, 2012, 4:53 p.m. UTC
Create a CPU subclass, and register classes matching all CPU models
except for "pxa270", which is an alias for "pxa270-a0".

Let arm_cpu_list() enumerate CPU subclasses in alphabetical order,
except for special value "any".

Replace cpu_arm_find_by_name()'s string -> CPUID lookup by storing the
CPUID in the class.
NB: CPUIDs were first introduced by Paul Brook in r1765 (2006).

Signed-off-by: Andreas Färber <afaerber@suse.de>
Cc: Anthony Liguori <anthony@codemonkey.ws>
Cc: Paul Brook <paul@codesourcery.com>
Cc: Peter Maydell <peter.maydell@linaro.org>
---
 Makefile.target      |    1 +
 target-arm/cpu-qom.h |   64 +++++++++++++++++
 target-arm/cpu.c     |  193 ++++++++++++++++++++++++++++++++++++++++++++++++++
 target-arm/cpu.h     |    1 -
 target-arm/helper.c  |  108 +++++++++++++---------------
 5 files changed, 308 insertions(+), 59 deletions(-)
 create mode 100644 target-arm/cpu-qom.h
 create mode 100644 target-arm/cpu.c

Comments

Mitsyanko Igor March 13, 2012, 12:31 p.m. UTC | #1
On 03/10/2012 08:53 PM, Andreas Färber wrote:
> Create a CPU subclass, and register classes matching all CPU models
> except for "pxa270", which is an alias for "pxa270-a0".
>
> Let arm_cpu_list() enumerate CPU subclasses in alphabetical order,
> except for special value "any".
>
> Replace cpu_arm_find_by_name()'s string ->  CPUID lookup by storing the
> CPUID in the class.
> NB: CPUIDs were first introduced by Paul Brook in r1765 (2006).
>
> Signed-off-by: Andreas Färber<afaerber@suse.de>
> Cc: Anthony Liguori<anthony@codemonkey.ws>
> Cc: Paul Brook<paul@codesourcery.com>
> Cc: Peter Maydell<peter.maydell@linaro.org>
> ---
>   Makefile.target      |    1 +
>   target-arm/cpu-qom.h |   64 +++++++++++++++++
>   target-arm/cpu.c     |  193 ++++++++++++++++++++++++++++++++++++++++++++++++++
>   target-arm/cpu.h     |    1 -
>   target-arm/helper.c  |  108 +++++++++++++---------------
>   5 files changed, 308 insertions(+), 59 deletions(-)
>   create mode 100644 target-arm/cpu-qom.h
>   create mode 100644 target-arm/cpu.c
>
> diff --git a/Makefile.target b/Makefile.target
> index cb1532a..c2c4bca 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -87,6 +87,7 @@ endif
>   libobj-$(TARGET_SPARC64) += vis_helper.o
>   libobj-$(CONFIG_NEED_MMU) += mmu.o
>   libobj-$(TARGET_ARM) += neon_helper.o iwmmxt_helper.o
> +libobj-$(TARGET_ARM) += cpu.o
>   ifeq ($(TARGET_BASE_ARCH), sparc)
>   libobj-y += fop_helper.o cc_helper.o win_helper.o mmu_helper.o ldst_helper.o
>   libobj-y += cpu_init.o
> diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
> new file mode 100644
> index 0000000..b2917ea
> --- /dev/null
> +++ b/target-arm/cpu-qom.h
> @@ -0,0 +1,64 @@
> +/*
> + * QEMU ARM CPU
> + *
> + * Copyright (c) 2012 SUSE LINUX Products GmbH
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see
> + *<http://www.gnu.org/licenses/gpl-2.0.html>
> + */
> +#ifndef QEMU_ARM_CPU_QOM_H
> +#define QEMU_ARM_CPU_QOM_H
> +
> +#include "qemu/cpu.h"
> +
> +#define TYPE_ARM_CPU "arm-cpu"
> +
> +#define ARM_CPU_CLASS(klass) \
> +    OBJECT_CLASS_CHECK(ARMCPUClass, (klass), TYPE_ARM_CPU)
> +#define ARM_CPU(obj) \
> +    OBJECT_CHECK(ARMCPU, (obj), TYPE_ARM_CPU)
> +#define ARM_CPU_GET_CLASS(obj) \
> +    OBJECT_GET_CLASS(ARMCPUClass, (obj), TYPE_ARM_CPU)
> +
> +/**
> + * ARMCPUClass:
> + * @parent_reset: The parent class' reset handler.
> + *
> + * An ARM CPU model.
> + */
> +typedef struct ARMCPUClass {
> +    /*<  private>*/
> +    CPUClass parent_class;
> +    /*<  public>*/
> +
> +    void (*parent_reset)(CPUState *cpu);
> +
> +    struct {
> +        uint32_t c0_cpuid;
> +    } cp15;
> +} ARMCPUClass;
> +
> +/**
> + * ARMCPU:
> + *
> + * An ARM CPU core.
> + */
> +typedef struct ARMCPU {
> +    /*<  private>*/
> +    CPUState parent_obj;
> +    /*<  public>*/
> +} ARMCPU;
> +
> +
> +#endif
> diff --git a/target-arm/cpu.c b/target-arm/cpu.c
> new file mode 100644
> index 0000000..dabc094
> --- /dev/null
> +++ b/target-arm/cpu.c
> @@ -0,0 +1,193 @@
> +/*
> + * QEMU ARM CPU
> + *
> + * Copyright (c) 2012 SUSE LINUX Products GmbH
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see
> + *<http://www.gnu.org/licenses/gpl-2.0.html>
> + */
> +
> +#include "cpu-qom.h"
> +#include "qemu-common.h"
> +
> +static void arm_cpu_reset(CPUState *c)
> +{
> +    ARMCPUClass *klass = ARM_CPU_GET_CLASS(c);
> +
> +    klass->parent_reset(c);
> +}
> +
> +/* CPU models */
> +
> +typedef struct ARMCPUInfo {
> +    const char *name;
> +    uint32_t id;
> +} ARMCPUInfo;
> +
> +static const ARMCPUInfo arm_cpus[] = {
> +    {
> +        .name = "arm926",
> +        .id = 0x41069265,
> +    },
> +    {
> +        .name = "arm946",
> +        .id = 0x41059461,
> +    },
> +    {
> +        .name = "arm1026",
> +        .id = 0x4106a262,
> +    },
> +    /* What QEMU calls "arm1136-r2" is actually the 1136 r0p2, i.e. an
> +     * older core than plain "arm1136". In particular this does not
> +     * have the v6K features.
> +     */
> +    {
> +        .name = "arm1136-r2",
> +        .id = 0x4107b362,
> +    },
> +    {
> +        .name = "arm1136",
> +        .id = 0x4117b363,
> +    },
> +    {
> +        .name = "arm1176",
> +        .id = 0x410fb767,
> +    },
> +    {
> +        .name = "arm11mpcore",
> +        .id = 0x410fb022,
> +    },
> +    {
> +        .name = "cortex-m3",
> +        .id = 0x410fc231,
> +    },
> +    {
> +        .name = "cortex-a8",
> +        .id = 0x410fc080,
> +    },
> +    {
> +        .name = "cortex-a9",
> +        .id = 0x410fc090,
> +    },
> +    {
> +        .name = "cortex-a15",
> +        .id = 0x412fc0f1,
> +    },
> +    {
> +        .name = "ti925t",
> +        .id = 0x54029252,
> +    },
> +    {
> +        .name = "sa1100",
> +        .id = 0x4401A11B,
> +    },
> +    {
> +        .name = "sa1110",
> +        .id = 0x6901B119,
> +    },
> +    {
> +        .name = "pxa250",
> +        .id = 0x69052100,
> +    },
> +    {
> +        .name = "pxa255",
> +        .id = 0x69052d00,
> +    },
> +    {
> +        .name = "pxa260",
> +        .id = 0x69052903,
> +    },
> +    {
> +        .name = "pxa261",
> +        .id = 0x69052d05,
> +    },
> +    {
> +        .name = "pxa262",
> +        .id = 0x69052d06,
> +    },
> +    {
> +        .name = "pxa270-a0",
> +        .id = 0x69054110,
> +    },
> +    {
> +        .name = "pxa270-a1",
> +        .id = 0x69054111,
> +    },
> +    {
> +        .name = "pxa270-b0",
> +        .id = 0x69054112,
> +    },
> +    {
> +        .name = "pxa270-b1",
> +        .id = 0x69054113,
> +    },
> +    {
> +        .name = "pxa270-c0",
> +        .id = 0x69054114,
> +    },
> +    {
> +        .name = "pxa270-c5",
> +        .id = 0x69054117,
> +    },
> +    {
> +        .name = "any",
> +        .id = 0xffffffff,
> +    },
> +};
> +
> +static void arm_cpu_class_init(ObjectClass *klass, void *data)
> +{
> +    ARMCPUClass *k = ARM_CPU_CLASS(klass);
> +    CPUClass *cpu_class = CPU_CLASS(klass);
> +    const ARMCPUInfo *info = data;
> +
> +    k->parent_reset = cpu_class->reset;
> +    cpu_class->reset = arm_cpu_reset;
> +
> +    k->cp15.c0_cpuid = info->id;
> +}
> +
> +static void cpu_register(const ARMCPUInfo *info)
> +{
> +    TypeInfo type = {
> +        .name = info->name,
> +        .parent = TYPE_ARM_CPU,
> +        .instance_size = sizeof(ARMCPU),
> +        .class_size = sizeof(ARMCPUClass),
> +        .class_init = arm_cpu_class_init,
> +        .class_data = (void *)info,
> +    };

Are non-initialized members guaranteed to be zero here?

> +    type_register_static(&type);
> +}
> +

Probably should be type_register() here in case these two will actually 
differ in the future.


If this information is of any help, we've got no problems when emulating 
ARM-based Exynos boards in QEMU with this whole patchset applied.
Andreas Färber March 13, 2012, 5:58 p.m. UTC | #2
Am 13.03.2012 13:31, schrieb Igor Mitsyanko:
> On 03/10/2012 08:53 PM, Andreas Färber wrote:
>> diff --git a/target-arm/cpu.c b/target-arm/cpu.c
>> new file mode 100644
>> index 0000000..dabc094
>> --- /dev/null
>> +++ b/target-arm/cpu.c
[...]
>> +static void cpu_register(const ARMCPUInfo *info)
>> +{
>> +    TypeInfo type = {
>> +        .name = info->name,
>> +        .parent = TYPE_ARM_CPU,
>> +        .instance_size = sizeof(ARMCPU),
>> +        .class_size = sizeof(ARMCPUClass),
>> +        .class_init = arm_cpu_class_init,
>> +        .class_data = (void *)info,
>> +    };
> 
> Are non-initialized members guaranteed to be zero here?

I thought so for the C99-style struct initialization... I never ran into
crashes while testing. Do we need static to be safe?

>> +    type_register_static(&type);
>> +}
>> +
> 
> Probably should be type_register() here in case these two will actually
> differ in the future.

My thinking was we don't need it here because the data (esp. strings)
are not dynamically allocated. By comparison, I used type_register() for
-cpudef in target-i386, I believe.

But I really guess it's a bug that they're just an alias right now! ;)

> If this information is of any help, we've got no problems when emulating
> ARM-based Exynos boards in QEMU with this whole patchset applied.

Thanks a lot for testing!

Have you thought about how to QOM'ify your boards? Mid-term I'd like to
see an "exynos4210" object with the CPUs on it - maybe "cpu[0]" and
"cpu[1]" child properties? Or "core[x]"? I had played with the sh7750 a
bit on my branch but like the arm926 it's a single-core.

Andreas
Eric Blake March 13, 2012, 6:04 p.m. UTC | #3
On 03/13/2012 11:58 AM, Andreas Färber wrote:

>>> +static void cpu_register(const ARMCPUInfo *info)
>>> +{
>>> +    TypeInfo type = {
>>> +        .name = info->name,

>>
>> Are non-initialized members guaranteed to be zero here?
> 
> I thought so for the C99-style struct initialization... I never ran into
> crashes while testing. Do we need static to be safe?

Yes, C99 guarantees in 6.7.8 p19:

The initialization shall occur in initializer list order, each
initializer provided for a particular subobject overriding any
previously listed initializer for the same subobject; all subobjects
that are not initialized explicitly shall be initialized implicitly the
same as objects that have static storage duration.
Paolo Bonzini March 13, 2012, 6:05 p.m. UTC | #4
Il 13/03/2012 18:58, Andreas Färber ha scritto:
>> > Are non-initialized members guaranteed to be zero here?
> I thought so for the C99-style struct initialization...

Yes.

> I never ran into
> crashes while testing. Do we need static to be safe?

No, I don't think so.  It would raise other questions (static seems to
imply that the lifetime needs to extend across multiple calls, one could
wonder if it is safe to reuse TypeInfos that way; with stack-allocated
storage it's more obvious).

Paolo
Eric Blake March 13, 2012, 6:09 p.m. UTC | #5
On 03/13/2012 12:04 PM, Eric Blake wrote:
> On 03/13/2012 11:58 AM, Andreas Färber wrote:
> 
>>>> +static void cpu_register(const ARMCPUInfo *info)
>>>> +{
>>>> +    TypeInfo type = {
>>>> +        .name = info->name,
> 
>>>
>>> Are non-initialized members guaranteed to be zero here?
>>
>> I thought so for the C99-style struct initialization... I never ran into
>> crashes while testing. Do we need static to be safe?
> 
> Yes, C99 guarantees in 6.7.8 p19:

Apologies for my ambiguous answer.  Yes, C99 guarantees that
non-mentioned members are 0-initialized if you have at least one
explicit initialization.  Therefore, no, you don't need static to be safe.
Peter Maydell March 13, 2012, 6:12 p.m. UTC | #6
On 13 March 2012 17:58, Andreas Färber <afaerber@suse.de> wrote:
> Have you thought about how to QOM'ify your boards? Mid-term I'd like to
> see an "exynos4210" object with the CPUs on it - maybe "cpu[0]" and
> "cpu[1]" child properties? Or "core[x]"? I had played with the sh7750 a
> bit on my branch but like the arm926 it's a single-core.

I think the exynos4210 object should have a single child object
cortex-a9 which in turn has child objects for core[0], core[1],
gic, scu, and the private timers and watchdogs.

In hardware the A9 is a single block which includes all the
cores and all the onboard peripherals; we should model it
the same way.

One step in this direction might be to disentangle the GIC
so that a9mpcore_priv and friends instantiate a GIC object
as a child object rather than subclassing the GIC. I think
now that Avi has made sub-page-size memory regions work
we can get the M profile NVIC code out of arm_gic.c and
have a standalone GIC object everybody instantiates.

-- PMM
Mitsyanko Igor March 14, 2012, 8:58 a.m. UTC | #7
On 03/13/2012 09:58 PM, Andreas Färber wrote:
> Am 13.03.2012 13:31, schrieb Igor Mitsyanko:
>> On 03/10/2012 08:53 PM, Andreas Färber wrote:
>>> diff --git a/target-arm/cpu.c b/target-arm/cpu.c
>>> new file mode 100644
>>> index 0000000..dabc094
>>> --- /dev/null
>>> +++ b/target-arm/cpu.c
> [...]
>>> +static void cpu_register(const ARMCPUInfo *info)
>>> +{
>>> +    TypeInfo type = {
>>> +        .name = info->name,
>>> +        .parent = TYPE_ARM_CPU,
>>> +        .instance_size = sizeof(ARMCPU),
>>> +        .class_size = sizeof(ARMCPUClass),
>>> +        .class_init = arm_cpu_class_init,
>>> +        .class_data = (void *)info,
>>> +    };
>> Are non-initialized members guaranteed to be zero here?
> I thought so for the C99-style struct initialization... I never ran into
> crashes while testing. Do we need static to be safe?
>
>>> +    type_register_static(&type);
>>> +}
>>> +
>> Probably should be type_register() here in case these two will actually
>> differ in the future.
> My thinking was we don't need it here because the data (esp. strings)
> are not dynamically allocated. By comparison, I used type_register() for
> -cpudef in target-i386, I believe.
>
> But I really guess it's a bug that they're just an alias right now! ;)
>

Object.h states we need to use type_register_static() only when TypeInfo 
exists for the lifetime of
Type. As I understand this, TypeInfo must have *static const* storage 
qualifier when used with type_register_static() and can have automatic 
storage qualifier when used with type_register().
Maybe in the future type_register_static() will just copy a pointer to 
string variables of passed
TypeInfo instead of g_strdup() them, then your code wouldn't work as 
expected.

>> If this information is of any help, we've got no problems when emulating
>> ARM-based Exynos boards in QEMU with this whole patchset applied.
> Thanks a lot for testing!
>
> Have you thought about how to QOM'ify your boards? Mid-term I'd like to
> see an "exynos4210" object with the CPUs on it - maybe "cpu[0]" and
> "cpu[1]" child properties? Or "core[x]"? I had played with the sh7750 a
> bit on my branch but like the arm926 it's a single-core.
>
> Andreas
>
Yes, we've done some work on this, but we are waiting to see how you 
want to proceed with SoC
QOM type, and if you're going to introduce it at all. Personally I have 
no idea what members and
methods a general SoC type could have.
And another question, should we somehow exploit a fact that majority of 
peripheral devices on
SoCs share i/o ports with each other and only a handful of them are 
actually active at the same
time. Currently we instantiate all SoC devices even if half of them are 
not used in emulated configuration. Maybe we should dynamically 
create/destroy devices depending on runtime GPIO
configuration, or make all devices a board's child instead of SoC's 
child. Not sure that it's worth an effort though.

As for CPU's object, Peter's idea with CortexA9 and core1, core2 childs 
looks good to me.
diff mbox

Patch

diff --git a/Makefile.target b/Makefile.target
index cb1532a..c2c4bca 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -87,6 +87,7 @@  endif
 libobj-$(TARGET_SPARC64) += vis_helper.o
 libobj-$(CONFIG_NEED_MMU) += mmu.o
 libobj-$(TARGET_ARM) += neon_helper.o iwmmxt_helper.o
+libobj-$(TARGET_ARM) += cpu.o
 ifeq ($(TARGET_BASE_ARCH), sparc)
 libobj-y += fop_helper.o cc_helper.o win_helper.o mmu_helper.o ldst_helper.o
 libobj-y += cpu_init.o
diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
new file mode 100644
index 0000000..b2917ea
--- /dev/null
+++ b/target-arm/cpu-qom.h
@@ -0,0 +1,64 @@ 
+/*
+ * QEMU ARM CPU
+ *
+ * Copyright (c) 2012 SUSE LINUX Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see
+ * <http://www.gnu.org/licenses/gpl-2.0.html>
+ */
+#ifndef QEMU_ARM_CPU_QOM_H
+#define QEMU_ARM_CPU_QOM_H
+
+#include "qemu/cpu.h"
+
+#define TYPE_ARM_CPU "arm-cpu"
+
+#define ARM_CPU_CLASS(klass) \
+    OBJECT_CLASS_CHECK(ARMCPUClass, (klass), TYPE_ARM_CPU)
+#define ARM_CPU(obj) \
+    OBJECT_CHECK(ARMCPU, (obj), TYPE_ARM_CPU)
+#define ARM_CPU_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(ARMCPUClass, (obj), TYPE_ARM_CPU)
+
+/**
+ * ARMCPUClass:
+ * @parent_reset: The parent class' reset handler.
+ *
+ * An ARM CPU model.
+ */
+typedef struct ARMCPUClass {
+    /*< private >*/
+    CPUClass parent_class;
+    /*< public >*/
+
+    void (*parent_reset)(CPUState *cpu);
+
+    struct {
+        uint32_t c0_cpuid;
+    } cp15;
+} ARMCPUClass;
+
+/**
+ * ARMCPU:
+ *
+ * An ARM CPU core.
+ */
+typedef struct ARMCPU {
+    /*< private >*/
+    CPUState parent_obj;
+    /*< public >*/
+} ARMCPU;
+
+
+#endif
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
new file mode 100644
index 0000000..dabc094
--- /dev/null
+++ b/target-arm/cpu.c
@@ -0,0 +1,193 @@ 
+/*
+ * QEMU ARM CPU
+ *
+ * Copyright (c) 2012 SUSE LINUX Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see
+ * <http://www.gnu.org/licenses/gpl-2.0.html>
+ */
+
+#include "cpu-qom.h"
+#include "qemu-common.h"
+
+static void arm_cpu_reset(CPUState *c)
+{
+    ARMCPUClass *klass = ARM_CPU_GET_CLASS(c);
+
+    klass->parent_reset(c);
+}
+
+/* CPU models */
+
+typedef struct ARMCPUInfo {
+    const char *name;
+    uint32_t id;
+} ARMCPUInfo;
+
+static const ARMCPUInfo arm_cpus[] = {
+    {
+        .name = "arm926",
+        .id = 0x41069265,
+    },
+    {
+        .name = "arm946",
+        .id = 0x41059461,
+    },
+    {
+        .name = "arm1026",
+        .id = 0x4106a262,
+    },
+    /* What QEMU calls "arm1136-r2" is actually the 1136 r0p2, i.e. an
+     * older core than plain "arm1136". In particular this does not
+     * have the v6K features.
+     */
+    {
+        .name = "arm1136-r2",
+        .id = 0x4107b362,
+    },
+    {
+        .name = "arm1136",
+        .id = 0x4117b363,
+    },
+    {
+        .name = "arm1176",
+        .id = 0x410fb767,
+    },
+    {
+        .name = "arm11mpcore",
+        .id = 0x410fb022,
+    },
+    {
+        .name = "cortex-m3",
+        .id = 0x410fc231,
+    },
+    {
+        .name = "cortex-a8",
+        .id = 0x410fc080,
+    },
+    {
+        .name = "cortex-a9",
+        .id = 0x410fc090,
+    },
+    {
+        .name = "cortex-a15",
+        .id = 0x412fc0f1,
+    },
+    {
+        .name = "ti925t",
+        .id = 0x54029252,
+    },
+    {
+        .name = "sa1100",
+        .id = 0x4401A11B,
+    },
+    {
+        .name = "sa1110",
+        .id = 0x6901B119,
+    },
+    {
+        .name = "pxa250",
+        .id = 0x69052100,
+    },
+    {
+        .name = "pxa255",
+        .id = 0x69052d00,
+    },
+    {
+        .name = "pxa260",
+        .id = 0x69052903,
+    },
+    {
+        .name = "pxa261",
+        .id = 0x69052d05,
+    },
+    {
+        .name = "pxa262",
+        .id = 0x69052d06,
+    },
+    {
+        .name = "pxa270-a0",
+        .id = 0x69054110,
+    },
+    {
+        .name = "pxa270-a1",
+        .id = 0x69054111,
+    },
+    {
+        .name = "pxa270-b0",
+        .id = 0x69054112,
+    },
+    {
+        .name = "pxa270-b1",
+        .id = 0x69054113,
+    },
+    {
+        .name = "pxa270-c0",
+        .id = 0x69054114,
+    },
+    {
+        .name = "pxa270-c5",
+        .id = 0x69054117,
+    },
+    {
+        .name = "any",
+        .id = 0xffffffff,
+    },
+};
+
+static void arm_cpu_class_init(ObjectClass *klass, void *data)
+{
+    ARMCPUClass *k = ARM_CPU_CLASS(klass);
+    CPUClass *cpu_class = CPU_CLASS(klass);
+    const ARMCPUInfo *info = data;
+
+    k->parent_reset = cpu_class->reset;
+    cpu_class->reset = arm_cpu_reset;
+
+    k->cp15.c0_cpuid = info->id;
+}
+
+static void cpu_register(const ARMCPUInfo *info)
+{
+    TypeInfo type = {
+        .name = info->name,
+        .parent = TYPE_ARM_CPU,
+        .instance_size = sizeof(ARMCPU),
+        .class_size = sizeof(ARMCPUClass),
+        .class_init = arm_cpu_class_init,
+        .class_data = (void *)info,
+    };
+
+    type_register_static(&type);
+}
+
+static TypeInfo arm_cpu_type_info = {
+    .name = TYPE_ARM_CPU,
+    .parent = TYPE_CPU,
+    .instance_size = sizeof(ARMCPU),
+    .abstract = true,
+    .class_size = sizeof(ARMCPUClass),
+};
+
+static void arm_cpu_register_types(void)
+{
+    int i;
+
+    type_register_static(&arm_cpu_type_info);
+    for (i = 0; i < ARRAY_SIZE(arm_cpus); i++) {
+        cpu_register(&arm_cpus[i]);
+    }
+}
+
+type_init(arm_cpu_register_types)
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 26c114b..4cfa09c 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -420,7 +420,6 @@  void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
 #define ARM_CPUID_PXA260      0x69052903
 #define ARM_CPUID_PXA261      0x69052d05
 #define ARM_CPUID_PXA262      0x69052d06
-#define ARM_CPUID_PXA270      0x69054110
 #define ARM_CPUID_PXA270_A0   0x69054110
 #define ARM_CPUID_PXA270_A1   0x69054111
 #define ARM_CPUID_PXA270_B0   0x69054112
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 8a08db8..bc29183 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -6,6 +6,7 @@ 
 #include "hw/loader.h"
 #endif
 #include "sysemu.h"
+#include "cpu-qom.h"
 
 static uint32_t cortexa15_cp15_c0_c1[8] = {
     0x00001131, 0x00011011, 0x02010555, 0x00000000,
@@ -46,8 +47,6 @@  static uint32_t arm1176_cp15_c0_c1[8] =
 static uint32_t arm1176_cp15_c0_c2[8] =
 { 0x0140011, 0x12002111, 0x11231121, 0x01102131, 0x01141, 0, 0, 0 };
 
-static uint32_t cpu_arm_find_by_name(const char *name);
-
 static inline void set_feature(CPUARMState *env, int feature)
 {
     env->features |= 1u << feature;
@@ -400,13 +399,21 @@  static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg)
 
 CPUARMState *cpu_arm_init(const char *cpu_model)
 {
+    ObjectClass *klass;
+    ARMCPUClass *cpu_class;
     CPUARMState *env;
-    uint32_t id;
     static int inited = 0;
 
-    id = cpu_arm_find_by_name(cpu_model);
-    if (id == 0)
+    /* One legacy alias to check */
+    if (strcmp(cpu_model, "pxa270") == 0) {
+        cpu_model = "pxa270-a0";
+    }
+
+    klass = object_class_by_name(cpu_model);
+    if (klass == NULL) {
         return NULL;
+    }
+    cpu_class = ARM_CPU_CLASS(klass);
     env = g_malloc0(sizeof(CPUARMState));
     cpu_exec_init(env);
     if (tcg_enabled() && !inited) {
@@ -415,7 +422,7 @@  CPUARMState *cpu_arm_init(const char *cpu_model)
     }
 
     env->cpu_model_str = cpu_model;
-    env->cp15.c0_cpuid = id;
+    env->cp15.c0_cpuid = cpu_class->cp15.c0_cpuid;
     cpu_state_reset(env);
     if (arm_feature(env, ARM_FEATURE_NEON)) {
         gdb_register_coprocessor(env, vfp_gdb_get_reg, vfp_gdb_set_reg,
@@ -431,66 +438,51 @@  CPUARMState *cpu_arm_init(const char *cpu_model)
     return env;
 }
 
-struct arm_cpu_t {
-    uint32_t id;
-    const char *name;
-};
-
-static const struct arm_cpu_t arm_cpu_names[] = {
-    { ARM_CPUID_ARM926, "arm926"},
-    { ARM_CPUID_ARM946, "arm946"},
-    { ARM_CPUID_ARM1026, "arm1026"},
-    { ARM_CPUID_ARM1136, "arm1136"},
-    { ARM_CPUID_ARM1136_R2, "arm1136-r2"},
-    { ARM_CPUID_ARM1176, "arm1176"},
-    { ARM_CPUID_ARM11MPCORE, "arm11mpcore"},
-    { ARM_CPUID_CORTEXM3, "cortex-m3"},
-    { ARM_CPUID_CORTEXA8, "cortex-a8"},
-    { ARM_CPUID_CORTEXA9, "cortex-a9"},
-    { ARM_CPUID_CORTEXA15, "cortex-a15" },
-    { ARM_CPUID_TI925T, "ti925t" },
-    { ARM_CPUID_PXA250, "pxa250" },
-    { ARM_CPUID_SA1100,    "sa1100" },
-    { ARM_CPUID_SA1110,    "sa1110" },
-    { ARM_CPUID_PXA255, "pxa255" },
-    { ARM_CPUID_PXA260, "pxa260" },
-    { ARM_CPUID_PXA261, "pxa261" },
-    { ARM_CPUID_PXA262, "pxa262" },
-    { ARM_CPUID_PXA270, "pxa270" },
-    { ARM_CPUID_PXA270_A0, "pxa270-a0" },
-    { ARM_CPUID_PXA270_A1, "pxa270-a1" },
-    { ARM_CPUID_PXA270_B0, "pxa270-b0" },
-    { ARM_CPUID_PXA270_B1, "pxa270-b1" },
-    { ARM_CPUID_PXA270_C0, "pxa270-c0" },
-    { ARM_CPUID_PXA270_C5, "pxa270-c5" },
-    { ARM_CPUID_ANY, "any"},
-    { 0, NULL}
-};
+typedef struct ARMCPUListState {
+    fprintf_function cpu_fprintf;
+    FILE *file;
+} ARMCPUListState;
 
-void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+/* Sort alphabetically by type name, except for "any". */
+static gint arm_cpu_list_compare(gconstpointer a, gconstpointer b)
 {
-    int i;
+    ObjectClass *class_a = (ObjectClass *)a;
+    ObjectClass *class_b = (ObjectClass *)b;
+    const char *name_a, *name_b;
 
-    (*cpu_fprintf)(f, "Available CPUs:\n");
-    for (i = 0; arm_cpu_names[i].name; i++) {
-        (*cpu_fprintf)(f, "  %s\n", arm_cpu_names[i].name);
+    name_a = object_class_get_name(class_a);
+    name_b = object_class_get_name(class_b);
+    if (strcmp(name_a, "any") == 0) {
+        return 1;
+    } else if (strcmp(name_b, "any") == 0) {
+        return -1;
+    } else {
+        return strcmp(name_a, name_b);
     }
 }
 
-/* return 0 if not found */
-static uint32_t cpu_arm_find_by_name(const char *name)
+static void arm_cpu_list_entry(gpointer data, gpointer user_data)
 {
-    int i;
-    uint32_t id;
+    ObjectClass *klass = data;
+    ARMCPUListState *s = user_data;
 
-    id = 0;
-    for (i = 0; arm_cpu_names[i].name; i++) {
-        if (strcmp(name, arm_cpu_names[i].name) == 0) {
-            id = arm_cpu_names[i].id;
-            break;
-        }
-    }
-    return id;
+    (*s->cpu_fprintf)(s->file, "  %s\n",
+                      object_class_get_name(klass));
+}
+
+void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+{
+    ARMCPUListState s = {
+        .file = f,
+        .cpu_fprintf = cpu_fprintf,
+    };
+    GSList *list;
+
+    list = object_class_get_list(TYPE_ARM_CPU, false);
+    list = g_slist_sort(list, arm_cpu_list_compare);
+    (*cpu_fprintf)(f, "Available CPUs:\n");
+    g_slist_foreach(list, arm_cpu_list_entry, &s);
+    g_slist_free(list);
 }
 
 void cpu_arm_close(CPUARMState *env)