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 }
Cache object: 479d7008de9e2c03a286c9282bf95934
|