xref: /freebsd/sys/dev/vmware/vmci/vmci_driver.c (revision f126890ac5386406dadf7c4cfa9566cbb56537c5)
1 /*-
2  * Copyright (c) 2018 VMware, Inc.
3  *
4  * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
5  */
6 
7 /* VMCI initialization. */
8 
9 #include <sys/cdefs.h>
10 #include "vmci.h"
11 #include "vmci_doorbell.h"
12 #include "vmci_driver.h"
13 #include "vmci_event.h"
14 #include "vmci_kernel_api.h"
15 #include "vmci_kernel_defs.h"
16 #include "vmci_resource.h"
17 
18 #define LGPFX			"vmci: "
19 #define VMCI_UTIL_NUM_RESOURCES	1
20 
21 static vmci_id ctx_update_sub_id = VMCI_INVALID_ID;
22 static volatile int vm_context_id = VMCI_INVALID_ID;
23 
24 /*
25  *------------------------------------------------------------------------------
26  *
27  * vmci_util_cid_update --
28  *
29  *     Gets called with the new context id if updated or resumed.
30  *
31  * Results:
32  *     Context id.
33  *
34  * Side effects:
35  *     None.
36  *
37  *------------------------------------------------------------------------------
38  */
39 
40 static void
41 vmci_util_cid_update(vmci_id sub_id, struct vmci_event_data *event_data,
42     void *client_data)
43 {
44 	struct vmci_event_payload_context *ev_payload;
45 
46 	ev_payload = vmci_event_data_payload(event_data);
47 
48 	if (sub_id != ctx_update_sub_id) {
49 		VMCI_LOG_DEBUG(LGPFX"Invalid subscriber (ID=0x%x).\n", sub_id);
50 		return;
51 	}
52 	if (event_data == NULL || ev_payload->context_id == VMCI_INVALID_ID) {
53 		VMCI_LOG_DEBUG(LGPFX"Invalid event data.\n");
54 		return;
55 	}
56 	VMCI_LOG_INFO(LGPFX"Updating context from (ID=0x%x) to (ID=0x%x) on "
57 	    "event (type=%d).\n", atomic_load_int(&vm_context_id),
58 	    ev_payload->context_id, event_data->event);
59 	atomic_store_int(&vm_context_id, ev_payload->context_id);
60 }
61 
62 /*
63  *------------------------------------------------------------------------------
64  *
65  * vmci_util_init --
66  *
67  *     Subscribe to context id update event.
68  *
69  * Results:
70  *     None.
71  *
72  * Side effects:
73  *     None.
74  *
75  *------------------------------------------------------------------------------
76  */
77 
78 void
79 vmci_util_init(void)
80 {
81 
82 	/*
83 	 * We subscribe to the VMCI_EVENT_CTX_ID_UPDATE here so we can update
84 	 * the internal context id when needed.
85 	 */
86 	if (vmci_event_subscribe(VMCI_EVENT_CTX_ID_UPDATE,
87 	    vmci_util_cid_update, NULL, &ctx_update_sub_id) < VMCI_SUCCESS) {
88 		VMCI_LOG_WARNING(LGPFX"Failed to subscribe to event "
89 		    "(type=%d).\n", VMCI_EVENT_CTX_ID_UPDATE);
90 	}
91 }
92 
93 /*
94  *------------------------------------------------------------------------------
95  *
96  * vmci_util_exit --
97  *
98  *     Cleanup
99  *
100  * Results:
101  *     None.
102  *
103  * Side effects:
104  *     None.
105  *
106  *------------------------------------------------------------------------------
107  */
108 
109 void
110 vmci_util_exit(void)
111 {
112 
113 	if (vmci_event_unsubscribe(ctx_update_sub_id) < VMCI_SUCCESS)
114 		VMCI_LOG_WARNING(LGPFX"Failed to unsubscribe to event "
115 		    "(type=%d) with subscriber (ID=0x%x).\n",
116 		    VMCI_EVENT_CTX_ID_UPDATE, ctx_update_sub_id);
117 }
118 
119 /*
120  *------------------------------------------------------------------------------
121  *
122  * vmci_util_check_host_capabilities --
123  *
124  *     Verify that the host supports the hypercalls we need. If it does not, try
125  *     to find fallback hypercalls and use those instead.
126  *
127  * Results:
128  *     true if required hypercalls (or fallback hypercalls) are supported by the
129  *     host, false otherwise.
130  *
131  * Side effects:
132  *     None.
133  *
134  *------------------------------------------------------------------------------
135  */
136 
137 static bool
138 vmci_util_check_host_capabilities(void)
139 {
140 	struct vmci_resources_query_msg *msg;
141 	struct vmci_datagram *check_msg;
142 	int result;
143 	uint32_t msg_size;
144 
145 	msg_size = sizeof(struct vmci_resources_query_hdr) +
146 	    VMCI_UTIL_NUM_RESOURCES * sizeof(vmci_resource);
147 	check_msg = vmci_alloc_kernel_mem(msg_size, VMCI_MEMORY_NORMAL);
148 
149 	if (check_msg == NULL) {
150 		VMCI_LOG_WARNING(LGPFX"Check host: Insufficient memory.\n");
151 		return (false);
152 	}
153 
154 	check_msg->dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
155 	    VMCI_RESOURCES_QUERY);
156 	check_msg->src = VMCI_ANON_SRC_HANDLE;
157 	check_msg->payload_size = msg_size - VMCI_DG_HEADERSIZE;
158 	msg = (struct vmci_resources_query_msg *)VMCI_DG_PAYLOAD(check_msg);
159 
160 	msg->num_resources = VMCI_UTIL_NUM_RESOURCES;
161 	msg->resources[0] = VMCI_GET_CONTEXT_ID;
162 
163 	result = vmci_send_datagram(check_msg);
164 	vmci_free_kernel_mem(check_msg, msg_size);
165 
166 	/* We need the vector. There are no fallbacks. */
167 	return (result == 0x1);
168 }
169 
170 /*
171  *------------------------------------------------------------------------------
172  *
173  * vmci_check_host_capabilities --
174  *
175  *     Tell host which guestcalls we support and let each API check that the
176  *     host supports the hypercalls it needs. If a hypercall is not supported,
177  *     the API can check for a fallback hypercall, or fail the check.
178  *
179  * Results:
180  *     true if successful, false otherwise.
181  *
182  * Side effects:
183  *     Fallback mechanisms may be enabled in the API and vmmon.
184  *
185  *------------------------------------------------------------------------------
186  */
187 
188 bool
189 vmci_check_host_capabilities(void)
190 {
191 	bool result;
192 
193 	result = vmci_event_check_host_capabilities();
194 	result &= vmci_datagram_check_host_capabilities();
195 	result &= vmci_util_check_host_capabilities();
196 
197 	if (!result) {
198 		/*
199 		 * If it failed, then make sure this goes to the system event
200 		 * log.
201 		 */
202 		VMCI_LOG_WARNING(LGPFX"Host capability checked failed.\n");
203 	} else
204 		VMCI_LOG_DEBUG(LGPFX"Host capability check passed.\n");
205 
206 	return (result);
207 }
208 
209 /*
210  *------------------------------------------------------------------------------
211  *
212  * vmci_read_datagrams_from_port --
213  *
214  *     Reads datagrams from the data in port and dispatches them. We always
215  *     start reading datagrams into only the first page of the datagram buffer.
216  *     If the datagrams don't fit into one page, we use the maximum datagram
217  *     buffer size for the remainder of the  invocation. This is a simple
218  *     heuristic for not penalizing small datagrams.
219  *
220  *     This function assumes that it has exclusive access to the data in port
221  *     for the duration of the call.
222  *
223  * Results:
224  *     No result.
225  *
226  * Side effects:
227  *     Datagram handlers may be invoked.
228  *
229  *------------------------------------------------------------------------------
230  */
231 
232 void
233 vmci_read_datagrams_from_port(vmci_io_handle io_handle, vmci_io_port dg_in_port,
234     uint8_t *dg_in_buffer, size_t dg_in_buffer_size)
235 {
236 	struct vmci_datagram *dg;
237 	size_t current_dg_in_buffer_size;
238 	size_t remaining_bytes;
239 
240 	current_dg_in_buffer_size = PAGE_SIZE;
241 
242 	ASSERT(dg_in_buffer_size >= PAGE_SIZE);
243 
244 	vmci_read_port_bytes(io_handle, dg_in_port, dg_in_buffer,
245 	    current_dg_in_buffer_size);
246 	dg = (struct vmci_datagram *)dg_in_buffer;
247 	remaining_bytes = current_dg_in_buffer_size;
248 
249 	while (dg->dst.resource != VMCI_INVALID_ID ||
250 	    remaining_bytes > PAGE_SIZE) {
251 		size_t dg_in_size;
252 
253 		/*
254 		 * When the input buffer spans multiple pages, a datagram can
255 		 * start on any page boundary in the buffer.
256 		 */
257 
258 		if (dg->dst.resource == VMCI_INVALID_ID) {
259 			ASSERT(remaining_bytes > PAGE_SIZE);
260 			dg = (struct vmci_datagram *)ROUNDUP((uintptr_t)dg + 1,
261 			    PAGE_SIZE);
262 			ASSERT((uint8_t *)dg < dg_in_buffer +
263 			    current_dg_in_buffer_size);
264 			remaining_bytes = (size_t)(dg_in_buffer +
265 			    current_dg_in_buffer_size - (uint8_t *)dg);
266 			continue;
267 		}
268 
269 		dg_in_size = VMCI_DG_SIZE_ALIGNED(dg);
270 
271 		if (dg_in_size <= dg_in_buffer_size) {
272 			int result;
273 
274 			/*
275 			 * If the remaining bytes in the datagram buffer doesn't
276 			 * contain the complete datagram, we first make sure we
277 			 * have enough room for it and then we read the reminder
278 			 * of the datagram and possibly any following datagrams.
279 			 */
280 
281 			if (dg_in_size > remaining_bytes) {
282 				if (remaining_bytes !=
283 				    current_dg_in_buffer_size) {
284 					/*
285 					 * We move the partial datagram to the
286 					 * front and read the reminder of the
287 					 * datagram and possibly following calls
288 					 * into the following bytes.
289 					 */
290 
291 					memmove(dg_in_buffer, dg_in_buffer +
292 					    current_dg_in_buffer_size -
293 					    remaining_bytes,
294 					    remaining_bytes);
295 
296 					dg = (struct vmci_datagram *)
297 					    dg_in_buffer;
298 				}
299 
300 				if (current_dg_in_buffer_size !=
301 				    dg_in_buffer_size)
302 					current_dg_in_buffer_size =
303 					    dg_in_buffer_size;
304 
305 				vmci_read_port_bytes(io_handle, dg_in_port,
306 				    dg_in_buffer + remaining_bytes,
307 				    current_dg_in_buffer_size -
308 				    remaining_bytes);
309 			}
310 
311 			/*
312 			 * We special case event datagrams from the
313 			 * hypervisor.
314 			 */
315 			if (dg->src.context == VMCI_HYPERVISOR_CONTEXT_ID &&
316 			    dg->dst.resource == VMCI_EVENT_HANDLER)
317 				result = vmci_event_dispatch(dg);
318 			else
319 				result =
320 				    vmci_datagram_invoke_guest_handler(dg);
321 			if (result < VMCI_SUCCESS)
322 				VMCI_LOG_DEBUG(LGPFX"Datagram with resource"
323 				    " (ID=0x%x) failed (err=%d).\n",
324 				    dg->dst.resource, result);
325 
326 			/* On to the next datagram. */
327 			dg = (struct vmci_datagram *)((uint8_t *)dg +
328 			    dg_in_size);
329 		} else {
330 			size_t bytes_to_skip;
331 
332 			/*
333 			 * Datagram doesn't fit in datagram buffer of maximal
334 			 * size. We drop it.
335 			 */
336 
337 			VMCI_LOG_DEBUG(LGPFX"Failed to receive datagram "
338 			    "(size=%zu bytes).\n", dg_in_size);
339 
340 			bytes_to_skip = dg_in_size - remaining_bytes;
341 			if (current_dg_in_buffer_size != dg_in_buffer_size)
342 				current_dg_in_buffer_size = dg_in_buffer_size;
343 			for (;;) {
344 				vmci_read_port_bytes(io_handle, dg_in_port,
345 				    dg_in_buffer, current_dg_in_buffer_size);
346 				if (bytes_to_skip <=
347 				    current_dg_in_buffer_size)
348 					break;
349 				bytes_to_skip -= current_dg_in_buffer_size;
350 			}
351 			dg = (struct vmci_datagram *)(dg_in_buffer +
352 			    bytes_to_skip);
353 		}
354 
355 		remaining_bytes = (size_t) (dg_in_buffer +
356 		    current_dg_in_buffer_size - (uint8_t *)dg);
357 
358 		if (remaining_bytes < VMCI_DG_HEADERSIZE) {
359 			/* Get the next batch of datagrams. */
360 
361 			vmci_read_port_bytes(io_handle, dg_in_port,
362 			    dg_in_buffer, current_dg_in_buffer_size);
363 			dg = (struct vmci_datagram *)dg_in_buffer;
364 			remaining_bytes = current_dg_in_buffer_size;
365 		}
366 	}
367 }
368 
369 /*
370  *------------------------------------------------------------------------------
371  *
372  * vmci_get_context_id --
373  *
374  *     Returns the current context ID.  Note that since this is accessed only
375  *     from code running in the host, this always returns the host context ID.
376  *
377  * Results:
378  *     Context ID.
379  *
380  * Side effects:
381  *     None.
382  *
383  *------------------------------------------------------------------------------
384  */
385 
386 vmci_id
387 vmci_get_context_id(void)
388 {
389 	if (atomic_load_int(&vm_context_id) == VMCI_INVALID_ID) {
390 		uint32_t result;
391 		struct vmci_datagram get_cid_msg;
392 		get_cid_msg.dst =  VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
393 		    VMCI_GET_CONTEXT_ID);
394 		get_cid_msg.src = VMCI_ANON_SRC_HANDLE;
395 		get_cid_msg.payload_size = 0;
396 		result = vmci_send_datagram(&get_cid_msg);
397 		atomic_store_int(&vm_context_id, result);
398 	}
399 	return (atomic_load_int(&vm_context_id));
400 }
401 
402 /*
403  *------------------------------------------------------------------------------
404  *
405  * vmci_components_init --
406  *
407  *     Initializes VMCI components and registers core hypercalls.
408  *
409  * Results:
410  *     VMCI_SUCCESS if successful, appropriate error code otherwise.
411  *
412  * Side effects:
413  *     None.
414  *
415  *------------------------------------------------------------------------------
416  */
417 
418 int
419 vmci_components_init(void)
420 {
421 	int result;
422 
423 	result = vmci_resource_init();
424 	if (result < VMCI_SUCCESS) {
425 		VMCI_LOG_WARNING(LGPFX"Failed to initialize vmci_resource "
426 		    "(result=%d).\n", result);
427 		goto error_exit;
428 	}
429 
430 	result = vmci_event_init();
431 	if (result < VMCI_SUCCESS) {
432 		VMCI_LOG_WARNING(LGPFX"Failed to initialize vmci_event "
433 		    "(result=%d).\n", result);
434 		goto resource_exit;
435 	}
436 
437 	result = vmci_doorbell_init();
438 	if (result < VMCI_SUCCESS) {
439 		VMCI_LOG_WARNING(LGPFX"Failed to initialize vmci_doorbell "
440 		    "(result=%d).\n", result);
441 		goto event_exit;
442 	}
443 
444 	VMCI_LOG_DEBUG(LGPFX"components initialized.\n");
445 	return (VMCI_SUCCESS);
446 
447 event_exit:
448 	vmci_event_exit();
449 
450 resource_exit:
451 	vmci_resource_exit();
452 
453 error_exit:
454 	return (result);
455 }
456 
457 /*
458  *------------------------------------------------------------------------------
459  *
460  * vmci_components_cleanup --
461  *
462  *     Cleans up VMCI components.
463  *
464  * Results:
465  *     None.
466  *
467  * Side effects:
468  *     None.
469  *
470  *------------------------------------------------------------------------------
471  */
472 
473 void
474 vmci_components_cleanup(void)
475 {
476 
477 	vmci_doorbell_exit();
478 	vmci_event_exit();
479 	vmci_resource_exit();
480 }
481