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 "exec/exec-all.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 int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id, 52 uint32_t target_el, bool target_aa64) 53 { 54 CPUState *target_cpu_state; 55 ARMCPU *target_cpu; 56 57 DPRINTF("cpu %" PRId64 " (EL %d, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64 58 "\n", cpuid, target_el, target_aa64 ? "aarch64" : "aarch32", entry, 59 context_id); 60 61 /* requested EL level need to be in the 1 to 3 range */ 62 assert((target_el > 0) && (target_el < 4)); 63 64 if (target_aa64 && (entry & 3)) { 65 /* 66 * if we are booting in AArch64 mode then "entry" needs to be 4 bytes 67 * aligned. 68 */ 69 return QEMU_ARM_POWERCTL_INVALID_PARAM; 70 } 71 72 /* Retrieve the cpu we are powering up */ 73 target_cpu_state = arm_get_cpu_by_id(cpuid); 74 if (!target_cpu_state) { 75 /* The cpu was not found */ 76 return QEMU_ARM_POWERCTL_INVALID_PARAM; 77 } 78 79 target_cpu = ARM_CPU(target_cpu_state); 80 if (!target_cpu->powered_off) { 81 qemu_log_mask(LOG_GUEST_ERROR, 82 "[ARM]%s: CPU %" PRId64 " is already on\n", 83 __func__, cpuid); 84 return QEMU_ARM_POWERCTL_ALREADY_ON; 85 } 86 87 /* 88 * The newly brought CPU is requested to enter the exception level 89 * "target_el" and be in the requested mode (AArch64 or AArch32). 90 */ 91 92 if (((target_el == 3) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) || 93 ((target_el == 2) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL2))) { 94 /* 95 * The CPU does not support requested level 96 */ 97 return QEMU_ARM_POWERCTL_INVALID_PARAM; 98 } 99 100 if (!target_aa64 && arm_feature(&target_cpu->env, ARM_FEATURE_AARCH64)) { 101 /* 102 * For now we don't support booting an AArch64 CPU in AArch32 mode 103 * TODO: We should add this support later 104 */ 105 qemu_log_mask(LOG_UNIMP, 106 "[ARM]%s: Starting AArch64 CPU %" PRId64 107 " in AArch32 mode is not supported yet\n", 108 __func__, cpuid); 109 return QEMU_ARM_POWERCTL_INVALID_PARAM; 110 } 111 112 /* Initialize the cpu we are turning on */ 113 cpu_reset(target_cpu_state); 114 target_cpu->powered_off = false; 115 target_cpu_state->halted = 0; 116 117 if (target_aa64) { 118 if ((target_el < 3) && arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) { 119 /* 120 * As target mode is AArch64, we need to set lower 121 * exception level (the requested level 2) to AArch64 122 */ 123 target_cpu->env.cp15.scr_el3 |= SCR_RW; 124 } 125 126 if ((target_el < 2) && arm_feature(&target_cpu->env, ARM_FEATURE_EL2)) { 127 /* 128 * As target mode is AArch64, we need to set lower 129 * exception level (the requested level 1) to AArch64 130 */ 131 target_cpu->env.cp15.hcr_el2 |= HCR_RW; 132 } 133 134 target_cpu->env.pstate = aarch64_pstate_mode(target_el, true); 135 } else { 136 /* We are requested to boot in AArch32 mode */ 137 static uint32_t mode_for_el[] = { 0, 138 ARM_CPU_MODE_SVC, 139 ARM_CPU_MODE_HYP, 140 ARM_CPU_MODE_SVC }; 141 142 cpsr_write(&target_cpu->env, mode_for_el[target_el], CPSR_M, 143 CPSRWriteRaw); 144 } 145 146 if (target_el == 3) { 147 /* Processor is in secure mode */ 148 target_cpu->env.cp15.scr_el3 &= ~SCR_NS; 149 } else { 150 /* Processor is not in secure mode */ 151 target_cpu->env.cp15.scr_el3 |= SCR_NS; 152 } 153 154 /* We check if the started CPU is now at the correct level */ 155 assert(target_el == arm_current_el(&target_cpu->env)); 156 157 if (target_aa64) { 158 target_cpu->env.xregs[0] = context_id; 159 target_cpu->env.thumb = false; 160 } else { 161 target_cpu->env.regs[0] = context_id; 162 target_cpu->env.thumb = entry & 1; 163 entry &= 0xfffffffe; 164 } 165 166 /* Start the new CPU at the requested address */ 167 cpu_set_pc(target_cpu_state, entry); 168 169 qemu_cpu_kick(target_cpu_state); 170 171 /* We are good to go */ 172 return QEMU_ARM_POWERCTL_RET_SUCCESS; 173 } 174 175 int arm_set_cpu_off(uint64_t cpuid) 176 { 177 CPUState *target_cpu_state; 178 ARMCPU *target_cpu; 179 180 DPRINTF("cpu %" PRId64 "\n", cpuid); 181 182 /* change to the cpu we are powering up */ 183 target_cpu_state = arm_get_cpu_by_id(cpuid); 184 if (!target_cpu_state) { 185 return QEMU_ARM_POWERCTL_INVALID_PARAM; 186 } 187 target_cpu = ARM_CPU(target_cpu_state); 188 if (target_cpu->powered_off) { 189 qemu_log_mask(LOG_GUEST_ERROR, 190 "[ARM]%s: CPU %" PRId64 " is already off\n", 191 __func__, cpuid); 192 return QEMU_ARM_POWERCTL_IS_OFF; 193 } 194 195 target_cpu->powered_off = true; 196 target_cpu_state->halted = 1; 197 target_cpu_state->exception_index = EXCP_HLT; 198 cpu_loop_exit(target_cpu_state); 199 /* notreached */ 200 201 return QEMU_ARM_POWERCTL_RET_SUCCESS; 202 } 203 204 int arm_reset_cpu(uint64_t cpuid) 205 { 206 CPUState *target_cpu_state; 207 ARMCPU *target_cpu; 208 209 DPRINTF("cpu %" PRId64 "\n", cpuid); 210 211 /* change to the cpu we are resetting */ 212 target_cpu_state = arm_get_cpu_by_id(cpuid); 213 if (!target_cpu_state) { 214 return QEMU_ARM_POWERCTL_INVALID_PARAM; 215 } 216 target_cpu = ARM_CPU(target_cpu_state); 217 if (target_cpu->powered_off) { 218 qemu_log_mask(LOG_GUEST_ERROR, 219 "[ARM]%s: CPU %" PRId64 " is off\n", 220 __func__, cpuid); 221 return QEMU_ARM_POWERCTL_IS_OFF; 222 } 223 224 /* Reset the cpu */ 225 cpu_reset(target_cpu_state); 226 227 return QEMU_ARM_POWERCTL_RET_SUCCESS; 228 } 229