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