qemu-devel
[Top][All Lists]
Advanced

[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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]