]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/vmware/vmci/vmci_datagram.c
MFHead @348740
[FreeBSD/FreeBSD.git] / sys / dev / vmware / vmci / vmci_datagram.c
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 }