On 05/21/2014 07:21 PM, Alexander Graf wrote: > > On 21.05.14 10:27, Alexey Kardashevskiy wrote: >> Modern Linux kernels support last POWERPC CPUs so when a kernel boots, >> in most cases it can find a matching cpu_spec in the kernel's cpu_specs >> list. However if the kernel is quite old, it may be missing a definition >> of the actual CPU. To provide an ability for old kernels to work on modern >> hardware, a Processor Compatibility Mode has been introduced >> by the PowerISA specification. >> >> From the hardware prospective, it is supported by the Processor >> Compatibility Register (PCR) which is defined in PowerISA. The register >> enables one of the compatibility modes (2.05/2.06/2.07). >> Since PCR is a hypervisor privileged register and cannot be >> directly accessed from the guest, the mode selection is done via >> ibm,client-architecture-support (CAS) RTAS call using which the guest >> specifies what "raw" and "architected" CPU versions it supports. >> QEMU works out the best match, changes a "cpu-version" property of >> every CPU and notifies the guest about the change by setting these >> properties in the buffer passed as a response on a custom H_CAS hypercall. >> >> This implements ibm,client-architecture-support parameters parsing >> (now only for PVRs) and cooks the device tree diff with new values for >> "cpu-version", "ibm,ppc-interrupt-server#s" and >> "ibm,ppc-interrupt-server#s" properties. >> >> Signed-off-by: Alexey Kardashevskiy <a...@ozlabs.ru> >> --- >> hw/ppc/spapr.c | 4 ++- >> hw/ppc/spapr_hcall.c | 85 >> ++++++++++++++++++++++++++++++++++++++++++++++++++++ >> trace-events | 4 +++ >> 3 files changed, 92 insertions(+), 1 deletion(-) >> >> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c >> index 14c72d9..020426a 100644 >> --- a/hw/ppc/spapr.c >> +++ b/hw/ppc/spapr.c >> @@ -34,6 +34,7 @@ >> #include "sysemu/kvm.h" >> #include "kvm_ppc.h" >> #include "mmu-hash64.h" >> +#include "qom/cpu.h" >> #include "hw/boards.h" >> #include "hw/ppc/ppc.h" >> @@ -601,7 +602,8 @@ int spapr_h_cas_compose_response(target_ulong addr, >> target_ulong size) >> _FDT((fdt_open_into(fdt_skel, fdt, size))); >> g_free(fdt_skel); >> - /* Place to make changes to the tree */ >> + /* Fix skeleton up */ >> + _FDT((spapr_fixup_cpu_dt(fdt, spapr))); >> /* Pack resulting tree */ >> _FDT((fdt_pack(fdt))); >> diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c >> index 2f6aa5c..2e0a49c 100644 >> --- a/hw/ppc/spapr_hcall.c >> +++ b/hw/ppc/spapr_hcall.c >> @@ -3,6 +3,9 @@ >> #include "helper_regs.h" >> #include "hw/ppc/spapr.h" >> #include "mmu-hash64.h" >> +#include "cpu-models.h" >> +#include "trace.h" >> +#include "kvm_ppc.h" >> struct SPRSyncState { >> CPUState *cs; >> @@ -752,12 +755,94 @@ out: >> return ret; >> } >> +#define get_compat_level(cpuver) ( \ >> + ((cpuver) == CPU_POWERPC_LOGICAL_2_05) ? 2050 : \ >> + ((cpuver) == CPU_POWERPC_LOGICAL_2_06) ? 2060 : \ >> + ((cpuver) == CPU_POWERPC_LOGICAL_2_06_PLUS) ? 2061 : \ >> + ((cpuver) == CPU_POWERPC_LOGICAL_2_07) ? 2070 : 0) >> + >> static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, >> sPAPREnvironment *spapr, >> target_ulong opcode, >> target_ulong *args) >> { >> target_ulong list = args[0]; >> + PowerPCCPUClass *pcc_ = POWERPC_CPU_GET_CLASS(cpu_); >> + CPUState *cs; >> + bool cpu_match = false; >> + unsigned old_cpu_version = cpu_->cpu_version; >> + unsigned compat_lvl = 0, cpu_version = 0; >> + unsigned max_lvl = get_compat_level(cpu_->max_compat); >> + >> + /* Parse PVR list */ >> + for ( ; ; ) { >> + uint32_t pvr, pvr_mask; >> + >> + pvr_mask = rtas_ld(list, 0); >> + list += 4; >> + pvr = rtas_ld(list, 0); >> + list += 4; >> + >> + trace_spapr_cas_pvr_try(pvr); >> + if (!max_lvl && >> + ((cpu_->env.spr[SPR_PVR] & pvr_mask) == (pvr & pvr_mask))) { >> + cpu_match = true; >> + cpu_version = 0; >> + } else if (pvr == cpu_->cpu_version) { >> + cpu_match = true; >> + cpu_version = cpu_->cpu_version; >> + } else if (!cpu_match) { >> + /* If it is a logical PVR, try to determine the highest >> level */ >> + unsigned lvl = get_compat_level(pvr); >> + if (lvl) { >> + bool is205 = (pcc_->pcr_mask & PCR_COMPAT_2_05) && >> + (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_05)); >> + bool is206 = (pcc_->pcr_mask & PCR_COMPAT_2_06) && >> + ((lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06)) || >> + (lvl == >> get_compat_level(CPU_POWERPC_LOGICAL_2_06_PLUS))); >> + >> + if (is205 || is206) { >> + if (!max_lvl) { >> + /* User did not set the level, choose the >> highest */ >> + if (compat_lvl <= lvl) { >> + compat_lvl = lvl; >> + cpu_version = pvr; >> + } >> + } else if (max_lvl >= lvl) { >> + /* User chose the level, don't set higher than >> this */ >> + compat_lvl = lvl; >> + cpu_version = pvr; >> + } >> + } >> + } >> + } >> + /* Terminator record */ >> + if (~pvr_mask & pvr) { > > This loop can be used by the guest to stall QEMU for a long period of time, > no? Better add a safety net check somewhere to allow for early abort.
Like what? No more than 64 PVRs would be enough? >> + break; >> + } >> + } >> + >> + /* For the future use: here @list points to the first capability */ >> + >> + /* Parsing finished */ >> + trace_spapr_cas_pvr(cpu_->cpu_version, cpu_match, >> + cpu_version, pcc_->pcr_mask); >> + >> + /* Update CPUs */ >> + if (old_cpu_version != cpu_version) { >> + CPU_FOREACH(cs) { >> + PowerPCCPU *cpu = POWERPC_CPU(cs); >> + >> + if (ppc_set_compat(cpu, cpu_version) < 0) { > > Please run the updates on the vcpu threads themselves. Ouch. Forgot. Sorry :( -- Alexey