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