cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

vdpa_sim_blk.c (8461B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * VDPA simulator for block device.
      4 *
      5 * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
      6 * Copyright (c) 2021, Red Hat Inc. All rights reserved.
      7 *
      8 */
      9
     10#include <linux/init.h>
     11#include <linux/module.h>
     12#include <linux/device.h>
     13#include <linux/kernel.h>
     14#include <linux/sched.h>
     15#include <linux/blkdev.h>
     16#include <linux/vringh.h>
     17#include <linux/vdpa.h>
     18#include <uapi/linux/virtio_blk.h>
     19
     20#include "vdpa_sim.h"
     21
     22#define DRV_VERSION  "0.1"
     23#define DRV_AUTHOR   "Max Gurtovoy <mgurtovoy@nvidia.com>"
     24#define DRV_DESC     "vDPA Device Simulator for block device"
     25#define DRV_LICENSE  "GPL v2"
     26
     27#define VDPASIM_BLK_FEATURES	(VDPASIM_FEATURES | \
     28				 (1ULL << VIRTIO_BLK_F_SIZE_MAX) | \
     29				 (1ULL << VIRTIO_BLK_F_SEG_MAX)  | \
     30				 (1ULL << VIRTIO_BLK_F_BLK_SIZE) | \
     31				 (1ULL << VIRTIO_BLK_F_TOPOLOGY) | \
     32				 (1ULL << VIRTIO_BLK_F_MQ))
     33
     34#define VDPASIM_BLK_CAPACITY	0x40000
     35#define VDPASIM_BLK_SIZE_MAX	0x1000
     36#define VDPASIM_BLK_SEG_MAX	32
     37#define VDPASIM_BLK_VQ_NUM	1
     38
     39static char vdpasim_blk_id[VIRTIO_BLK_ID_BYTES] = "vdpa_blk_sim";
     40
     41static bool vdpasim_blk_check_range(u64 start_sector, size_t range_size)
     42{
     43	u64 range_sectors = range_size >> SECTOR_SHIFT;
     44
     45	if (range_size > VDPASIM_BLK_SIZE_MAX * VDPASIM_BLK_SEG_MAX)
     46		return false;
     47
     48	if (start_sector > VDPASIM_BLK_CAPACITY)
     49		return false;
     50
     51	if (range_sectors > VDPASIM_BLK_CAPACITY - start_sector)
     52		return false;
     53
     54	return true;
     55}
     56
     57/* Returns 'true' if the request is handled (with or without an I/O error)
     58 * and the status is correctly written in the last byte of the 'in iov',
     59 * 'false' otherwise.
     60 */
     61static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim,
     62				   struct vdpasim_virtqueue *vq)
     63{
     64	size_t pushed = 0, to_pull, to_push;
     65	struct virtio_blk_outhdr hdr;
     66	ssize_t bytes;
     67	loff_t offset;
     68	u64 sector;
     69	u8 status;
     70	u32 type;
     71	int ret;
     72
     73	ret = vringh_getdesc_iotlb(&vq->vring, &vq->out_iov, &vq->in_iov,
     74				   &vq->head, GFP_ATOMIC);
     75	if (ret != 1)
     76		return false;
     77
     78	if (vq->out_iov.used < 1 || vq->in_iov.used < 1) {
     79		dev_err(&vdpasim->vdpa.dev, "missing headers - out_iov: %u in_iov %u\n",
     80			vq->out_iov.used, vq->in_iov.used);
     81		return false;
     82	}
     83
     84	if (vq->in_iov.iov[vq->in_iov.used - 1].iov_len < 1) {
     85		dev_err(&vdpasim->vdpa.dev, "request in header too short\n");
     86		return false;
     87	}
     88
     89	/* The last byte is the status and we checked if the last iov has
     90	 * enough room for it.
     91	 */
     92	to_push = vringh_kiov_length(&vq->in_iov) - 1;
     93
     94	to_pull = vringh_kiov_length(&vq->out_iov);
     95
     96	bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov, &hdr,
     97				      sizeof(hdr));
     98	if (bytes != sizeof(hdr)) {
     99		dev_err(&vdpasim->vdpa.dev, "request out header too short\n");
    100		return false;
    101	}
    102
    103	to_pull -= bytes;
    104
    105	type = vdpasim32_to_cpu(vdpasim, hdr.type);
    106	sector = vdpasim64_to_cpu(vdpasim, hdr.sector);
    107	offset = sector << SECTOR_SHIFT;
    108	status = VIRTIO_BLK_S_OK;
    109
    110	switch (type) {
    111	case VIRTIO_BLK_T_IN:
    112		if (!vdpasim_blk_check_range(sector, to_push)) {
    113			dev_err(&vdpasim->vdpa.dev,
    114				"reading over the capacity - offset: 0x%llx len: 0x%zx\n",
    115				offset, to_push);
    116			status = VIRTIO_BLK_S_IOERR;
    117			break;
    118		}
    119
    120		bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov,
    121					      vdpasim->buffer + offset,
    122					      to_push);
    123		if (bytes < 0) {
    124			dev_err(&vdpasim->vdpa.dev,
    125				"vringh_iov_push_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n",
    126				bytes, offset, to_push);
    127			status = VIRTIO_BLK_S_IOERR;
    128			break;
    129		}
    130
    131		pushed += bytes;
    132		break;
    133
    134	case VIRTIO_BLK_T_OUT:
    135		if (!vdpasim_blk_check_range(sector, to_pull)) {
    136			dev_err(&vdpasim->vdpa.dev,
    137				"writing over the capacity - offset: 0x%llx len: 0x%zx\n",
    138				offset, to_pull);
    139			status = VIRTIO_BLK_S_IOERR;
    140			break;
    141		}
    142
    143		bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov,
    144					      vdpasim->buffer + offset,
    145					      to_pull);
    146		if (bytes < 0) {
    147			dev_err(&vdpasim->vdpa.dev,
    148				"vringh_iov_pull_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n",
    149				bytes, offset, to_pull);
    150			status = VIRTIO_BLK_S_IOERR;
    151			break;
    152		}
    153		break;
    154
    155	case VIRTIO_BLK_T_GET_ID:
    156		bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov,
    157					      vdpasim_blk_id,
    158					      VIRTIO_BLK_ID_BYTES);
    159		if (bytes < 0) {
    160			dev_err(&vdpasim->vdpa.dev,
    161				"vringh_iov_push_iotlb() error: %zd\n", bytes);
    162			status = VIRTIO_BLK_S_IOERR;
    163			break;
    164		}
    165
    166		pushed += bytes;
    167		break;
    168
    169	default:
    170		dev_warn(&vdpasim->vdpa.dev,
    171			 "Unsupported request type %d\n", type);
    172		status = VIRTIO_BLK_S_IOERR;
    173		break;
    174	}
    175
    176	/* If some operations fail, we need to skip the remaining bytes
    177	 * to put the status in the last byte
    178	 */
    179	if (to_push - pushed > 0)
    180		vringh_kiov_advance(&vq->in_iov, to_push - pushed);
    181
    182	/* Last byte is the status */
    183	bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov, &status, 1);
    184	if (bytes != 1)
    185		return false;
    186
    187	pushed += bytes;
    188
    189	/* Make sure data is wrote before advancing index */
    190	smp_wmb();
    191
    192	vringh_complete_iotlb(&vq->vring, vq->head, pushed);
    193
    194	return true;
    195}
    196
    197static void vdpasim_blk_work(struct work_struct *work)
    198{
    199	struct vdpasim *vdpasim = container_of(work, struct vdpasim, work);
    200	int i;
    201
    202	spin_lock(&vdpasim->lock);
    203
    204	if (!(vdpasim->status & VIRTIO_CONFIG_S_DRIVER_OK))
    205		goto out;
    206
    207	for (i = 0; i < VDPASIM_BLK_VQ_NUM; i++) {
    208		struct vdpasim_virtqueue *vq = &vdpasim->vqs[i];
    209
    210		if (!vq->ready)
    211			continue;
    212
    213		while (vdpasim_blk_handle_req(vdpasim, vq)) {
    214			/* Make sure used is visible before rasing the interrupt. */
    215			smp_wmb();
    216
    217			local_bh_disable();
    218			if (vringh_need_notify_iotlb(&vq->vring) > 0)
    219				vringh_notify(&vq->vring);
    220			local_bh_enable();
    221		}
    222	}
    223out:
    224	spin_unlock(&vdpasim->lock);
    225}
    226
    227static void vdpasim_blk_get_config(struct vdpasim *vdpasim, void *config)
    228{
    229	struct virtio_blk_config *blk_config = config;
    230
    231	memset(config, 0, sizeof(struct virtio_blk_config));
    232
    233	blk_config->capacity = cpu_to_vdpasim64(vdpasim, VDPASIM_BLK_CAPACITY);
    234	blk_config->size_max = cpu_to_vdpasim32(vdpasim, VDPASIM_BLK_SIZE_MAX);
    235	blk_config->seg_max = cpu_to_vdpasim32(vdpasim, VDPASIM_BLK_SEG_MAX);
    236	blk_config->num_queues = cpu_to_vdpasim16(vdpasim, VDPASIM_BLK_VQ_NUM);
    237	blk_config->min_io_size = cpu_to_vdpasim16(vdpasim, 1);
    238	blk_config->opt_io_size = cpu_to_vdpasim32(vdpasim, 1);
    239	blk_config->blk_size = cpu_to_vdpasim32(vdpasim, SECTOR_SIZE);
    240}
    241
    242static void vdpasim_blk_mgmtdev_release(struct device *dev)
    243{
    244}
    245
    246static struct device vdpasim_blk_mgmtdev = {
    247	.init_name = "vdpasim_blk",
    248	.release = vdpasim_blk_mgmtdev_release,
    249};
    250
    251static int vdpasim_blk_dev_add(struct vdpa_mgmt_dev *mdev, const char *name,
    252			       const struct vdpa_dev_set_config *config)
    253{
    254	struct vdpasim_dev_attr dev_attr = {};
    255	struct vdpasim *simdev;
    256	int ret;
    257
    258	dev_attr.mgmt_dev = mdev;
    259	dev_attr.name = name;
    260	dev_attr.id = VIRTIO_ID_BLOCK;
    261	dev_attr.supported_features = VDPASIM_BLK_FEATURES;
    262	dev_attr.nvqs = VDPASIM_BLK_VQ_NUM;
    263	dev_attr.config_size = sizeof(struct virtio_blk_config);
    264	dev_attr.get_config = vdpasim_blk_get_config;
    265	dev_attr.work_fn = vdpasim_blk_work;
    266	dev_attr.buffer_size = VDPASIM_BLK_CAPACITY << SECTOR_SHIFT;
    267
    268	simdev = vdpasim_create(&dev_attr);
    269	if (IS_ERR(simdev))
    270		return PTR_ERR(simdev);
    271
    272	ret = _vdpa_register_device(&simdev->vdpa, VDPASIM_BLK_VQ_NUM);
    273	if (ret)
    274		goto put_dev;
    275
    276	return 0;
    277
    278put_dev:
    279	put_device(&simdev->vdpa.dev);
    280	return ret;
    281}
    282
    283static void vdpasim_blk_dev_del(struct vdpa_mgmt_dev *mdev,
    284				struct vdpa_device *dev)
    285{
    286	struct vdpasim *simdev = container_of(dev, struct vdpasim, vdpa);
    287
    288	_vdpa_unregister_device(&simdev->vdpa);
    289}
    290
    291static const struct vdpa_mgmtdev_ops vdpasim_blk_mgmtdev_ops = {
    292	.dev_add = vdpasim_blk_dev_add,
    293	.dev_del = vdpasim_blk_dev_del
    294};
    295
    296static struct virtio_device_id id_table[] = {
    297	{ VIRTIO_ID_BLOCK, VIRTIO_DEV_ANY_ID },
    298	{ 0 },
    299};
    300
    301static struct vdpa_mgmt_dev mgmt_dev = {
    302	.device = &vdpasim_blk_mgmtdev,
    303	.id_table = id_table,
    304	.ops = &vdpasim_blk_mgmtdev_ops,
    305};
    306
    307static int __init vdpasim_blk_init(void)
    308{
    309	int ret;
    310
    311	ret = device_register(&vdpasim_blk_mgmtdev);
    312	if (ret)
    313		return ret;
    314
    315	ret = vdpa_mgmtdev_register(&mgmt_dev);
    316	if (ret)
    317		goto parent_err;
    318
    319	return 0;
    320
    321parent_err:
    322	device_unregister(&vdpasim_blk_mgmtdev);
    323	return ret;
    324}
    325
    326static void __exit vdpasim_blk_exit(void)
    327{
    328	vdpa_mgmtdev_unregister(&mgmt_dev);
    329	device_unregister(&vdpasim_blk_mgmtdev);
    330}
    331
    332module_init(vdpasim_blk_init)
    333module_exit(vdpasim_blk_exit)
    334
    335MODULE_VERSION(DRV_VERSION);
    336MODULE_LICENSE(DRV_LICENSE);
    337MODULE_AUTHOR(DRV_AUTHOR);
    338MODULE_DESCRIPTION(DRV_DESC);