lkdtm/heap: Hide allocation size from -Warray-bounds
[linux-2.6-microblaze.git] / arch / riscv / kvm / vcpu_sbi.c
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 #ifdef CONFIG_RISCV_SBI_V01
36 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_v01;
37 #else
38 static const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_v01 = {
39         .extid_start = -1UL,
40         .extid_end = -1UL,
41         .handler = NULL,
42 };
43 #endif
44 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_base;
45 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_time;
46 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_ipi;
47 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_rfence;
48 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_srst;
49 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_hsm;
50 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_experimental;
51 extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_vendor;
52
53 static 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
65 void 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
84 void kvm_riscv_vcpu_sbi_system_reset(struct kvm_vcpu *vcpu,
85                                      struct kvm_run *run,
86                                      u32 type, u64 flags)
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.flags = flags;
98         run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
99 }
100
101 int kvm_riscv_vcpu_sbi_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
102 {
103         struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
104
105         /* Handle SBI return only once */
106         if (vcpu->arch.sbi_context.return_handled)
107                 return 0;
108         vcpu->arch.sbi_context.return_handled = 1;
109
110         /* Update return values */
111         cp->a0 = run->riscv_sbi.ret[0];
112         cp->a1 = run->riscv_sbi.ret[1];
113
114         /* Move to next instruction */
115         vcpu->arch.guest_context.sepc += 4;
116
117         return 0;
118 }
119
120 const struct kvm_vcpu_sbi_extension *kvm_vcpu_sbi_find_ext(unsigned long extid)
121 {
122         int i = 0;
123
124         for (i = 0; i < ARRAY_SIZE(sbi_ext); i++) {
125                 if (sbi_ext[i]->extid_start <= extid &&
126                     sbi_ext[i]->extid_end >= extid)
127                         return sbi_ext[i];
128         }
129
130         return NULL;
131 }
132
133 int kvm_riscv_vcpu_sbi_ecall(struct kvm_vcpu *vcpu, struct kvm_run *run)
134 {
135         int ret = 1;
136         bool next_sepc = true;
137         bool userspace_exit = false;
138         struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
139         const struct kvm_vcpu_sbi_extension *sbi_ext;
140         struct kvm_cpu_trap utrap = { 0 };
141         unsigned long out_val = 0;
142         bool ext_is_v01 = false;
143
144         sbi_ext = kvm_vcpu_sbi_find_ext(cp->a7);
145         if (sbi_ext && sbi_ext->handler) {
146 #ifdef CONFIG_RISCV_SBI_V01
147                 if (cp->a7 >= SBI_EXT_0_1_SET_TIMER &&
148                     cp->a7 <= SBI_EXT_0_1_SHUTDOWN)
149                         ext_is_v01 = true;
150 #endif
151                 ret = sbi_ext->handler(vcpu, run, &out_val, &utrap, &userspace_exit);
152         } else {
153                 /* Return error for unsupported SBI calls */
154                 cp->a0 = SBI_ERR_NOT_SUPPORTED;
155                 goto ecall_done;
156         }
157
158         /* Handle special error cases i.e trap, exit or userspace forward */
159         if (utrap.scause) {
160                 /* No need to increment sepc or exit ioctl loop */
161                 ret = 1;
162                 utrap.sepc = cp->sepc;
163                 kvm_riscv_vcpu_trap_redirect(vcpu, &utrap);
164                 next_sepc = false;
165                 goto ecall_done;
166         }
167
168         /* Exit ioctl loop or Propagate the error code the guest */
169         if (userspace_exit) {
170                 next_sepc = false;
171                 ret = 0;
172         } else {
173                 /**
174                  * SBI extension handler always returns an Linux error code. Convert
175                  * it to the SBI specific error code that can be propagated the SBI
176                  * caller.
177                  */
178                 ret = kvm_linux_err_map_sbi(ret);
179                 cp->a0 = ret;
180                 ret = 1;
181         }
182 ecall_done:
183         if (next_sepc)
184                 cp->sepc += 4;
185         if (!ext_is_v01)
186                 cp->a1 = out_val;
187
188         return ret;
189 }