cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

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
     30CPUState *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
     51struct CpuOnInfo {
     52    uint64_t entry;
     53    uint64_t context_id;
     54    uint32_t target_el;
     55    bool target_aa64;
     56};
     57
     58
     59static 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
    143int 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
    237static 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
    251int 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
    293static 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
    304int 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
    333static 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
    340int 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}