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 #include <cpu.h>
19 #include <cpu-qom.h>
20 #include <exec/helper-proto.h>
21 #include <kvm-consts.h>
22 #include <sysemu/sysemu.h>
23 #include "internals.h"
24 
arm_is_psci_call(ARMCPU * cpu,int excp_type)25 bool arm_is_psci_call(ARMCPU *cpu, int excp_type)
26 {
27     /* Return true if the r0/x0 value indicates a PSCI call and
28      * the exception type matches the configured PSCI conduit. This is
29      * called before the SMC/HVC instruction is executed, to decide whether
30      * we should treat it as a PSCI call or with the architecturally
31      * defined behaviour for an SMC or HVC (which might be UNDEF or trap
32      * to EL2 or to EL3).
33      */
34     CPUARMState *env = &cpu->env;
35     uint64_t param = is_a64(env) ? env->xregs[0] : env->regs[0];
36 
37     switch (excp_type) {
38     case EXCP_HVC:
39         if (cpu->psci_conduit != QEMU_PSCI_CONDUIT_HVC) {
40             return false;
41         }
42         break;
43     case EXCP_SMC:
44         if (cpu->psci_conduit != QEMU_PSCI_CONDUIT_SMC) {
45             return false;
46         }
47         break;
48     default:
49         return false;
50     }
51 
52     switch (param) {
53     case QEMU_PSCI_0_2_FN_PSCI_VERSION:
54     case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
55     case QEMU_PSCI_0_2_FN_AFFINITY_INFO:
56     case QEMU_PSCI_0_2_FN64_AFFINITY_INFO:
57     case QEMU_PSCI_0_2_FN_SYSTEM_RESET:
58     case QEMU_PSCI_0_2_FN_SYSTEM_OFF:
59     case QEMU_PSCI_0_1_FN_CPU_ON:
60     case QEMU_PSCI_0_2_FN_CPU_ON:
61     case QEMU_PSCI_0_2_FN64_CPU_ON:
62     case QEMU_PSCI_0_1_FN_CPU_OFF:
63     case QEMU_PSCI_0_2_FN_CPU_OFF:
64     case QEMU_PSCI_0_1_FN_CPU_SUSPEND:
65     case QEMU_PSCI_0_2_FN_CPU_SUSPEND:
66     case QEMU_PSCI_0_2_FN64_CPU_SUSPEND:
67     case QEMU_PSCI_0_1_FN_MIGRATE:
68     case QEMU_PSCI_0_2_FN_MIGRATE:
69         return true;
70     default:
71         return false;
72     }
73 }
74 
arm_handle_psci_call(ARMCPU * cpu)75 void arm_handle_psci_call(ARMCPU *cpu)
76 {
77     /*
78      * This function partially implements the logic for dispatching Power State
79      * Coordination Interface (PSCI) calls (as described in ARM DEN 0022B.b),
80      * to the extent required for bringing up and taking down secondary cores,
81      * and for handling reset and poweroff requests.
82      * Additional information about the calling convention used is available in
83      * the document 'SMC Calling Convention' (ARM DEN 0028)
84      */
85     CPUState *cs = CPU(cpu);
86     CPUARMState *env = &cpu->env;
87     uint64_t param[4];
88     uint64_t context_id, mpidr;
89     target_ulong entry;
90     int32_t ret = 0;
91     int i;
92 
93     for (i = 0; i < 4; i++) {
94         /*
95          * All PSCI functions take explicit 32-bit or native int sized
96          * arguments so we can simply zero-extend all arguments regardless
97          * of which exact function we are about to call.
98          */
99         param[i] = is_a64(env) ? env->xregs[i] : env->regs[i];
100     }
101 
102     if ((param[0] & QEMU_PSCI_0_2_64BIT) && !is_a64(env)) {
103         ret = QEMU_PSCI_RET_INVALID_PARAMS;
104         goto err;
105     }
106 
107     switch (param[0]) {
108         CPUState *target_cpu_state;
109         ARMCPU *target_cpu;
110         CPUClass *target_cpu_class;
111 
112     case QEMU_PSCI_0_2_FN_PSCI_VERSION:
113         ret = QEMU_PSCI_0_2_RET_VERSION_0_2;
114         break;
115     case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
116         ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS */
117         break;
118     case QEMU_PSCI_0_2_FN_AFFINITY_INFO:
119     case QEMU_PSCI_0_2_FN64_AFFINITY_INFO:
120         mpidr = param[1];
121 
122         switch (param[2]) {
123         case 0:
124             target_cpu_state = qemu_get_cpu(env->uc, mpidr & 0xff);
125             if (!target_cpu_state) {
126                 ret = QEMU_PSCI_RET_INVALID_PARAMS;
127                 break;
128             }
129             target_cpu = ARM_CPU(env->uc, target_cpu_state);
130             ret = target_cpu->powered_off ? 1 : 0;
131             break;
132         default:
133             /* Everything above affinity level 0 is always on. */
134             ret = 0;
135         }
136         break;
137     case QEMU_PSCI_0_2_FN_SYSTEM_RESET:
138         qemu_system_reset_request(env->uc);
139         /* QEMU reset and shutdown are async requests, but PSCI
140          * mandates that we never return from the reset/shutdown
141          * call, so power the CPU off now so it doesn't execute
142          * anything further.
143          */
144         goto cpu_off;
145     case QEMU_PSCI_0_2_FN_SYSTEM_OFF:
146         qemu_system_shutdown_request();
147         goto cpu_off;
148     case QEMU_PSCI_0_1_FN_CPU_ON:
149     case QEMU_PSCI_0_2_FN_CPU_ON:
150     case QEMU_PSCI_0_2_FN64_CPU_ON:
151         mpidr = param[1];
152         entry = param[2];
153         context_id = param[3];
154 
155         /* change to the cpu we are powering up */
156         target_cpu_state = qemu_get_cpu(env->uc, mpidr & 0xff);
157         if (!target_cpu_state) {
158             ret = QEMU_PSCI_RET_INVALID_PARAMS;
159             break;
160         }
161         target_cpu = ARM_CPU(env->uc, target_cpu_state);
162         if (!target_cpu->powered_off) {
163             ret = QEMU_PSCI_RET_ALREADY_ON;
164             break;
165         }
166         target_cpu_class = CPU_GET_CLASS(env->uc, target_cpu);
167 
168         /* Initialize the cpu we are turning on */
169         cpu_reset(target_cpu_state);
170         target_cpu->powered_off = false;
171         target_cpu_state->halted = 0;
172 
173         /*
174          * The PSCI spec mandates that newly brought up CPUs enter the
175          * exception level of the caller in the same execution mode as
176          * the caller, with context_id in x0/r0, respectively.
177          *
178          * For now, it is sufficient to assert() that CPUs come out of
179          * reset in the same mode as the calling CPU, since we only
180          * implement EL1, which means that
181          * (a) there is no EL2 for the calling CPU to trap into to change
182          *     its state
183          * (b) the newly brought up CPU enters EL1 immediately after coming
184          *     out of reset in the default state
185          */
186         assert(is_a64(env) == is_a64(&target_cpu->env));
187         if (is_a64(env)) {
188             if (entry & 1) {
189                 ret = QEMU_PSCI_RET_INVALID_PARAMS;
190                 break;
191             }
192             target_cpu->env.xregs[0] = context_id;
193         } else {
194             target_cpu->env.regs[0] = context_id;
195             target_cpu->env.thumb = entry & 1;
196         }
197         target_cpu_class->set_pc(target_cpu_state, entry);
198 
199         ret = 0;
200         break;
201     case QEMU_PSCI_0_1_FN_CPU_OFF:
202     case QEMU_PSCI_0_2_FN_CPU_OFF:
203         goto cpu_off;
204     case QEMU_PSCI_0_1_FN_CPU_SUSPEND:
205     case QEMU_PSCI_0_2_FN_CPU_SUSPEND:
206     case QEMU_PSCI_0_2_FN64_CPU_SUSPEND:
207         /* Affinity levels are not supported in QEMU */
208         if (param[1] & 0xfffe0000) {
209             ret = QEMU_PSCI_RET_INVALID_PARAMS;
210             break;
211         }
212         /* Powerdown is not supported, we always go into WFI */
213         if (is_a64(env)) {
214             env->xregs[0] = 0;
215         } else {
216             env->regs[0] = 0;
217         }
218         helper_wfi(env);
219         break;
220     case QEMU_PSCI_0_1_FN_MIGRATE:
221     case QEMU_PSCI_0_2_FN_MIGRATE:
222         ret = QEMU_PSCI_RET_NOT_SUPPORTED;
223         break;
224     default:
225         g_assert_not_reached();
226     }
227 
228 err:
229     if (is_a64(env)) {
230         env->xregs[0] = ret;
231     } else {
232         env->regs[0] = ret;
233     }
234     return;
235 
236 cpu_off:
237     cpu->powered_off = true;
238     cs->halted = 1;
239     cs->exception_index = EXCP_HLT;
240     cpu_loop_exit(cs);
241     /* notreached */
242 }
243