To be able to boot a guest on the hardware which is newer than the guest actually supports, PowerISA defines a logical PVR per every PowerISA specification version from 2.04.
This adds the "compat" option which takes values 205 or 206 and forces QEMU to boot the guest with a logical PVR (CPU_POWERPC_LOGICAL_2_05 or CPU_POWERPC_LOGICAL_2_06) which is stored in the "cpu-version" property of CPU device nodes. Cc: Andreas Färber <afaer...@suse.de> Signed-off-by: Alexey Kardashevskiy <a...@ozlabs.ru> --- Changes: v3: * "compat" option accepts "power6" and "power7" now --- hw/ppc/spapr.c | 9 +++++++ target-ppc/cpu-models.h | 10 ++++++++ target-ppc/cpu.h | 4 +++ target-ppc/translate_init.c | 61 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 2e18bdb..9fcbd96 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -206,6 +206,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr) CPU_FOREACH(cpu) { DeviceClass *dc = DEVICE_GET_CLASS(cpu); + CPUPPCState *env = &POWERPC_CPU(cpu)->env; uint32_t associativity[] = {cpu_to_be32(0x5), cpu_to_be32(0x0), cpu_to_be32(0x0), @@ -238,6 +239,14 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr) if (ret < 0) { return ret; } + + if (env->compat) { + ret = fdt_setprop(fdt, offset, "cpu-version", + &env->compat, sizeof(env->compat)); + if (ret < 0) { + return ret; + } + } } return ret; } diff --git a/target-ppc/cpu-models.h b/target-ppc/cpu-models.h index 49ba4a4..d7c033c 100644 --- a/target-ppc/cpu-models.h +++ b/target-ppc/cpu-models.h @@ -583,6 +583,16 @@ enum { CPU_POWERPC_RS64II = 0x00340000, CPU_POWERPC_RS64III = 0x00360000, CPU_POWERPC_RS64IV = 0x00370000, + + /* Logical CPUs */ + CPU_POWERPC_LOGICAL_MASK = 0xFFFFFFFF, + CPU_POWERPC_LOGICAL_2_04 = 0x0F000001, + CPU_POWERPC_LOGICAL_2_05 = 0x0F000002, + CPU_POWERPC_LOGICAL_2_06 = 0x0F000003, + CPU_POWERPC_LOGICAL_2_06_PLUS = 0x0F100003, + CPU_POWERPC_LOGICAL_2_07 = 0x0F000004, + CPU_POWERPC_LOGICAL_2_08 = 0x0F000005, + #endif /* defined(TARGET_PPC64) */ /* Original POWER */ /* XXX: should be POWER (RIOS), RSC3308, RSC4608, diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index bb84767..8e30518 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1006,6 +1006,9 @@ struct CPUPPCState { /* Device control registers */ ppc_dcr_t *dcr_env; + /* Architecture compatibility mode PVR */ + uint32_t compat; + int dcache_line_size; int icache_line_size; @@ -1330,6 +1333,7 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_BOOKE_DVC1 (0x13E) #define SPR_BOOKE_DVC2 (0x13F) #define SPR_BOOKE_TSR (0x150) +#define SPR_PCR (0x152) #define SPR_BOOKE_TCR (0x154) #define SPR_BOOKE_TLB0PS (0x158) #define SPR_BOOKE_TLB1PS (0x159) diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 9e4af56..df0d81c 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -28,6 +28,7 @@ #include "mmu-hash32.h" #include "mmu-hash64.h" #include "qemu/error-report.h" +#include "qapi/visitor.h" //#define PPC_DUMP_CPU //#define PPC_DEBUG_SPR @@ -7135,8 +7136,60 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } +static void powerpc_get_compat(Object *obj, Visitor *v, + void *opaque, const char *name, Error **errp) +{ + PowerPCCPU *cpu = POWERPC_CPU(obj); + char *value = (char *)""; + + switch (cpu->env.compat) { + case CPU_POWERPC_LOGICAL_2_05: + value = (char *)"power6"; + break; + case CPU_POWERPC_LOGICAL_2_06: + value = (char *)"power7"; + break; + case 0: + break; + default: + error_setg(errp, "Internal error: compat is set to %x", + cpu->env.compat); + break; + } + + visit_type_str(v, &value, name, errp); +} + +static void powerpc_set_compat(Object *obj, Visitor *v, + void *opaque, const char *name, Error **errp) +{ + PowerPCCPU *cpu = POWERPC_CPU(obj); + Error *error = NULL; + /* TODO: Implement check for the value length */ + char *value = NULL; + + visit_type_str(v, &value, name, &error); + if (error) { + error_propagate(errp, error); + return; + } + + if (strcmp(value, "power6") == 0) { + cpu->env.compat = CPU_POWERPC_LOGICAL_2_05; + } else if (strcmp(value, "power7") == 0) { + cpu->env.compat = CPU_POWERPC_LOGICAL_2_06; + } else { + error_setg(errp, "Invalid compatibility mode \"%s\", only \"power6\" and \"power7\" supported", + value); + } + + g_free(value); +} + static void init_proc_POWER7 (CPUPPCState *env) { + PowerPCCPU *cpu = container_of(env, PowerPCCPU, env); + gen_spr_ne_601(env); gen_spr_7xx(env); /* Time base */ @@ -7201,6 +7254,10 @@ static void init_proc_POWER7 (CPUPPCState *env) &spr_read_generic, &spr_write_generic, &spr_read_generic, &spr_write_generic, 0x00000000); + spr_register(env, SPR_PCR, "PCR", + &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, + 0x00000000); #if !defined(CONFIG_USER_ONLY) env->slb_nr = 32; #endif @@ -7213,6 +7270,10 @@ static void init_proc_POWER7 (CPUPPCState *env) /* Can't find information on what this should be on reset. This * value is the one used by 74xx processors. */ vscr_init(env, 0x00010000); + + object_property_add(OBJECT(cpu), "compat", "int", + powerpc_get_compat, powerpc_set_compat, + NULL, NULL, NULL); } POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) -- 1.8.4.rc4