1 // © 2023 Qualcomm Innovation Center, Inc. All rights reserved.
2 //
3 // SPDX-License-Identifier: BSD-3-Clause
4
5 #include <hyptypes.h>
6 #include <string.h>
7
8 #include <hypcall_def.h>
9 #include <hyprights.h>
10
11 #include <atomic.h>
12 #include <compiler.h>
13 #include <cspace.h>
14 #include <cspace_lookup.h>
15 #include <object.h>
16 #include <partition.h>
17 #include <spinlock.h>
18 #include <util.h>
19 #include <virq.h>
20
21 #include <asm/nospec_checks.h>
22
23 #include "useraccess.h"
24 #include "virtio_input.h"
25
26 error_t
hypercall_virtio_input_configure(cap_id_t virtio_mmio_cap,uint64_t devids,uint32_t prop_bits,uint32_t num_evtypes,uint32_t num_absaxes)27 hypercall_virtio_input_configure(cap_id_t virtio_mmio_cap, uint64_t devids,
28 uint32_t prop_bits, uint32_t num_evtypes,
29 uint32_t num_absaxes)
30 {
31 error_t ret;
32 cspace_t *cspace = cspace_get_self();
33
34 virtio_mmio_ptr_result_t p = cspace_lookup_virtio_mmio(
35 cspace, virtio_mmio_cap, CAP_RIGHTS_VIRTIO_MMIO_CONFIG);
36 if (compiler_unexpected(p.e != OK)) {
37 ret = p.e;
38 goto out;
39 }
40 virtio_mmio_t *virtio_mmio = p.r;
41 partition_t *partition = virtio_mmio->header.partition;
42
43 // Must be a virtio-input device
44 if (virtio_mmio->device_type != VIRTIO_DEVICE_TYPE_INPUT) {
45 ret = ERROR_OBJECT_CONFIG;
46 goto release_virtio_object;
47 }
48
49 // save the devids and propbits
50 virtio_mmio->input_data->devids = devids;
51 virtio_mmio->input_data->prop_bits = prop_bits;
52
53 // Validate the upper bound for evtypes and absaxes
54 if ((num_evtypes > VIRTIO_INPUT_MAX_EV_TYPES) ||
55 (num_absaxes > VIRTIO_INPUT_MAX_ABS_AXES)) {
56 ret = ERROR_ARGUMENT_INVALID;
57 goto release_virtio_object;
58 }
59
60 size_t alloc_size = 0U;
61 void_ptr_result_t alloc_ret;
62 // allocate mem for evtypes, if not already allocated and count is > 0
63 if ((virtio_mmio->input_data->ev_bits == NULL) && (num_evtypes > 0U)) {
64 alloc_size = num_evtypes * sizeof(virtio_input_ev_bits_t);
65 alloc_ret = partition_alloc(partition, alloc_size,
66 alignof(virtio_input_ev_bits_t));
67 if (alloc_ret.e != OK) {
68 ret = ERROR_NOMEM;
69 goto release_virtio_object;
70 }
71 (void)memset_s(alloc_ret.r, alloc_size, 0, alloc_size);
72
73 virtio_mmio->input_data->ev_bits =
74 (virtio_input_ev_bits_t *)alloc_ret.r;
75 virtio_mmio->input_data->ev_bits_count = num_evtypes;
76
77 // set entry of each ev as VIRTIO_INPUT_SUBSEL_INVALID
78 for (uint32_t entry = 0; entry < num_evtypes; entry++) {
79 virtio_mmio->input_data->ev_bits[entry].subsel =
80 (uint8_t)VIRTIO_INPUT_SUBSEL_INVALID;
81 }
82 } else {
83 if (num_evtypes > 0U) {
84 ret = ERROR_BUSY;
85 goto release_virtio_object;
86 } else {
87 // it means device has no evtypes to register, no
88 // worries
89 }
90 }
91
92 // allocate mem for absaxes, if not already allocated and count is > 0
93 if ((virtio_mmio->input_data->absinfo == NULL) && (num_absaxes > 0U)) {
94 alloc_size = num_absaxes * sizeof(virtio_input_absinfo_t);
95 alloc_ret = partition_alloc(partition, alloc_size,
96 alignof(virtio_input_absinfo_t));
97 if (alloc_ret.e != OK) {
98 ret = ERROR_NOMEM;
99 goto release_virtio_object;
100 }
101 (void)memset_s(alloc_ret.r, alloc_size, 0, alloc_size);
102
103 virtio_mmio->input_data->absinfo =
104 (virtio_input_absinfo_t *)alloc_ret.r;
105 virtio_mmio->input_data->absinfo_count = num_absaxes;
106
107 // set entry of each absinfo as VIRTIO_INPUT_SUBSEL_INVALID
108 for (uint32_t entry = 0; entry < num_absaxes; entry++) {
109 virtio_mmio->input_data->absinfo[entry].subsel =
110 (uint8_t)VIRTIO_INPUT_SUBSEL_INVALID;
111 }
112 } else if (num_absaxes > 0U) {
113 ret = ERROR_BUSY;
114 goto release_virtio_object;
115 } else {
116 // device has no absaxes info to register
117 }
118
119 ret = OK;
120 release_virtio_object:
121 object_put_virtio_mmio(virtio_mmio);
122 out:
123 return ret;
124 }
125
126 error_t
hypercall_virtio_input_set_data(cap_id_t virtio_mmio_cap,uint32_t sel,uint32_t subsel,uint32_t size,vmaddr_t data)127 hypercall_virtio_input_set_data(cap_id_t virtio_mmio_cap, uint32_t sel,
128 uint32_t subsel, uint32_t size, vmaddr_t data)
129 {
130 error_t ret;
131 cspace_t *cspace = cspace_get_self();
132
133 virtio_mmio_ptr_result_t p = cspace_lookup_virtio_mmio(
134 cspace, virtio_mmio_cap, CAP_RIGHTS_VIRTIO_MMIO_CONFIG);
135 if (compiler_unexpected(p.e != OK)) {
136 ret = p.e;
137 goto out;
138 }
139 virtio_mmio_t *virtio_mmio = p.r;
140
141 // Must be a virtio-input device
142 if (virtio_mmio->device_type != VIRTIO_DEVICE_TYPE_INPUT) {
143 ret = ERROR_CSPACE_WRONG_OBJECT_TYPE;
144 goto release_virtio_object;
145 }
146
147 switch ((virtio_input_config_select_t)sel) {
148 case VIRTIO_INPUT_CONFIG_SELECT_CFG_ID_NAME: {
149 // Only subsel 0 is valid for this sel value
150 if (subsel == 0U) {
151 // copy data from guest va; size is checked by this API
152 ret = useraccess_copy_from_guest_va(
153 virtio_mmio->input_data->name,
154 sizeof(virtio_mmio->input_data->name),
155 data, size)
156 .e;
157 if (ret == OK) {
158 virtio_mmio->input_data->name_size = size;
159 } else {
160 virtio_mmio->input_data->name_size = 0U;
161 }
162 } else {
163 ret = ERROR_ARGUMENT_INVALID;
164 }
165 break;
166 }
167 case VIRTIO_INPUT_CONFIG_SELECT_CFG_ID_SERIAL: {
168 // Only subsel 0 is valid for this sel value
169 if (subsel == 0U) {
170 // copy data from guest va; size is checked by this API
171 ret = useraccess_copy_from_guest_va(
172 virtio_mmio->input_data->serial,
173 sizeof(virtio_mmio->input_data->serial),
174 data, size)
175 .e;
176 if (ret == OK) {
177 virtio_mmio->input_data->serial_size = size;
178 } else {
179 virtio_mmio->input_data->serial_size = 0U;
180 }
181 } else {
182 ret = ERROR_ARGUMENT_INVALID;
183 }
184 break;
185 }
186 case VIRTIO_INPUT_CONFIG_SELECT_CFG_ID_DEVIDS: {
187 // Only subsel 0 is valid for this sel value
188 if (subsel == 0U) {
189 // copy data from guest va; size is checked by this API
190 // TODO: should we memset here?
191 ret = useraccess_copy_from_guest_va(
192 &virtio_mmio->input_data->devids,
193 sizeof(virtio_mmio->input_data->devids),
194 data, size)
195 .e;
196 } else {
197 ret = ERROR_ARGUMENT_INVALID;
198 }
199 break;
200 }
201 case VIRTIO_INPUT_CONFIG_SELECT_CFG_PROP_BITS: {
202 // Only subsel 0 is valid for this sel value
203 if (subsel == 0U) {
204 // copy data from guest va; size is checked by this API
205 // TODO: should we memset here?
206 ret = useraccess_copy_from_guest_va(
207 &virtio_mmio->input_data->prop_bits,
208 sizeof(virtio_mmio->input_data->prop_bits),
209 data, size)
210 .e;
211 } else {
212 ret = ERROR_ARGUMENT_INVALID;
213 }
214 break;
215 }
216 case VIRTIO_INPUT_CONFIG_SELECT_CFG_EV_BITS: {
217 // check if mem is allocated for ev_bits
218 if (virtio_mmio->input_data->ev_bits != NULL) {
219 ret = set_data_sel_ev_bits(
220 (const virtio_mmio_t *)virtio_mmio, subsel,
221 size, data);
222 } else {
223 // Not properly configured
224 ret = ERROR_ARGUMENT_INVALID;
225 }
226 break;
227 }
228 case VIRTIO_INPUT_CONFIG_SELECT_CFG_ABS_INFO: {
229 // check if mem is allocated for absinfo
230 if (virtio_mmio->input_data->absinfo != NULL) {
231 ret = set_data_sel_abs_info(
232 (const virtio_mmio_t *)virtio_mmio, subsel,
233 size, data);
234 } else {
235 // Not properly configured
236 ret = ERROR_ARGUMENT_INVALID;
237 }
238 break;
239 }
240 case VIRTIO_INPUT_CONFIG_SELECT_CFG_UNSET:
241 default:
242 // invalid select event
243 ret = ERROR_ARGUMENT_INVALID;
244 break;
245 }
246
247 release_virtio_object:
248 object_put_virtio_mmio(virtio_mmio);
249 out:
250 return ret;
251 }
252