qemu/target/arm/arm-powerctl.c
Thomas Huth fcf5ef2ab5 Move target-* CPU file into a target/ folder
We've currently got 18 architectures in QEMU, and thus 18 target-xxx
folders in the root folder of the QEMU source tree. More architectures
(e.g. RISC-V, AVR) are likely to be included soon, too, so the main
folder of the QEMU sources slowly gets quite overcrowded with the
target-xxx folders.
To disburden the main folder a little bit, let's move the target-xxx
folders into a dedicated target/ folder, so that target-xxx/ simply
becomes target/xxx/ instead.

Acked-by: Laurent Vivier <laurent@vivier.eu> [m68k part]
Acked-by: Bastian Koppelmann <kbastian@mail.uni-paderborn.de> [tricore part]
Acked-by: Michael Walle <michael@walle.cc> [lm32 part]
Acked-by: Cornelia Huck <cornelia.huck@de.ibm.com> [s390x part]
Reviewed-by: Christian Borntraeger <borntraeger@de.ibm.com> [s390x part]
Acked-by: Eduardo Habkost <ehabkost@redhat.com> [i386 part]
Acked-by: Artyom Tarasenko <atar4qemu@gmail.com> [sparc part]
Acked-by: Richard Henderson <rth@twiddle.net> [alpha part]
Acked-by: Max Filippov <jcmvbkbc@gmail.com> [xtensa part]
Reviewed-by: David Gibson <david@gibson.dropbear.id.au> [ppc part]
Acked-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> [cris&microblaze part]
Acked-by: Guan Xuetao <gxt@mprc.pku.edu.cn> [unicore32 part]
Signed-off-by: Thomas Huth <thuth@redhat.com>
2016-12-20 21:52:12 +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;
}