xref: /freebsd/sys/dev/vmware/vmci/vmci_datagram.c (revision 148a8da8)
1 /*-
2  * Copyright (c) 2018 VMware, Inc.
3  *
4  * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
5  */
6 
7 /* This file implements the VMCI Simple Datagram API on the host. */
8 
9 #include <sys/cdefs.h>
10 __FBSDID("$FreeBSD$");
11 
12 #include <sys/types.h>
13 #include <sys/systm.h>
14 
15 #include "vmci_datagram.h"
16 #include "vmci_driver.h"
17 #include "vmci_kernel_api.h"
18 #include "vmci_kernel_defs.h"
19 #include "vmci_resource.h"
20 
21 #define LGPFX "vmci_datagram: "
22 
23 /*
24  * datagram_entry describes the datagram entity. It is used for datagram
25  * entities created only on the host.
26  */
27 struct datagram_entry {
28 	struct vmci_resource	resource;
29 	uint32_t		flags;
30 	bool			run_delayed;
31 	vmci_datagram_recv_cb	recv_cb;
32 	void			*client_data;
33 	vmci_event		destroy_event;
34 	vmci_privilege_flags	priv_flags;
35 };
36 
37 struct vmci_delayed_datagram_info {
38 	struct datagram_entry	*entry;
39 	struct vmci_datagram	msg;
40 };
41 
42 static int	vmci_datagram_get_priv_flags_int(vmci_id contextID,
43 		    struct vmci_handle handle,
44 		    vmci_privilege_flags *priv_flags);
45 static void	datagram_free_cb(void *resource);
46 static int	datagram_release_cb(void *client_data);
47 
48 /*------------------------------ Helper functions ----------------------------*/
49 
50 /*
51  *------------------------------------------------------------------------------
52  *
53  * datagram_free_cb --
54  *
55  *     Callback to free datagram structure when resource is no longer used,
56  *     ie. the reference count reached 0.
57  *
58  * Result:
59  *     None.
60  *
61  * Side effects:
62  *     None.
63  *
64  *------------------------------------------------------------------------------
65  */
66 
67 static void
68 datagram_free_cb(void *client_data)
69 {
70 	struct datagram_entry *entry = (struct datagram_entry *)client_data;
71 
72 	ASSERT(entry);
73 
74 	vmci_signal_event(&entry->destroy_event);
75 
76 	/*
77 	 * The entry is freed in vmci_datagram_destroy_hnd, who is waiting for
78 	 * the above signal.
79 	 */
80 }
81 
82 /*
83  *------------------------------------------------------------------------------
84  *
85  * datagram_release_cb --
86  *
87  *     Callback to release the resource reference. It is called by the
88  *     vmci_wait_on_event function before it blocks.
89  *
90  * Result:
91  *     None.
92  *
93  * Side effects:
94  *     None.
95  *
96  *------------------------------------------------------------------------------
97  */
98 
99 static int
100 datagram_release_cb(void *client_data)
101 {
102 	struct datagram_entry *entry;
103 
104 	entry = (struct datagram_entry *)client_data;
105 
106 	ASSERT(entry);
107 
108 	vmci_resource_release(&entry->resource);
109 
110 	return (0);
111 }
112 
113 /*
114  *------------------------------------------------------------------------------
115  *
116  * datagram_create_hnd --
117  *
118  *     Internal function to create a datagram entry given a handle.
119  *
120  * Results:
121  *     VMCI_SUCCESS if created, negative errno value otherwise.
122  *
123  * Side effects:
124  *     None.
125  *
126  *------------------------------------------------------------------------------
127  */
128 
129 static int
130 datagram_create_hnd(vmci_id resource_id, uint32_t flags,
131     vmci_privilege_flags priv_flags, vmci_datagram_recv_cb recv_cb,
132     void *client_data, struct vmci_handle *out_handle)
133 {
134 	struct datagram_entry *entry;
135 	struct vmci_handle handle;
136 	vmci_id context_id;
137 	int result;
138 
139 	ASSERT(recv_cb != NULL);
140 	ASSERT(out_handle != NULL);
141 	ASSERT(!(priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS));
142 
143 	if ((flags & VMCI_FLAG_WELLKNOWN_DG_HND) != 0)
144 		return (VMCI_ERROR_INVALID_ARGS);
145 	else {
146 		if ((flags & VMCI_FLAG_ANYCID_DG_HND) != 0)
147 			context_id = VMCI_INVALID_ID;
148 		else {
149 			context_id = vmci_get_context_id();
150 			if (context_id == VMCI_INVALID_ID)
151 				return (VMCI_ERROR_NO_RESOURCES);
152 		}
153 
154 		if (resource_id == VMCI_INVALID_ID) {
155 			resource_id = vmci_resource_get_id(context_id);
156 			if (resource_id == VMCI_INVALID_ID)
157 				return (VMCI_ERROR_NO_HANDLE);
158 		}
159 
160 		handle = VMCI_MAKE_HANDLE(context_id, resource_id);
161 	}
162 
163 	entry = vmci_alloc_kernel_mem(sizeof(*entry), VMCI_MEMORY_NORMAL);
164 	if (entry == NULL) {
165 		VMCI_LOG_WARNING(LGPFX"Failed allocating memory for datagram "
166 		    "entry.\n");
167 		return (VMCI_ERROR_NO_MEM);
168 	}
169 
170 	if (!vmci_can_schedule_delayed_work()) {
171 		if (flags & VMCI_FLAG_DG_DELAYED_CB) {
172 			vmci_free_kernel_mem(entry, sizeof(*entry));
173 			return (VMCI_ERROR_INVALID_ARGS);
174 		}
175 		entry->run_delayed = false;
176 	} else
177 		entry->run_delayed = (flags & VMCI_FLAG_DG_DELAYED_CB) ?
178 		    true : false;
179 
180 	entry->flags = flags;
181 	entry->recv_cb = recv_cb;
182 	entry->client_data = client_data;
183 	vmci_create_event(&entry->destroy_event);
184 	entry->priv_flags = priv_flags;
185 
186 	/* Make datagram resource live. */
187 	result = vmci_resource_add(&entry->resource,
188 	    VMCI_RESOURCE_TYPE_DATAGRAM, handle, datagram_free_cb, entry);
189 	if (result != VMCI_SUCCESS) {
190 		VMCI_LOG_WARNING(LGPFX"Failed to add new resource "
191 		    "(handle=0x%x:0x%x).\n", handle.context, handle.resource);
192 		vmci_destroy_event(&entry->destroy_event);
193 		vmci_free_kernel_mem(entry, sizeof(*entry));
194 		return (result);
195 	}
196 	*out_handle = handle;
197 
198 	return (VMCI_SUCCESS);
199 }
200 
201 /*------------------------------ Public API functions ------------------------*/
202 
203 /*
204  *------------------------------------------------------------------------------
205  *
206  * vmci_datagram_create_handle --
207  *
208  *     Creates a host context datagram endpoint and returns a handle to it.
209  *
210  * Results:
211  *     VMCI_SUCCESS if created, negative errno value otherwise.
212  *
213  * Side effects:
214  *     None.
215  *
216  *------------------------------------------------------------------------------
217  */
218 
219 int
220 vmci_datagram_create_handle(vmci_id resource_id, uint32_t flags,
221     vmci_datagram_recv_cb recv_cb, void *client_data,
222     struct vmci_handle *out_handle)
223 {
224 
225 	if (out_handle == NULL)
226 		return (VMCI_ERROR_INVALID_ARGS);
227 
228 	if (recv_cb == NULL) {
229 		VMCI_LOG_DEBUG(LGPFX"Client callback needed when creating "
230 		    "datagram.\n");
231 		return (VMCI_ERROR_INVALID_ARGS);
232 	}
233 
234 	return (datagram_create_hnd(resource_id, flags,
235 	    VMCI_DEFAULT_PROC_PRIVILEGE_FLAGS,
236 	    recv_cb, client_data, out_handle));
237 }
238 
239 /*
240  *------------------------------------------------------------------------------
241  *
242  * vmci_datagram_create_handle_priv --
243  *
244  *     Creates a host context datagram endpoint and returns a handle to it.
245  *
246  * Results:
247  *     VMCI_SUCCESS if created, negative errno value otherwise.
248  *
249  * Side effects:
250  *     None.
251  *
252  *------------------------------------------------------------------------------
253  */
254 
255 int
256 vmci_datagram_create_handle_priv(vmci_id resource_id, uint32_t flags,
257     vmci_privilege_flags priv_flags, vmci_datagram_recv_cb recv_cb,
258     void *client_data, struct vmci_handle *out_handle)
259 {
260 
261 	if (out_handle == NULL)
262 		return (VMCI_ERROR_INVALID_ARGS);
263 
264 	if (recv_cb == NULL) {
265 		VMCI_LOG_DEBUG(LGPFX"Client callback needed when creating "
266 		    "datagram.\n");
267 		return (VMCI_ERROR_INVALID_ARGS);
268 	}
269 
270 	if (priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS)
271 		return (VMCI_ERROR_INVALID_ARGS);
272 
273 	return (datagram_create_hnd(resource_id, flags, priv_flags, recv_cb,
274 	    client_data, out_handle));
275 }
276 
277 /*
278  *------------------------------------------------------------------------------
279  *
280  * vmci_datagram_destroy_handle --
281  *
282  *     Destroys a handle.
283  *
284  * Results:
285  *     None.
286  *
287  * Side effects:
288  *     None.
289  *
290  *------------------------------------------------------------------------------
291  */
292 
293 int
294 vmci_datagram_destroy_handle(struct vmci_handle handle)
295 {
296 	struct datagram_entry *entry;
297 	struct vmci_resource *resource;
298 
299 	resource = vmci_resource_get(handle,
300 	    VMCI_RESOURCE_TYPE_DATAGRAM);
301 	if (resource == NULL) {
302 		VMCI_LOG_DEBUG(LGPFX"Failed to destroy datagram "
303 		    "(handle=0x%x:0x%x).\n", handle.context, handle.resource);
304 		return (VMCI_ERROR_NOT_FOUND);
305 	}
306 	entry = RESOURCE_CONTAINER(resource, struct datagram_entry, resource);
307 
308 	vmci_resource_remove(handle, VMCI_RESOURCE_TYPE_DATAGRAM);
309 
310 	/*
311 	 * We now wait on the destroyEvent and release the reference we got
312 	 * above.
313 	 */
314 	vmci_wait_on_event(&entry->destroy_event, datagram_release_cb, entry);
315 
316 	/*
317 	 * We know that we are now the only reference to the above entry so
318 	 * can safely free it.
319 	 */
320 	vmci_destroy_event(&entry->destroy_event);
321 	vmci_free_kernel_mem(entry, sizeof(*entry));
322 
323 	return (VMCI_SUCCESS);
324 }
325 
326 /*
327  *------------------------------------------------------------------------------
328  *
329  *  vmci_datagram_get_priv_flags_int --
330  *
331  *      Internal utilility function with the same purpose as
332  *      vmci_datagram_get_priv_flags that also takes a context_id.
333  *
334  *  Result:
335  *      VMCI_SUCCESS on success, VMCI_ERROR_INVALID_ARGS if handle is invalid.
336  *
337  *  Side effects:
338  *      None.
339  *
340  *------------------------------------------------------------------------------
341  */
342 
343 static int
344 vmci_datagram_get_priv_flags_int(vmci_id context_id, struct vmci_handle handle,
345     vmci_privilege_flags *priv_flags)
346 {
347 
348 	ASSERT(priv_flags);
349 	ASSERT(context_id != VMCI_INVALID_ID);
350 
351 	if (context_id == VMCI_HOST_CONTEXT_ID) {
352 		struct datagram_entry *src_entry;
353 		struct vmci_resource *resource;
354 
355 		resource = vmci_resource_get(handle,
356 		    VMCI_RESOURCE_TYPE_DATAGRAM);
357 		if (resource == NULL)
358 			return (VMCI_ERROR_INVALID_ARGS);
359 		src_entry = RESOURCE_CONTAINER(resource, struct datagram_entry,
360 		    resource);
361 		*priv_flags = src_entry->priv_flags;
362 		vmci_resource_release(resource);
363 	} else if (context_id == VMCI_HYPERVISOR_CONTEXT_ID)
364 		*priv_flags = VMCI_MAX_PRIVILEGE_FLAGS;
365 	else
366 		*priv_flags = VMCI_NO_PRIVILEGE_FLAGS;
367 
368 	return (VMCI_SUCCESS);
369 }
370 
371 /*
372  *------------------------------------------------------------------------------
373  *
374  *  vmci_datagram_fet_priv_flags --
375  *
376  *      Utility function that retrieves the privilege flags associated with a
377  *      given datagram handle. For hypervisor and guest endpoints, the
378  *      privileges are determined by the context ID, but for host endpoints
379  *      privileges are associated with the complete handle.
380  *
381  *  Result:
382  *      VMCI_SUCCESS on success, VMCI_ERROR_INVALID_ARGS if handle is invalid.
383  *
384  *  Side effects:
385  *      None.
386  *
387  *------------------------------------------------------------------------------
388  */
389 
390 int
391 vmci_datagram_get_priv_flags(struct vmci_handle handle,
392     vmci_privilege_flags *priv_flags)
393 {
394 
395 	if (priv_flags == NULL || handle.context == VMCI_INVALID_ID)
396 		return (VMCI_ERROR_INVALID_ARGS);
397 
398 	return (vmci_datagram_get_priv_flags_int(handle.context, handle,
399 	    priv_flags));
400 }
401 
402 /*
403  *------------------------------------------------------------------------------
404  *
405  * vmci_datagram_delayed_dispatch_cb --
406  *
407  *     Calls the specified callback in a delayed context.
408  *
409  * Results:
410  *     None.
411  *
412  * Side effects:
413  *     None.
414  *
415  *------------------------------------------------------------------------------
416  */
417 
418 static void
419 vmci_datagram_delayed_dispatch_cb(void *data)
420 {
421 	struct vmci_delayed_datagram_info *dg_info;
422 
423 	dg_info = (struct vmci_delayed_datagram_info *)data;
424 
425 	ASSERT(data);
426 
427 	dg_info->entry->recv_cb(dg_info->entry->client_data, &dg_info->msg);
428 
429 	vmci_resource_release(&dg_info->entry->resource);
430 
431 	vmci_free_kernel_mem(dg_info, sizeof(*dg_info) +
432 	    (size_t)dg_info->msg.payload_size);
433 }
434 
435 /*
436  *------------------------------------------------------------------------------
437  *
438  * vmci_datagram_dispatch_as_guest --
439  *
440  *     Dispatch datagram as a guest, down through the VMX and potentially to
441  *     the host.
442  *
443  * Result:
444  *     Number of bytes sent on success, appropriate error code otherwise.
445  *
446  * Side effects:
447  *     None.
448  *
449  *------------------------------------------------------------------------------
450  */
451 
452 static int
453 vmci_datagram_dispatch_as_guest(struct vmci_datagram *dg)
454 {
455 	struct vmci_resource *resource;
456 	int retval;
457 
458 	resource = vmci_resource_get(dg->src, VMCI_RESOURCE_TYPE_DATAGRAM);
459 	if (NULL == resource)
460 		return VMCI_ERROR_NO_HANDLE;
461 
462 	retval = vmci_send_datagram(dg);
463 	vmci_resource_release(resource);
464 
465 	return (retval);
466 }
467 
468 /*
469  *------------------------------------------------------------------------------
470  *
471  * vmci_datagram_dispatch --
472  *
473  *     Dispatch datagram. This will determine the routing for the datagram and
474  *     dispatch it accordingly.
475  *
476  * Result:
477  *     Number of bytes sent on success, appropriate error code otherwise.
478  *
479  * Side effects:
480  *     None.
481  *
482  *------------------------------------------------------------------------------
483  */
484 
485 int
486 vmci_datagram_dispatch(vmci_id context_id, struct vmci_datagram *dg)
487 {
488 
489 	ASSERT(dg);
490 	ASSERT_ON_COMPILE(sizeof(struct vmci_datagram) == 24);
491 
492 	if (VMCI_DG_SIZE(dg) > VMCI_MAX_DG_SIZE) {
493 		VMCI_LOG_DEBUG(LGPFX"Payload (size=%lu bytes) too big to send."
494 		    "\n", dg->payload_size);
495 		return (VMCI_ERROR_INVALID_ARGS);
496 	}
497 
498 	return (vmci_datagram_dispatch_as_guest(dg));
499 }
500 
501 /*
502  *------------------------------------------------------------------------------
503  *
504  * vmci_datagram_invoke_guest_handler --
505  *
506  *     Invoke the handler for the given datagram. This is intended to be called
507  *     only when acting as a guest and receiving a datagram from the virtual
508  *     device.
509  *
510  * Result:
511  *     VMCI_SUCCESS on success, other error values on failure.
512  *
513  * Side effects:
514  *     None.
515  *
516  *------------------------------------------------------------------------------
517  */
518 
519 int
520 vmci_datagram_invoke_guest_handler(struct vmci_datagram *dg)
521 {
522 	struct datagram_entry *dst_entry;
523 	struct vmci_resource *resource;
524 	int retval;
525 
526 	ASSERT(dg);
527 
528 	if (dg->payload_size > VMCI_MAX_DG_PAYLOAD_SIZE) {
529 		VMCI_LOG_DEBUG(LGPFX"Payload (size=%lu bytes) too large to "
530 		    "deliver.\n", dg->payload_size);
531 		return (VMCI_ERROR_PAYLOAD_TOO_LARGE);
532 	}
533 
534 	resource = vmci_resource_get(dg->dst, VMCI_RESOURCE_TYPE_DATAGRAM);
535 	if (NULL == resource) {
536 		VMCI_LOG_DEBUG(LGPFX"destination (handle=0x%x:0x%x) doesn't "
537 		    "exist.\n", dg->dst.context, dg->dst.resource);
538 		return (VMCI_ERROR_NO_HANDLE);
539 	}
540 
541 	dst_entry = RESOURCE_CONTAINER(resource, struct datagram_entry,
542 	    resource);
543 	if (dst_entry->run_delayed) {
544 		struct vmci_delayed_datagram_info *dg_info;
545 
546 		dg_info = vmci_alloc_kernel_mem(sizeof(*dg_info) +
547 		    (size_t)dg->payload_size, VMCI_MEMORY_ATOMIC);
548 		if (NULL == dg_info) {
549 			vmci_resource_release(resource);
550 			retval = VMCI_ERROR_NO_MEM;
551 			goto exit;
552 		}
553 
554 		dg_info->entry = dst_entry;
555 		memcpy(&dg_info->msg, dg, VMCI_DG_SIZE(dg));
556 
557 		retval = vmci_schedule_delayed_work(
558 		    vmci_datagram_delayed_dispatch_cb, dg_info);
559 		if (retval < VMCI_SUCCESS) {
560 			VMCI_LOG_WARNING(LGPFX"Failed to schedule delayed "
561 			    "work for datagram (result=%d).\n", retval);
562 			vmci_free_kernel_mem(dg_info, sizeof(*dg_info) +
563 			    (size_t)dg->payload_size);
564 			vmci_resource_release(resource);
565 			dg_info = NULL;
566 			goto exit;
567 		}
568 	} else {
569 		dst_entry->recv_cb(dst_entry->client_data, dg);
570 		vmci_resource_release(resource);
571 		retval = VMCI_SUCCESS;
572 	}
573 
574 exit:
575 	return (retval);
576 }
577 
578 /*
579  *------------------------------------------------------------------------------
580  *
581  * vmci_datagram_send --
582  *
583  *     Sends the payload to the destination datagram handle.
584  *
585  * Results:
586  *     Returns number of bytes sent if success, or error code if failure.
587  *
588  * Side effects:
589  *     None.
590  *
591  *------------------------------------------------------------------------------
592  */
593 
594 int
595 vmci_datagram_send(struct vmci_datagram *msg)
596 {
597 
598 	if (msg == NULL)
599 		return (VMCI_ERROR_INVALID_ARGS);
600 
601 	return (vmci_datagram_dispatch(VMCI_INVALID_ID, msg));
602 }
603 
604 /*
605  *------------------------------------------------------------------------------
606  *
607  * vmci_datagram_sync --
608  *
609  *     Use this as a synchronization point when setting globals, for example,
610  *     during device shutdown.
611  *
612  * Results:
613  *     None.
614  *
615  * Side effects:
616  *     None.
617  *
618  *------------------------------------------------------------------------------
619  */
620 
621 void
622 vmci_datagram_sync(void)
623 {
624 
625 	vmci_resource_sync();
626 }
627 
628 /*
629  *------------------------------------------------------------------------------
630  *
631  * vmci_datagram_check_host_capabilities --
632  *
633  *     Verify that the host supports the resources we need. None are required
634  *     for datagrams since they are implicitly supported.
635  *
636  * Results:
637  *     true.
638  *
639  * Side effects:
640  *     None.
641  *
642  *------------------------------------------------------------------------------
643  */
644 
645 bool
646 vmci_datagram_check_host_capabilities(void)
647 {
648 
649 	return (true);
650 }
651