1 // © 2023 Qualcomm Innovation Center, Inc. All rights reserved.
2 //
3 // SPDX-License-Identifier: BSD-3-Clause
4
5 #include <assert.h>
6 #include <hyptypes.h>
7 #include <string.h>
8
9 #include <hypconstants.h>
10 #include <hypcontainers.h>
11
12 #include <atomic.h>
13 #include <partition.h>
14 #include <spinlock.h>
15 #include <thread.h>
16 #include <util.h>
17 #include <virq.h>
18
19 #include <events/virtio_mmio.h>
20
21 #include <asm/nospec_checks.h>
22
23 #include "event_handlers.h"
24 #include "useraccess.h"
25 #include "virtio_input.h"
26
27 error_t
virtio_input_handle_object_activate(virtio_mmio_t * virtio_mmio)28 virtio_input_handle_object_activate(virtio_mmio_t *virtio_mmio)
29 {
30 error_t ret;
31
32 partition_t *partition = virtio_mmio->header.partition;
33 // Allocate memory for virtio input data struct if device type
34 // virtio-input
35 if (virtio_mmio->device_type == VIRTIO_DEVICE_TYPE_INPUT) {
36 size_t alloc_size = sizeof(virtio_input_data_t);
37 void_ptr_result_t alloc_ret = partition_alloc(
38 partition, alloc_size, alignof(virtio_input_data_t));
39 if (alloc_ret.e != OK) {
40 ret = ERROR_NOMEM;
41 goto out;
42 }
43
44 (void)memset_s(alloc_ret.r, alloc_size, 0, alloc_size);
45
46 virtio_mmio->input_data = (virtio_input_data_t *)alloc_ret.r;
47 }
48
49 ret = OK;
50 out:
51 return ret;
52 }
53
54 error_t
virtio_input_handle_object_cleanup(virtio_mmio_t * virtio_mmio)55 virtio_input_handle_object_cleanup(virtio_mmio_t *virtio_mmio)
56 {
57 if (virtio_mmio->input_data != NULL) {
58 partition_t *partition = virtio_mmio->header.partition;
59 size_t alloc_size;
60 void *alloc_base;
61 /* first free memory for absinfo and evtypes if any */
62 if (virtio_mmio->input_data->absinfo_count != 0U) {
63 alloc_size = virtio_mmio->input_data->absinfo_count *
64 sizeof(virtio_input_absinfo_t);
65 alloc_base = (void *)virtio_mmio->input_data->absinfo;
66
67 error_t err = partition_free(partition, alloc_base,
68 alloc_size);
69 assert(err == OK);
70
71 virtio_mmio->input_data->absinfo = NULL;
72 virtio_mmio->input_data->absinfo_count = 0U;
73 }
74
75 if (virtio_mmio->input_data->ev_bits_count != 0U) {
76 alloc_size = virtio_mmio->input_data->ev_bits_count *
77 sizeof(virtio_input_ev_bits_t);
78 alloc_base = (void *)virtio_mmio->input_data->ev_bits;
79
80 error_t err = partition_free(partition, alloc_base,
81 alloc_size);
82 assert(err == OK);
83
84 virtio_mmio->input_data->ev_bits = NULL;
85 virtio_mmio->input_data->ev_bits_count = 0U;
86 }
87
88 /* now safely free the virtio_input struct */
89 alloc_size = sizeof(virtio_mmio->input_data);
90 alloc_base = (void *)&virtio_mmio->input_data;
91
92 error_t err = partition_free(partition, alloc_base, alloc_size);
93 assert(err == OK);
94
95 virtio_mmio->input_data = NULL;
96 } else {
97 // ignore
98 }
99
100 return OK;
101 }
102
103 error_t
set_data_sel_abs_info(const virtio_mmio_t * virtio_mmio,uint32_t subsel,uint32_t size,vmaddr_t data)104 set_data_sel_abs_info(const virtio_mmio_t *virtio_mmio, uint32_t subsel,
105 uint32_t size, vmaddr_t data)
106 {
107 error_t ret;
108 if (subsel < VIRTIO_INPUT_MAX_ABS_AXES) {
109 // find free entry
110 uint32_t entry;
111 for (entry = 0; entry < virtio_mmio->input_data->absinfo_count;
112 entry++) {
113 if (virtio_mmio->input_data->absinfo[entry].subsel ==
114 VIRTIO_INPUT_SUBSEL_INVALID) {
115 // got the free entry
116 break;
117 } else {
118 continue;
119 }
120 }
121
122 if (entry == virtio_mmio->input_data->absinfo_count) {
123 // no free entry
124 ret = ERROR_NORESOURCES;
125 } else {
126 // copy data from guest va; size is checked by this API
127 ret = useraccess_copy_from_guest_va(
128 &(virtio_mmio->input_data->absinfo[entry]
129 .data),
130 VIRTIO_INPUT_MAX_ABSINFO_SIZE, data, size)
131 .e;
132 if (ret == OK) {
133 // successful copy, update subsel
134 virtio_mmio->input_data->absinfo[entry].subsel =
135 (uint8_t)subsel;
136 } else {
137 // ignore
138 }
139 }
140 } else {
141 ret = ERROR_ARGUMENT_INVALID;
142 }
143 return ret;
144 }
145
146 error_t
set_data_sel_ev_bits(const virtio_mmio_t * virtio_mmio,uint32_t subsel,uint32_t size,vmaddr_t data)147 set_data_sel_ev_bits(const virtio_mmio_t *virtio_mmio, uint32_t subsel,
148 uint32_t size, vmaddr_t data)
149 {
150 error_t ret;
151 if (subsel < VIRTIO_INPUT_MAX_EV_TYPES) {
152 // find free entry
153 uint32_t entry;
154 for (entry = 0; entry < virtio_mmio->input_data->ev_bits_count;
155 entry++) {
156 if (virtio_mmio->input_data->ev_bits[entry].subsel ==
157 VIRTIO_INPUT_SUBSEL_INVALID) {
158 // got the free entry
159 break;
160 } else {
161 continue;
162 }
163 }
164
165 if (entry == virtio_mmio->input_data->ev_bits_count) {
166 // no free entry
167 ret = ERROR_NORESOURCES;
168 } else {
169 // copy data from guest va; size is checked by this API
170 ret = useraccess_copy_from_guest_va(
171 &(virtio_mmio->input_data->ev_bits[entry]
172 .data),
173 VIRTIO_INPUT_MAX_BITMAP_SIZE, data, size)
174 .e;
175 if (ret == OK) {
176 /*successful copy, update the size info
177 * and subsel*/
178 virtio_mmio->input_data->ev_bits[entry].size =
179 (uint8_t)size;
180 virtio_mmio->input_data->ev_bits[entry].subsel =
181 (uint8_t)subsel;
182 } else {
183 // ignore
184 }
185 }
186 } else {
187 ret = ERROR_ARGUMENT_INVALID;
188 }
189 return ret;
190 }
191
192 static void
sel_cfg_abs_info_write(const virtio_mmio_t * virtio_mmio,uint8_t subsel)193 sel_cfg_abs_info_write(const virtio_mmio_t *virtio_mmio, uint8_t subsel)
194 {
195 if (subsel < VIRTIO_INPUT_MAX_ABS_AXES) {
196 // find the ev entry where this subsel entry is stored
197 uint32_t entry;
198 for (entry = 0; entry < virtio_mmio->input_data->absinfo_count;
199 entry++) {
200 if (virtio_mmio->input_data->absinfo[entry].subsel ==
201 subsel) {
202 // found the entry
203 break;
204 } else {
205 continue;
206 }
207 }
208 if (entry == virtio_mmio->input_data->absinfo_count) {
209 // entry not found, invalid subsel set size 0
210 atomic_store_relaxed(&virtio_mmio->regs->device_config
211 .input_config.size,
212 0U);
213 } else {
214 // valid subsel
215 uint8_t size = (uint8_t)VIRTIO_INPUT_MAX_ABSINFO_SIZE;
216
217 (void)memcpy(
218 virtio_mmio->regs->device_config.input_config.u
219 .abs,
220 virtio_mmio->input_data->absinfo[entry].data,
221 size);
222
223 // update the size
224 atomic_store_relaxed(&virtio_mmio->regs->device_config
225 .input_config.size,
226 size);
227 }
228 } else {
229 // invalid subsel set size 0
230 atomic_store_relaxed(
231 &virtio_mmio->regs->device_config.input_config.size,
232 0U);
233 }
234 }
235
236 static void
sel_cfg_ev_bits_write(const virtio_mmio_t * virtio_mmio,uint8_t subsel)237 sel_cfg_ev_bits_write(const virtio_mmio_t *virtio_mmio, uint8_t subsel)
238 {
239 if (subsel < VIRTIO_INPUT_MAX_EV_TYPES) {
240 // find the ev entry where this subsel entry is stored
241 uint32_t entry;
242 for (entry = 0; entry < virtio_mmio->input_data->ev_bits_count;
243 entry++) {
244 if (virtio_mmio->input_data->ev_bits[entry].subsel ==
245 subsel) {
246 // found the entry
247 break;
248 } else {
249 continue;
250 }
251 }
252 if (entry == virtio_mmio->input_data->ev_bits_count) {
253 // entry not found, invalid subsel set size 0
254 atomic_store_relaxed(&virtio_mmio->regs->device_config
255 .input_config.size,
256 0U);
257 } else {
258 // valid subsel
259 uint8_t size =
260 virtio_mmio->input_data->ev_bits[entry].size;
261
262 (void)memcpy(
263 virtio_mmio->regs->device_config.input_config.u
264 .bitmap,
265 virtio_mmio->input_data->ev_bits[entry].data,
266 size);
267
268 // update the size
269 atomic_store_relaxed(&virtio_mmio->regs->device_config
270 .input_config.size,
271 size);
272 }
273 } else {
274 // invalid subsel set size 0
275 atomic_store_relaxed(
276 &virtio_mmio->regs->device_config.input_config.size,
277 0U);
278 }
279 }
280
281 static void
virtio_input_config_u_write(const virtio_mmio_t * virtio_mmio,uint8_t sel,uint8_t subsel)282 virtio_input_config_u_write(const virtio_mmio_t *virtio_mmio, uint8_t sel,
283 uint8_t subsel)
284 {
285 switch ((virtio_input_config_select_t)sel) {
286 case VIRTIO_INPUT_CONFIG_SELECT_CFG_ID_NAME: {
287 if (subsel != 0U) { // only subsel 0 is valid
288 atomic_store_relaxed(&virtio_mmio->regs->device_config
289 .input_config.size,
290 0U);
291 } else {
292 size_t size = virtio_mmio->input_data->name_size;
293 for (index_t i = 0U; i < size; i++) {
294 atomic_store_relaxed(
295 &virtio_mmio->regs->device_config
296 .input_config.u.string[i],
297 virtio_mmio->input_data->name[i]);
298 }
299 // update the size
300 atomic_store_relaxed(&virtio_mmio->regs->device_config
301 .input_config.size,
302 (uint8_t)size);
303 }
304 break;
305 }
306 case VIRTIO_INPUT_CONFIG_SELECT_CFG_ID_SERIAL: {
307 if (subsel != 0U) { // only subsel 0 is valid
308 atomic_store_relaxed(&virtio_mmio->regs->device_config
309 .input_config.size,
310 0U);
311 } else {
312 size_t size = virtio_mmio->input_data->serial_size;
313 for (index_t i = 0U; i < size; i++) {
314 atomic_store_relaxed(
315 &virtio_mmio->regs->device_config
316 .input_config.u.string[i],
317 virtio_mmio->input_data->serial[i]);
318 }
319 // update the size
320 atomic_store_relaxed(&virtio_mmio->regs->device_config
321 .input_config.size,
322 (uint8_t)size);
323 }
324 break;
325 }
326 case VIRTIO_INPUT_CONFIG_SELECT_CFG_ID_DEVIDS: {
327 if (subsel != 0U) { // only subsel 0 is valid
328 atomic_store_relaxed(&virtio_mmio->regs->device_config
329 .input_config.size,
330 0U);
331 } else {
332 size_t size = sizeof(virtio_mmio->input_data->devids);
333 atomic_store_relaxed(&virtio_mmio->regs->device_config
334 .input_config.u.ids,
335 virtio_mmio->input_data->devids);
336 // update the size
337 atomic_store_relaxed(&virtio_mmio->regs->device_config
338 .input_config.size,
339 (uint8_t)size);
340 }
341 break;
342 }
343 case VIRTIO_INPUT_CONFIG_SELECT_CFG_PROP_BITS: {
344 if (subsel != 0U) { // only subsel 0 is valid
345 atomic_store_relaxed(&virtio_mmio->regs->device_config
346 .input_config.size,
347 0U);
348 } else {
349 size_t size =
350 sizeof(virtio_mmio->input_data->prop_bits);
351 uint8_t *prop_bits_addr =
352 (uint8_t *)&virtio_mmio->input_data->prop_bits;
353 for (index_t i = 0U; i < size; i++) {
354 atomic_store_relaxed(
355 &virtio_mmio->regs->device_config
356 .input_config.u.bitmap[i],
357 *prop_bits_addr);
358 prop_bits_addr++;
359 }
360 // update the size
361 atomic_store_relaxed(&virtio_mmio->regs->device_config
362 .input_config.size,
363 (uint8_t)size);
364 }
365 break;
366 }
367 case VIRTIO_INPUT_CONFIG_SELECT_CFG_EV_BITS: {
368 sel_cfg_ev_bits_write(virtio_mmio, subsel);
369 break;
370 }
371 case VIRTIO_INPUT_CONFIG_SELECT_CFG_ABS_INFO: {
372 sel_cfg_abs_info_write(virtio_mmio, subsel);
373 break;
374 }
375 case VIRTIO_INPUT_CONFIG_SELECT_CFG_UNSET:
376 default:
377 // No data; set size to 0
378 atomic_store_relaxed(
379 &virtio_mmio->regs->device_config.input_config.size,
380 0U);
381 break;
382 }
383 }
384
385 vcpu_trap_result_t
virtio_input_config_write(const virtio_mmio_t * virtio_mmio,size_t write_offset,register_t reg_val,size_t access_size)386 virtio_input_config_write(const virtio_mmio_t *virtio_mmio, size_t write_offset,
387 register_t reg_val, size_t access_size)
388 {
389 vcpu_trap_result_t ret;
390 register_t val = reg_val;
391 size_t offset;
392 size_t access_size_remaining = access_size;
393
394 if (write_offset >= (size_t)OFS_VIRTIO_MMIO_REGS_DEVICE_CONFIG) {
395 ret = VCPU_TRAP_RESULT_FAULT;
396 offset = write_offset -
397 (size_t)OFS_VIRTIO_MMIO_REGS_DEVICE_CONFIG;
398 while (access_size_remaining != 0U) {
399 switch (offset) {
400 case OFS_VIRTIO_INPUT_CONFIG_SELECT: {
401 atomic_store_relaxed(
402 &virtio_mmio->regs->device_config
403 .input_config.select,
404 (uint8_t)val);
405
406 uint8_t subsel = atomic_load_relaxed(
407 &virtio_mmio->regs->device_config
408 .input_config.subsel);
409
410 // write the appropriate data in u regs
411 virtio_input_config_u_write(
412 virtio_mmio, (uint8_t)val, subsel);
413 // update remianing size
414 access_size_remaining =
415 access_size_remaining - 1U;
416 offset += 1U; // update offset
417 val >>= 8; // update the value
418 ret = VCPU_TRAP_RESULT_EMULATED;
419 break;
420 }
421 case OFS_VIRTIO_INPUT_CONFIG_SUBSEL: {
422 atomic_store_relaxed(
423 &virtio_mmio->regs->device_config
424 .input_config.subsel,
425 (uint8_t)val);
426
427 uint8_t sel = atomic_load_relaxed(
428 &virtio_mmio->regs->device_config
429 .input_config.select);
430
431 // write the appropriate data in u regs
432 virtio_input_config_u_write(virtio_mmio, sel,
433 (uint8_t)val);
434 // update remianing size
435 access_size_remaining =
436 access_size_remaining - 1U;
437 offset += 1U; // update offset
438 val >>= 8; // update the value
439 ret = VCPU_TRAP_RESULT_EMULATED;
440 break;
441 }
442 default:
443 (void)access_size;
444 // we will not handle offset after subsel
445 access_size_remaining = 0U;
446 ret = VCPU_TRAP_RESULT_FAULT;
447 break;
448 }
449 }
450 } else {
451 ret = VCPU_TRAP_RESULT_FAULT;
452 }
453
454 return ret;
455 }
456