Add qemu 2.4.0
[kvmfornfv.git] / qemu / target-arm / psci.c
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
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
75 static CPUState *get_cpu_by_id(uint64_t id)
76 {
77     CPUState *cpu;
78
79     CPU_FOREACH(cpu) {
80         ARMCPU *armcpu = ARM_CPU(cpu);
81
82         if (armcpu->mp_affinity == id) {
83             return cpu;
84         }
85     }
86
87     return NULL;
88 }
89
90 void arm_handle_psci_call(ARMCPU *cpu)
91 {
92     /*
93      * This function partially implements the logic for dispatching Power State
94      * Coordination Interface (PSCI) calls (as described in ARM DEN 0022B.b),
95      * to the extent required for bringing up and taking down secondary cores,
96      * and for handling reset and poweroff requests.
97      * Additional information about the calling convention used is available in
98      * the document 'SMC Calling Convention' (ARM DEN 0028)
99      */
100     CPUState *cs = CPU(cpu);
101     CPUARMState *env = &cpu->env;
102     uint64_t param[4];
103     uint64_t context_id, mpidr;
104     target_ulong entry;
105     int32_t ret = 0;
106     int i;
107
108     for (i = 0; i < 4; i++) {
109         /*
110          * All PSCI functions take explicit 32-bit or native int sized
111          * arguments so we can simply zero-extend all arguments regardless
112          * of which exact function we are about to call.
113          */
114         param[i] = is_a64(env) ? env->xregs[i] : env->regs[i];
115     }
116
117     if ((param[0] & QEMU_PSCI_0_2_64BIT) && !is_a64(env)) {
118         ret = QEMU_PSCI_RET_INVALID_PARAMS;
119         goto err;
120     }
121
122     switch (param[0]) {
123         CPUState *target_cpu_state;
124         ARMCPU *target_cpu;
125         CPUClass *target_cpu_class;
126
127     case QEMU_PSCI_0_2_FN_PSCI_VERSION:
128         ret = QEMU_PSCI_0_2_RET_VERSION_0_2;
129         break;
130     case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
131         ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS */
132         break;
133     case QEMU_PSCI_0_2_FN_AFFINITY_INFO:
134     case QEMU_PSCI_0_2_FN64_AFFINITY_INFO:
135         mpidr = param[1];
136
137         switch (param[2]) {
138         case 0:
139             target_cpu_state = get_cpu_by_id(mpidr);
140             if (!target_cpu_state) {
141                 ret = QEMU_PSCI_RET_INVALID_PARAMS;
142                 break;
143             }
144             target_cpu = ARM_CPU(target_cpu_state);
145             ret = target_cpu->powered_off ? 1 : 0;
146             break;
147         default:
148             /* Everything above affinity level 0 is always on. */
149             ret = 0;
150         }
151         break;
152     case QEMU_PSCI_0_2_FN_SYSTEM_RESET:
153         qemu_system_reset_request();
154         /* QEMU reset and shutdown are async requests, but PSCI
155          * mandates that we never return from the reset/shutdown
156          * call, so power the CPU off now so it doesn't execute
157          * anything further.
158          */
159         goto cpu_off;
160     case QEMU_PSCI_0_2_FN_SYSTEM_OFF:
161         qemu_system_shutdown_request();
162         goto cpu_off;
163     case QEMU_PSCI_0_1_FN_CPU_ON:
164     case QEMU_PSCI_0_2_FN_CPU_ON:
165     case QEMU_PSCI_0_2_FN64_CPU_ON:
166         mpidr = param[1];
167         entry = param[2];
168         context_id = param[3];
169
170         /* change to the cpu we are powering up */
171         target_cpu_state = get_cpu_by_id(mpidr);
172         if (!target_cpu_state) {
173             ret = QEMU_PSCI_RET_INVALID_PARAMS;
174             break;
175         }
176         target_cpu = ARM_CPU(target_cpu_state);
177         if (!target_cpu->powered_off) {
178             ret = QEMU_PSCI_RET_ALREADY_ON;
179             break;
180         }
181         target_cpu_class = CPU_GET_CLASS(target_cpu);
182
183         /* Initialize the cpu we are turning on */
184         cpu_reset(target_cpu_state);
185         target_cpu->powered_off = false;
186         target_cpu_state->halted = 0;
187
188         /*
189          * The PSCI spec mandates that newly brought up CPUs enter the
190          * exception level of the caller in the same execution mode as
191          * the caller, with context_id in x0/r0, respectively.
192          *
193          * For now, it is sufficient to assert() that CPUs come out of
194          * reset in the same mode as the calling CPU, since we only
195          * implement EL1, which means that
196          * (a) there is no EL2 for the calling CPU to trap into to change
197          *     its state
198          * (b) the newly brought up CPU enters EL1 immediately after coming
199          *     out of reset in the default state
200          */
201         assert(is_a64(env) == is_a64(&target_cpu->env));
202         if (is_a64(env)) {
203             if (entry & 1) {
204                 ret = QEMU_PSCI_RET_INVALID_PARAMS;
205                 break;
206             }
207             target_cpu->env.xregs[0] = context_id;
208         } else {
209             target_cpu->env.regs[0] = context_id;
210             target_cpu->env.thumb = entry & 1;
211         }
212         target_cpu_class->set_pc(target_cpu_state, entry);
213
214         ret = 0;
215         break;
216     case QEMU_PSCI_0_1_FN_CPU_OFF:
217     case QEMU_PSCI_0_2_FN_CPU_OFF:
218         goto cpu_off;
219     case QEMU_PSCI_0_1_FN_CPU_SUSPEND:
220     case QEMU_PSCI_0_2_FN_CPU_SUSPEND:
221     case QEMU_PSCI_0_2_FN64_CPU_SUSPEND:
222         /* Affinity levels are not supported in QEMU */
223         if (param[1] & 0xfffe0000) {
224             ret = QEMU_PSCI_RET_INVALID_PARAMS;
225             break;
226         }
227         /* Powerdown is not supported, we always go into WFI */
228         if (is_a64(env)) {
229             env->xregs[0] = 0;
230         } else {
231             env->regs[0] = 0;
232         }
233         helper_wfi(env);
234         break;
235     case QEMU_PSCI_0_1_FN_MIGRATE:
236     case QEMU_PSCI_0_2_FN_MIGRATE:
237         ret = QEMU_PSCI_RET_NOT_SUPPORTED;
238         break;
239     default:
240         g_assert_not_reached();
241     }
242
243 err:
244     if (is_a64(env)) {
245         env->xregs[0] = ret;
246     } else {
247         env->regs[0] = ret;
248     }
249     return;
250
251 cpu_off:
252     cpu->powered_off = true;
253     cs->halted = 1;
254     cs->exception_index = EXCP_HLT;
255     cpu_loop_exit(cs);
256     /* notreached */
257 }