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