[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 5/7] target-arm: add emulation of PSCI calls for sys
From: |
Rob Herring |
Subject: |
[Qemu-devel] [PATCH 5/7] target-arm: add emulation of PSCI calls for system emulation |
Date: |
Mon, 5 May 2014 11:00:21 -0500 |
From: Rob Herring <address@hidden>
Add support for handling PSCI calls in system emulation. Both version
0.1 and 0.2 of the PSCI spec are supported. Platforms can enable support
by setting "psci-method" QOM property on the cpus to SMC or HVC
emulation and having PSCI binding in their dtb.
Signed-off-by: Rob Herring <address@hidden>
---
target-arm/Makefile.objs | 1 +
target-arm/cpu-qom.h | 6 ++
target-arm/cpu.c | 1 +
target-arm/helper.c | 16 ++---
target-arm/kvm-consts.h | 6 ++
target-arm/psci.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 174 insertions(+), 8 deletions(-)
create mode 100644 target-arm/psci.c
diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs
index dcd167e..deda9f4 100644
--- a/target-arm/Makefile.objs
+++ b/target-arm/Makefile.objs
@@ -7,5 +7,6 @@ obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
obj-y += translate.o op_helper.o helper.o cpu.o
obj-y += neon_helper.o iwmmxt_helper.o
obj-y += gdbstub.o
+obj-y += psci.o
obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o
obj-y += crypto_helper.o
diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
index 88aaf6a..2905525 100644
--- a/target-arm/cpu-qom.h
+++ b/target-arm/cpu-qom.h
@@ -98,6 +98,11 @@ typedef struct ARMCPU {
bool start_powered_off;
bool powered_off;
+ /* PSCI emulation state
+ * 0 - disabled, 1 - smc, 2 - hvc
+ */
+ uint32_t psci_method;
+
/* [QEMU_]KVM_ARM_TARGET_* constant for this CPU, or
* QEMU_KVM_ARM_TARGET_NONE if the kernel doesn't support this CPU type.
*/
@@ -185,6 +190,7 @@ extern const struct VMStateDescription vmstate_arm_cpu;
void register_cp_regs_for_features(ARMCPU *cpu);
void init_cpreg_list(ARMCPU *cpu);
+bool arm_handle_psci(CPUState *cs);
bool arm_cpu_do_hvc(CPUState *cs);
bool arm_cpu_do_smc(CPUState *cs);
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 2d18a20..eb21a52 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -1009,6 +1009,7 @@ static const ARMCPUInfo arm_cpus[] = {
static Property arm_cpu_properties[] = {
DEFINE_PROP_BOOL("start-powered-off", ARMCPU, start_powered_off, false),
+ DEFINE_PROP_UINT32("psci-method", ARMCPU, psci_method, 0),
DEFINE_PROP_UINT32("midr", ARMCPU, midr, 0),
DEFINE_PROP_END_OF_LIST()
};
diff --git a/target-arm/helper.c b/target-arm/helper.c
index b5b4a17..637c46a 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -3255,23 +3255,23 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
bool arm_cpu_do_hvc(CPUState *cs)
{
- bool ret;
+ ARMCPU *cpu = ARM_CPU(cs);
- ret = arm_handle_psci(cs);
- if (ret) {
- return ret;
+ if (cpu->psci_method == QEMU_PSCI_METHOD_HVC) {
+ return arm_handle_psci(cs);
}
+
return false;
}
bool arm_cpu_do_smc(CPUState *cs)
{
- bool ret;
+ ARMCPU *cpu = ARM_CPU(cs);
- ret = arm_handle_psci(cs);
- if (ret) {
- return ret;
+ if (cpu->psci_method == QEMU_PSCI_METHOD_SMC) {
+ return arm_handle_psci(cs);
}
+
return false;
}
diff --git a/target-arm/kvm-consts.h b/target-arm/kvm-consts.h
index 5cf93ab..d0a89c7 100644
--- a/target-arm/kvm-consts.h
+++ b/target-arm/kvm-consts.h
@@ -91,6 +91,12 @@ MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_MIGRATE,
PSCI_0_2_FN64_MIGRATE)
MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU, \
PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU)
+enum {
+ QEMU_PSCI_METHOD_DISABLED = 0,
+ QEMU_PSCI_METHOD_SMC = 1,
+ QEMU_PSCI_METHOD_HVC = 2,
+};
+
/* Note that KVM uses overlapping values for AArch32 and AArch64
* target CPU numbers. AArch32 targets:
*/
diff --git a/target-arm/psci.c b/target-arm/psci.c
new file mode 100644
index 0000000..5c66236
--- /dev/null
+++ b/target-arm/psci.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2014 - Linaro
+ * Author: Rob Herring <address@hidden>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include <cpu.h>
+#include <cpu-qom.h>
+#include <kvm-consts.h>
+#include <sysemu/sysemu.h>
+#include <linux/psci.h>
+
+#if !defined(CONFIG_USER_ONLY)
+
+bool arm_handle_psci(CPUState *cs)
+{
+ ARMCPU *cpu = ARM_CPU(cs);
+ CPUARMState *env = &cpu->env;
+ uint64_t param[4];
+ uint64_t context_id, mpidr;
+ target_ulong entry;
+ int32_t ret = 0;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ param[i] = is_a64(env) ? env->xregs[i] : env->regs[i];
+ }
+
+ if ((param[0] & PSCI_0_2_64BIT) && !is_a64(env)) {
+ ret = PSCI_RET_INVALID_PARAMS;
+ goto err;
+ }
+
+ switch (param[0]) {
+ case PSCI_0_2_FN_PSCI_VERSION:
+ ret = PSCI_VERSION_MAJOR(0) | PSCI_VERSION_MINOR(2);
+ break;
+ case PSCI_0_2_FN_MIGRATE_INFO_TYPE:
+ ret = PSCI_0_2_TOS_MP; /* No trusted OS */
+ break;
+ case PSCI_0_2_FN_AFFINITY_INFO:
+ case PSCI_0_2_FN64_AFFINITY_INFO:
+ mpidr = param[1];
+
+ switch (param[2]) {
+ case 0:
+ cs = qemu_get_cpu(mpidr & 0xff);
+ if (!cs) {
+ ret = PSCI_RET_INVALID_PARAMS;
+ break;
+ }
+ cpu = ARM_CPU(cs);
+ ret = cpu->powered_off ? 1 : 0;
+ break;
+ default:
+ /* Everything above affinity level 0 is always on. */
+ ret = 0;
+ }
+ break;
+ case PSCI_0_2_FN_SYSTEM_RESET:
+ qemu_system_reset_request();
+ break;
+ case PSCI_0_2_FN_SYSTEM_OFF:
+ qemu_system_powerdown_request();
+ break;
+ case QEMU_PSCI_FN_CPU_ON:
+ case PSCI_0_2_FN_CPU_ON:
+ case PSCI_0_2_FN64_CPU_ON:
+ mpidr = param[1];
+ entry = param[2];
+ context_id = param[3];
+
+ /* change to the cpu we are powering up */
+ cs = qemu_get_cpu(mpidr & 0xff);
+ if (!cs) {
+ ret = PSCI_RET_INVALID_PARAMS;
+ break;
+ }
+ cpu = ARM_CPU(cs);
+
+ if (!cpu->powered_off) {
+ ret = PSCI_RET_ALREADY_ON;
+ break;
+ }
+
+ /* Initialize the cpu we are turning on */
+ cpu_reset(cs);
+ arm_cpu_set_pc(cs, entry);
+ cpu->powered_off = false;
+ cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
+
+ /* Set the context_id in r0/x0 */
+ if (is_a64(env)) {
+ cpu->env.xregs[0] = context_id;
+ } else {
+ cpu->env.regs[0] = context_id;
+ }
+
+ ret = 0;
+ break;
+ case QEMU_PSCI_FN_CPU_OFF:
+ case PSCI_0_2_FN_CPU_OFF:
+ cpu->powered_off = true;
+ cs->exit_request = 1;
+ cs->halted = 1;
+
+ /* CPU_OFF should never return, but if it does return an error */
+ ret = PSCI_RET_DENIED;
+ break;
+ case QEMU_PSCI_FN_CPU_SUSPEND:
+ case PSCI_0_2_FN_CPU_SUSPEND:
+ case PSCI_0_2_FN64_CPU_SUSPEND:
+ /* Affinity levels are not supported in QEMU */
+ if (param[1] & 0xfffe0000) {
+ ret = PSCI_RET_INVALID_PARAMS;
+ break;
+ }
+ /* Powerdown is not supported, we always go into WFI */
+ cs->halted = 1;
+ cs->exit_request = 1;
+
+ /* Return success when we wakeup */
+ ret = 0;
+ break;
+ case QEMU_PSCI_FN_MIGRATE:
+ case PSCI_0_2_FN_MIGRATE:
+ ret = PSCI_RET_NOT_SUPPORTED;
+ break;
+ default:
+ return false;
+ }
+
+err:
+ if (is_a64(env)) {
+ env->xregs[0] = ret;
+ } else {
+ env->regs[0] = ret;
+ }
+ return true;
+}
+#endif
--
1.9.1