1 /* 2 * QEMU support -- ARM Power Control specific functions. 3 * 4 * Copyright (c) 2016 Jean-Christophe Dubois 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2 or later. 7 * See the COPYING file in the top-level directory. 8 * 9 */ 10 11 #include "qemu/osdep.h" 12 #include "cpu.h" 13 #include "cpu-qom.h" 14 #include "internals.h" 15 #include "arm-powerctl.h" 16 #include "qemu/log.h" 17 #include "qemu/main-loop.h" 18 #include "sysemu/tcg.h" 19 20 #ifndef DEBUG_ARM_POWERCTL 21 #define DEBUG_ARM_POWERCTL 0 22 #endif 23 24 #define DPRINTF(fmt, args...) \ 25 do { \ 26 if (DEBUG_ARM_POWERCTL) { \ 27 fprintf(stderr, "[ARM]%s: " fmt , __func__, ##args); \ 28 } \ 29 } while (0) 30 31 CPUState *arm_get_cpu_by_id(uint64_t id) 32 { 33 CPUState *cpu; 34 35 DPRINTF("cpu %" PRId64 "\n", id); 36 37 CPU_FOREACH(cpu) { 38 ARMCPU *armcpu = ARM_CPU(cpu); 39 40 if (arm_cpu_mp_affinity(armcpu) == id) { 41 return cpu; 42 } 43 } 44 45 qemu_log_mask(LOG_GUEST_ERROR, 46 "[ARM]%s: Requesting unknown CPU %" PRId64 "\n", 47 __func__, id); 48 49 return NULL; 50 } 51 52 struct CpuOnInfo { 53 uint64_t entry; 54 uint64_t context_id; 55 uint32_t target_el; 56 bool target_aa64; 57 }; 58 59 60 static void arm_set_cpu_on_async_work(CPUState *target_cpu_state, 61 run_on_cpu_data data) 62 { 63 ARMCPU *target_cpu = ARM_CPU(target_cpu_state); 64 struct CpuOnInfo *info = (struct CpuOnInfo *) data.host_ptr; 65 66 /* Initialize the cpu we are turning on */ 67 cpu_reset(target_cpu_state); 68 arm_emulate_firmware_reset(target_cpu_state, info->target_el); 69 target_cpu_state->halted = 0; 70 71 /* We check if the started CPU is now at the correct level */ 72 assert(info->target_el == arm_current_el(&target_cpu->env)); 73 74 if (info->target_aa64) { 75 target_cpu->env.xregs[0] = info->context_id; 76 } else { 77 target_cpu->env.regs[0] = info->context_id; 78 } 79 80 if (tcg_enabled()) { 81 /* CP15 update requires rebuilding hflags */ 82 arm_rebuild_hflags(&target_cpu->env); 83 } 84 85 /* Start the new CPU at the requested address */ 86 cpu_set_pc(target_cpu_state, info->entry); 87 88 g_free(info); 89 90 /* Finally set the power status */ 91 assert(bql_locked()); 92 target_cpu->power_state = PSCI_ON; 93 } 94 95 int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id, 96 uint32_t target_el, bool target_aa64) 97 { 98 CPUState *target_cpu_state; 99 ARMCPU *target_cpu; 100 struct CpuOnInfo *info; 101 102 assert(bql_locked()); 103 104 DPRINTF("cpu %" PRId64 " (EL %d, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64 105 "\n", cpuid, target_el, target_aa64 ? "aarch64" : "aarch32", entry, 106 context_id); 107 108 /* requested EL level need to be in the 1 to 3 range */ 109 assert((target_el > 0) && (target_el < 4)); 110 111 if (target_aa64 && (entry & 3)) { 112 /* 113 * if we are booting in AArch64 mode then "entry" needs to be 4 bytes 114 * aligned. 115 */ 116 return QEMU_ARM_POWERCTL_INVALID_PARAM; 117 } 118 119 /* Retrieve the cpu we are powering up */ 120 target_cpu_state = arm_get_cpu_by_id(cpuid); 121 if (!target_cpu_state) { 122 /* The cpu was not found */ 123 return QEMU_ARM_POWERCTL_INVALID_PARAM; 124 } 125 126 target_cpu = ARM_CPU(target_cpu_state); 127 if (target_cpu->power_state == PSCI_ON) { 128 qemu_log_mask(LOG_GUEST_ERROR, 129 "[ARM]%s: CPU %" PRId64 " is already on\n", 130 __func__, cpuid); 131 return QEMU_ARM_POWERCTL_ALREADY_ON; 132 } 133 134 /* 135 * The newly brought CPU is requested to enter the exception level 136 * "target_el" and be in the requested mode (AArch64 or AArch32). 137 */ 138 139 if (((target_el == 3) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) || 140 ((target_el == 2) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL2))) { 141 /* 142 * The CPU does not support requested level 143 */ 144 return QEMU_ARM_POWERCTL_INVALID_PARAM; 145 } 146 147 if (!target_aa64 && arm_feature(&target_cpu->env, ARM_FEATURE_AARCH64)) { 148 /* 149 * For now we don't support booting an AArch64 CPU in AArch32 mode 150 * TODO: We should add this support later 151 */ 152 qemu_log_mask(LOG_UNIMP, 153 "[ARM]%s: Starting AArch64 CPU %" PRId64 154 " in AArch32 mode is not supported yet\n", 155 __func__, cpuid); 156 return QEMU_ARM_POWERCTL_INVALID_PARAM; 157 } 158 159 /* 160 * If another CPU has powered the target on we are in the state 161 * ON_PENDING and additional attempts to power on the CPU should 162 * fail (see 6.6 Implementation CPU_ON/CPU_OFF races in the PSCI 163 * spec) 164 */ 165 if (target_cpu->power_state == PSCI_ON_PENDING) { 166 qemu_log_mask(LOG_GUEST_ERROR, 167 "[ARM]%s: CPU %" PRId64 " is already powering on\n", 168 __func__, cpuid); 169 return QEMU_ARM_POWERCTL_ON_PENDING; 170 } 171 172 /* To avoid racing with a CPU we are just kicking off we do the 173 * final bit of preparation for the work in the target CPUs 174 * context. 175 */ 176 info = g_new(struct CpuOnInfo, 1); 177 info->entry = entry; 178 info->context_id = context_id; 179 info->target_el = target_el; 180 info->target_aa64 = target_aa64; 181 182 async_run_on_cpu(target_cpu_state, arm_set_cpu_on_async_work, 183 RUN_ON_CPU_HOST_PTR(info)); 184 185 /* We are good to go */ 186 return QEMU_ARM_POWERCTL_RET_SUCCESS; 187 } 188 189 static void arm_set_cpu_on_and_reset_async_work(CPUState *target_cpu_state, 190 run_on_cpu_data data) 191 { 192 ARMCPU *target_cpu = ARM_CPU(target_cpu_state); 193 194 /* Initialize the cpu we are turning on */ 195 cpu_reset(target_cpu_state); 196 target_cpu_state->halted = 0; 197 198 /* Finally set the power status */ 199 assert(bql_locked()); 200 target_cpu->power_state = PSCI_ON; 201 } 202 203 int arm_set_cpu_on_and_reset(uint64_t cpuid) 204 { 205 CPUState *target_cpu_state; 206 ARMCPU *target_cpu; 207 208 assert(bql_locked()); 209 210 /* Retrieve the cpu we are powering up */ 211 target_cpu_state = arm_get_cpu_by_id(cpuid); 212 if (!target_cpu_state) { 213 /* The cpu was not found */ 214 return QEMU_ARM_POWERCTL_INVALID_PARAM; 215 } 216 217 target_cpu = ARM_CPU(target_cpu_state); 218 if (target_cpu->power_state == PSCI_ON) { 219 qemu_log_mask(LOG_GUEST_ERROR, 220 "[ARM]%s: CPU %" PRId64 " is already on\n", 221 __func__, cpuid); 222 return QEMU_ARM_POWERCTL_ALREADY_ON; 223 } 224 225 /* 226 * If another CPU has powered the target on we are in the state 227 * ON_PENDING and additional attempts to power on the CPU should 228 * fail (see 6.6 Implementation CPU_ON/CPU_OFF races in the PSCI 229 * spec) 230 */ 231 if (target_cpu->power_state == PSCI_ON_PENDING) { 232 qemu_log_mask(LOG_GUEST_ERROR, 233 "[ARM]%s: CPU %" PRId64 " is already powering on\n", 234 __func__, cpuid); 235 return QEMU_ARM_POWERCTL_ON_PENDING; 236 } 237 238 async_run_on_cpu(target_cpu_state, arm_set_cpu_on_and_reset_async_work, 239 RUN_ON_CPU_NULL); 240 241 /* We are good to go */ 242 return QEMU_ARM_POWERCTL_RET_SUCCESS; 243 } 244 245 static void arm_set_cpu_off_async_work(CPUState *target_cpu_state, 246 run_on_cpu_data data) 247 { 248 ARMCPU *target_cpu = ARM_CPU(target_cpu_state); 249 250 assert(bql_locked()); 251 target_cpu->power_state = PSCI_OFF; 252 target_cpu_state->halted = 1; 253 target_cpu_state->exception_index = EXCP_HLT; 254 } 255 256 int arm_set_cpu_off(uint64_t cpuid) 257 { 258 CPUState *target_cpu_state; 259 ARMCPU *target_cpu; 260 261 assert(bql_locked()); 262 263 DPRINTF("cpu %" PRId64 "\n", cpuid); 264 265 /* change to the cpu we are powering up */ 266 target_cpu_state = arm_get_cpu_by_id(cpuid); 267 if (!target_cpu_state) { 268 return QEMU_ARM_POWERCTL_INVALID_PARAM; 269 } 270 target_cpu = ARM_CPU(target_cpu_state); 271 if (target_cpu->power_state == PSCI_OFF) { 272 qemu_log_mask(LOG_GUEST_ERROR, 273 "[ARM]%s: CPU %" PRId64 " is already off\n", 274 __func__, cpuid); 275 return QEMU_ARM_POWERCTL_IS_OFF; 276 } 277 278 /* Queue work to run under the target vCPUs context */ 279 async_run_on_cpu(target_cpu_state, arm_set_cpu_off_async_work, 280 RUN_ON_CPU_NULL); 281 282 return QEMU_ARM_POWERCTL_RET_SUCCESS; 283 } 284 285 static void arm_reset_cpu_async_work(CPUState *target_cpu_state, 286 run_on_cpu_data data) 287 { 288 /* Reset the cpu */ 289 cpu_reset(target_cpu_state); 290 } 291 292 int arm_reset_cpu(uint64_t cpuid) 293 { 294 CPUState *target_cpu_state; 295 ARMCPU *target_cpu; 296 297 assert(bql_locked()); 298 299 DPRINTF("cpu %" PRId64 "\n", cpuid); 300 301 /* change to the cpu we are resetting */ 302 target_cpu_state = arm_get_cpu_by_id(cpuid); 303 if (!target_cpu_state) { 304 return QEMU_ARM_POWERCTL_INVALID_PARAM; 305 } 306 target_cpu = ARM_CPU(target_cpu_state); 307 308 if (target_cpu->power_state == PSCI_OFF) { 309 qemu_log_mask(LOG_GUEST_ERROR, 310 "[ARM]%s: CPU %" PRId64 " is off\n", 311 __func__, cpuid); 312 return QEMU_ARM_POWERCTL_IS_OFF; 313 } 314 315 /* Queue work to run under the target vCPUs context */ 316 async_run_on_cpu(target_cpu_state, arm_reset_cpu_async_work, 317 RUN_ON_CPU_NULL); 318 319 return QEMU_ARM_POWERCTL_RET_SUCCESS; 320 } 321