qemu-patch-raspberry4/target-arm/arm-powerctl.c
Alex Bennée 548ebcaf36 target-arm/arm-powerctl: wake up sleeping CPUs
Testing with Alexander's bare metal syncronisation tests fails in MTTCG
leaving one CPU spinning forever waiting for the second CPU to wake up.
We simply need to kick the vCPU once we have processed the PSCI power on
call.

As the power control API is for system emulation only as is the
qemu_kick_cpu function we also ensure we only build arm-powerctl for
SoftMMU builds.

Tested-by: Alex Bennée <alex.bennee@linaro.org>
CC: Alexander Spyridakis <a.spyridakis@virtualopensystems.com>
Message-Id: <1439220437-23957-20-git-send-email-fred.konrad@greensocs.com>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Message-Id: <20161027151030.20863-11-alex.bennee@linaro.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-10-31 10:51:16 +01:00

229 lines
6.6 KiB
C

/*
* QEMU support -- ARM Power Control specific functions.
*
* Copyright (c) 2016 Jean-Christophe Dubois
*
* 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 "cpu.h"
#include "cpu-qom.h"
#include "internals.h"
#include "arm-powerctl.h"
#include "qemu/log.h"
#include "exec/exec-all.h"
#ifndef DEBUG_ARM_POWERCTL
#define DEBUG_ARM_POWERCTL 0
#endif
#define DPRINTF(fmt, args...) \
do { \
if (DEBUG_ARM_POWERCTL) { \
fprintf(stderr, "[ARM]%s: " fmt , __func__, ##args); \
} \
} while (0)
CPUState *arm_get_cpu_by_id(uint64_t id)
{
CPUState *cpu;
DPRINTF("cpu %" PRId64 "\n", id);
CPU_FOREACH(cpu) {
ARMCPU *armcpu = ARM_CPU(cpu);
if (armcpu->mp_affinity == id) {
return cpu;
}
}
qemu_log_mask(LOG_GUEST_ERROR,
"[ARM]%s: Requesting unknown CPU %" PRId64 "\n",
__func__, id);
return NULL;
}
int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
uint32_t target_el, bool target_aa64)
{
CPUState *target_cpu_state;
ARMCPU *target_cpu;
DPRINTF("cpu %" PRId64 " (EL %d, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64
"\n", cpuid, target_el, target_aa64 ? "aarch64" : "aarch32", entry,
context_id);
/* requested EL level need to be in the 1 to 3 range */
assert((target_el > 0) && (target_el < 4));
if (target_aa64 && (entry & 3)) {
/*
* if we are booting in AArch64 mode then "entry" needs to be 4 bytes
* aligned.
*/
return QEMU_ARM_POWERCTL_INVALID_PARAM;
}
/* Retrieve the cpu we are powering up */
target_cpu_state = arm_get_cpu_by_id(cpuid);
if (!target_cpu_state) {
/* The cpu was not found */
return QEMU_ARM_POWERCTL_INVALID_PARAM;
}
target_cpu = ARM_CPU(target_cpu_state);
if (!target_cpu->powered_off) {
qemu_log_mask(LOG_GUEST_ERROR,
"[ARM]%s: CPU %" PRId64 " is already on\n",
__func__, cpuid);
return QEMU_ARM_POWERCTL_ALREADY_ON;
}
/*
* The newly brought CPU is requested to enter the exception level
* "target_el" and be in the requested mode (AArch64 or AArch32).
*/
if (((target_el == 3) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) ||
((target_el == 2) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL2))) {
/*
* The CPU does not support requested level
*/
return QEMU_ARM_POWERCTL_INVALID_PARAM;
}
if (!target_aa64 && arm_feature(&target_cpu->env, ARM_FEATURE_AARCH64)) {
/*
* For now we don't support booting an AArch64 CPU in AArch32 mode
* TODO: We should add this support later
*/
qemu_log_mask(LOG_UNIMP,
"[ARM]%s: Starting AArch64 CPU %" PRId64
" in AArch32 mode is not supported yet\n",
__func__, cpuid);
return QEMU_ARM_POWERCTL_INVALID_PARAM;
}
/* Initialize the cpu we are turning on */
cpu_reset(target_cpu_state);
target_cpu->powered_off = false;
target_cpu_state->halted = 0;
if (target_aa64) {
if ((target_el < 3) && arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) {
/*
* As target mode is AArch64, we need to set lower
* exception level (the requested level 2) to AArch64
*/
target_cpu->env.cp15.scr_el3 |= SCR_RW;
}
if ((target_el < 2) && arm_feature(&target_cpu->env, ARM_FEATURE_EL2)) {
/*
* As target mode is AArch64, we need to set lower
* exception level (the requested level 1) to AArch64
*/
target_cpu->env.cp15.hcr_el2 |= HCR_RW;
}
target_cpu->env.pstate = aarch64_pstate_mode(target_el, true);
} else {
/* We are requested to boot in AArch32 mode */
static uint32_t mode_for_el[] = { 0,
ARM_CPU_MODE_SVC,
ARM_CPU_MODE_HYP,
ARM_CPU_MODE_SVC };
cpsr_write(&target_cpu->env, mode_for_el[target_el], CPSR_M,
CPSRWriteRaw);
}
if (target_el == 3) {
/* Processor is in secure mode */
target_cpu->env.cp15.scr_el3 &= ~SCR_NS;
} else {
/* Processor is not in secure mode */
target_cpu->env.cp15.scr_el3 |= SCR_NS;
}
/* We check if the started CPU is now at the correct level */
assert(target_el == arm_current_el(&target_cpu->env));
if (target_aa64) {
target_cpu->env.xregs[0] = context_id;
target_cpu->env.thumb = false;
} else {
target_cpu->env.regs[0] = context_id;
target_cpu->env.thumb = entry & 1;
entry &= 0xfffffffe;
}
/* Start the new CPU at the requested address */
cpu_set_pc(target_cpu_state, entry);
qemu_cpu_kick(target_cpu_state);
/* We are good to go */
return QEMU_ARM_POWERCTL_RET_SUCCESS;
}
int arm_set_cpu_off(uint64_t cpuid)
{
CPUState *target_cpu_state;
ARMCPU *target_cpu;
DPRINTF("cpu %" PRId64 "\n", cpuid);
/* change to the cpu we are powering up */
target_cpu_state = arm_get_cpu_by_id(cpuid);
if (!target_cpu_state) {
return QEMU_ARM_POWERCTL_INVALID_PARAM;
}
target_cpu = ARM_CPU(target_cpu_state);
if (target_cpu->powered_off) {
qemu_log_mask(LOG_GUEST_ERROR,
"[ARM]%s: CPU %" PRId64 " is already off\n",
__func__, cpuid);
return QEMU_ARM_POWERCTL_IS_OFF;
}
target_cpu->powered_off = true;
target_cpu_state->halted = 1;
target_cpu_state->exception_index = EXCP_HLT;
cpu_loop_exit(target_cpu_state);
/* notreached */
return QEMU_ARM_POWERCTL_RET_SUCCESS;
}
int arm_reset_cpu(uint64_t cpuid)
{
CPUState *target_cpu_state;
ARMCPU *target_cpu;
DPRINTF("cpu %" PRId64 "\n", cpuid);
/* change to the cpu we are resetting */
target_cpu_state = arm_get_cpu_by_id(cpuid);
if (!target_cpu_state) {
return QEMU_ARM_POWERCTL_INVALID_PARAM;
}
target_cpu = ARM_CPU(target_cpu_state);
if (target_cpu->powered_off) {
qemu_log_mask(LOG_GUEST_ERROR,
"[ARM]%s: CPU %" PRId64 " is off\n",
__func__, cpuid);
return QEMU_ARM_POWERCTL_IS_OFF;
}
/* Reset the cpu */
cpu_reset(target_cpu_state);
return QEMU_ARM_POWERCTL_RET_SUCCESS;
}