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