linux/arch/riscv/kvm/vcpu_sbi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
   4 *
   5 * Authors:
   6 *     Atish Patra <atish.patra@wdc.com>
   7 */
   8
   9#include <linux/errno.h>
  10#include <linux/err.h>
  11#include <linux/kvm_host.h>
  12#include <asm/sbi.h>
  13#include <asm/kvm_vcpu_sbi.h>
  14
  15static int kvm_linux_err_map_sbi(int err)
  16{
  17        switch (err) {
  18        case 0:
  19                return SBI_SUCCESS;
  20        case -EPERM:
  21                return SBI_ERR_DENIED;
  22        case -EINVAL:
  23                return SBI_ERR_INVALID_PARAM;
  24        case -EFAULT:
  25                return SBI_ERR_INVALID_ADDRESS;
  26        case -EOPNOTSUPP:
  27                return SBI_ERR_NOT_SUPPORTED;
  28        case -EALREADY:
  29                return SBI_ERR_ALREADY_AVAILABLE;
  30        default:
  31                return SBI_ERR_FAILURE;
  32        };
  33}
  34
  35#ifdef CONFIG_RISCV_SBI_V01
  36extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_v01;
  37#else
  38static const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_v01 = {
  39        .extid_start = -1UL,
  40        .extid_end = -1UL,
  41        .handler = NULL,
  42};
  43#endif
  44extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_base;
  45extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_time;
  46extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_ipi;
  47extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_rfence;
  48extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_srst;
  49extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_hsm;
  50extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_experimental;
  51extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_vendor;
  52
  53static const struct kvm_vcpu_sbi_extension *sbi_ext[] = {
  54        &vcpu_sbi_ext_v01,
  55        &vcpu_sbi_ext_base,
  56        &vcpu_sbi_ext_time,
  57        &vcpu_sbi_ext_ipi,
  58        &vcpu_sbi_ext_rfence,
  59        &vcpu_sbi_ext_srst,
  60        &vcpu_sbi_ext_hsm,
  61        &vcpu_sbi_ext_experimental,
  62        &vcpu_sbi_ext_vendor,
  63};
  64
  65void kvm_riscv_vcpu_sbi_forward(struct kvm_vcpu *vcpu, struct kvm_run *run)
  66{
  67        struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
  68
  69        vcpu->arch.sbi_context.return_handled = 0;
  70        vcpu->stat.ecall_exit_stat++;
  71        run->exit_reason = KVM_EXIT_RISCV_SBI;
  72        run->riscv_sbi.extension_id = cp->a7;
  73        run->riscv_sbi.function_id = cp->a6;
  74        run->riscv_sbi.args[0] = cp->a0;
  75        run->riscv_sbi.args[1] = cp->a1;
  76        run->riscv_sbi.args[2] = cp->a2;
  77        run->riscv_sbi.args[3] = cp->a3;
  78        run->riscv_sbi.args[4] = cp->a4;
  79        run->riscv_sbi.args[5] = cp->a5;
  80        run->riscv_sbi.ret[0] = cp->a0;
  81        run->riscv_sbi.ret[1] = cp->a1;
  82}
  83
  84void kvm_riscv_vcpu_sbi_system_reset(struct kvm_vcpu *vcpu,
  85                                     struct kvm_run *run,
  86                                     u32 type, u64 reason)
  87{
  88        unsigned long i;
  89        struct kvm_vcpu *tmp;
  90
  91        kvm_for_each_vcpu(i, tmp, vcpu->kvm)
  92                tmp->arch.power_off = true;
  93        kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_SLEEP);
  94
  95        memset(&run->system_event, 0, sizeof(run->system_event));
  96        run->system_event.type = type;
  97        run->system_event.ndata = 1;
  98        run->system_event.data[0] = reason;
  99        run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
 100}
 101
 102int kvm_riscv_vcpu_sbi_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
 103{
 104        struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
 105
 106        /* Handle SBI return only once */
 107        if (vcpu->arch.sbi_context.return_handled)
 108                return 0;
 109        vcpu->arch.sbi_context.return_handled = 1;
 110
 111        /* Update return values */
 112        cp->a0 = run->riscv_sbi.ret[0];
 113        cp->a1 = run->riscv_sbi.ret[1];
 114
 115        /* Move to next instruction */
 116        vcpu->arch.guest_context.sepc += 4;
 117
 118        return 0;
 119}
 120
 121const struct kvm_vcpu_sbi_extension *kvm_vcpu_sbi_find_ext(unsigned long extid)
 122{
 123        int i = 0;
 124
 125        for (i = 0; i < ARRAY_SIZE(sbi_ext); i++) {
 126                if (sbi_ext[i]->extid_start <= extid &&
 127                    sbi_ext[i]->extid_end >= extid)
 128                        return sbi_ext[i];
 129        }
 130
 131        return NULL;
 132}
 133
 134int kvm_riscv_vcpu_sbi_ecall(struct kvm_vcpu *vcpu, struct kvm_run *run)
 135{
 136        int ret = 1;
 137        bool next_sepc = true;
 138        bool userspace_exit = false;
 139        struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
 140        const struct kvm_vcpu_sbi_extension *sbi_ext;
 141        struct kvm_cpu_trap utrap = { 0 };
 142        unsigned long out_val = 0;
 143        bool ext_is_v01 = false;
 144
 145        sbi_ext = kvm_vcpu_sbi_find_ext(cp->a7);
 146        if (sbi_ext && sbi_ext->handler) {
 147#ifdef CONFIG_RISCV_SBI_V01
 148                if (cp->a7 >= SBI_EXT_0_1_SET_TIMER &&
 149                    cp->a7 <= SBI_EXT_0_1_SHUTDOWN)
 150                        ext_is_v01 = true;
 151#endif
 152                ret = sbi_ext->handler(vcpu, run, &out_val, &utrap, &userspace_exit);
 153        } else {
 154                /* Return error for unsupported SBI calls */
 155                cp->a0 = SBI_ERR_NOT_SUPPORTED;
 156                goto ecall_done;
 157        }
 158
 159        /* Handle special error cases i.e trap, exit or userspace forward */
 160        if (utrap.scause) {
 161                /* No need to increment sepc or exit ioctl loop */
 162                ret = 1;
 163                utrap.sepc = cp->sepc;
 164                kvm_riscv_vcpu_trap_redirect(vcpu, &utrap);
 165                next_sepc = false;
 166                goto ecall_done;
 167        }
 168
 169        /* Exit ioctl loop or Propagate the error code the guest */
 170        if (userspace_exit) {
 171                next_sepc = false;
 172                ret = 0;
 173        } else {
 174                /**
 175                 * SBI extension handler always returns an Linux error code. Convert
 176                 * it to the SBI specific error code that can be propagated the SBI
 177                 * caller.
 178                 */
 179                ret = kvm_linux_err_map_sbi(ret);
 180                cp->a0 = ret;
 181                ret = 1;
 182        }
 183ecall_done:
 184        if (next_sepc)
 185                cp->sepc += 4;
 186        if (!ext_is_v01)
 187                cp->a1 = out_val;
 188
 189        return ret;
 190}
 191