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