xref: /openbmc/qemu/target/arm/tcg/psci.c (revision 861bbc88)
1 /*
2  * Copyright (C) 2014 - Linaro
3  * Author: Rob Herring <rob.herring@linaro.org>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "qemu/osdep.h"
20 #include "cpu.h"
21 #include "exec/helper-proto.h"
22 #include "kvm-consts.h"
23 #include "qemu/main-loop.h"
24 #include "sysemu/runstate.h"
25 #include "internals.h"
26 #include "arm-powerctl.h"
27 #include "target/arm/multiprocessing.h"
28 
29 bool arm_is_psci_call(ARMCPU *cpu, int excp_type)
30 {
31     /*
32      * Return true if the exception type matches the configured PSCI conduit.
33      * This is called before the SMC/HVC instruction is executed, to decide
34      * whether we should treat it as a PSCI call or with the architecturally
35      * defined behaviour for an SMC or HVC (which might be UNDEF or trap
36      * to EL2 or to EL3).
37      */
38 
39     switch (excp_type) {
40     case EXCP_HVC:
41         if (cpu->psci_conduit != QEMU_PSCI_CONDUIT_HVC) {
42             return false;
43         }
44         break;
45     case EXCP_SMC:
46         if (cpu->psci_conduit != QEMU_PSCI_CONDUIT_SMC) {
47             return false;
48         }
49         break;
50     default:
51         return false;
52     }
53 
54     return true;
55 }
56 
57 void arm_handle_psci_call(ARMCPU *cpu)
58 {
59     /*
60      * This function partially implements the logic for dispatching Power State
61      * Coordination Interface (PSCI) calls (as described in ARM DEN 0022D.b),
62      * to the extent required for bringing up and taking down secondary cores,
63      * and for handling reset and poweroff requests.
64      * Additional information about the calling convention used is available in
65      * the document 'SMC Calling Convention' (ARM DEN 0028)
66      */
67     CPUARMState *env = &cpu->env;
68     uint64_t param[4];
69     uint64_t context_id, mpidr;
70     target_ulong entry;
71     int32_t ret = 0;
72     int i;
73 
74     for (i = 0; i < 4; i++) {
75         /*
76          * All PSCI functions take explicit 32-bit or native int sized
77          * arguments so we can simply zero-extend all arguments regardless
78          * of which exact function we are about to call.
79          */
80         param[i] = is_a64(env) ? env->xregs[i] : env->regs[i];
81     }
82 
83     if ((param[0] & QEMU_PSCI_0_2_64BIT) && !is_a64(env)) {
84         ret = QEMU_PSCI_RET_NOT_SUPPORTED;
85         goto err;
86     }
87 
88     switch (param[0]) {
89         CPUState *target_cpu_state;
90         ARMCPU *target_cpu;
91 
92     case QEMU_PSCI_0_2_FN_PSCI_VERSION:
93         ret = QEMU_PSCI_VERSION_1_1;
94         break;
95     case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
96         ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS */
97         break;
98     case QEMU_PSCI_0_2_FN_AFFINITY_INFO:
99     case QEMU_PSCI_0_2_FN64_AFFINITY_INFO:
100         mpidr = param[1];
101 
102         switch (param[2]) {
103         case 0:
104             target_cpu_state = arm_get_cpu_by_id(mpidr);
105             if (!target_cpu_state) {
106                 ret = QEMU_PSCI_RET_INVALID_PARAMS;
107                 break;
108             }
109             target_cpu = ARM_CPU(target_cpu_state);
110 
111             g_assert(bql_locked());
112             ret = target_cpu->power_state;
113             break;
114         default:
115             /* Everything above affinity level 0 is always on. */
116             ret = 0;
117         }
118         break;
119     case QEMU_PSCI_0_2_FN_SYSTEM_RESET:
120         qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
121         /* QEMU reset and shutdown are async requests, but PSCI
122          * mandates that we never return from the reset/shutdown
123          * call, so power the CPU off now so it doesn't execute
124          * anything further.
125          */
126         goto cpu_off;
127     case QEMU_PSCI_0_2_FN_SYSTEM_OFF:
128         qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
129         goto cpu_off;
130     case QEMU_PSCI_0_1_FN_CPU_ON:
131     case QEMU_PSCI_0_2_FN_CPU_ON:
132     case QEMU_PSCI_0_2_FN64_CPU_ON:
133     {
134         /* The PSCI spec mandates that newly brought up CPUs start
135          * in the highest exception level which exists and is enabled
136          * on the calling CPU. Since the QEMU PSCI implementation is
137          * acting as a "fake EL3" or "fake EL2" firmware, this for us
138          * means that we want to start at the highest NS exception level
139          * that we are providing to the guest.
140          * The execution mode should be that which is currently in use
141          * by the same exception level on the calling CPU.
142          * The CPU should be started with the context_id value
143          * in x0 (if AArch64) or r0 (if AArch32).
144          */
145         int target_el = arm_feature(env, ARM_FEATURE_EL2) ? 2 : 1;
146         bool target_aarch64 = arm_el_is_aa64(env, target_el);
147 
148         mpidr = param[1];
149         entry = param[2];
150         context_id = param[3];
151         ret = arm_set_cpu_on(mpidr, entry, context_id,
152                              target_el, target_aarch64);
153         break;
154     }
155     case QEMU_PSCI_0_1_FN_CPU_OFF:
156     case QEMU_PSCI_0_2_FN_CPU_OFF:
157         goto cpu_off;
158     case QEMU_PSCI_0_1_FN_CPU_SUSPEND:
159     case QEMU_PSCI_0_2_FN_CPU_SUSPEND:
160     case QEMU_PSCI_0_2_FN64_CPU_SUSPEND:
161         /* Affinity levels are not supported in QEMU */
162         if (param[1] & 0xfffe0000) {
163             ret = QEMU_PSCI_RET_INVALID_PARAMS;
164             break;
165         }
166         /* Powerdown is not supported, we always go into WFI */
167         if (is_a64(env)) {
168             env->xregs[0] = 0;
169         } else {
170             env->regs[0] = 0;
171         }
172         helper_wfi(env, 4);
173         break;
174     case QEMU_PSCI_1_0_FN_PSCI_FEATURES:
175         switch (param[1]) {
176         case QEMU_PSCI_0_2_FN_PSCI_VERSION:
177         case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
178         case QEMU_PSCI_0_2_FN_AFFINITY_INFO:
179         case QEMU_PSCI_0_2_FN64_AFFINITY_INFO:
180         case QEMU_PSCI_0_2_FN_SYSTEM_RESET:
181         case QEMU_PSCI_0_2_FN_SYSTEM_OFF:
182         case QEMU_PSCI_0_1_FN_CPU_ON:
183         case QEMU_PSCI_0_2_FN_CPU_ON:
184         case QEMU_PSCI_0_2_FN64_CPU_ON:
185         case QEMU_PSCI_0_1_FN_CPU_OFF:
186         case QEMU_PSCI_0_2_FN_CPU_OFF:
187         case QEMU_PSCI_0_1_FN_CPU_SUSPEND:
188         case QEMU_PSCI_0_2_FN_CPU_SUSPEND:
189         case QEMU_PSCI_0_2_FN64_CPU_SUSPEND:
190         case QEMU_PSCI_1_0_FN_PSCI_FEATURES:
191             if (!(param[1] & QEMU_PSCI_0_2_64BIT) || is_a64(env)) {
192                 ret = 0;
193                 break;
194             }
195             /* fallthrough */
196         case QEMU_PSCI_0_1_FN_MIGRATE:
197         case QEMU_PSCI_0_2_FN_MIGRATE:
198         default:
199             ret = QEMU_PSCI_RET_NOT_SUPPORTED;
200             break;
201         }
202         break;
203     case QEMU_PSCI_0_1_FN_MIGRATE:
204     case QEMU_PSCI_0_2_FN_MIGRATE:
205     default:
206         ret = QEMU_PSCI_RET_NOT_SUPPORTED;
207         break;
208     }
209 
210 err:
211     if (is_a64(env)) {
212         env->xregs[0] = ret;
213     } else {
214         env->regs[0] = ret;
215     }
216     return;
217 
218 cpu_off:
219     ret = arm_set_cpu_off(arm_cpu_mp_affinity(cpu));
220     /* notreached */
221     /* sanity check in case something failed */
222     assert(ret == QEMU_ARM_POWERCTL_RET_SUCCESS);
223 }
224