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