diff mbox

[v2,4/6] target-arm: add emulation of PSCI calls for system emulation

Message ID 1400812209-26743-5-git-send-email-robherring2@gmail.com
State New
Headers show

Commit Message

Rob Herring May 23, 2014, 2:30 a.m. UTC
From: Rob Herring <rob.herring@linaro.org>

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 <rob.herring@linaro.org>
---
v2:
- Add reference to PSCI spec
- Add target_cs instead of cs when doing operations on a different core.
- Move arm_cpu_do_hvc/smc contents from previous patch.
- Set context_id on both x0 and r0.

 target-arm/Makefile.objs |   2 +-
 target-arm/cpu-qom.h     |   6 ++
 target-arm/cpu.c         |   1 +
 target-arm/helper.c      |  12 ++++
 target-arm/kvm-consts.h  |   6 ++
 target-arm/psci.c        | 157 +++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 183 insertions(+), 1 deletion(-)
 create mode 100644 target-arm/psci.c

--
1.9.1

Comments

Peter Maydell June 3, 2014, 11:14 a.m. UTC | #1
On 23 May 2014 03:30, Rob Herring <robherring2@gmail.com> wrote:
> From: Rob Herring <rob.herring@linaro.org>
>
> 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.

> diff --git a/target-arm/psci.c b/target-arm/psci.c
> new file mode 100644
> index 0000000..88af3f9
> --- /dev/null
> +++ b/target-arm/psci.c
> @@ -0,0 +1,157 @@
> +/*
> + * Copyright (C) 2014 - Linaro
> + * Author: Rob Herring <rob.herring@linaro.org>
> + *
> + *  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>

This won't build on non-Linux hosts: you can't assume
the linux-headers/ includes are available except within
code guarded by CONFIG_KVM.

thanks
-- PMM
Rob Herring June 4, 2014, 10:09 a.m. UTC | #2
On Tue, Jun 3, 2014 at 6:14 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 23 May 2014 03:30, Rob Herring <robherring2@gmail.com> wrote:
>> From: Rob Herring <rob.herring@linaro.org>
>>
>> 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.
>
>> diff --git a/target-arm/psci.c b/target-arm/psci.c
>> new file mode 100644
>> index 0000000..88af3f9
>> --- /dev/null
>> +++ b/target-arm/psci.c
>> @@ -0,0 +1,157 @@
>> +/*
>> + * Copyright (C) 2014 - Linaro
>> + * Author: Rob Herring <rob.herring@linaro.org>
>> + *
>> + *  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>
>
> This won't build on non-Linux hosts: you can't assume
> the linux-headers/ includes are available except within
> code guarded by CONFIG_KVM.

What do you suggest doing here then. Really, psci.h is OS independent.
Can we copy it to somewhere else in the qemu tree?

Rob
Peter Maydell June 4, 2014, 10:16 a.m. UTC | #3
On 4 June 2014 11:09, Rob Herring <robherring2@gmail.com> wrote:
> On Tue, Jun 3, 2014 at 6:14 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>> This won't build on non-Linux hosts: you can't assume
>> the linux-headers/ includes are available except within
>> code guarded by CONFIG_KVM.
>
> What do you suggest doing here then. Really, psci.h is OS independent.
> Can we copy it to somewhere else in the qemu tree?

Only if you can guarantee that it won't clash with the one that is
in the linux-headers/ tree, or the one in /usr/include, ie that it
doesn't matter which of those three we pick up. That pretty much
means "kernel KVM headers can't ever rely on new PSCI
constants being added to psci.h" so that doesn't seem like it's
going to work. Otherwise it would need to be
copy-and-rename-symbols, same as we do for anything else
where we need them outside the KVM code. I agree that's pretty
ugly.

thanks
-- PMM
diff mbox

Patch

diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs
index dcd167e..50f6c43 100644
--- a/target-arm/Makefile.objs
+++ b/target-arm/Makefile.objs
@@ -1,5 +1,5 @@ 
 obj-y += arm-semi.o
-obj-$(CONFIG_SOFTMMU) += machine.o
+obj-$(CONFIG_SOFTMMU) += machine.o psci.o
 obj-$(CONFIG_KVM) += kvm.o
 obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o
 obj-$(call land,$(CONFIG_KVM),$(TARGET_AARCH64)) += kvm64.o
diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
index d2ff087..56999f1 100644
--- a/target-arm/cpu-qom.h
+++ b/target-arm/cpu-qom.h
@@ -105,6 +105,11 @@  typedef struct ARMCPU {
     /* CPU currently in PSCI powered-off state */
     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.
      */
@@ -195,6 +200,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 a0f9916..64f1157 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -1024,6 +1024,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 552e601..c1be201 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -3257,11 +3257,23 @@  void arm_v7m_cpu_do_interrupt(CPUState *cs)

 bool arm_cpu_do_hvc(CPUState *cs)
 {
+    ARMCPU *cpu = ARM_CPU(cs);
+
+    if (cpu->psci_method == QEMU_PSCI_METHOD_HVC) {
+        return arm_handle_psci(cs);
+    }
+
     return false;
 }

 bool arm_cpu_do_smc(CPUState *cs)
 {
+    ARMCPU *cpu = ARM_CPU(cs);
+
+    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 6009a33..d25b9b8 100644
--- a/target-arm/kvm-consts.h
+++ b/target-arm/kvm-consts.h
@@ -50,6 +50,12 @@  MISMATCH_CHECK(PSCI_FN_CPU_OFF, KVM_PSCI_FN_CPU_OFF)
 MISMATCH_CHECK(PSCI_FN_CPU_ON, KVM_PSCI_FN_CPU_ON)
 MISMATCH_CHECK(PSCI_FN_MIGRATE, KVM_PSCI_FN_MIGRATE)

+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..88af3f9
--- /dev/null
+++ b/target-arm/psci.c
@@ -0,0 +1,157 @@ 
+/*
+ * Copyright (C) 2014 - Linaro
+ * Author: Rob Herring <rob.herring@linaro.org>
+ *
+ *  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>
+
+/*
+ * This function implements emulation of ARM Power State Coordination
+ * Interface (PSCI) version 0.2. Details of the PSCI functionality can be
+ * found at:
+ * http://infocenter.arm.com/help//topic/com.arm.doc.den0022b/index.html
+ */
+bool arm_handle_psci(CPUState *cs)
+{
+    CPUState *target_cs;
+    CPUClass *cc;
+    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++) {
+        /* Zero extending registers on 32-bit is okay for PSCI */
+        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:
+            /* Get the target cpu */
+            target_cs = qemu_get_cpu(mpidr & 0xff);
+            if (!target_cs) {
+                ret = PSCI_RET_INVALID_PARAMS;
+                break;
+            }
+            cpu = ARM_CPU(target_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 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 */
+        target_cs = qemu_get_cpu(mpidr & 0xff);
+        if (!target_cs) {
+            ret = PSCI_RET_INVALID_PARAMS;
+            break;
+        }
+        cpu = ARM_CPU(target_cs);
+
+        if (!cpu->powered_off) {
+            ret = PSCI_RET_ALREADY_ON;
+            break;
+        }
+
+        /* Initialize the cpu we are turning on */
+        cpu_reset(target_cs);
+        cc = CPU_GET_CLASS(target_cs);
+        cc->set_pc(target_cs, entry);
+
+        cpu->powered_off = false;
+        target_cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
+
+        /* Set the context_id in r0/x0 */
+        cpu->env.xregs[0] = cpu->env.regs[0] = context_id;
+
+        ret = 0;
+        break;
+    case 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 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 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;
+}