target-ppc: Slim conversion of model definitions to QOM subclasses

Since the model list is highly macrofied, keep ppc_def_t for now and
save a pointer to it in PowerPCCPUClass. This results in a flat list of
subclasses including aliases, to be refined later.

Move cpu_ppc_init() to translate_init.c and drop helper.c.
Long-term the idea is to turn translate_init.c into a standalone cpu.c.

Inline cpu_ppc_usable() into type registration.

Split cpu_ppc_register() in two by code movement into the initfn and
by turning the remaining part into a realizefn.
Move qemu_init_vcpu() call into the new realizefn and adapt
create_ppc_opcodes() to return an Error.

Change ppc_find_by_pvr() -> ppc_cpu_class_by_pvr().
Change ppc_find_by_name() -> ppc_cpu_class_by_name().

Turn -cpu host into its own subclass. This requires to move the
kvm_enabled() check in ppc_cpu_class_by_name() to avoid the class being
found via the normal name lookup in the !kvm_enabled() case.
Turn kvmppc_host_cpu_def() into the class_init and add an initfn that
asserts KVM is in fact enabled.

Implement -cpu ? and the QMP equivalent in terms of subclasses.
This newly exposes -cpu host to the user, ordered last for -cpu ?.

Signed-off-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
Andreas Färber 2013-01-06 08:31:30 +00:00 committed by Alexander Graf
parent 68c2dd7006
commit 2985b86b5c
7 changed files with 280 additions and 180 deletions

View File

@ -1,7 +1,6 @@
obj-y += translate.o helper.o
obj-y += translate.o
obj-$(CONFIG_SOFTMMU) += machine.o
obj-$(CONFIG_KVM) += kvm.o kvm_ppc.o
obj-y += helper.o
obj-y += excp_helper.o
obj-y += fpu_helper.o
obj-y += int_helper.o

View File

@ -50,6 +50,9 @@ typedef struct PowerPCCPUClass {
/*< public >*/
void (*parent_reset)(CPUState *cpu);
/* TODO inline fields here */
ppc_def_t *info;
} PowerPCCPUClass;
/**
@ -73,5 +76,7 @@ static inline PowerPCCPU *ppc_env_get_cpu(CPUPPCState *env)
#define ENV_GET_CPU(e) CPU(ppc_env_get_cpu(e))
PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr);
#endif

View File

@ -1158,10 +1158,6 @@ void ppc_store_msr (CPUPPCState *env, target_ulong value);
void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf);
const ppc_def_t *ppc_find_by_pvr(uint32_t pvr);
const ppc_def_t *cpu_ppc_find_by_name (const char *name);
int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def);
/* Time-base and decrementer management */
#ifndef NO_CPU_IO_DEFS
uint64_t cpu_ppc_load_tbl (CPUPPCState *env);

View File

@ -1,50 +0,0 @@
/*
* PowerPC emulation helpers for QEMU.
*
* Copyright (c) 2003-2007 Jocelyn Mayer
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "cpu.h"
#include "helper_regs.h"
#include "sysemu/kvm.h"
#include "kvm_ppc.h"
#include "sysemu/cpus.h"
PowerPCCPU *cpu_ppc_init(const char *cpu_model)
{
PowerPCCPU *cpu;
CPUPPCState *env;
const ppc_def_t *def;
def = cpu_ppc_find_by_name(cpu_model);
if (!def) {
return NULL;
}
cpu = POWERPC_CPU(object_new(TYPE_POWERPC_CPU));
env = &cpu->env;
if (tcg_enabled()) {
ppc_translate_init();
}
env->cpu_model_str = cpu_model;
cpu_ppc_register_internal(env, def);
qemu_init_vcpu(env);
return cpu;
}

View File

@ -1230,18 +1230,29 @@ static void alter_insns(uint64_t *word, uint64_t flags, bool on)
}
}
const ppc_def_t *kvmppc_host_cpu_def(void)
static void kvmppc_host_cpu_initfn(Object *obj)
{
assert(kvm_enabled());
}
static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data)
{
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
uint32_t host_pvr = mfpvr();
const ppc_def_t *base_spec;
PowerPCCPUClass *pvr_pcc;
ppc_def_t *spec;
uint32_t vmx = kvmppc_get_vmx();
uint32_t dfp = kvmppc_get_dfp();
base_spec = ppc_find_by_pvr(host_pvr);
spec = g_malloc0(sizeof(*spec));
memcpy(spec, base_spec, sizeof(*spec));
pvr_pcc = ppc_cpu_class_by_pvr(host_pvr);
if (pvr_pcc != NULL) {
memcpy(spec, pvr_pcc->info, sizeof(*spec));
}
pcc->info = spec;
/* Override the display name for -cpu ? and QMP */
pcc->info->name = "host";
/* Now fix up the spec with information we can query from the host */
@ -1254,8 +1265,6 @@ const ppc_def_t *kvmppc_host_cpu_def(void)
/* Only override when we know what the host supports */
alter_insns(&spec->insns_flags2, PPC2_DFP, dfp);
}
return spec;
}
int kvmppc_fixup_cpu(CPUPPCState *env)
@ -1285,3 +1294,17 @@ int kvm_arch_on_sigbus(int code, void *addr)
{
return 1;
}
static const TypeInfo kvm_host_cpu_type_info = {
.name = TYPE_HOST_POWERPC_CPU,
.parent = TYPE_POWERPC_CPU,
.instance_init = kvmppc_host_cpu_initfn,
.class_init = kvmppc_host_cpu_class_init,
};
static void kvm_ppc_register_types(void)
{
type_register_static(&kvm_host_cpu_type_info);
}
type_init(kvm_ppc_register_types)

View File

@ -11,6 +11,8 @@
#include "exec/memory.h"
#define TYPE_HOST_POWERPC_CPU "host-" TYPE_POWERPC_CPU
void kvmppc_init(void);
#ifdef CONFIG_KVM
@ -31,7 +33,6 @@ int kvmppc_remove_spapr_tce(void *table, int pfd, uint32_t window_size);
int kvmppc_reset_htab(int shift_hint);
uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift);
#endif /* !CONFIG_USER_ONLY */
const ppc_def_t *kvmppc_host_cpu_def(void);
int kvmppc_fixup_cpu(CPUPPCState *env);
#else
@ -121,11 +122,6 @@ static inline int kvmppc_update_sdr1(CPUPPCState *env)
#endif /* !CONFIG_USER_ONLY */
static inline const ppc_def_t *kvmppc_host_cpu_def(void)
{
return NULL;
}
static inline int kvmppc_fixup_cpu(CPUPPCState *env)
{
return -1;

View File

@ -9792,8 +9792,11 @@ static void fix_opcode_tables (opc_handler_t **ppc_opcodes)
}
/*****************************************************************************/
static int create_ppc_opcodes (CPUPPCState *env, const ppc_def_t *def)
static void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp)
{
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
CPUPPCState *env = &cpu->env;
const ppc_def_t *def = pcc->info;
opcode_t *opc;
fill_new_table(env->opcodes, 0x40);
@ -9801,18 +9804,16 @@ static int create_ppc_opcodes (CPUPPCState *env, const ppc_def_t *def)
if (((opc->handler.type & def->insns_flags) != 0) ||
((opc->handler.type2 & def->insns_flags2) != 0)) {
if (register_insn(env->opcodes, opc) < 0) {
printf("*** ERROR initializing PowerPC instruction "
"0x%02x 0x%02x 0x%02x\n", opc->opc1, opc->opc2,
opc->opc3);
return -1;
error_setg(errp, "ERROR initializing PowerPC instruction "
"0x%02x 0x%02x 0x%02x\n", opc->opc1, opc->opc2,
opc->opc3);
return;
}
}
}
fix_opcode_tables(env->opcodes);
fflush(stdout);
fflush(stderr);
return 0;
}
#if defined(PPC_DUMP_CPU)
@ -10026,53 +10027,31 @@ static int ppc_fixup_cpu(CPUPPCState *env)
return 0;
}
int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def)
static void ppc_cpu_realize(Object *obj, Error **errp)
{
env->msr_mask = def->msr_mask;
env->mmu_model = def->mmu_model;
env->excp_model = def->excp_model;
env->bus_model = def->bus_model;
env->insns_flags = def->insns_flags;
env->insns_flags2 = def->insns_flags2;
env->flags = def->flags;
env->bfd_mach = def->bfd_mach;
env->check_pow = def->check_pow;
#if defined(TARGET_PPC64)
if (def->sps)
env->sps = *def->sps;
else if (env->mmu_model & POWERPC_MMU_64) {
/* Use default sets of page sizes */
static const struct ppc_segment_page_sizes defsps = {
.sps = {
{ .page_shift = 12, /* 4K */
.slb_enc = 0,
.enc = { { .page_shift = 12, .pte_enc = 0 } }
},
{ .page_shift = 24, /* 16M */
.slb_enc = 0x100,
.enc = { { .page_shift = 24, .pte_enc = 0 } }
},
},
};
env->sps = defsps;
}
#endif /* defined(TARGET_PPC64) */
PowerPCCPU *cpu = POWERPC_CPU(obj);
CPUPPCState *env = &cpu->env;
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
ppc_def_t *def = pcc->info;
Error *local_err = NULL;
if (kvm_enabled()) {
if (kvmppc_fixup_cpu(env) != 0) {
fprintf(stderr, "Unable to virtualize selected CPU with KVM\n");
exit(1);
error_setg(errp, "Unable to virtualize selected CPU with KVM");
return;
}
} else {
if (ppc_fixup_cpu(env) != 0) {
fprintf(stderr, "Unable to emulate selected CPU with TCG\n");
exit(1);
error_setg(errp, "Unable to emulate selected CPU with TCG");
return;
}
}
if (create_ppc_opcodes(env, def) < 0)
return -1;
create_ppc_opcodes(cpu, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
return;
}
init_ppc_proc(env, def);
if (def->insns_flags & PPC_FLOAT) {
@ -10088,6 +10067,8 @@ int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def)
34, "power-spe.xml", 0);
}
qemu_init_vcpu(env);
#if defined(PPC_DUMP_CPU)
{
const char *mmu_model, *excp_model, *bus_model;
@ -10249,50 +10230,65 @@ int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def)
dump_ppc_sprs(env);
fflush(stdout);
#endif
return 0;
}
static bool ppc_cpu_usable(const ppc_def_t *def)
static gint ppc_cpu_compare_class_pvr(gconstpointer a, gconstpointer b)
{
#if defined(TARGET_PPCEMB)
/* When using the ppcemb target, we only support 440 style cores */
if (def->mmu_model != POWERPC_MMU_BOOKE) {
return false;
}
#endif
ObjectClass *oc = (ObjectClass *)a;
uint32_t pvr = *(uint32_t *)b;
PowerPCCPUClass *pcc = (PowerPCCPUClass *)a;
return true;
/* -cpu host does a PVR lookup during construction */
if (unlikely(strcmp(object_class_get_name(oc),
TYPE_HOST_POWERPC_CPU) == 0)) {
return -1;
}
return pcc->info->pvr == pvr ? 0 : -1;
}
const ppc_def_t *ppc_find_by_pvr(uint32_t pvr)
PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr)
{
int i;
GSList *list, *item;
PowerPCCPUClass *pcc = NULL;
for (i = 0; i < ARRAY_SIZE(ppc_defs); i++) {
if (!ppc_cpu_usable(&ppc_defs[i])) {
continue;
}
/* If we have an exact match, we're done */
if (pvr == ppc_defs[i].pvr) {
return &ppc_defs[i];
}
list = object_class_get_list(TYPE_POWERPC_CPU, false);
item = g_slist_find_custom(list, &pvr, ppc_cpu_compare_class_pvr);
if (item != NULL) {
pcc = POWERPC_CPU_CLASS(item->data);
}
g_slist_free(list);
return NULL;
return pcc;
}
static gint ppc_cpu_compare_class_name(gconstpointer a, gconstpointer b)
{
ObjectClass *oc = (ObjectClass *)a;
const char *name = b;
if (strncasecmp(name, object_class_get_name(oc), strlen(name)) == 0 &&
strcmp(object_class_get_name(oc) + strlen(name),
"-" TYPE_POWERPC_CPU) == 0) {
return 0;
}
return -1;
}
#include <ctype.h>
const ppc_def_t *cpu_ppc_find_by_name (const char *name)
static ObjectClass *ppc_cpu_class_by_name(const char *name)
{
const ppc_def_t *ret;
GSList *list, *item;
ObjectClass *ret = NULL;
const char *p;
int i, max, len;
int i, len;
if (kvm_enabled() && (strcasecmp(name, "host") == 0)) {
return kvmppc_host_cpu_def();
if (strcasecmp(name, "host") == 0) {
if (kvm_enabled()) {
ret = object_class_by_name(TYPE_HOST_POWERPC_CPU);
}
return ret;
}
/* Check if the given name is a PVR */
@ -10307,65 +10303,154 @@ const ppc_def_t *cpu_ppc_find_by_name (const char *name)
if (!qemu_isxdigit(*p++))
break;
}
if (i == 8)
return ppc_find_by_pvr(strtoul(name, NULL, 16));
}
ret = NULL;
max = ARRAY_SIZE(ppc_defs);
for (i = 0; i < max; i++) {
if (!ppc_cpu_usable(&ppc_defs[i])) {
continue;
if (i == 8) {
ret = OBJECT_CLASS(ppc_cpu_class_by_pvr(strtoul(name, NULL, 16)));
return ret;
}
}
if (strcasecmp(name, ppc_defs[i].name) == 0) {
ret = &ppc_defs[i];
break;
}
list = object_class_get_list(TYPE_POWERPC_CPU, false);
item = g_slist_find_custom(list, name, ppc_cpu_compare_class_name);
if (item != NULL) {
ret = OBJECT_CLASS(item->data);
}
g_slist_free(list);
return ret;
}
void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf)
PowerPCCPU *cpu_ppc_init(const char *cpu_model)
{
int i, max;
PowerPCCPU *cpu;
CPUPPCState *env;
ObjectClass *oc;
Error *err = NULL;
max = ARRAY_SIZE(ppc_defs);
for (i = 0; i < max; i++) {
if (!ppc_cpu_usable(&ppc_defs[i])) {
continue;
}
(*cpu_fprintf)(f, "PowerPC %-16s PVR %08x\n",
ppc_defs[i].name, ppc_defs[i].pvr);
oc = ppc_cpu_class_by_name(cpu_model);
if (oc == NULL) {
return NULL;
}
cpu = POWERPC_CPU(object_new(object_class_get_name(oc)));
env = &cpu->env;
if (tcg_enabled()) {
ppc_translate_init();
}
env->cpu_model_str = cpu_model;
ppc_cpu_realize(OBJECT(cpu), &err);
if (err != NULL) {
fprintf(stderr, "%s\n", error_get_pretty(err));
error_free(err);
object_delete(OBJECT(cpu));
return NULL;
}
return cpu;
}
/* Sort by PVR, ordering special case "host" last. */
static gint ppc_cpu_list_compare(gconstpointer a, gconstpointer b)
{
ObjectClass *oc_a = (ObjectClass *)a;
ObjectClass *oc_b = (ObjectClass *)b;
PowerPCCPUClass *pcc_a = POWERPC_CPU_CLASS(oc_a);
PowerPCCPUClass *pcc_b = POWERPC_CPU_CLASS(oc_b);
const char *name_a = object_class_get_name(oc_a);
const char *name_b = object_class_get_name(oc_b);
if (strcmp(name_a, TYPE_HOST_POWERPC_CPU) == 0) {
return 1;
} else if (strcmp(name_b, TYPE_HOST_POWERPC_CPU) == 0) {
return -1;
} else {
/* Avoid an integer overflow during subtraction */
if (pcc_a->info->pvr < pcc_b->info->pvr) {
return -1;
} else if (pcc_a->info->pvr > pcc_b->info->pvr) {
return 1;
} else {
return 0;
}
}
}
static void ppc_cpu_list_entry(gpointer data, gpointer user_data)
{
ObjectClass *oc = data;
CPUListState *s = user_data;
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
(*s->cpu_fprintf)(s->file, "PowerPC %-16s PVR %08x\n",
pcc->info->name, pcc->info->pvr);
}
void ppc_cpu_list(FILE *f, fprintf_function cpu_fprintf)
{
CPUListState s = {
.file = f,
.cpu_fprintf = cpu_fprintf,
};
GSList *list;
list = object_class_get_list(TYPE_POWERPC_CPU, false);
list = g_slist_sort(list, ppc_cpu_list_compare);
g_slist_foreach(list, ppc_cpu_list_entry, &s);
g_slist_free(list);
}
static void ppc_cpu_defs_entry(gpointer data, gpointer user_data)
{
ObjectClass *oc = data;
CpuDefinitionInfoList **first = user_data;
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
CpuDefinitionInfoList *entry;
CpuDefinitionInfo *info;
info = g_malloc0(sizeof(*info));
info->name = g_strdup(pcc->info->name);
entry = g_malloc0(sizeof(*entry));
entry->value = info;
entry->next = *first;
*first = entry;
}
CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
{
CpuDefinitionInfoList *cpu_list = NULL;
int i;
GSList *list;
for (i = 0; i < ARRAY_SIZE(ppc_defs); i++) {
CpuDefinitionInfoList *entry;
CpuDefinitionInfo *info;
if (!ppc_cpu_usable(&ppc_defs[i])) {
continue;
}
info = g_malloc0(sizeof(*info));
info->name = g_strdup(ppc_defs[i].name);
entry = g_malloc0(sizeof(*entry));
entry->value = info;
entry->next = cpu_list;
cpu_list = entry;
}
list = object_class_get_list(TYPE_POWERPC_CPU, false);
g_slist_foreach(list, ppc_cpu_defs_entry, &cpu_list);
g_slist_free(list);
return cpu_list;
}
static void ppc_cpu_def_class_init(ObjectClass *oc, void *data)
{
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
ppc_def_t *info = data;
pcc->info = info;
}
static void ppc_cpu_register_model(const ppc_def_t *def)
{
TypeInfo type_info = {
.parent = TYPE_POWERPC_CPU,
.class_init = ppc_cpu_def_class_init,
.class_data = (void *)def,
};
type_info.name = g_strdup_printf("%s-" TYPE_POWERPC_CPU, def->name),
type_register(&type_info);
g_free((gpointer)type_info.name);
}
/* CPUClass::reset() */
static void ppc_cpu_reset(CPUState *s)
{
@ -10434,9 +10519,42 @@ static void ppc_cpu_reset(CPUState *s)
static void ppc_cpu_initfn(Object *obj)
{
PowerPCCPU *cpu = POWERPC_CPU(obj);
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
CPUPPCState *env = &cpu->env;
ppc_def_t *def = pcc->info;
cpu_exec_init(env);
env->msr_mask = def->msr_mask;
env->mmu_model = def->mmu_model;
env->excp_model = def->excp_model;
env->bus_model = def->bus_model;
env->insns_flags = def->insns_flags;
env->insns_flags2 = def->insns_flags2;
env->flags = def->flags;
env->bfd_mach = def->bfd_mach;
env->check_pow = def->check_pow;
#if defined(TARGET_PPC64)
if (def->sps) {
env->sps = *def->sps;
} else if (env->mmu_model & POWERPC_MMU_64) {
/* Use default sets of page sizes */
static const struct ppc_segment_page_sizes defsps = {
.sps = {
{ .page_shift = 12, /* 4K */
.slb_enc = 0,
.enc = { { .page_shift = 12, .pte_enc = 0 } }
},
{ .page_shift = 24, /* 16M */
.slb_enc = 0x100,
.enc = { { .page_shift = 24, .pte_enc = 0 } }
},
},
};
env->sps = defsps;
}
#endif /* defined(TARGET_PPC64) */
}
static void ppc_cpu_class_init(ObjectClass *oc, void *data)
@ -10453,14 +10571,27 @@ static const TypeInfo ppc_cpu_type_info = {
.parent = TYPE_CPU,
.instance_size = sizeof(PowerPCCPU),
.instance_init = ppc_cpu_initfn,
.abstract = false,
.abstract = true,
.class_size = sizeof(PowerPCCPUClass),
.class_init = ppc_cpu_class_init,
};
static void ppc_cpu_register_types(void)
{
int i;
type_register_static(&ppc_cpu_type_info);
for (i = 0; i < ARRAY_SIZE(ppc_defs); i++) {
const ppc_def_t *def = &ppc_defs[i];
#if defined(TARGET_PPCEMB)
/* When using the ppcemb target, we only support 440 style cores */
if (def->mmu_model != POWERPC_MMU_BOOKE) {
continue;
}
#endif
ppc_cpu_register_model(def);
}
}
type_init(ppc_cpu_register_types)