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 15 static 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 #ifndef CONFIG_RISCV_SBI_V01 36 static const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_v01 = { 37 .extid_start = -1UL, 38 .extid_end = -1UL, 39 .handler = NULL, 40 }; 41 #endif 42 43 static const struct kvm_vcpu_sbi_extension *sbi_ext[] = { 44 &vcpu_sbi_ext_v01, 45 &vcpu_sbi_ext_base, 46 &vcpu_sbi_ext_time, 47 &vcpu_sbi_ext_ipi, 48 &vcpu_sbi_ext_rfence, 49 &vcpu_sbi_ext_srst, 50 &vcpu_sbi_ext_hsm, 51 &vcpu_sbi_ext_experimental, 52 &vcpu_sbi_ext_vendor, 53 }; 54 55 void kvm_riscv_vcpu_sbi_forward(struct kvm_vcpu *vcpu, struct kvm_run *run) 56 { 57 struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 58 59 vcpu->arch.sbi_context.return_handled = 0; 60 vcpu->stat.ecall_exit_stat++; 61 run->exit_reason = KVM_EXIT_RISCV_SBI; 62 run->riscv_sbi.extension_id = cp->a7; 63 run->riscv_sbi.function_id = cp->a6; 64 run->riscv_sbi.args[0] = cp->a0; 65 run->riscv_sbi.args[1] = cp->a1; 66 run->riscv_sbi.args[2] = cp->a2; 67 run->riscv_sbi.args[3] = cp->a3; 68 run->riscv_sbi.args[4] = cp->a4; 69 run->riscv_sbi.args[5] = cp->a5; 70 run->riscv_sbi.ret[0] = cp->a0; 71 run->riscv_sbi.ret[1] = cp->a1; 72 } 73 74 void kvm_riscv_vcpu_sbi_system_reset(struct kvm_vcpu *vcpu, 75 struct kvm_run *run, 76 u32 type, u64 reason) 77 { 78 unsigned long i; 79 struct kvm_vcpu *tmp; 80 81 kvm_for_each_vcpu(i, tmp, vcpu->kvm) 82 tmp->arch.power_off = true; 83 kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_SLEEP); 84 85 memset(&run->system_event, 0, sizeof(run->system_event)); 86 run->system_event.type = type; 87 run->system_event.ndata = 1; 88 run->system_event.data[0] = reason; 89 run->exit_reason = KVM_EXIT_SYSTEM_EVENT; 90 } 91 92 int kvm_riscv_vcpu_sbi_return(struct kvm_vcpu *vcpu, struct kvm_run *run) 93 { 94 struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 95 96 /* Handle SBI return only once */ 97 if (vcpu->arch.sbi_context.return_handled) 98 return 0; 99 vcpu->arch.sbi_context.return_handled = 1; 100 101 /* Update return values */ 102 cp->a0 = run->riscv_sbi.ret[0]; 103 cp->a1 = run->riscv_sbi.ret[1]; 104 105 /* Move to next instruction */ 106 vcpu->arch.guest_context.sepc += 4; 107 108 return 0; 109 } 110 111 const struct kvm_vcpu_sbi_extension *kvm_vcpu_sbi_find_ext(unsigned long extid) 112 { 113 int i = 0; 114 115 for (i = 0; i < ARRAY_SIZE(sbi_ext); i++) { 116 if (sbi_ext[i]->extid_start <= extid && 117 sbi_ext[i]->extid_end >= extid) 118 return sbi_ext[i]; 119 } 120 121 return NULL; 122 } 123 124 int kvm_riscv_vcpu_sbi_ecall(struct kvm_vcpu *vcpu, struct kvm_run *run) 125 { 126 int ret = 1; 127 bool next_sepc = true; 128 bool userspace_exit = false; 129 struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 130 const struct kvm_vcpu_sbi_extension *sbi_ext; 131 struct kvm_cpu_trap utrap = { 0 }; 132 unsigned long out_val = 0; 133 bool ext_is_v01 = false; 134 135 sbi_ext = kvm_vcpu_sbi_find_ext(cp->a7); 136 if (sbi_ext && sbi_ext->handler) { 137 #ifdef CONFIG_RISCV_SBI_V01 138 if (cp->a7 >= SBI_EXT_0_1_SET_TIMER && 139 cp->a7 <= SBI_EXT_0_1_SHUTDOWN) 140 ext_is_v01 = true; 141 #endif 142 ret = sbi_ext->handler(vcpu, run, &out_val, &utrap, &userspace_exit); 143 } else { 144 /* Return error for unsupported SBI calls */ 145 cp->a0 = SBI_ERR_NOT_SUPPORTED; 146 goto ecall_done; 147 } 148 149 /* Handle special error cases i.e trap, exit or userspace forward */ 150 if (utrap.scause) { 151 /* No need to increment sepc or exit ioctl loop */ 152 ret = 1; 153 utrap.sepc = cp->sepc; 154 kvm_riscv_vcpu_trap_redirect(vcpu, &utrap); 155 next_sepc = false; 156 goto ecall_done; 157 } 158 159 /* Exit ioctl loop or Propagate the error code the guest */ 160 if (userspace_exit) { 161 next_sepc = false; 162 ret = 0; 163 } else { 164 /** 165 * SBI extension handler always returns an Linux error code. Convert 166 * it to the SBI specific error code that can be propagated the SBI 167 * caller. 168 */ 169 ret = kvm_linux_err_map_sbi(ret); 170 cp->a0 = ret; 171 ret = 1; 172 } 173 ecall_done: 174 if (next_sepc) 175 cp->sepc += 4; 176 if (!ext_is_v01) 177 cp->a1 = out_val; 178 179 return ret; 180 } 181