qemu

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

psci.c (7424B)


      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 
     19 #include "qemu/osdep.h"
     20 #include "cpu.h"
     21 #include "exec/helper-proto.h"
     22 #include "kvm-consts.h"
     23 #include "qemu/main-loop.h"
     24 #include "sysemu/runstate.h"
     25 #include "internals.h"
     26 #include "arm-powerctl.h"
     27 
     28 bool arm_is_psci_call(ARMCPU *cpu, int excp_type)
     29 {
     30     /*
     31      * Return true if the exception type matches the configured PSCI conduit.
     32      * This is called before the SMC/HVC instruction is executed, to decide
     33      * whether we should treat it as a PSCI call or with the architecturally
     34      * defined behaviour for an SMC or HVC (which might be UNDEF or trap
     35      * to EL2 or to EL3).
     36      */
     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     return true;
     54 }
     55 
     56 void arm_handle_psci_call(ARMCPU *cpu)
     57 {
     58     /*
     59      * This function partially implements the logic for dispatching Power State
     60      * Coordination Interface (PSCI) calls (as described in ARM DEN 0022D.b),
     61      * to the extent required for bringing up and taking down secondary cores,
     62      * and for handling reset and poweroff requests.
     63      * Additional information about the calling convention used is available in
     64      * the document 'SMC Calling Convention' (ARM DEN 0028)
     65      */
     66     CPUARMState *env = &cpu->env;
     67     uint64_t param[4];
     68     uint64_t context_id, mpidr;
     69     target_ulong entry;
     70     int32_t ret = 0;
     71     int i;
     72 
     73     for (i = 0; i < 4; i++) {
     74         /*
     75          * All PSCI functions take explicit 32-bit or native int sized
     76          * arguments so we can simply zero-extend all arguments regardless
     77          * of which exact function we are about to call.
     78          */
     79         param[i] = is_a64(env) ? env->xregs[i] : env->regs[i];
     80     }
     81 
     82     if ((param[0] & QEMU_PSCI_0_2_64BIT) && !is_a64(env)) {
     83         ret = QEMU_PSCI_RET_NOT_SUPPORTED;
     84         goto err;
     85     }
     86 
     87     switch (param[0]) {
     88         CPUState *target_cpu_state;
     89         ARMCPU *target_cpu;
     90 
     91     case QEMU_PSCI_0_2_FN_PSCI_VERSION:
     92         ret = QEMU_PSCI_VERSION_1_1;
     93         break;
     94     case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
     95         ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS */
     96         break;
     97     case QEMU_PSCI_0_2_FN_AFFINITY_INFO:
     98     case QEMU_PSCI_0_2_FN64_AFFINITY_INFO:
     99         mpidr = param[1];
    100 
    101         switch (param[2]) {
    102         case 0:
    103             target_cpu_state = arm_get_cpu_by_id(mpidr);
    104             if (!target_cpu_state) {
    105                 ret = QEMU_PSCI_RET_INVALID_PARAMS;
    106                 break;
    107             }
    108             target_cpu = ARM_CPU(target_cpu_state);
    109 
    110             g_assert(qemu_mutex_iothread_locked());
    111             ret = target_cpu->power_state;
    112             break;
    113         default:
    114             /* Everything above affinity level 0 is always on. */
    115             ret = 0;
    116         }
    117         break;
    118     case QEMU_PSCI_0_2_FN_SYSTEM_RESET:
    119         qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
    120         /* QEMU reset and shutdown are async requests, but PSCI
    121          * mandates that we never return from the reset/shutdown
    122          * call, so power the CPU off now so it doesn't execute
    123          * anything further.
    124          */
    125         goto cpu_off;
    126     case QEMU_PSCI_0_2_FN_SYSTEM_OFF:
    127         qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
    128         goto cpu_off;
    129     case QEMU_PSCI_0_1_FN_CPU_ON:
    130     case QEMU_PSCI_0_2_FN_CPU_ON:
    131     case QEMU_PSCI_0_2_FN64_CPU_ON:
    132     {
    133         /* The PSCI spec mandates that newly brought up CPUs start
    134          * in the highest exception level which exists and is enabled
    135          * on the calling CPU. Since the QEMU PSCI implementation is
    136          * acting as a "fake EL3" or "fake EL2" firmware, this for us
    137          * means that we want to start at the highest NS exception level
    138          * that we are providing to the guest.
    139          * The execution mode should be that which is currently in use
    140          * by the same exception level on the calling CPU.
    141          * The CPU should be started with the context_id value
    142          * in x0 (if AArch64) or r0 (if AArch32).
    143          */
    144         int target_el = arm_feature(env, ARM_FEATURE_EL2) ? 2 : 1;
    145         bool target_aarch64 = arm_el_is_aa64(env, target_el);
    146 
    147         mpidr = param[1];
    148         entry = param[2];
    149         context_id = param[3];
    150         ret = arm_set_cpu_on(mpidr, entry, context_id,
    151                              target_el, target_aarch64);
    152         break;
    153     }
    154     case QEMU_PSCI_0_1_FN_CPU_OFF:
    155     case QEMU_PSCI_0_2_FN_CPU_OFF:
    156         goto cpu_off;
    157     case QEMU_PSCI_0_1_FN_CPU_SUSPEND:
    158     case QEMU_PSCI_0_2_FN_CPU_SUSPEND:
    159     case QEMU_PSCI_0_2_FN64_CPU_SUSPEND:
    160         /* Affinity levels are not supported in QEMU */
    161         if (param[1] & 0xfffe0000) {
    162             ret = QEMU_PSCI_RET_INVALID_PARAMS;
    163             break;
    164         }
    165         /* Powerdown is not supported, we always go into WFI */
    166         if (is_a64(env)) {
    167             env->xregs[0] = 0;
    168         } else {
    169             env->regs[0] = 0;
    170         }
    171         helper_wfi(env, 4);
    172         break;
    173     case QEMU_PSCI_1_0_FN_PSCI_FEATURES:
    174         switch (param[1]) {
    175         case QEMU_PSCI_0_2_FN_PSCI_VERSION:
    176         case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
    177         case QEMU_PSCI_0_2_FN_AFFINITY_INFO:
    178         case QEMU_PSCI_0_2_FN64_AFFINITY_INFO:
    179         case QEMU_PSCI_0_2_FN_SYSTEM_RESET:
    180         case QEMU_PSCI_0_2_FN_SYSTEM_OFF:
    181         case QEMU_PSCI_0_1_FN_CPU_ON:
    182         case QEMU_PSCI_0_2_FN_CPU_ON:
    183         case QEMU_PSCI_0_2_FN64_CPU_ON:
    184         case QEMU_PSCI_0_1_FN_CPU_OFF:
    185         case QEMU_PSCI_0_2_FN_CPU_OFF:
    186         case QEMU_PSCI_0_1_FN_CPU_SUSPEND:
    187         case QEMU_PSCI_0_2_FN_CPU_SUSPEND:
    188         case QEMU_PSCI_0_2_FN64_CPU_SUSPEND:
    189         case QEMU_PSCI_1_0_FN_PSCI_FEATURES:
    190             if (!(param[1] & QEMU_PSCI_0_2_64BIT) || is_a64(env)) {
    191                 ret = 0;
    192                 break;
    193             }
    194             /* fallthrough */
    195         case QEMU_PSCI_0_1_FN_MIGRATE:
    196         case QEMU_PSCI_0_2_FN_MIGRATE:
    197         default:
    198             ret = QEMU_PSCI_RET_NOT_SUPPORTED;
    199             break;
    200         }
    201         break;
    202     case QEMU_PSCI_0_1_FN_MIGRATE:
    203     case QEMU_PSCI_0_2_FN_MIGRATE:
    204     default:
    205         ret = QEMU_PSCI_RET_NOT_SUPPORTED;
    206         break;
    207     }
    208 
    209 err:
    210     if (is_a64(env)) {
    211         env->xregs[0] = ret;
    212     } else {
    213         env->regs[0] = ret;
    214     }
    215     return;
    216 
    217 cpu_off:
    218     ret = arm_set_cpu_off(cpu->mp_affinity);
    219     /* notreached */
    220     /* sanity check in case something failed */
    221     assert(ret == QEMU_ARM_POWERCTL_RET_SUCCESS);
    222 }