LCOV - code coverage report
Current view: top level - lib/vfio_user/host - vfio_user.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 0 185 0.0 %
Date: 2024-07-15 15:03:05 Functions: 0 11 0.0 %

          Line data    Source code
       1             : /*   SPDX-License-Identifier: BSD-3-Clause
       2             :  *   Copyright (C) 2020 Intel Corporation.
       3             :  *   All rights reserved.
       4             :  */
       5             : 
       6             : /*
       7             :  * vfio-user client socket messages.
       8             :  */
       9             : 
      10             : #include "spdk/stdinc.h"
      11             : #include "spdk/queue.h"
      12             : #include "spdk/util.h"
      13             : #include "spdk/log.h"
      14             : #include "spdk/vfio_user_spec.h"
      15             : 
      16             : #include "vfio_user_internal.h"
      17             : 
      18             : struct vfio_user_request {
      19             :         struct vfio_user_header hdr;
      20             : #define VFIO_USER_MAX_PAYLOAD_SIZE      (4096)
      21             :         uint8_t payload[VFIO_USER_MAX_PAYLOAD_SIZE];
      22             :         int fds[VFIO_MAXIMUM_SPARSE_MMAP_REGIONS];
      23             :         int fd_num;
      24             : };
      25             : 
      26             : #ifdef DEBUG
      27             : static const char *vfio_user_message_str[VFIO_USER_MAX] = {
      28             :         [VFIO_USER_VERSION]                     = "VFIO_USER_VERSION",
      29             :         [VFIO_USER_DMA_MAP]                     = "VFIO_USER_DMA_MAP",
      30             :         [VFIO_USER_DMA_UNMAP]                   = "VFIO_USER_DMA_UNMAP",
      31             :         [VFIO_USER_DEVICE_GET_INFO]             = "VFIO_USER_DEVICE_GET_INFO",
      32             :         [VFIO_USER_DEVICE_GET_REGION_INFO]      = "VFIO_USER_DEVICE_GET_REGION_INFO",
      33             :         [VFIO_USER_DEVICE_GET_IRQ_INFO]         = "VFIO_USER_DEVICE_GET_IRQ_INFO",
      34             :         [VFIO_USER_DEVICE_SET_IRQS]             = "VFIO_USER_DEVICE_SET_IRQS",
      35             :         [VFIO_USER_REGION_READ]                 = "VFIO_USER_REGION_READ",
      36             :         [VFIO_USER_REGION_WRITE]                = "VFIO_USER_REGION_WRITE",
      37             :         [VFIO_USER_DMA_READ]                    = "VFIO_USER_DMA_READ",
      38             :         [VFIO_USER_DMA_WRITE]                   = "VFIO_USER_DMA_WRITE",
      39             :         [VFIO_USER_DEVICE_RESET]                = "VFIO_USER_DEVICE_RESET",
      40             : };
      41             : #endif
      42             : 
      43             : static int
      44           0 : vfio_user_write(int fd, void *buf, int len, int *fds, int num_fds)
      45             : {
      46             :         int r;
      47           0 :         struct msghdr msgh;
      48           0 :         struct iovec iov;
      49           0 :         size_t fd_size = num_fds * sizeof(int);
      50           0 :         char control[CMSG_SPACE(VFIO_MAXIMUM_SPARSE_MMAP_REGIONS * sizeof(int))];
      51             :         struct cmsghdr *cmsg;
      52             : 
      53           0 :         memset(&msgh, 0, sizeof(msgh));
      54           0 :         memset(control, 0, sizeof(control));
      55             : 
      56           0 :         iov.iov_base = (uint8_t *)buf;
      57           0 :         iov.iov_len = len;
      58             : 
      59           0 :         msgh.msg_iov = &iov;
      60           0 :         msgh.msg_iovlen = 1;
      61             : 
      62           0 :         assert(num_fds <= VFIO_MAXIMUM_SPARSE_MMAP_REGIONS);
      63             : 
      64           0 :         if (fds && num_fds) {
      65           0 :                 msgh.msg_control = control;
      66           0 :                 msgh.msg_controllen = CMSG_SPACE(fd_size);
      67           0 :                 cmsg = CMSG_FIRSTHDR(&msgh);
      68           0 :                 assert(cmsg != NULL);
      69           0 :                 cmsg->cmsg_len = CMSG_LEN(fd_size);
      70           0 :                 cmsg->cmsg_level = SOL_SOCKET;
      71           0 :                 cmsg->cmsg_type = SCM_RIGHTS;
      72           0 :                 memcpy(CMSG_DATA(cmsg), fds, fd_size);
      73             :         } else {
      74           0 :                 msgh.msg_control = NULL;
      75           0 :                 msgh.msg_controllen = 0;
      76             :         }
      77             : 
      78             :         do {
      79           0 :                 r = sendmsg(fd, &msgh, MSG_NOSIGNAL);
      80           0 :         } while (r < 0 && errno == EINTR);
      81             : 
      82           0 :         if (r == -1) {
      83           0 :                 return -errno;
      84             :         }
      85             : 
      86           0 :         return 0;
      87             : }
      88             : 
      89             : static int
      90           0 : read_fd_message(int sockfd, char *buf, int buflen, int *fds, int *fd_num)
      91             : {
      92           0 :         struct iovec iov;
      93           0 :         struct msghdr msgh;
      94           0 :         char control[CMSG_SPACE(VFIO_MAXIMUM_SPARSE_MMAP_REGIONS * sizeof(int))];
      95             :         struct cmsghdr *cmsg;
      96           0 :         int got_fds = 0;
      97             :         int ret;
      98             : 
      99           0 :         memset(&msgh, 0, sizeof(msgh));
     100           0 :         iov.iov_base = buf;
     101           0 :         iov.iov_len  = buflen;
     102             : 
     103           0 :         msgh.msg_iov = &iov;
     104           0 :         msgh.msg_iovlen = 1;
     105           0 :         msgh.msg_control = control;
     106           0 :         msgh.msg_controllen = sizeof(control);
     107             : 
     108           0 :         ret = recvmsg(sockfd, &msgh, 0);
     109           0 :         if (ret <= 0) {
     110           0 :                 return ret;
     111             :         }
     112             : 
     113           0 :         if (msgh.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) {
     114           0 :                 return -ENOTSUP;
     115             :         }
     116             : 
     117           0 :         for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;
     118           0 :              cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
     119           0 :                 if ((cmsg->cmsg_level == SOL_SOCKET) &&
     120           0 :                     (cmsg->cmsg_type == SCM_RIGHTS)) {
     121           0 :                         got_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
     122           0 :                         *fd_num = got_fds;
     123           0 :                         assert(got_fds <= VFIO_MAXIMUM_SPARSE_MMAP_REGIONS);
     124           0 :                         memcpy(fds, CMSG_DATA(cmsg), got_fds * sizeof(int));
     125           0 :                         break;
     126             :                 }
     127             :         }
     128             : 
     129           0 :         return ret;
     130             : }
     131             : 
     132             : static int
     133           0 : vfio_user_read(int fd, struct vfio_user_request *req)
     134             : {
     135             :         int ret;
     136             :         size_t sz_payload;
     137             : 
     138           0 :         ret = read_fd_message(fd, (char *)req, sizeof(struct vfio_user_header), req->fds, &req->fd_num);
     139           0 :         if (ret <= 0) {
     140           0 :                 return ret;
     141             :         }
     142             : 
     143           0 :         if (req->hdr.flags.error) {
     144           0 :                 SPDK_ERRLOG("Command %u return failure\n", req->hdr.cmd);
     145           0 :                 errno = req->hdr.error_no;
     146           0 :                 return -EFAULT;
     147             :         }
     148             : 
     149           0 :         if (req->hdr.msg_size > sizeof(struct vfio_user_header)) {
     150           0 :                 sz_payload = req->hdr.msg_size - sizeof(struct vfio_user_header);
     151           0 :                 ret = read(fd, req->payload, sz_payload);
     152           0 :                 if (ret <= 0) {
     153           0 :                         return ret;
     154             :                 }
     155             :         }
     156             : 
     157           0 :         return 0;
     158             : }
     159             : 
     160             : int
     161           0 : vfio_user_dev_send_request(struct vfio_device *dev, enum vfio_user_command command,
     162             :                            void *arg, size_t arg_len, size_t buf_len, int *fds, int max_fds)
     163             : {
     164           0 :         struct vfio_user_request req = {};
     165             :         size_t sz_payload;
     166             :         int ret;
     167           0 :         bool fds_write = false;
     168             : 
     169           0 :         if (arg_len > VFIO_USER_MAX_PAYLOAD_SIZE) {
     170           0 :                 SPDK_ERRLOG("Oversized argument length, command %u\n", command);
     171           0 :                 return -EINVAL;
     172             :         }
     173             : 
     174           0 :         req.hdr.cmd = command;
     175           0 :         req.hdr.msg_size = sizeof(struct vfio_user_header) + arg_len;
     176           0 :         memcpy(req.payload, arg, arg_len);
     177             : 
     178           0 :         if (command == VFIO_USER_DMA_MAP || command == VFIO_USER_DMA_UNMAP) {
     179           0 :                 fds_write = true;
     180             :         }
     181             : 
     182           0 :         SPDK_DEBUGLOG(vfio_user, "[I] Command %s, msg size %u, fds %p, max_fds %d\n",
     183             :                       vfio_user_message_str[command], req.hdr.msg_size, fds, max_fds);
     184             : 
     185           0 :         if (fds_write && fds) {
     186           0 :                 ret = vfio_user_write(dev->fd, (void *)&req, req.hdr.msg_size, fds, max_fds);
     187             :         } else {
     188           0 :                 ret = vfio_user_write(dev->fd, (void *)&req, req.hdr.msg_size, NULL, 0);
     189             :         }
     190             : 
     191           0 :         if (ret) {
     192           0 :                 return ret;
     193             :         }
     194             : 
     195             :         /* a reply is mandatory */
     196           0 :         memset(&req, 0, sizeof(req));
     197           0 :         ret = vfio_user_read(dev->fd, &req);
     198           0 :         if (ret) {
     199           0 :                 return ret;
     200             :         }
     201             : 
     202           0 :         SPDK_DEBUGLOG(vfio_user, "[I] Command %s response, msg size %u\n",
     203             :                       vfio_user_message_str[req.hdr.cmd], req.hdr.msg_size);
     204             : 
     205           0 :         assert(req.hdr.flags.type == VFIO_USER_MESSAGE_REPLY);
     206           0 :         sz_payload = req.hdr.msg_size - sizeof(struct vfio_user_header);
     207           0 :         if (!sz_payload) {
     208           0 :                 return 0;
     209             :         }
     210             : 
     211           0 :         if (!fds_write) {
     212           0 :                 if (sz_payload > buf_len) {
     213           0 :                         SPDK_ERRLOG("Payload size error sz %zd, buf_len %zd\n", sz_payload, buf_len);
     214           0 :                         return -EIO;
     215             :                 }
     216           0 :                 memcpy(arg, req.payload, sz_payload);
     217             :                 /* VFIO_USER_DEVICE_GET_REGION_INFO may contains BAR fd */
     218           0 :                 if (fds && req.fd_num) {
     219           0 :                         assert(req.fd_num < max_fds);
     220           0 :                         memcpy(fds, req.fds, sizeof(int) * req.fd_num);
     221             :                 }
     222             :         }
     223             : 
     224           0 :         return 0;
     225             : }
     226             : 
     227             : static int
     228           0 : vfio_user_check_version(struct vfio_device *dev)
     229             : {
     230             :         int ret;
     231           0 :         struct vfio_user_request req = {};
     232           0 :         struct vfio_user_version *version = (struct vfio_user_version *)req.payload;
     233             : 
     234           0 :         version->major = VFIO_USER_MAJOR_VER;
     235           0 :         version->minor = VFIO_USER_MINOR_VER;
     236             : 
     237           0 :         ret = vfio_user_dev_send_request(dev, VFIO_USER_VERSION, req.payload,
     238             :                                          sizeof(struct vfio_user_version), sizeof(req.payload), NULL, 0);
     239           0 :         if (ret < 0) {
     240           0 :                 return ret;
     241             :         }
     242             : 
     243           0 :         SPDK_DEBUGLOG(vfio_user, "%s Negotiate version %u.%u\n", vfio_user_message_str[VFIO_USER_VERSION],
     244             :                       version->major, version->minor);
     245             : 
     246           0 :         return 0;
     247             : }
     248             : 
     249             : int
     250           0 : vfio_user_get_dev_region_info(struct vfio_device *dev, struct vfio_region_info *region_info,
     251             :                               size_t buf_len, int *fds, int num_fds)
     252             : {
     253           0 :         assert(buf_len > sizeof(struct vfio_region_info));
     254           0 :         region_info->argsz = buf_len - sizeof(struct vfio_region_info);
     255           0 :         return vfio_user_dev_send_request(dev, VFIO_USER_DEVICE_GET_REGION_INFO,
     256           0 :                                           region_info, region_info->argsz, buf_len, fds, num_fds);
     257             : }
     258             : 
     259             : int
     260           0 : vfio_user_get_dev_info(struct vfio_device *dev, struct vfio_user_device_info *dev_info,
     261             :                        size_t buf_len)
     262             : {
     263           0 :         dev_info->argsz = sizeof(struct vfio_user_device_info);
     264           0 :         return vfio_user_dev_send_request(dev, VFIO_USER_DEVICE_GET_INFO,
     265           0 :                                           dev_info, dev_info->argsz, buf_len, NULL, 0);
     266             : }
     267             : 
     268             : int
     269           0 : vfio_user_dev_dma_map_unmap(struct vfio_device *dev, struct vfio_memory_region *mr, bool map)
     270             : {
     271           0 :         struct vfio_user_dma_map dma_map = { 0 };
     272           0 :         struct vfio_user_dma_unmap dma_unmap = { 0 };
     273             : 
     274           0 :         if (map) {
     275           0 :                 dma_map.argsz = sizeof(struct vfio_user_dma_map);
     276           0 :                 dma_map.addr = mr->iova;
     277           0 :                 dma_map.size = mr->size;
     278           0 :                 dma_map.offset = mr->offset;
     279           0 :                 dma_map.flags = VFIO_USER_F_DMA_REGION_READ | VFIO_USER_F_DMA_REGION_WRITE;
     280             : 
     281           0 :                 return vfio_user_dev_send_request(dev, VFIO_USER_DMA_MAP,
     282             :                                                   &dma_map, sizeof(dma_map), sizeof(dma_map), &mr->fd, 1);
     283             :         } else {
     284           0 :                 dma_unmap.argsz = sizeof(struct vfio_user_dma_unmap);
     285           0 :                 dma_unmap.addr = mr->iova;
     286           0 :                 dma_unmap.size = mr->size;
     287           0 :                 return vfio_user_dev_send_request(dev, VFIO_USER_DMA_UNMAP,
     288             :                                                   &dma_unmap, sizeof(dma_unmap), sizeof(dma_unmap), &mr->fd, 1);
     289             :         }
     290             : }
     291             : 
     292             : int
     293           0 : vfio_user_dev_mmio_access(struct vfio_device *dev, uint32_t index, uint64_t offset,
     294             :                           size_t len, void *buf, bool is_write)
     295             : {
     296             :         struct vfio_user_region_access *access;
     297             :         size_t arg_len;
     298             :         int ret;
     299             : 
     300           0 :         arg_len = sizeof(*access) + len;
     301           0 :         access = calloc(1, arg_len);
     302           0 :         if (!access) {
     303           0 :                 return -ENOMEM;
     304             :         }
     305             : 
     306           0 :         access->offset = offset;
     307           0 :         access->region = index;
     308           0 :         access->count = len;
     309           0 :         if (is_write) {
     310           0 :                 memcpy(access->data, buf, len);
     311           0 :                 ret = vfio_user_dev_send_request(dev, VFIO_USER_REGION_WRITE,
     312             :                                                  access, arg_len, arg_len, NULL, 0);
     313             :         } else {
     314           0 :                 ret = vfio_user_dev_send_request(dev, VFIO_USER_REGION_READ,
     315             :                                                  access, sizeof(*access), arg_len, NULL, 0);
     316             :         }
     317             : 
     318           0 :         if (ret) {
     319           0 :                 free(access);
     320           0 :                 return ret;
     321             :         }
     322             : 
     323           0 :         if (!is_write) {
     324           0 :                 memcpy(buf, (void *)access->data, len);
     325             :         }
     326             : 
     327           0 :         free(access);
     328           0 :         return 0;
     329             : }
     330             : 
     331             : int
     332           0 : vfio_user_dev_setup(struct vfio_device *dev)
     333             : {
     334             :         int fd;
     335             :         int flag;
     336           0 :         struct sockaddr_un un;
     337             :         ssize_t rc;
     338             : 
     339           0 :         fd = socket(AF_UNIX, SOCK_STREAM, 0);
     340           0 :         if (fd < 0) {
     341           0 :                 SPDK_ERRLOG("socket() error\n");
     342           0 :                 return -errno;
     343             :         }
     344             : 
     345           0 :         flag = fcntl(fd, F_GETFD);
     346           0 :         if (fcntl(fd, F_SETFD, flag | FD_CLOEXEC) < 0) {
     347           0 :                 SPDK_ERRLOG("fcntl failed\n");
     348             :         }
     349             : 
     350           0 :         memset(&un, 0, sizeof(un));
     351           0 :         un.sun_family = AF_UNIX;
     352           0 :         rc = snprintf(un.sun_path, sizeof(un.sun_path), "%s", dev->path);
     353           0 :         if (rc < 0 || (size_t)rc >= sizeof(un.sun_path)) {
     354           0 :                 SPDK_ERRLOG("socket path too long\n");
     355           0 :                 close(fd);
     356           0 :                 if (rc < 0) {
     357           0 :                         return -errno;
     358             :                 } else {
     359           0 :                         return -EINVAL;
     360             :                 }
     361             :         }
     362           0 :         if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
     363           0 :                 SPDK_ERRLOG("connect error\n");
     364           0 :                 close(fd);
     365           0 :                 return -errno;
     366             :         }
     367             : 
     368           0 :         dev->fd = fd;
     369             : 
     370           0 :         if (vfio_user_check_version(dev)) {
     371           0 :                 SPDK_ERRLOG("Check VFIO_USER_VERSION message failed\n");
     372           0 :                 close(fd);
     373           0 :                 return -EFAULT;
     374             :         }
     375             : 
     376           0 :         return 0;
     377             : }
     378             : 
     379           0 : SPDK_LOG_REGISTER_COMPONENT(vfio_user)

Generated by: LCOV version 1.15