From 9d6f106552fa5ad9e3128b5052863835526ba271 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 4 Jan 2017 16:19:50 +1100 Subject: ppc: Rewrite ppc_set_compat() This rewrites the ppc_set_compat() function so that instead of open coding the various compatibility modes, it reads the relevant data from a table. This is a first step in consolidating the information on compatibility modes scattered across the code into a single place. It also makes one change to the logic. The old code masked the bits to be set in the PCR (Processor Compatibility Register) by which bits are valid on the host CPU. This made no sense, since it was done regardless of whether our guest CPU was the same as the host CPU or not. Furthermore, the actual PCR bits are only relevant for TCG[1] - KVM instead uses the compatibility mode we tell it in kvmppc_set_compat(). When using TCG host cpu information usually isn't even present. While we're at it, we put the new implementation in a new file to make the enormous translate_init.c a little smaller. [1] Actually it doesn't even do anything in TCG, but it will if / when we get to implementing compatibility mode logic at that level. Signed-off-by: David Gibson Reviewed-by: Alexey Kardashevskiy --- target/ppc/compat.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 target/ppc/compat.c (limited to 'target/ppc/compat.c') diff --git a/target/ppc/compat.c b/target/ppc/compat.c new file mode 100644 index 0000000000..f3fd9c695c --- /dev/null +++ b/target/ppc/compat.c @@ -0,0 +1,91 @@ +/* + * PowerPC CPU initialization for qemu. + * + * Copyright 2016, David Gibson, Red Hat Inc. + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "sysemu/kvm.h" +#include "kvm_ppc.h" +#include "sysemu/cpus.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "cpu-models.h" + +typedef struct { + uint32_t pvr; + uint64_t pcr; +} CompatInfo; + +static const CompatInfo compat_table[] = { + { /* POWER6, ISA2.05 */ + .pvr = CPU_POWERPC_LOGICAL_2_05, + .pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05 + | PCR_TM_DIS | PCR_VSX_DIS, + }, + { /* POWER7, ISA2.06 */ + .pvr = CPU_POWERPC_LOGICAL_2_06, + .pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS, + }, + { + .pvr = CPU_POWERPC_LOGICAL_2_06_PLUS, + .pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS, + }, + { /* POWER8, ISA2.07 */ + .pvr = CPU_POWERPC_LOGICAL_2_07, + .pcr = PCR_COMPAT_2_07, + }, +}; + +static const CompatInfo *compat_by_pvr(uint32_t pvr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(compat_table); i++) { + if (compat_table[i].pvr == pvr) { + return &compat_table[i]; + } + } + return NULL; +} + +void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp) +{ + const CompatInfo *compat = compat_by_pvr(compat_pvr); + CPUPPCState *env = &cpu->env; + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + uint64_t pcr; + + if (!compat_pvr) { + pcr = 0; + } else if (!compat) { + error_setg(errp, "Unknown compatibility PVR 0x%08"PRIx32, compat_pvr); + return; + } else { + pcr = compat->pcr; + } + + cpu->compat_pvr = compat_pvr; + env->spr[SPR_PCR] = pcr & pcc->pcr_mask; + + if (kvm_enabled()) { + int ret = kvmppc_set_compat(cpu, cpu->compat_pvr); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Unable to set CPU compatibility mode in KVM"); + } + } +} -- cgit v1.2.3 From 12dbeb16d0984fe03bd4bc5cd952187627a22ce9 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 28 Oct 2016 22:35:48 +1100 Subject: ppc: Rewrite ppc_get_compat_smt_threads() To continue consolidation of compatibility mode information, this rewrites the ppc_get_compat_smt_threads() function using the table of compatiblity modes in target-ppc/compat.c. It's not a direct replacement, the new ppc_compat_max_threads() function has simpler semantics - it just returns the number of threads the cpu model has, taking into account any compatiblity mode it is in. This no longer takes into account kvmppc_smt_threads() as the previous version did. That check wasn't useful because we check in ppc_cpu_realizefn() that CPUs aren't instantiated with more threads than kvm allows (or if we didn't things will already be broken and this won't make it any worse). Signed-off-by: David Gibson Reviewed-by: Alexey Kardashevskiy --- target/ppc/compat.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'target/ppc/compat.c') diff --git a/target/ppc/compat.c b/target/ppc/compat.c index f3fd9c695c..66529a6b83 100644 --- a/target/ppc/compat.c +++ b/target/ppc/compat.c @@ -28,6 +28,7 @@ typedef struct { uint32_t pvr; uint64_t pcr; + int max_threads; } CompatInfo; static const CompatInfo compat_table[] = { @@ -35,18 +36,22 @@ static const CompatInfo compat_table[] = { .pvr = CPU_POWERPC_LOGICAL_2_05, .pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05 | PCR_TM_DIS | PCR_VSX_DIS, + .max_threads = 2, }, { /* POWER7, ISA2.06 */ .pvr = CPU_POWERPC_LOGICAL_2_06, .pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS, + .max_threads = 4, }, { .pvr = CPU_POWERPC_LOGICAL_2_06_PLUS, .pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS, + .max_threads = 4, }, { /* POWER8, ISA2.07 */ .pvr = CPU_POWERPC_LOGICAL_2_07, .pcr = PCR_COMPAT_2_07, + .max_threads = 8, }, }; @@ -89,3 +94,16 @@ void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp) } } } + +int ppc_compat_max_threads(PowerPCCPU *cpu) +{ + const CompatInfo *compat = compat_by_pvr(cpu->compat_pvr); + int n_threads = CPU(cpu)->nr_threads; + + if (cpu->compat_pvr) { + g_assert(compat); + n_threads = MIN(n_threads, compat->max_threads); + } + + return n_threads; +} -- cgit v1.2.3 From 9d2179d6f960aef1b8aab4d014fd8385f0a187e5 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 28 Oct 2016 22:51:46 +1100 Subject: ppc: Validate compatibility modes when setting Current ppc_set_compat() will attempt to set any compatiblity mode specified, regardless of whether it's available on the CPU. The caller is expected to make sure it is setting a possible mode, which is awkwward because most of the information to make that decision is at the CPU level. This begins to clean this up by introducing a ppc_check_compat() function which will determine if a given compatiblity mode is supported on a CPU (and also whether it lies within specified minimum and maximum compat levels, which will be useful later). It also contains an assertion that the CPU has a "virtual hypervisor"[1], that is, that the guest isn't permitted to execute hypervisor privilege code. Without that, the guest would own the PCR and so could override any mode set here. Only machine types which use a virtual hypervisor (i.e. 'pseries') should use ppc_check_compat(). ppc_set_compat() is modified to validate the compatibility mode it is given and fail if it's not available on this CPU. [1] Or user-only mode, which also obviously doesn't allow access to the hypervisor privileged PCR. We don't use that now, but could in future. Signed-off-by: David Gibson Reviewed-by: Alexey Kardashevskiy --- target/ppc/compat.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'target/ppc/compat.c') diff --git a/target/ppc/compat.c b/target/ppc/compat.c index 66529a6b83..10595550f9 100644 --- a/target/ppc/compat.c +++ b/target/ppc/compat.c @@ -28,29 +28,37 @@ typedef struct { uint32_t pvr; uint64_t pcr; + uint64_t pcr_level; int max_threads; } CompatInfo; static const CompatInfo compat_table[] = { + /* + * Ordered from oldest to newest - the code relies on this + */ { /* POWER6, ISA2.05 */ .pvr = CPU_POWERPC_LOGICAL_2_05, .pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05 | PCR_TM_DIS | PCR_VSX_DIS, + .pcr_level = PCR_COMPAT_2_05, .max_threads = 2, }, { /* POWER7, ISA2.06 */ .pvr = CPU_POWERPC_LOGICAL_2_06, .pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS, + .pcr_level = PCR_COMPAT_2_06, .max_threads = 4, }, { .pvr = CPU_POWERPC_LOGICAL_2_06_PLUS, .pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS, + .pcr_level = PCR_COMPAT_2_06, .max_threads = 4, }, { /* POWER8, ISA2.07 */ .pvr = CPU_POWERPC_LOGICAL_2_07, .pcr = PCR_COMPAT_2_07, + .pcr_level = PCR_COMPAT_2_07, .max_threads = 8, }, }; @@ -67,6 +75,35 @@ static const CompatInfo *compat_by_pvr(uint32_t pvr) return NULL; } +bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr, + uint32_t min_compat_pvr, uint32_t max_compat_pvr) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + const CompatInfo *compat = compat_by_pvr(compat_pvr); + const CompatInfo *min = compat_by_pvr(min_compat_pvr); + const CompatInfo *max = compat_by_pvr(max_compat_pvr); + +#if !defined(CONFIG_USER_ONLY) + g_assert(cpu->vhyp); +#endif + g_assert(!min_compat_pvr || min); + g_assert(!max_compat_pvr || max); + + if (!compat) { + /* Not a recognized logical PVR */ + return false; + } + if ((min && (compat < min)) || (max && (compat > max))) { + /* Outside specified range */ + return false; + } + if (!(pcc->pcr_supported & compat->pcr_level)) { + /* Not supported by this CPU */ + return false; + } + return true; +} + void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp) { const CompatInfo *compat = compat_by_pvr(compat_pvr); @@ -79,6 +116,10 @@ void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp) } else if (!compat) { error_setg(errp, "Unknown compatibility PVR 0x%08"PRIx32, compat_pvr); return; + } else if (!ppc_check_compat(cpu, compat_pvr, 0, 0)) { + error_setg(errp, "Compatibility PVR 0x%08"PRIx32" not valid for CPU", + compat_pvr); + return; } else { pcr = compat->pcr; } -- cgit v1.2.3 From f6f242c7578fbedcdb53a14d4b057a7059b8dd1d Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 10 Nov 2016 14:37:38 +1100 Subject: ppc: Add ppc_set_compat_all() Once a compatiblity mode is negotiated with the guest, h_client_architecture_support() uses run_on_cpu() to update each CPU to the new mode. We're going to want this logic somewhere else shortly, so make a helper function to do this global update. We put it in target-ppc/compat.c - it makes as much sense at the CPU level as it does at the machine level. We also move the cpu_synchronize_state() into ppc_set_compat(), since it doesn't really make any sense to call that without synchronizing state. Signed-off-by: David Gibson --- target/ppc/compat.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'target/ppc/compat.c') diff --git a/target/ppc/compat.c b/target/ppc/compat.c index 10595550f9..458da262be 100644 --- a/target/ppc/compat.c +++ b/target/ppc/compat.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "sysemu/hw_accel.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "sysemu/cpus.h" @@ -124,6 +125,8 @@ void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp) pcr = compat->pcr; } + cpu_synchronize_state(CPU(cpu)); + cpu->compat_pvr = compat_pvr; env->spr[SPR_PCR] = pcr & pcc->pcr_mask; @@ -136,6 +139,38 @@ void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp) } } +typedef struct { + uint32_t compat_pvr; + Error *err; +} SetCompatState; + +static void do_set_compat(CPUState *cs, run_on_cpu_data arg) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + SetCompatState *s = arg.host_ptr; + + ppc_set_compat(cpu, s->compat_pvr, &s->err); +} + +void ppc_set_compat_all(uint32_t compat_pvr, Error **errp) +{ + CPUState *cs; + + CPU_FOREACH(cs) { + SetCompatState s = { + .compat_pvr = compat_pvr, + .err = NULL, + }; + + run_on_cpu(cs, do_set_compat, RUN_ON_CPU_HOST_PTR(&s)); + + if (s.err) { + error_propagate(errp, s.err); + return; + } + } +} + int ppc_compat_max_threads(PowerPCCPU *cpu) { const CompatInfo *compat = compat_by_pvr(cpu->compat_pvr); -- cgit v1.2.3