qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

arm-powerctl.c (11333B)


      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         /* Set NSACR.{CP11,CP10} so NS can access the FPU */
    108         target_cpu->env.cp15.nsacr |= 3 << 10;
    109 
    110         /*
    111          * If QEMU is providing the equivalent of EL3 firmware, then we need
    112          * to make sure a CPU targeting EL2 comes out of reset with a
    113          * functional HVC insn.
    114          */
    115         if (arm_feature(&target_cpu->env, ARM_FEATURE_EL3)
    116             && info->target_el == 2) {
    117             target_cpu->env.cp15.scr_el3 |= SCR_HCE;
    118         }
    119     }
    120 
    121     /* We check if the started CPU is now at the correct level */
    122     assert(info->target_el == arm_current_el(&target_cpu->env));
    123 
    124     if (info->target_aa64) {
    125         target_cpu->env.xregs[0] = info->context_id;
    126     } else {
    127         target_cpu->env.regs[0] = info->context_id;
    128     }
    129 
    130     /* CP15 update requires rebuilding hflags */
    131     arm_rebuild_hflags(&target_cpu->env);
    132 
    133     /* Start the new CPU at the requested address */
    134     cpu_set_pc(target_cpu_state, info->entry);
    135 
    136     g_free(info);
    137 
    138     /* Finally set the power status */
    139     assert(qemu_mutex_iothread_locked());
    140     target_cpu->power_state = PSCI_ON;
    141 }
    142 
    143 int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
    144                    uint32_t target_el, bool target_aa64)
    145 {
    146     CPUState *target_cpu_state;
    147     ARMCPU *target_cpu;
    148     struct CpuOnInfo *info;
    149 
    150     assert(qemu_mutex_iothread_locked());
    151 
    152     DPRINTF("cpu %" PRId64 " (EL %d, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64
    153             "\n", cpuid, target_el, target_aa64 ? "aarch64" : "aarch32", entry,
    154             context_id);
    155 
    156     /* requested EL level need to be in the 1 to 3 range */
    157     assert((target_el > 0) && (target_el < 4));
    158 
    159     if (target_aa64 && (entry & 3)) {
    160         /*
    161          * if we are booting in AArch64 mode then "entry" needs to be 4 bytes
    162          * aligned.
    163          */
    164         return QEMU_ARM_POWERCTL_INVALID_PARAM;
    165     }
    166 
    167     /* Retrieve the cpu we are powering up */
    168     target_cpu_state = arm_get_cpu_by_id(cpuid);
    169     if (!target_cpu_state) {
    170         /* The cpu was not found */
    171         return QEMU_ARM_POWERCTL_INVALID_PARAM;
    172     }
    173 
    174     target_cpu = ARM_CPU(target_cpu_state);
    175     if (target_cpu->power_state == PSCI_ON) {
    176         qemu_log_mask(LOG_GUEST_ERROR,
    177                       "[ARM]%s: CPU %" PRId64 " is already on\n",
    178                       __func__, cpuid);
    179         return QEMU_ARM_POWERCTL_ALREADY_ON;
    180     }
    181 
    182     /*
    183      * The newly brought CPU is requested to enter the exception level
    184      * "target_el" and be in the requested mode (AArch64 or AArch32).
    185      */
    186 
    187     if (((target_el == 3) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) ||
    188         ((target_el == 2) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL2))) {
    189         /*
    190          * The CPU does not support requested level
    191          */
    192         return QEMU_ARM_POWERCTL_INVALID_PARAM;
    193     }
    194 
    195     if (!target_aa64 && arm_feature(&target_cpu->env, ARM_FEATURE_AARCH64)) {
    196         /*
    197          * For now we don't support booting an AArch64 CPU in AArch32 mode
    198          * TODO: We should add this support later
    199          */
    200         qemu_log_mask(LOG_UNIMP,
    201                       "[ARM]%s: Starting AArch64 CPU %" PRId64
    202                       " in AArch32 mode is not supported yet\n",
    203                       __func__, cpuid);
    204         return QEMU_ARM_POWERCTL_INVALID_PARAM;
    205     }
    206 
    207     /*
    208      * If another CPU has powered the target on we are in the state
    209      * ON_PENDING and additional attempts to power on the CPU should
    210      * fail (see 6.6 Implementation CPU_ON/CPU_OFF races in the PSCI
    211      * spec)
    212      */
    213     if (target_cpu->power_state == PSCI_ON_PENDING) {
    214         qemu_log_mask(LOG_GUEST_ERROR,
    215                       "[ARM]%s: CPU %" PRId64 " is already powering on\n",
    216                       __func__, cpuid);
    217         return QEMU_ARM_POWERCTL_ON_PENDING;
    218     }
    219 
    220     /* To avoid racing with a CPU we are just kicking off we do the
    221      * final bit of preparation for the work in the target CPUs
    222      * context.
    223      */
    224     info = g_new(struct CpuOnInfo, 1);
    225     info->entry = entry;
    226     info->context_id = context_id;
    227     info->target_el = target_el;
    228     info->target_aa64 = target_aa64;
    229 
    230     async_run_on_cpu(target_cpu_state, arm_set_cpu_on_async_work,
    231                      RUN_ON_CPU_HOST_PTR(info));
    232 
    233     /* We are good to go */
    234     return QEMU_ARM_POWERCTL_RET_SUCCESS;
    235 }
    236 
    237 static void arm_set_cpu_on_and_reset_async_work(CPUState *target_cpu_state,
    238                                                 run_on_cpu_data data)
    239 {
    240     ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
    241 
    242     /* Initialize the cpu we are turning on */
    243     cpu_reset(target_cpu_state);
    244     target_cpu_state->halted = 0;
    245 
    246     /* Finally set the power status */
    247     assert(qemu_mutex_iothread_locked());
    248     target_cpu->power_state = PSCI_ON;
    249 }
    250 
    251 int arm_set_cpu_on_and_reset(uint64_t cpuid)
    252 {
    253     CPUState *target_cpu_state;
    254     ARMCPU *target_cpu;
    255 
    256     assert(qemu_mutex_iothread_locked());
    257 
    258     /* Retrieve the cpu we are powering up */
    259     target_cpu_state = arm_get_cpu_by_id(cpuid);
    260     if (!target_cpu_state) {
    261         /* The cpu was not found */
    262         return QEMU_ARM_POWERCTL_INVALID_PARAM;
    263     }
    264 
    265     target_cpu = ARM_CPU(target_cpu_state);
    266     if (target_cpu->power_state == PSCI_ON) {
    267         qemu_log_mask(LOG_GUEST_ERROR,
    268                       "[ARM]%s: CPU %" PRId64 " is already on\n",
    269                       __func__, cpuid);
    270         return QEMU_ARM_POWERCTL_ALREADY_ON;
    271     }
    272 
    273     /*
    274      * If another CPU has powered the target on we are in the state
    275      * ON_PENDING and additional attempts to power on the CPU should
    276      * fail (see 6.6 Implementation CPU_ON/CPU_OFF races in the PSCI
    277      * spec)
    278      */
    279     if (target_cpu->power_state == PSCI_ON_PENDING) {
    280         qemu_log_mask(LOG_GUEST_ERROR,
    281                       "[ARM]%s: CPU %" PRId64 " is already powering on\n",
    282                       __func__, cpuid);
    283         return QEMU_ARM_POWERCTL_ON_PENDING;
    284     }
    285 
    286     async_run_on_cpu(target_cpu_state, arm_set_cpu_on_and_reset_async_work,
    287                      RUN_ON_CPU_NULL);
    288 
    289     /* We are good to go */
    290     return QEMU_ARM_POWERCTL_RET_SUCCESS;
    291 }
    292 
    293 static void arm_set_cpu_off_async_work(CPUState *target_cpu_state,
    294                                        run_on_cpu_data data)
    295 {
    296     ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
    297 
    298     assert(qemu_mutex_iothread_locked());
    299     target_cpu->power_state = PSCI_OFF;
    300     target_cpu_state->halted = 1;
    301     target_cpu_state->exception_index = EXCP_HLT;
    302 }
    303 
    304 int arm_set_cpu_off(uint64_t cpuid)
    305 {
    306     CPUState *target_cpu_state;
    307     ARMCPU *target_cpu;
    308 
    309     assert(qemu_mutex_iothread_locked());
    310 
    311     DPRINTF("cpu %" PRId64 "\n", cpuid);
    312 
    313     /* change to the cpu we are powering up */
    314     target_cpu_state = arm_get_cpu_by_id(cpuid);
    315     if (!target_cpu_state) {
    316         return QEMU_ARM_POWERCTL_INVALID_PARAM;
    317     }
    318     target_cpu = ARM_CPU(target_cpu_state);
    319     if (target_cpu->power_state == PSCI_OFF) {
    320         qemu_log_mask(LOG_GUEST_ERROR,
    321                       "[ARM]%s: CPU %" PRId64 " is already off\n",
    322                       __func__, cpuid);
    323         return QEMU_ARM_POWERCTL_IS_OFF;
    324     }
    325 
    326     /* Queue work to run under the target vCPUs context */
    327     async_run_on_cpu(target_cpu_state, arm_set_cpu_off_async_work,
    328                      RUN_ON_CPU_NULL);
    329 
    330     return QEMU_ARM_POWERCTL_RET_SUCCESS;
    331 }
    332 
    333 static void arm_reset_cpu_async_work(CPUState *target_cpu_state,
    334                                      run_on_cpu_data data)
    335 {
    336     /* Reset the cpu */
    337     cpu_reset(target_cpu_state);
    338 }
    339 
    340 int arm_reset_cpu(uint64_t cpuid)
    341 {
    342     CPUState *target_cpu_state;
    343     ARMCPU *target_cpu;
    344 
    345     assert(qemu_mutex_iothread_locked());
    346 
    347     DPRINTF("cpu %" PRId64 "\n", cpuid);
    348 
    349     /* change to the cpu we are resetting */
    350     target_cpu_state = arm_get_cpu_by_id(cpuid);
    351     if (!target_cpu_state) {
    352         return QEMU_ARM_POWERCTL_INVALID_PARAM;
    353     }
    354     target_cpu = ARM_CPU(target_cpu_state);
    355 
    356     if (target_cpu->power_state == PSCI_OFF) {
    357         qemu_log_mask(LOG_GUEST_ERROR,
    358                       "[ARM]%s: CPU %" PRId64 " is off\n",
    359                       __func__, cpuid);
    360         return QEMU_ARM_POWERCTL_IS_OFF;
    361     }
    362 
    363     /* Queue work to run under the target vCPUs context */
    364     async_run_on_cpu(target_cpu_state, arm_reset_cpu_async_work,
    365                      RUN_ON_CPU_NULL);
    366 
    367     return QEMU_ARM_POWERCTL_RET_SUCCESS;
    368 }