70de096748
The processor stop status and control register (PSSCR) is used to control the power saving facilities of the thread. The exit criterion bit (EC) is used to specify whether the thread should be woken by any interrupt (EC == 0) or only an interrupt enabled in the LPCR to wake the thread (EC == 1). The rtas facilities start-cpu and self-stop are used to transition a vcpu between the stopped and running states. When a vcpu is stopped it may only be started again by the start-cpu rtas call. Currently a vcpu in the stopped state will start again whenever an interrupt comes along due to PSSCR_EC being cleared, and while this is architecturally correct for a hardware thread, a vcpu is expected to only be woken by calling start-cpu. This means when performing a reboot on a tcg machine that the secondary threads will restart while the primary is still in slof, this is unsupported and causes call traces like: SLOF ********************************************************************** QEMU Starting Build Date = Jan 14 2019 18:00:39 FW Version = git-a5b428e1c1eae703 Press "s" to enter Open Firmware. qemu: fatal: Trying to deliver HV exception (MSR) 70 with no HV support NIP 6d61676963313230 LR 000000003dbe0308 CTR 6d61676963313233 XER 0000000000000000 CPU#1 MSR 0000000000000000 HID0 0000000000000000 HF 0000000000000000 iidx 3 didx 3 TB 00000026 115746031956 DECR 18446744073326238463 GPR00 000000003dbe0308 000000003e669fe0 000000003dc10700 0000000000000003 GPR04 000000003dc62198 000000003dc62178 000000003dc0ea48 0000000000000030 GPR08 000000003dc621a8 0000000000000018 000000003e466008 000000003dc50700 GPR12 c00000000093a4e0 c00000003ffff300 c00000003e533f90 0000000000000000 GPR16 0000000000000000 0000000000000000 000000003e466010 000000003dc0b040 GPR20 0000000000008000 000000000000f003 0000000000000006 000000003e66a050 GPR24 000000003dc06400 000000003dc0ae70 0000000000000003 000000000000f001 GPR28 000000003e66a060 ffffffffffffffff 6d61676963313233 0000000000000028 CR 28000222 [ E L - - - E E E ] RES ffffffffffffffff FPR00 0000000000000000 0000000000000000 0000000000000000 0000000000000000 FPR04 0000000000000000 0000000000000000 0000000000000000 0000000000000000 FPR08 0000000000000000 0000000000000000 0000000000000000 00000000311825e0 FPR12 00000000311825e0 0000000000000000 0000000000000000 0000000000000000 FPR16 0000000000000000 0000000000000000 0000000000000000 0000000000000000 FPR20 0000000000000000 0000000000000000 0000000000000000 0000000000000000 FPR24 0000000000000000 0000000000000000 0000000000000000 0000000000000000 FPR28 0000000000000000 0000000000000000 0000000000000000 0000000000000000 FPSCR 0000000000000000 SRR0 000000003dbe06b0 SRR1 0000000000080000 PVR 00000000004e1200 VRSAVE 0000000000000000 SPRG0 000000003dbe0308 SPRG1 000000003e669fe0 SPRG2 00000000000000d8 SPRG3 000000003dbe0308 SPRG4 0000000000000000 SPRG5 0000000000000000 SPRG6 0000000000000000 SPRG7 0000000000000000 HSRR0 6d61676963313230 HSRR1 0000000000000000 CFAR 000000003dbe3e64 LPCR 0000000004020008 PTCR 0000000000000000 DAR 0000000000000000 DSISR 0000000000000000 Aborted (core dumped) To fix this, set the PSSCR_EC bit when a vcpu is stopped to disable it from coming back online until the start-cpu rtas call is made. Fixes: 21c0d66a9c99 ("target/ppc: Fix support for "STOP light" states on POWER9") Signed-off-by: Suraj Jitindar Singh <sjitindarsingh@gmail.com> Message-Id: <20190516005744.24366-1-sjitindarsingh@gmail.com> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
407 lines
11 KiB
C
407 lines
11 KiB
C
/*
|
|
* sPAPR CPU core device, acts as container of CPU thread devices.
|
|
*
|
|
* Copyright (C) 2016 Bharata B Rao <bharata@linux.vnet.ibm.com>
|
|
*
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
* See the COPYING file in the top-level directory.
|
|
*/
|
|
#include "qemu/osdep.h"
|
|
#include "hw/cpu/core.h"
|
|
#include "hw/ppc/spapr_cpu_core.h"
|
|
#include "target/ppc/cpu.h"
|
|
#include "hw/ppc/spapr.h"
|
|
#include "hw/boards.h"
|
|
#include "qapi/error.h"
|
|
#include "sysemu/cpus.h"
|
|
#include "sysemu/kvm.h"
|
|
#include "target/ppc/kvm_ppc.h"
|
|
#include "hw/ppc/ppc.h"
|
|
#include "target/ppc/mmu-hash64.h"
|
|
#include "sysemu/numa.h"
|
|
#include "sysemu/hw_accel.h"
|
|
#include "qemu/error-report.h"
|
|
|
|
static void spapr_cpu_reset(void *opaque)
|
|
{
|
|
PowerPCCPU *cpu = opaque;
|
|
CPUState *cs = CPU(cpu);
|
|
CPUPPCState *env = &cpu->env;
|
|
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
|
|
SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu);
|
|
target_ulong lpcr;
|
|
|
|
cpu_reset(cs);
|
|
|
|
/* All CPUs start halted. CPU0 is unhalted from the machine level
|
|
* reset code and the rest are explicitly started up by the guest
|
|
* using an RTAS call */
|
|
cs->halted = 1;
|
|
|
|
/* Set compatibility mode to match the boot CPU, which was either set
|
|
* by the machine reset code or by CAS. This should never fail.
|
|
*/
|
|
ppc_set_compat(cpu, POWERPC_CPU(first_cpu)->compat_pvr, &error_abort);
|
|
|
|
env->spr[SPR_HIOR] = 0;
|
|
|
|
lpcr = env->spr[SPR_LPCR];
|
|
|
|
/* Set emulated LPCR to not send interrupts to hypervisor. Note that
|
|
* under KVM, the actual HW LPCR will be set differently by KVM itself,
|
|
* the settings below ensure proper operations with TCG in absence of
|
|
* a real hypervisor.
|
|
*
|
|
* Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for
|
|
* real mode accesses, which thankfully defaults to 0 and isn't
|
|
* accessible in guest mode.
|
|
*
|
|
* Disable Power-saving mode Exit Cause exceptions for the CPU, so
|
|
* we don't get spurious wakups before an RTAS start-cpu call.
|
|
* For the same reason, set PSSCR_EC.
|
|
*/
|
|
lpcr &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | pcc->lpcr_pm);
|
|
lpcr |= LPCR_LPES0 | LPCR_LPES1;
|
|
env->spr[SPR_PSSCR] |= PSSCR_EC;
|
|
|
|
/* Set RMLS to the max (ie, 16G) */
|
|
lpcr &= ~LPCR_RMLS;
|
|
lpcr |= 1ull << LPCR_RMLS_SHIFT;
|
|
|
|
ppc_store_lpcr(cpu, lpcr);
|
|
|
|
/* Set a full AMOR so guest can use the AMR as it sees fit */
|
|
env->spr[SPR_AMOR] = 0xffffffffffffffffull;
|
|
|
|
spapr_cpu->vpa_addr = 0;
|
|
spapr_cpu->slb_shadow_addr = 0;
|
|
spapr_cpu->slb_shadow_size = 0;
|
|
spapr_cpu->dtl_addr = 0;
|
|
spapr_cpu->dtl_size = 0;
|
|
|
|
spapr_caps_cpu_apply(SPAPR_MACHINE(qdev_get_machine()), cpu);
|
|
|
|
kvm_check_mmu(cpu, &error_fatal);
|
|
}
|
|
|
|
void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3)
|
|
{
|
|
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
|
|
CPUPPCState *env = &cpu->env;
|
|
|
|
env->nip = nip;
|
|
env->gpr[3] = r3;
|
|
kvmppc_set_reg_ppc_online(cpu, 1);
|
|
CPU(cpu)->halted = 0;
|
|
/* Enable Power-saving mode Exit Cause exceptions */
|
|
ppc_store_lpcr(cpu, env->spr[SPR_LPCR] | pcc->lpcr_pm);
|
|
}
|
|
|
|
/*
|
|
* Return the sPAPR CPU core type for @model which essentially is the CPU
|
|
* model specified with -cpu cmdline option.
|
|
*/
|
|
const char *spapr_get_cpu_core_type(const char *cpu_type)
|
|
{
|
|
int len = strlen(cpu_type) - strlen(POWERPC_CPU_TYPE_SUFFIX);
|
|
char *core_type = g_strdup_printf(SPAPR_CPU_CORE_TYPE_NAME("%.*s"),
|
|
len, cpu_type);
|
|
ObjectClass *oc = object_class_by_name(core_type);
|
|
|
|
g_free(core_type);
|
|
if (!oc) {
|
|
return NULL;
|
|
}
|
|
|
|
return object_class_get_name(oc);
|
|
}
|
|
|
|
static bool slb_shadow_needed(void *opaque)
|
|
{
|
|
SpaprCpuState *spapr_cpu = opaque;
|
|
|
|
return spapr_cpu->slb_shadow_addr != 0;
|
|
}
|
|
|
|
static const VMStateDescription vmstate_spapr_cpu_slb_shadow = {
|
|
.name = "spapr_cpu/vpa/slb_shadow",
|
|
.version_id = 1,
|
|
.minimum_version_id = 1,
|
|
.needed = slb_shadow_needed,
|
|
.fields = (VMStateField[]) {
|
|
VMSTATE_UINT64(slb_shadow_addr, SpaprCpuState),
|
|
VMSTATE_UINT64(slb_shadow_size, SpaprCpuState),
|
|
VMSTATE_END_OF_LIST()
|
|
}
|
|
};
|
|
|
|
static bool dtl_needed(void *opaque)
|
|
{
|
|
SpaprCpuState *spapr_cpu = opaque;
|
|
|
|
return spapr_cpu->dtl_addr != 0;
|
|
}
|
|
|
|
static const VMStateDescription vmstate_spapr_cpu_dtl = {
|
|
.name = "spapr_cpu/vpa/dtl",
|
|
.version_id = 1,
|
|
.minimum_version_id = 1,
|
|
.needed = dtl_needed,
|
|
.fields = (VMStateField[]) {
|
|
VMSTATE_UINT64(dtl_addr, SpaprCpuState),
|
|
VMSTATE_UINT64(dtl_size, SpaprCpuState),
|
|
VMSTATE_END_OF_LIST()
|
|
}
|
|
};
|
|
|
|
static bool vpa_needed(void *opaque)
|
|
{
|
|
SpaprCpuState *spapr_cpu = opaque;
|
|
|
|
return spapr_cpu->vpa_addr != 0;
|
|
}
|
|
|
|
static const VMStateDescription vmstate_spapr_cpu_vpa = {
|
|
.name = "spapr_cpu/vpa",
|
|
.version_id = 1,
|
|
.minimum_version_id = 1,
|
|
.needed = vpa_needed,
|
|
.fields = (VMStateField[]) {
|
|
VMSTATE_UINT64(vpa_addr, SpaprCpuState),
|
|
VMSTATE_END_OF_LIST()
|
|
},
|
|
.subsections = (const VMStateDescription * []) {
|
|
&vmstate_spapr_cpu_slb_shadow,
|
|
&vmstate_spapr_cpu_dtl,
|
|
NULL
|
|
}
|
|
};
|
|
|
|
static const VMStateDescription vmstate_spapr_cpu_state = {
|
|
.name = "spapr_cpu",
|
|
.version_id = 1,
|
|
.minimum_version_id = 1,
|
|
.fields = (VMStateField[]) {
|
|
VMSTATE_END_OF_LIST()
|
|
},
|
|
.subsections = (const VMStateDescription * []) {
|
|
&vmstate_spapr_cpu_vpa,
|
|
NULL
|
|
}
|
|
};
|
|
|
|
static void spapr_unrealize_vcpu(PowerPCCPU *cpu, SpaprCpuCore *sc)
|
|
{
|
|
if (!sc->pre_3_0_migration) {
|
|
vmstate_unregister(NULL, &vmstate_spapr_cpu_state, cpu->machine_data);
|
|
}
|
|
qemu_unregister_reset(spapr_cpu_reset, cpu);
|
|
if (spapr_cpu_state(cpu)->icp) {
|
|
object_unparent(OBJECT(spapr_cpu_state(cpu)->icp));
|
|
}
|
|
if (spapr_cpu_state(cpu)->tctx) {
|
|
object_unparent(OBJECT(spapr_cpu_state(cpu)->tctx));
|
|
}
|
|
cpu_remove_sync(CPU(cpu));
|
|
object_unparent(OBJECT(cpu));
|
|
}
|
|
|
|
static void spapr_cpu_core_unrealize(DeviceState *dev, Error **errp)
|
|
{
|
|
SpaprCpuCore *sc = SPAPR_CPU_CORE(OBJECT(dev));
|
|
CPUCore *cc = CPU_CORE(dev);
|
|
int i;
|
|
|
|
for (i = 0; i < cc->nr_threads; i++) {
|
|
spapr_unrealize_vcpu(sc->threads[i], sc);
|
|
}
|
|
g_free(sc->threads);
|
|
}
|
|
|
|
static void spapr_realize_vcpu(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
|
SpaprCpuCore *sc, Error **errp)
|
|
{
|
|
CPUPPCState *env = &cpu->env;
|
|
CPUState *cs = CPU(cpu);
|
|
Error *local_err = NULL;
|
|
|
|
object_property_set_bool(OBJECT(cpu), true, "realized", &local_err);
|
|
if (local_err) {
|
|
goto error;
|
|
}
|
|
|
|
/* Set time-base frequency to 512 MHz */
|
|
cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ);
|
|
|
|
cpu_ppc_set_vhyp(cpu, PPC_VIRTUAL_HYPERVISOR(spapr));
|
|
kvmppc_set_papr(cpu);
|
|
|
|
qemu_register_reset(spapr_cpu_reset, cpu);
|
|
spapr_cpu_reset(cpu);
|
|
|
|
spapr->irq->cpu_intc_create(spapr, cpu, &local_err);
|
|
if (local_err) {
|
|
goto error_unregister;
|
|
}
|
|
|
|
if (!sc->pre_3_0_migration) {
|
|
vmstate_register(NULL, cs->cpu_index, &vmstate_spapr_cpu_state,
|
|
cpu->machine_data);
|
|
}
|
|
|
|
return;
|
|
|
|
error_unregister:
|
|
qemu_unregister_reset(spapr_cpu_reset, cpu);
|
|
cpu_remove_sync(CPU(cpu));
|
|
error:
|
|
error_propagate(errp, local_err);
|
|
}
|
|
|
|
static PowerPCCPU *spapr_create_vcpu(SpaprCpuCore *sc, int i, Error **errp)
|
|
{
|
|
SpaprCpuCoreClass *scc = SPAPR_CPU_CORE_GET_CLASS(sc);
|
|
CPUCore *cc = CPU_CORE(sc);
|
|
Object *obj;
|
|
char *id;
|
|
CPUState *cs;
|
|
PowerPCCPU *cpu;
|
|
Error *local_err = NULL;
|
|
|
|
obj = object_new(scc->cpu_type);
|
|
|
|
cs = CPU(obj);
|
|
cpu = POWERPC_CPU(obj);
|
|
cs->cpu_index = cc->core_id + i;
|
|
spapr_set_vcpu_id(cpu, cs->cpu_index, &local_err);
|
|
if (local_err) {
|
|
goto err;
|
|
}
|
|
|
|
cpu->node_id = sc->node_id;
|
|
|
|
id = g_strdup_printf("thread[%d]", i);
|
|
object_property_add_child(OBJECT(sc), id, obj, &local_err);
|
|
g_free(id);
|
|
if (local_err) {
|
|
goto err;
|
|
}
|
|
|
|
cpu->machine_data = g_new0(SpaprCpuState, 1);
|
|
|
|
object_unref(obj);
|
|
return cpu;
|
|
|
|
err:
|
|
object_unref(obj);
|
|
error_propagate(errp, local_err);
|
|
return NULL;
|
|
}
|
|
|
|
static void spapr_delete_vcpu(PowerPCCPU *cpu, SpaprCpuCore *sc)
|
|
{
|
|
SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu);
|
|
|
|
cpu->machine_data = NULL;
|
|
g_free(spapr_cpu);
|
|
object_unparent(OBJECT(cpu));
|
|
}
|
|
|
|
static void spapr_cpu_core_realize(DeviceState *dev, Error **errp)
|
|
{
|
|
/* We don't use SPAPR_MACHINE() in order to exit gracefully if the user
|
|
* tries to add a sPAPR CPU core to a non-pseries machine.
|
|
*/
|
|
SpaprMachineState *spapr =
|
|
(SpaprMachineState *) object_dynamic_cast(qdev_get_machine(),
|
|
TYPE_SPAPR_MACHINE);
|
|
SpaprCpuCore *sc = SPAPR_CPU_CORE(OBJECT(dev));
|
|
CPUCore *cc = CPU_CORE(OBJECT(dev));
|
|
Error *local_err = NULL;
|
|
int i, j;
|
|
|
|
if (!spapr) {
|
|
error_setg(errp, TYPE_SPAPR_CPU_CORE " needs a pseries machine");
|
|
return;
|
|
}
|
|
|
|
sc->threads = g_new(PowerPCCPU *, cc->nr_threads);
|
|
for (i = 0; i < cc->nr_threads; i++) {
|
|
sc->threads[i] = spapr_create_vcpu(sc, i, &local_err);
|
|
if (local_err) {
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < cc->nr_threads; j++) {
|
|
spapr_realize_vcpu(sc->threads[j], spapr, sc, &local_err);
|
|
if (local_err) {
|
|
goto err_unrealize;
|
|
}
|
|
}
|
|
return;
|
|
|
|
err_unrealize:
|
|
while (--j >= 0) {
|
|
spapr_unrealize_vcpu(sc->threads[j], sc);
|
|
}
|
|
err:
|
|
while (--i >= 0) {
|
|
spapr_delete_vcpu(sc->threads[i], sc);
|
|
}
|
|
g_free(sc->threads);
|
|
error_propagate(errp, local_err);
|
|
}
|
|
|
|
static Property spapr_cpu_core_properties[] = {
|
|
DEFINE_PROP_INT32("node-id", SpaprCpuCore, node_id, CPU_UNSET_NUMA_NODE_ID),
|
|
DEFINE_PROP_BOOL("pre-3.0-migration", SpaprCpuCore, pre_3_0_migration,
|
|
false),
|
|
DEFINE_PROP_END_OF_LIST()
|
|
};
|
|
|
|
static void spapr_cpu_core_class_init(ObjectClass *oc, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
|
SpaprCpuCoreClass *scc = SPAPR_CPU_CORE_CLASS(oc);
|
|
|
|
dc->realize = spapr_cpu_core_realize;
|
|
dc->unrealize = spapr_cpu_core_unrealize;
|
|
dc->props = spapr_cpu_core_properties;
|
|
scc->cpu_type = data;
|
|
}
|
|
|
|
#define DEFINE_SPAPR_CPU_CORE_TYPE(cpu_model) \
|
|
{ \
|
|
.parent = TYPE_SPAPR_CPU_CORE, \
|
|
.class_data = (void *) POWERPC_CPU_TYPE_NAME(cpu_model), \
|
|
.class_init = spapr_cpu_core_class_init, \
|
|
.name = SPAPR_CPU_CORE_TYPE_NAME(cpu_model), \
|
|
}
|
|
|
|
static const TypeInfo spapr_cpu_core_type_infos[] = {
|
|
{
|
|
.name = TYPE_SPAPR_CPU_CORE,
|
|
.parent = TYPE_CPU_CORE,
|
|
.abstract = true,
|
|
.instance_size = sizeof(SpaprCpuCore),
|
|
.class_size = sizeof(SpaprCpuCoreClass),
|
|
},
|
|
DEFINE_SPAPR_CPU_CORE_TYPE("970_v2.2"),
|
|
DEFINE_SPAPR_CPU_CORE_TYPE("970mp_v1.0"),
|
|
DEFINE_SPAPR_CPU_CORE_TYPE("970mp_v1.1"),
|
|
DEFINE_SPAPR_CPU_CORE_TYPE("power5+_v2.1"),
|
|
DEFINE_SPAPR_CPU_CORE_TYPE("power7_v2.3"),
|
|
DEFINE_SPAPR_CPU_CORE_TYPE("power7+_v2.1"),
|
|
DEFINE_SPAPR_CPU_CORE_TYPE("power8_v2.0"),
|
|
DEFINE_SPAPR_CPU_CORE_TYPE("power8e_v2.1"),
|
|
DEFINE_SPAPR_CPU_CORE_TYPE("power8nvl_v1.0"),
|
|
DEFINE_SPAPR_CPU_CORE_TYPE("power9_v1.0"),
|
|
DEFINE_SPAPR_CPU_CORE_TYPE("power9_v2.0"),
|
|
#ifdef CONFIG_KVM
|
|
DEFINE_SPAPR_CPU_CORE_TYPE("host"),
|
|
#endif
|
|
};
|
|
|
|
DEFINE_TYPES(spapr_cpu_core_type_infos)
|