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 */
27struct 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
37struct vmci_delayed_datagram_info {
38	struct datagram_entry	*entry;
39	struct vmci_datagram	msg;
40};
41
42static int	vmci_datagram_get_priv_flags_int(vmci_id contextID,
43		    struct vmci_handle handle,
44		    vmci_privilege_flags *priv_flags);
45static void	datagram_free_cb(void *resource);
46static 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
67static void
68datagram_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
99static int
100datagram_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
129static int
130datagram_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
219int
220vmci_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
255int
256vmci_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
293int
294vmci_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
343static int
344vmci_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
390int
391vmci_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
418static void
419vmci_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
452static int
453vmci_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
485int
486vmci_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
519int
520vmci_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
574exit:
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
594int
595vmci_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
621void
622vmci_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
645bool
646vmci_datagram_check_host_capabilities(void)
647{
648
649	return (true);
650}
651