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