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