linux/arch/arm/kvm/handle_exit.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2012 - Virtual Open Systems and Columbia University
   4 * Author: Christoffer Dall <c.dall@virtualopensystems.com>
   5 */
   6
   7#include <linux/kvm.h>
   8#include <linux/kvm_host.h>
   9#include <asm/kvm_emulate.h>
  10#include <asm/kvm_coproc.h>
  11#include <asm/kvm_mmu.h>
  12#include <kvm/arm_psci.h>
  13#include <trace/events/kvm.h>
  14
  15#include "trace.h"
  16
  17typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *);
  18
  19static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run)
  20{
  21        int ret;
  22
  23        trace_kvm_hvc(*vcpu_pc(vcpu), *vcpu_reg(vcpu, 0),
  24                      kvm_vcpu_hvc_get_imm(vcpu));
  25        vcpu->stat.hvc_exit_stat++;
  26
  27        ret = kvm_hvc_call_handler(vcpu);
  28        if (ret < 0) {
  29                vcpu_set_reg(vcpu, 0, ~0UL);
  30                return 1;
  31        }
  32
  33        return ret;
  34}
  35
  36static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run)
  37{
  38        /*
  39         * "If an SMC instruction executed at Non-secure EL1 is
  40         * trapped to EL2 because HCR_EL2.TSC is 1, the exception is a
  41         * Trap exception, not a Secure Monitor Call exception [...]"
  42         *
  43         * We need to advance the PC after the trap, as it would
  44         * otherwise return to the same address...
  45         */
  46        vcpu_set_reg(vcpu, 0, ~0UL);
  47        kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
  48        return 1;
  49}
  50
  51/**
  52 * kvm_handle_wfx - handle a WFI or WFE instructions trapped in guests
  53 * @vcpu:       the vcpu pointer
  54 * @run:        the kvm_run structure pointer
  55 *
  56 * WFE: Yield the CPU and come back to this vcpu when the scheduler
  57 * decides to.
  58 * WFI: Simply call kvm_vcpu_block(), which will halt execution of
  59 * world-switches and schedule other host processes until there is an
  60 * incoming IRQ or FIQ to the VM.
  61 */
  62static int kvm_handle_wfx(struct kvm_vcpu *vcpu, struct kvm_run *run)
  63{
  64        if (kvm_vcpu_get_hsr(vcpu) & HSR_WFI_IS_WFE) {
  65                trace_kvm_wfx(*vcpu_pc(vcpu), true);
  66                vcpu->stat.wfe_exit_stat++;
  67                kvm_vcpu_on_spin(vcpu, vcpu_mode_priv(vcpu));
  68        } else {
  69                trace_kvm_wfx(*vcpu_pc(vcpu), false);
  70                vcpu->stat.wfi_exit_stat++;
  71                kvm_vcpu_block(vcpu);
  72                kvm_clear_request(KVM_REQ_UNHALT, vcpu);
  73        }
  74
  75        kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
  76
  77        return 1;
  78}
  79
  80static int kvm_handle_unknown_ec(struct kvm_vcpu *vcpu, struct kvm_run *run)
  81{
  82        u32 hsr = kvm_vcpu_get_hsr(vcpu);
  83
  84        kvm_pr_unimpl("Unknown exception class: hsr: %#08x\n",
  85                      hsr);
  86
  87        kvm_inject_undefined(vcpu);
  88        return 1;
  89}
  90
  91static exit_handle_fn arm_exit_handlers[] = {
  92        [0 ... HSR_EC_MAX]      = kvm_handle_unknown_ec,
  93        [HSR_EC_WFI]            = kvm_handle_wfx,
  94        [HSR_EC_CP15_32]        = kvm_handle_cp15_32,
  95        [HSR_EC_CP15_64]        = kvm_handle_cp15_64,
  96        [HSR_EC_CP14_MR]        = kvm_handle_cp14_32,
  97        [HSR_EC_CP14_LS]        = kvm_handle_cp14_load_store,
  98        [HSR_EC_CP14_64]        = kvm_handle_cp14_64,
  99        [HSR_EC_CP_0_13]        = kvm_handle_cp_0_13_access,
 100        [HSR_EC_CP10_ID]        = kvm_handle_cp10_id,
 101        [HSR_EC_HVC]            = handle_hvc,
 102        [HSR_EC_SMC]            = handle_smc,
 103        [HSR_EC_IABT]           = kvm_handle_guest_abort,
 104        [HSR_EC_DABT]           = kvm_handle_guest_abort,
 105};
 106
 107static exit_handle_fn kvm_get_exit_handler(struct kvm_vcpu *vcpu)
 108{
 109        u8 hsr_ec = kvm_vcpu_trap_get_class(vcpu);
 110
 111        return arm_exit_handlers[hsr_ec];
 112}
 113
 114/*
 115 * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on
 116 * proper exit to userspace.
 117 */
 118int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
 119                       int exception_index)
 120{
 121        exit_handle_fn exit_handler;
 122
 123        if (ARM_ABORT_PENDING(exception_index)) {
 124                u8 hsr_ec = kvm_vcpu_trap_get_class(vcpu);
 125
 126                /*
 127                 * HVC/SMC already have an adjusted PC, which we need
 128                 * to correct in order to return to after having
 129                 * injected the abort.
 130                 */
 131                if (hsr_ec == HSR_EC_HVC || hsr_ec == HSR_EC_SMC) {
 132                        u32 adj =  kvm_vcpu_trap_il_is32bit(vcpu) ? 4 : 2;
 133                        *vcpu_pc(vcpu) -= adj;
 134                }
 135
 136                kvm_inject_vabt(vcpu);
 137                return 1;
 138        }
 139
 140        exception_index = ARM_EXCEPTION_CODE(exception_index);
 141
 142        switch (exception_index) {
 143        case ARM_EXCEPTION_IRQ:
 144                return 1;
 145        case ARM_EXCEPTION_HVC:
 146                /*
 147                 * See ARM ARM B1.14.1: "Hyp traps on instructions
 148                 * that fail their condition code check"
 149                 */
 150                if (!kvm_condition_valid(vcpu)) {
 151                        kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
 152                        return 1;
 153                }
 154
 155                exit_handler = kvm_get_exit_handler(vcpu);
 156
 157                return exit_handler(vcpu, run);
 158        case ARM_EXCEPTION_DATA_ABORT:
 159                kvm_inject_vabt(vcpu);
 160                return 1;
 161        case ARM_EXCEPTION_HYP_GONE:
 162                /*
 163                 * HYP has been reset to the hyp-stub. This happens
 164                 * when a guest is pre-empted by kvm_reboot()'s
 165                 * shutdown call.
 166                 */
 167                run->exit_reason = KVM_EXIT_FAIL_ENTRY;
 168                return 0;
 169        default:
 170                kvm_pr_unimpl("Unsupported exception type: %d",
 171                              exception_index);
 172                run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
 173                return 0;
 174        }
 175}
 176