qemu/target-arm/psci.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2014 - Linaro
   3 * Author: Rob Herring <rob.herring@linaro.org>
   4 *
   5 *  This program is free software; you can redistribute it and/or modify
   6 *  it under the terms of the GNU General Public License as published by
   7 *  the Free Software Foundation; either version 2 of the License, or
   8 *  (at your option) any later version.
   9 *
  10 *  This program is distributed in the hope that it will be useful,
  11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 *  GNU General Public License for more details.
  14 *
  15 *  You should have received a copy of the GNU General Public License
  16 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
  17 */
  18#include "qemu/osdep.h"
  19#include <cpu.h>
  20#include <cpu-qom.h>
  21#include <exec/helper-proto.h>
  22#include <kvm-consts.h>
  23#include <sysemu/sysemu.h>
  24#include "internals.h"
  25
  26bool arm_is_psci_call(ARMCPU *cpu, int excp_type)
  27{
  28    /* Return true if the r0/x0 value indicates a PSCI call and
  29     * the exception type matches the configured PSCI conduit. This is
  30     * called before the SMC/HVC instruction is executed, to decide whether
  31     * we should treat it as a PSCI call or with the architecturally
  32     * defined behaviour for an SMC or HVC (which might be UNDEF or trap
  33     * to EL2 or to EL3).
  34     */
  35    CPUARMState *env = &cpu->env;
  36    uint64_t param = is_a64(env) ? env->xregs[0] : env->regs[0];
  37
  38    switch (excp_type) {
  39    case EXCP_HVC:
  40        if (cpu->psci_conduit != QEMU_PSCI_CONDUIT_HVC) {
  41            return false;
  42        }
  43        break;
  44    case EXCP_SMC:
  45        if (cpu->psci_conduit != QEMU_PSCI_CONDUIT_SMC) {
  46            return false;
  47        }
  48        break;
  49    default:
  50        return false;
  51    }
  52
  53    switch (param) {
  54    case QEMU_PSCI_0_2_FN_PSCI_VERSION:
  55    case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
  56    case QEMU_PSCI_0_2_FN_AFFINITY_INFO:
  57    case QEMU_PSCI_0_2_FN64_AFFINITY_INFO:
  58    case QEMU_PSCI_0_2_FN_SYSTEM_RESET:
  59    case QEMU_PSCI_0_2_FN_SYSTEM_OFF:
  60    case QEMU_PSCI_0_1_FN_CPU_ON:
  61    case QEMU_PSCI_0_2_FN_CPU_ON:
  62    case QEMU_PSCI_0_2_FN64_CPU_ON:
  63    case QEMU_PSCI_0_1_FN_CPU_OFF:
  64    case QEMU_PSCI_0_2_FN_CPU_OFF:
  65    case QEMU_PSCI_0_1_FN_CPU_SUSPEND:
  66    case QEMU_PSCI_0_2_FN_CPU_SUSPEND:
  67    case QEMU_PSCI_0_2_FN64_CPU_SUSPEND:
  68    case QEMU_PSCI_0_1_FN_MIGRATE:
  69    case QEMU_PSCI_0_2_FN_MIGRATE:
  70        return true;
  71    default:
  72        return false;
  73    }
  74}
  75
  76static CPUState *get_cpu_by_id(uint64_t id)
  77{
  78    CPUState *cpu;
  79
  80    CPU_FOREACH(cpu) {
  81        ARMCPU *armcpu = ARM_CPU(cpu);
  82
  83        if (armcpu->mp_affinity == id) {
  84            return cpu;
  85        }
  86    }
  87
  88    return NULL;
  89}
  90
  91void arm_handle_psci_call(ARMCPU *cpu)
  92{
  93    /*
  94     * This function partially implements the logic for dispatching Power State
  95     * Coordination Interface (PSCI) calls (as described in ARM DEN 0022B.b),
  96     * to the extent required for bringing up and taking down secondary cores,
  97     * and for handling reset and poweroff requests.
  98     * Additional information about the calling convention used is available in
  99     * the document 'SMC Calling Convention' (ARM DEN 0028)
 100     */
 101    CPUState *cs = CPU(cpu);
 102    CPUARMState *env = &cpu->env;
 103    uint64_t param[4];
 104    uint64_t context_id, mpidr;
 105    target_ulong entry;
 106    int32_t ret = 0;
 107    int i;
 108
 109    for (i = 0; i < 4; i++) {
 110        /*
 111         * All PSCI functions take explicit 32-bit or native int sized
 112         * arguments so we can simply zero-extend all arguments regardless
 113         * of which exact function we are about to call.
 114         */
 115        param[i] = is_a64(env) ? env->xregs[i] : env->regs[i];
 116    }
 117
 118    if ((param[0] & QEMU_PSCI_0_2_64BIT) && !is_a64(env)) {
 119        ret = QEMU_PSCI_RET_INVALID_PARAMS;
 120        goto err;
 121    }
 122
 123    switch (param[0]) {
 124        CPUState *target_cpu_state;
 125        ARMCPU *target_cpu;
 126        CPUClass *target_cpu_class;
 127
 128    case QEMU_PSCI_0_2_FN_PSCI_VERSION:
 129        ret = QEMU_PSCI_0_2_RET_VERSION_0_2;
 130        break;
 131    case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
 132        ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS */
 133        break;
 134    case QEMU_PSCI_0_2_FN_AFFINITY_INFO:
 135    case QEMU_PSCI_0_2_FN64_AFFINITY_INFO:
 136        mpidr = param[1];
 137
 138        switch (param[2]) {
 139        case 0:
 140            target_cpu_state = get_cpu_by_id(mpidr);
 141            if (!target_cpu_state) {
 142                ret = QEMU_PSCI_RET_INVALID_PARAMS;
 143                break;
 144            }
 145            target_cpu = ARM_CPU(target_cpu_state);
 146            ret = target_cpu->powered_off ? 1 : 0;
 147            break;
 148        default:
 149            /* Everything above affinity level 0 is always on. */
 150            ret = 0;
 151        }
 152        break;
 153    case QEMU_PSCI_0_2_FN_SYSTEM_RESET:
 154        qemu_system_reset_request();
 155        /* QEMU reset and shutdown are async requests, but PSCI
 156         * mandates that we never return from the reset/shutdown
 157         * call, so power the CPU off now so it doesn't execute
 158         * anything further.
 159         */
 160        goto cpu_off;
 161    case QEMU_PSCI_0_2_FN_SYSTEM_OFF:
 162        qemu_system_shutdown_request();
 163        goto cpu_off;
 164    case QEMU_PSCI_0_1_FN_CPU_ON:
 165    case QEMU_PSCI_0_2_FN_CPU_ON:
 166    case QEMU_PSCI_0_2_FN64_CPU_ON:
 167        mpidr = param[1];
 168        entry = param[2];
 169        context_id = param[3];
 170
 171        /* change to the cpu we are powering up */
 172        target_cpu_state = get_cpu_by_id(mpidr);
 173        if (!target_cpu_state) {
 174            ret = QEMU_PSCI_RET_INVALID_PARAMS;
 175            break;
 176        }
 177        target_cpu = ARM_CPU(target_cpu_state);
 178        if (!target_cpu->powered_off) {
 179            ret = QEMU_PSCI_RET_ALREADY_ON;
 180            break;
 181        }
 182        target_cpu_class = CPU_GET_CLASS(target_cpu);
 183
 184        /* Initialize the cpu we are turning on */
 185        cpu_reset(target_cpu_state);
 186        target_cpu->powered_off = false;
 187        target_cpu_state->halted = 0;
 188
 189        /*
 190         * The PSCI spec mandates that newly brought up CPUs enter the
 191         * exception level of the caller in the same execution mode as
 192         * the caller, with context_id in x0/r0, respectively.
 193         *
 194         * For now, it is sufficient to assert() that CPUs come out of
 195         * reset in the same mode as the calling CPU, since we only
 196         * implement EL1, which means that
 197         * (a) there is no EL2 for the calling CPU to trap into to change
 198         *     its state
 199         * (b) the newly brought up CPU enters EL1 immediately after coming
 200         *     out of reset in the default state
 201         */
 202        assert(is_a64(env) == is_a64(&target_cpu->env));
 203        if (is_a64(env)) {
 204            if (entry & 1) {
 205                ret = QEMU_PSCI_RET_INVALID_PARAMS;
 206                break;
 207            }
 208            target_cpu->env.xregs[0] = context_id;
 209        } else {
 210            target_cpu->env.regs[0] = context_id;
 211            target_cpu->env.thumb = entry & 1;
 212        }
 213        target_cpu_class->set_pc(target_cpu_state, entry);
 214
 215        ret = 0;
 216        break;
 217    case QEMU_PSCI_0_1_FN_CPU_OFF:
 218    case QEMU_PSCI_0_2_FN_CPU_OFF:
 219        goto cpu_off;
 220    case QEMU_PSCI_0_1_FN_CPU_SUSPEND:
 221    case QEMU_PSCI_0_2_FN_CPU_SUSPEND:
 222    case QEMU_PSCI_0_2_FN64_CPU_SUSPEND:
 223        /* Affinity levels are not supported in QEMU */
 224        if (param[1] & 0xfffe0000) {
 225            ret = QEMU_PSCI_RET_INVALID_PARAMS;
 226            break;
 227        }
 228        /* Powerdown is not supported, we always go into WFI */
 229        if (is_a64(env)) {
 230            env->xregs[0] = 0;
 231        } else {
 232            env->regs[0] = 0;
 233        }
 234        helper_wfi(env);
 235        break;
 236    case QEMU_PSCI_0_1_FN_MIGRATE:
 237    case QEMU_PSCI_0_2_FN_MIGRATE:
 238        ret = QEMU_PSCI_RET_NOT_SUPPORTED;
 239        break;
 240    default:
 241        g_assert_not_reached();
 242    }
 243
 244err:
 245    if (is_a64(env)) {
 246        env->xregs[0] = ret;
 247    } else {
 248        env->regs[0] = ret;
 249    }
 250    return;
 251
 252cpu_off:
 253    cpu->powered_off = true;
 254    cs->halted = 1;
 255    cs->exception_index = EXCP_HLT;
 256    cpu_loop_exit(cs);
 257    /* notreached */
 258}
 259