// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Virtio vhost-user driver
*
* Copyright(c) 2019 Intel Corporation
*
* This driver allows virtio devices to be used over a vhost-user socket.
*
* Guest devices can be instantiated by kernel module or command line
* parameters. One device will be created for each parameter. Syntax:
*
* virtio_uml.device=<socket>:<virtio_id>[:<platform_id>]
* where:
* <socket> := vhost-user socket path to connect
* <virtio_id> := virtio device id (as in virtio_ids.h)
* <platform_id> := (optional) platform device id
*
* example:
* virtio_uml.device=/var/uml.socket:1
*
* Based on Virtio MMIO driver by Pawel Moll, copyright 2011-2014, ARM Ltd.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/virtio.h>
#include <linux/virtio_config.h>
#include <linux/virtio_ring.h>
#include <shared/as-layout.h>
#include <irq_kern.h>
#include <init.h>
#include <os.h>
#include "vhost_user.h"
/* Workaround due to a conflict between irq_user.h and irqreturn.h */
#ifdef IRQ_NONE
#undef IRQ_NONE
#endif
#define MAX_SUPPORTED_QUEUE_SIZE 256
#define to_virtio_uml_device(_vdev) \
container_of(_vdev, struct virtio_uml_device, vdev)
struct virtio_uml_platform_data {
u32 virtio_device_id;
const char *socket_path;
struct work_struct conn_broken_wk;
struct platform_device *pdev;
};
struct virtio_uml_device {
struct virtio_device vdev;
struct platform_device *pdev;
int sock, req_fd;
u64 features;
u64 protocol_features;
u8 status;
u8 registered:1;
};
struct virtio_uml_vq_info {
int kick_fd, call_fd;
char name[32];
};
extern unsigned long long physmem_size, highmem;
#define vu_err(vu_dev, ...) dev_err(&(vu_dev)->pdev->dev, __VA_ARGS__)
/* Vhost-user protocol */
static int full_sendmsg_fds(int fd, const void *buf, unsigned int len,
const int *fds, unsigned int fds_num)
{
int rc;
do {
rc = os_sendmsg_fds(fd, buf, len, fds, fds_num);
if (rc > 0) {
buf += rc;
len -= rc;
fds = NULL;
fds_num = 0;
}
} while (len && (rc >= 0 || rc == -EINTR));
if (rc < 0)
return rc;
return 0;
}
static int full_read(int fd, void *buf, int len, bool abortable)
{
int rc;
do {
rc = os_read_file(fd, buf, len);
if (rc > 0) {
buf += rc;
len -= rc;
}
} while (len && (rc > 0 || rc == -EINTR || (!abortable && rc == -EAGAIN)));
if (rc < 0)
return rc;
if (rc == 0)
return -ECONNRESET;
return 0;
}
static int vhost_user_recv_header(int fd, struct vhost_user_msg *msg)
{
return full_read(fd, msg, sizeof(msg->header), true);
}
static int vhost_user_recv