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