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