Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mei: add connect with vtag ioctl

This IOCTL is used to associate the current file descriptor
with a FW Client (given by UUID), and virtual tag (vtag).
The IOCTL opens a communication channel between a host client
and a FW client on a tagged channel. From this point on,
every reader and write will communicate with the associated
FW client on the tagged channel. Upon close() the communication
is terminated.

The IOCTL argument is a struct with a union that contains
the input parameter and the output parameter for this IOCTL.

The input parameter is UUID of the FW Client, a vtag [0,255]
The output parameter is the properties of the FW client

Clients that do not support tagged connection
will respond with -EOPNOTSUPP

Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Link: https://lore.kernel.org/r/20200818115147.2567012-12-tomas.winkler@intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Alexander Usyskin and committed by
Greg Kroah-Hartman
aa207a05 224ae607

+243 -16
+194 -16
drivers/misc/mei/main.c
··· 395 395 * mei_ioctl_connect_client - the connect to fw client IOCTL function 396 396 * 397 397 * @file: private data of the file object 398 - * @data: IOCTL connect data, input and output parameters 398 + * @in_client_uuid: requested UUID for connection 399 + * @client: IOCTL connect data, output parameters 399 400 * 400 401 * Locking: called under "dev->device_lock" lock 401 402 * 402 403 * Return: 0 on success, <0 on failure. 403 404 */ 404 405 static int mei_ioctl_connect_client(struct file *file, 405 - struct mei_connect_client_data *data) 406 + const uuid_le *in_client_uuid, 407 + struct mei_client *client) 406 408 { 407 409 struct mei_device *dev; 408 - struct mei_client *client; 409 410 struct mei_me_client *me_cl; 410 411 struct mei_cl *cl; 411 412 int rets; ··· 414 413 cl = file->private_data; 415 414 dev = cl->dev; 416 415 417 - if (dev->dev_state != MEI_DEV_ENABLED) 418 - return -ENODEV; 419 - 420 416 if (cl->state != MEI_FILE_INITIALIZING && 421 417 cl->state != MEI_FILE_DISCONNECTED) 422 418 return -EBUSY; 423 419 424 420 /* find ME client we're trying to connect to */ 425 - me_cl = mei_me_cl_by_uuid(dev, &data->in_client_uuid); 421 + me_cl = mei_me_cl_by_uuid(dev, in_client_uuid); 426 422 if (!me_cl) { 427 423 dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n", 428 - &data->in_client_uuid); 424 + in_client_uuid); 429 425 rets = -ENOTTY; 430 426 goto end; 431 427 } ··· 432 434 !dev->allow_fixed_address : !dev->hbm_f_fa_supported; 433 435 if (forbidden) { 434 436 dev_dbg(dev->dev, "Connection forbidden to FW Client UUID = %pUl\n", 435 - &data->in_client_uuid); 437 + in_client_uuid); 436 438 rets = -ENOTTY; 437 439 goto end; 438 440 } ··· 446 448 me_cl->props.max_msg_length); 447 449 448 450 /* prepare the output buffer */ 449 - client = &data->out_client_properties; 450 451 client->max_msg_length = me_cl->props.max_msg_length; 451 452 client->protocol_version = me_cl->props.protocol_version; 452 453 dev_dbg(dev->dev, "Can connect?\n"); ··· 455 458 end: 456 459 mei_me_cl_put(me_cl); 457 460 return rets; 461 + } 462 + 463 + /** 464 + * mei_vt_support_check - check if client support vtags 465 + * 466 + * Locking: called under "dev->device_lock" lock 467 + * 468 + * @dev: mei_device 469 + * @uuid: client UUID 470 + * 471 + * Return: 472 + * 0 - supported 473 + * -ENOTTY - no such client 474 + * -EOPNOTSUPP - vtags are not supported by client 475 + */ 476 + static int mei_vt_support_check(struct mei_device *dev, const uuid_le *uuid) 477 + { 478 + struct mei_me_client *me_cl; 479 + int ret; 480 + 481 + if (!dev->hbm_f_vt_supported) 482 + return -EOPNOTSUPP; 483 + 484 + me_cl = mei_me_cl_by_uuid(dev, uuid); 485 + if (!me_cl) { 486 + dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n", 487 + uuid); 488 + return -ENOTTY; 489 + } 490 + ret = me_cl->props.vt_supported ? 0 : -EOPNOTSUPP; 491 + mei_me_cl_put(me_cl); 492 + 493 + return ret; 494 + } 495 + 496 + /** 497 + * mei_ioctl_connect_vtag - connect to fw client with vtag IOCTL function 498 + * 499 + * @file: private data of the file object 500 + * @in_client_uuid: requested UUID for connection 501 + * @client: IOCTL connect data, output parameters 502 + * @vtag: vm tag 503 + * 504 + * Locking: called under "dev->device_lock" lock 505 + * 506 + * Return: 0 on success, <0 on failure. 507 + */ 508 + static int mei_ioctl_connect_vtag(struct file *file, 509 + const uuid_le *in_client_uuid, 510 + struct mei_client *client, 511 + u8 vtag) 512 + { 513 + struct mei_device *dev; 514 + struct mei_cl *cl; 515 + struct mei_cl *pos; 516 + struct mei_cl_vtag *cl_vtag; 517 + 518 + cl = file->private_data; 519 + dev = cl->dev; 520 + 521 + dev_dbg(dev->dev, "FW Client %pUl vtag %d\n", in_client_uuid, vtag); 522 + 523 + switch (cl->state) { 524 + case MEI_FILE_DISCONNECTED: 525 + if (mei_cl_vtag_by_fp(cl, file) != vtag) { 526 + dev_err(dev->dev, "reconnect with different vtag\n"); 527 + return -EINVAL; 528 + } 529 + break; 530 + case MEI_FILE_INITIALIZING: 531 + /* malicious connect from another thread may push vtag */ 532 + if (!IS_ERR(mei_cl_fp_by_vtag(cl, vtag))) { 533 + dev_err(dev->dev, "vtag already filled\n"); 534 + return -EINVAL; 535 + } 536 + 537 + list_for_each_entry(pos, &dev->file_list, link) { 538 + if (pos == cl) 539 + continue; 540 + if (!pos->me_cl) 541 + continue; 542 + 543 + /* only search for same UUID */ 544 + if (uuid_le_cmp(*mei_cl_uuid(pos), *in_client_uuid)) 545 + continue; 546 + 547 + /* if tag already exist try another fp */ 548 + if (!IS_ERR(mei_cl_fp_by_vtag(pos, vtag))) 549 + continue; 550 + 551 + /* replace cl with acquired one */ 552 + dev_dbg(dev->dev, "replacing with existing cl\n"); 553 + mei_cl_unlink(cl); 554 + kfree(cl); 555 + file->private_data = pos; 556 + cl = pos; 557 + break; 558 + } 559 + 560 + cl_vtag = mei_cl_vtag_alloc(file, vtag); 561 + if (IS_ERR(cl_vtag)) 562 + return -ENOMEM; 563 + 564 + list_add_tail(&cl_vtag->list, &cl->vtag_map); 565 + break; 566 + default: 567 + return -EBUSY; 568 + } 569 + 570 + while (cl->state != MEI_FILE_INITIALIZING && 571 + cl->state != MEI_FILE_DISCONNECTED && 572 + cl->state != MEI_FILE_CONNECTED) { 573 + mutex_unlock(&dev->device_lock); 574 + wait_event_timeout(cl->wait, 575 + (cl->state == MEI_FILE_CONNECTED || 576 + cl->state == MEI_FILE_DISCONNECTED || 577 + cl->state == MEI_FILE_DISCONNECT_REQUIRED || 578 + cl->state == MEI_FILE_DISCONNECT_REPLY), 579 + mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 580 + mutex_lock(&dev->device_lock); 581 + } 582 + 583 + if (!mei_cl_is_connected(cl)) 584 + return mei_ioctl_connect_client(file, in_client_uuid, client); 585 + 586 + client->max_msg_length = cl->me_cl->props.max_msg_length; 587 + client->protocol_version = cl->me_cl->props.protocol_version; 588 + 589 + return 0; 458 590 } 459 591 460 592 /** ··· 642 516 { 643 517 struct mei_device *dev; 644 518 struct mei_cl *cl = file->private_data; 645 - struct mei_connect_client_data connect_data; 519 + struct mei_connect_client_data conn; 520 + struct mei_connect_client_data_vtag conn_vtag; 521 + const uuid_le *cl_uuid; 522 + struct mei_client *props; 523 + u8 vtag; 646 524 u32 notify_get, notify_req; 647 525 int rets; 648 526 ··· 667 537 switch (cmd) { 668 538 case IOCTL_MEI_CONNECT_CLIENT: 669 539 dev_dbg(dev->dev, ": IOCTL_MEI_CONNECT_CLIENT.\n"); 670 - if (copy_from_user(&connect_data, (char __user *)data, 671 - sizeof(connect_data))) { 540 + if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) { 541 + dev_dbg(dev->dev, "failed to copy data from userland\n"); 542 + rets = -EFAULT; 543 + goto out; 544 + } 545 + cl_uuid = &conn.in_client_uuid; 546 + props = &conn.out_client_properties; 547 + vtag = 0; 548 + 549 + rets = mei_vt_support_check(dev, cl_uuid); 550 + if (rets == -ENOTTY) 551 + goto out; 552 + if (!rets) 553 + rets = mei_ioctl_connect_vtag(file, cl_uuid, props, 554 + vtag); 555 + else 556 + rets = mei_ioctl_connect_client(file, cl_uuid, props); 557 + if (rets) 558 + goto out; 559 + 560 + /* if all is ok, copying the data back to user. */ 561 + if (copy_to_user((char __user *)data, &conn, sizeof(conn))) { 562 + dev_dbg(dev->dev, "failed to copy data to userland\n"); 563 + rets = -EFAULT; 564 + goto out; 565 + } 566 + 567 + break; 568 + 569 + case IOCTL_MEI_CONNECT_CLIENT_VTAG: 570 + dev_dbg(dev->dev, "IOCTL_MEI_CONNECT_CLIENT_VTAG\n"); 571 + if (copy_from_user(&conn_vtag, (char __user *)data, 572 + sizeof(conn_vtag))) { 672 573 dev_dbg(dev->dev, "failed to copy data from userland\n"); 673 574 rets = -EFAULT; 674 575 goto out; 675 576 } 676 577 677 - rets = mei_ioctl_connect_client(file, &connect_data); 578 + cl_uuid = &conn_vtag.connect.in_client_uuid; 579 + props = &conn_vtag.out_client_properties; 580 + vtag = conn_vtag.connect.vtag; 581 + 582 + rets = mei_vt_support_check(dev, cl_uuid); 583 + if (rets == -EOPNOTSUPP) 584 + dev_dbg(dev->dev, "FW Client %pUl does not support vtags\n", 585 + cl_uuid); 586 + if (rets) 587 + goto out; 588 + 589 + if (!vtag) { 590 + dev_dbg(dev->dev, "vtag can't be zero\n"); 591 + rets = -EINVAL; 592 + goto out; 593 + } 594 + 595 + rets = mei_ioctl_connect_vtag(file, cl_uuid, props, vtag); 678 596 if (rets) 679 597 goto out; 680 598 681 599 /* if all is ok, copying the data back to user. */ 682 - if (copy_to_user((char __user *)data, &connect_data, 683 - sizeof(connect_data))) { 600 + if (copy_to_user((char __user *)data, &conn_vtag, 601 + sizeof(conn_vtag))) { 684 602 dev_dbg(dev->dev, "failed to copy data to userland\n"); 685 603 rets = -EFAULT; 686 604 goto out;
+49
include/uapi/linux/mei.h
··· 66 66 */ 67 67 #define IOCTL_MEI_NOTIFY_GET _IOR('H', 0x03, __u32) 68 68 69 + /** 70 + * struct mei_connect_client_vtag - mei client information struct with vtag 71 + * 72 + * @in_client_uuid: UUID of client to connect 73 + * @vtag: virtual tag 74 + * @reserved: reserved for future use 75 + */ 76 + struct mei_connect_client_vtag { 77 + uuid_le in_client_uuid; 78 + __u8 vtag; 79 + __u8 reserved[3]; 80 + }; 81 + 82 + /** 83 + * struct mei_connect_client_data_vtag - IOCTL connect data union 84 + * 85 + * @connect: input connect data 86 + * @out_client_properties: output client data 87 + */ 88 + struct mei_connect_client_data_vtag { 89 + union { 90 + struct mei_connect_client_vtag connect; 91 + struct mei_client out_client_properties; 92 + }; 93 + }; 94 + 95 + /** 96 + * DOC: 97 + * This IOCTL is used to associate the current file descriptor with a 98 + * FW Client (given by UUID), and virtual tag (vtag). 99 + * The IOCTL opens a communication channel between a host client and 100 + * a FW client on a tagged channel. From this point on, every read 101 + * and write will communicate with the associated FW client with 102 + * on the tagged channel. 103 + * Upone close() the communication is terminated. 104 + * 105 + * The IOCTL argument is a struct with a union that contains 106 + * the input parameter and the output parameter for this IOCTL. 107 + * 108 + * The input parameter is UUID of the FW Client, a vtag [0,255] 109 + * The output parameter is the properties of the FW client 110 + * (FW protocool version and max message size). 111 + * 112 + * Clients that do not support tagged connection 113 + * will respond with -EOPNOTSUPP. 114 + */ 115 + #define IOCTL_MEI_CONNECT_CLIENT_VTAG \ 116 + _IOWR('H', 0x04, struct mei_connect_client_data_vtag) 117 + 69 118 #endif /* _LINUX_MEI_H */