* inside "#if defined(TODO) ... #endif" statements to make tests easier.
*/
-#include "dis-asm.h"
-#include "gdbstub.h"
-#include <kvm.h>
+#include "disas/bfd.h"
+#include "exec/gdbstub.h"
+#include <sysemu/kvm.h>
#include "kvm_ppc.h"
-#include "arch_init.h"
+#include "sysemu/arch_init.h"
+#include "sysemu/cpus.h"
//#define PPC_DUMP_CPU
//#define PPC_DEBUG_SPR
/* XXX : not implemented */
spr_register(env, SPR_BOOKE_DBCR0, "DBCR0",
SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_40x_dbcr0,
0x00000000);
/* XXX : not implemented */
spr_register(env, SPR_BOOKE_DBCR1, "DBCR1",
tcg_temp_free(mas7);
}
-static void spr_load_epr(void *opaque, int gprn, int sprn)
-{
- gen_helper_load_epr(cpu_gpr[gprn], cpu_env);
-}
-
#endif
enum fsl_e500_version {
0x00000000);
spr_register(env, SPR_BOOKE_EPR, "EPR",
SPR_NOACCESS, SPR_NOACCESS,
- &spr_load_epr, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
0x00000000);
/* XXX better abstract into Emb.xxx features */
if (version == fsl_e5500) {
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
PPC_FLOAT_STFIWX | \
- PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
PPC_MEM_SYNC | PPC_MEM_EIEIO | \
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
PPC_64B | PPC_ALTIVEC | \
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
PPC_FLOAT_STFIWX | \
- PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
PPC_MEM_SYNC | PPC_MEM_EIEIO | \
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
PPC_64B | PPC_ALTIVEC | \
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
PPC_FLOAT_STFIWX | \
- PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
PPC_MEM_SYNC | PPC_MEM_EIEIO | \
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
PPC_64B | PPC_ALTIVEC | \
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
PPC_FLOAT_STFIWX | \
- PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
PPC_MEM_SYNC | PPC_MEM_EIEIO | \
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
PPC_64B | PPC_ALTIVEC | \
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
PPC_FLOAT_STFIWX | \
- PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
PPC_MEM_SYNC | PPC_MEM_EIEIO | \
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
PPC_64B | PPC_ALTIVEC | \
POWERPC_DEF("e500v2_v22", CPU_POWERPC_e500v2_v22, e500v2),
/* PowerPC e500v2 v3.0 core */
POWERPC_DEF("e500v2_v30", CPU_POWERPC_e500v2_v30, e500v2),
- POWERPC_DEF("e500mc", CPU_POWERPC_e500mc, e500mc),
+ POWERPC_DEF_SVR("e500mc", CPU_POWERPC_e500mc, POWERPC_SVR_E500, e500mc),
#ifdef TARGET_PPC64
- POWERPC_DEF("e5500", CPU_POWERPC_e5500, e5500),
+ POWERPC_DEF_SVR("e5500", CPU_POWERPC_e5500, POWERPC_SVR_E500, e5500),
#endif
/* PowerPC e500 microcontrollers */
/* MPC8533 */
}
/*****************************************************************************/
-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);
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", opc->opc1, opc->opc2,
+ opc->opc3);
+ return;
}
}
}
fix_opcode_tables(env->opcodes);
fflush(stdout);
fflush(stderr);
-
- return 0;
}
#if defined(PPC_DUMP_CPU)
return 0;
}
-static int ppc_fixup_cpu(CPUPPCState *env)
+static int ppc_fixup_cpu(PowerPCCPU *cpu)
{
+ CPUPPCState *env = &cpu->env;
+
/* TCG doesn't (yet) emulate some groups of instructions that
* are implemented on some otherwise supported CPUs (e.g. VSX
* and decimal floating point instructions on POWER7). We
return 0;
}
-int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def)
+static void ppc_cpu_realizefn(DeviceState *dev, 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;
+ PowerPCCPU *cpu = POWERPC_CPU(dev);
+ CPUPPCState *env = &cpu->env;
+ PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
+ ppc_def_t *def = pcc->info;
+ Error *local_err = NULL;
+#if !defined(CONFIG_USER_ONLY)
+ int max_smt = kvm_enabled() ? kvmppc_smt_threads() : 1;
+#endif
-#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;
+#if !defined(CONFIG_USER_ONLY)
+ if (smp_threads > max_smt) {
+ fprintf(stderr, "Cannot support more than %d threads on PPC with %s\n",
+ max_smt, kvm_enabled() ? "KVM" : "TCG");
+ exit(1);
}
-#endif /* defined(TARGET_PPC64) */
+#endif
if (kvm_enabled()) {
- if (kvmppc_fixup_cpu(env) != 0) {
- fprintf(stderr, "Unable to virtualize selected CPU with KVM\n");
- exit(1);
+ if (kvmppc_fixup_cpu(cpu) != 0) {
+ 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);
+ if (ppc_fixup_cpu(cpu) != 0) {
+ 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) {
34, "power-spe.xml", 0);
}
+ qemu_init_vcpu(env);
+
+ pcc->parent_realize(dev, errp);
+
#if defined(PPC_DUMP_CPU)
{
const char *mmu_model, *excp_model, *bus_model;
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;
+ ObjectClass *oc = (ObjectClass *)a;
+ uint32_t pvr = *(uint32_t *)b;
+ PowerPCCPUClass *pcc = (PowerPCCPUClass *)a;
+
+ /* -cpu host does a PVR lookup during construction */
+ if (unlikely(strcmp(object_class_get_name(oc),
+ TYPE_HOST_POWERPC_CPU) == 0)) {
+ return -1;
}
-#endif
- return true;
+ 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;
-
- for (i = 0; i < ARRAY_SIZE(ppc_defs); i++) {
- if (!ppc_cpu_usable(&ppc_defs[i])) {
- continue;
- }
+ GSList *list, *item;
+ PowerPCCPUClass *pcc = NULL;
- /* 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 pcc;
+}
- return NULL;
+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 */
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;
- }
+ 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;
+ env->cpu_model_str = cpu_model;
- (*cpu_fprintf)(f, "PowerPC %-16s PVR %08x\n",
- ppc_defs[i].name, ppc_defs[i].pvr);
+ object_property_set_bool(OBJECT(cpu), true, "realized", &err);
+ if (err != NULL) {
+ fprintf(stderr, "%s\n", error_get_pretty(err));
+ error_free(err);
+ object_unref(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;
+ list = object_class_get_list(TYPE_POWERPC_CPU, false);
+ g_slist_foreach(list, ppc_cpu_defs_entry, &cpu_list);
+ g_slist_free(list);
- if (!ppc_cpu_usable(&ppc_defs[i])) {
- continue;
- }
+ return cpu_list;
+}
- info = g_malloc0(sizeof(*info));
- info->name = g_strdup(ppc_defs[i].name);
+static void ppc_cpu_def_class_init(ObjectClass *oc, void *data)
+{
+ PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
+ ppc_def_t *info = data;
- entry = g_malloc0(sizeof(*entry));
- entry->value = info;
- entry->next = cpu_list;
- cpu_list = entry;
- }
+ pcc->info = info;
+}
- return cpu_list;
+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() */
target_ulong msr;
if (qemu_loglevel_mask(CPU_LOG_RESET)) {
- qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+ qemu_log("CPU Reset (CPU %d)\n", s->cpu_index);
log_cpu_state(env, 0);
}
env->pending_interrupts = 0;
env->exception_index = POWERPC_EXCP_NONE;
env->error_code = 0;
+
+#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
+ env->vpa_addr = 0;
+ env->slb_shadow_addr = 0;
+ env->slb_shadow_size = 0;
+ env->dtl_addr = 0;
+ env->dtl_size = 0;
+#endif /* TARGET_PPC64 */
+
/* Flush all TLBs */
tlb_flush(env, 1);
}
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) */
+
+ if (tcg_enabled()) {
+ ppc_translate_init();
+ }
}
static void ppc_cpu_class_init(ObjectClass *oc, void *data)
{
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
CPUClass *cc = CPU_CLASS(oc);
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ pcc->parent_realize = dc->realize;
+ dc->realize = ppc_cpu_realizefn;
pcc->parent_reset = cc->reset;
cc->reset = ppc_cpu_reset;
+
+ cc->class_by_name = ppc_cpu_class_by_name;
}
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)