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_net.c (7991B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * VDPA simulator for networking device.
      4 *
      5 * Copyright (c) 2020, Red Hat Inc. All rights reserved.
      6 *     Author: Jason Wang <jasowang@redhat.com>
      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/etherdevice.h>
     16#include <linux/vringh.h>
     17#include <linux/vdpa.h>
     18#include <uapi/linux/virtio_net.h>
     19#include <uapi/linux/vdpa.h>
     20
     21#include "vdpa_sim.h"
     22
     23#define DRV_VERSION  "0.1"
     24#define DRV_AUTHOR   "Jason Wang <jasowang@redhat.com>"
     25#define DRV_DESC     "vDPA Device Simulator for networking device"
     26#define DRV_LICENSE  "GPL v2"
     27
     28#define VDPASIM_NET_FEATURES	(VDPASIM_FEATURES | \
     29				 (1ULL << VIRTIO_NET_F_MAC) | \
     30				 (1ULL << VIRTIO_NET_F_MTU) | \
     31				 (1ULL << VIRTIO_NET_F_CTRL_VQ) | \
     32				 (1ULL << VIRTIO_NET_F_CTRL_MAC_ADDR))
     33
     34/* 3 virtqueues, 2 address spaces, 2 virtqueue groups */
     35#define VDPASIM_NET_VQ_NUM	3
     36#define VDPASIM_NET_AS_NUM	2
     37#define VDPASIM_NET_GROUP_NUM	2
     38
     39static void vdpasim_net_complete(struct vdpasim_virtqueue *vq, size_t len)
     40{
     41	/* Make sure data is wrote before advancing index */
     42	smp_wmb();
     43
     44	vringh_complete_iotlb(&vq->vring, vq->head, len);
     45
     46	/* Make sure used is visible before rasing the interrupt. */
     47	smp_wmb();
     48
     49	local_bh_disable();
     50	if (vringh_need_notify_iotlb(&vq->vring) > 0)
     51		vringh_notify(&vq->vring);
     52	local_bh_enable();
     53}
     54
     55static bool receive_filter(struct vdpasim *vdpasim, size_t len)
     56{
     57	bool modern = vdpasim->features & (1ULL << VIRTIO_F_VERSION_1);
     58	size_t hdr_len = modern ? sizeof(struct virtio_net_hdr_v1) :
     59				  sizeof(struct virtio_net_hdr);
     60	struct virtio_net_config *vio_config = vdpasim->config;
     61
     62	if (len < ETH_ALEN + hdr_len)
     63		return false;
     64
     65	if (!strncmp(vdpasim->buffer + hdr_len, vio_config->mac, ETH_ALEN))
     66		return true;
     67
     68	return false;
     69}
     70
     71static virtio_net_ctrl_ack vdpasim_handle_ctrl_mac(struct vdpasim *vdpasim,
     72						   u8 cmd)
     73{
     74	struct virtio_net_config *vio_config = vdpasim->config;
     75	struct vdpasim_virtqueue *cvq = &vdpasim->vqs[2];
     76	virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
     77	size_t read;
     78
     79	switch (cmd) {
     80	case VIRTIO_NET_CTRL_MAC_ADDR_SET:
     81		read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->in_iov,
     82					     vio_config->mac, ETH_ALEN);
     83		if (read == ETH_ALEN)
     84			status = VIRTIO_NET_OK;
     85		break;
     86	default:
     87		break;
     88	}
     89
     90	return status;
     91}
     92
     93static void vdpasim_handle_cvq(struct vdpasim *vdpasim)
     94{
     95	struct vdpasim_virtqueue *cvq = &vdpasim->vqs[2];
     96	virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
     97	struct virtio_net_ctrl_hdr ctrl;
     98	size_t read, write;
     99	int err;
    100
    101	if (!(vdpasim->features & (1ULL << VIRTIO_NET_F_CTRL_VQ)))
    102		return;
    103
    104	if (!cvq->ready)
    105		return;
    106
    107	while (true) {
    108		err = vringh_getdesc_iotlb(&cvq->vring, &cvq->in_iov,
    109					   &cvq->out_iov,
    110					   &cvq->head, GFP_ATOMIC);
    111		if (err <= 0)
    112			break;
    113
    114		read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->in_iov, &ctrl,
    115					     sizeof(ctrl));
    116		if (read != sizeof(ctrl))
    117			break;
    118
    119		switch (ctrl.class) {
    120		case VIRTIO_NET_CTRL_MAC:
    121			status = vdpasim_handle_ctrl_mac(vdpasim, ctrl.cmd);
    122			break;
    123		default:
    124			break;
    125		}
    126
    127		/* Make sure data is wrote before advancing index */
    128		smp_wmb();
    129
    130		write = vringh_iov_push_iotlb(&cvq->vring, &cvq->out_iov,
    131					      &status, sizeof(status));
    132		vringh_complete_iotlb(&cvq->vring, cvq->head, write);
    133		vringh_kiov_cleanup(&cvq->in_iov);
    134		vringh_kiov_cleanup(&cvq->out_iov);
    135
    136		/* Make sure used is visible before rasing the interrupt. */
    137		smp_wmb();
    138
    139		local_bh_disable();
    140		if (cvq->cb)
    141			cvq->cb(cvq->private);
    142		local_bh_enable();
    143	}
    144}
    145
    146static void vdpasim_net_work(struct work_struct *work)
    147{
    148	struct vdpasim *vdpasim = container_of(work, struct vdpasim, work);
    149	struct vdpasim_virtqueue *txq = &vdpasim->vqs[1];
    150	struct vdpasim_virtqueue *rxq = &vdpasim->vqs[0];
    151	ssize_t read, write;
    152	int pkts = 0;
    153	int err;
    154
    155	spin_lock(&vdpasim->lock);
    156
    157	if (!(vdpasim->status & VIRTIO_CONFIG_S_DRIVER_OK))
    158		goto out;
    159
    160	vdpasim_handle_cvq(vdpasim);
    161
    162	if (!txq->ready || !rxq->ready)
    163		goto out;
    164
    165	while (true) {
    166		err = vringh_getdesc_iotlb(&txq->vring, &txq->out_iov, NULL,
    167					   &txq->head, GFP_ATOMIC);
    168		if (err <= 0)
    169			break;
    170
    171		read = vringh_iov_pull_iotlb(&txq->vring, &txq->out_iov,
    172					     vdpasim->buffer,
    173					     PAGE_SIZE);
    174
    175		if (!receive_filter(vdpasim, read)) {
    176			vdpasim_net_complete(txq, 0);
    177			continue;
    178		}
    179
    180		err = vringh_getdesc_iotlb(&rxq->vring, NULL, &rxq->in_iov,
    181					   &rxq->head, GFP_ATOMIC);
    182		if (err <= 0) {
    183			vdpasim_net_complete(txq, 0);
    184			break;
    185		}
    186
    187		write = vringh_iov_push_iotlb(&rxq->vring, &rxq->in_iov,
    188					      vdpasim->buffer, read);
    189		if (write <= 0)
    190			break;
    191
    192		vdpasim_net_complete(txq, 0);
    193		vdpasim_net_complete(rxq, write);
    194
    195		if (++pkts > 4) {
    196			schedule_work(&vdpasim->work);
    197			goto out;
    198		}
    199	}
    200
    201out:
    202	spin_unlock(&vdpasim->lock);
    203}
    204
    205static void vdpasim_net_get_config(struct vdpasim *vdpasim, void *config)
    206{
    207	struct virtio_net_config *net_config = config;
    208
    209	net_config->status = cpu_to_vdpasim16(vdpasim, VIRTIO_NET_S_LINK_UP);
    210}
    211
    212static void vdpasim_net_setup_config(struct vdpasim *vdpasim,
    213				     const struct vdpa_dev_set_config *config)
    214{
    215	struct virtio_net_config *vio_config = vdpasim->config;
    216
    217	if (config->mask & (1 << VDPA_ATTR_DEV_NET_CFG_MACADDR))
    218		memcpy(vio_config->mac, config->net.mac, ETH_ALEN);
    219	if (config->mask & (1 << VDPA_ATTR_DEV_NET_CFG_MTU))
    220		vio_config->mtu = cpu_to_vdpasim16(vdpasim, config->net.mtu);
    221	else
    222		/* Setup default MTU to be 1500 */
    223		vio_config->mtu = cpu_to_vdpasim16(vdpasim, 1500);
    224}
    225
    226static void vdpasim_net_mgmtdev_release(struct device *dev)
    227{
    228}
    229
    230static struct device vdpasim_net_mgmtdev = {
    231	.init_name = "vdpasim_net",
    232	.release = vdpasim_net_mgmtdev_release,
    233};
    234
    235static int vdpasim_net_dev_add(struct vdpa_mgmt_dev *mdev, const char *name,
    236			       const struct vdpa_dev_set_config *config)
    237{
    238	struct vdpasim_dev_attr dev_attr = {};
    239	struct vdpasim *simdev;
    240	int ret;
    241
    242	dev_attr.mgmt_dev = mdev;
    243	dev_attr.name = name;
    244	dev_attr.id = VIRTIO_ID_NET;
    245	dev_attr.supported_features = VDPASIM_NET_FEATURES;
    246	dev_attr.nvqs = VDPASIM_NET_VQ_NUM;
    247	dev_attr.ngroups = VDPASIM_NET_GROUP_NUM;
    248	dev_attr.nas = VDPASIM_NET_AS_NUM;
    249	dev_attr.config_size = sizeof(struct virtio_net_config);
    250	dev_attr.get_config = vdpasim_net_get_config;
    251	dev_attr.work_fn = vdpasim_net_work;
    252	dev_attr.buffer_size = PAGE_SIZE;
    253
    254	simdev = vdpasim_create(&dev_attr);
    255	if (IS_ERR(simdev))
    256		return PTR_ERR(simdev);
    257
    258	vdpasim_net_setup_config(simdev, config);
    259
    260	ret = _vdpa_register_device(&simdev->vdpa, VDPASIM_NET_VQ_NUM);
    261	if (ret)
    262		goto reg_err;
    263
    264	return 0;
    265
    266reg_err:
    267	put_device(&simdev->vdpa.dev);
    268	return ret;
    269}
    270
    271static void vdpasim_net_dev_del(struct vdpa_mgmt_dev *mdev,
    272				struct vdpa_device *dev)
    273{
    274	struct vdpasim *simdev = container_of(dev, struct vdpasim, vdpa);
    275
    276	_vdpa_unregister_device(&simdev->vdpa);
    277}
    278
    279static const struct vdpa_mgmtdev_ops vdpasim_net_mgmtdev_ops = {
    280	.dev_add = vdpasim_net_dev_add,
    281	.dev_del = vdpasim_net_dev_del
    282};
    283
    284static struct virtio_device_id id_table[] = {
    285	{ VIRTIO_ID_NET, VIRTIO_DEV_ANY_ID },
    286	{ 0 },
    287};
    288
    289static struct vdpa_mgmt_dev mgmt_dev = {
    290	.device = &vdpasim_net_mgmtdev,
    291	.id_table = id_table,
    292	.ops = &vdpasim_net_mgmtdev_ops,
    293	.config_attr_mask = (1 << VDPA_ATTR_DEV_NET_CFG_MACADDR |
    294			     1 << VDPA_ATTR_DEV_NET_CFG_MTU),
    295	.max_supported_vqs = VDPASIM_NET_VQ_NUM,
    296	.supported_features = VDPASIM_NET_FEATURES,
    297};
    298
    299static int __init vdpasim_net_init(void)
    300{
    301	int ret;
    302
    303	ret = device_register(&vdpasim_net_mgmtdev);
    304	if (ret)
    305		return ret;
    306
    307	ret = vdpa_mgmtdev_register(&mgmt_dev);
    308	if (ret)
    309		goto parent_err;
    310	return 0;
    311
    312parent_err:
    313	device_unregister(&vdpasim_net_mgmtdev);
    314	return ret;
    315}
    316
    317static void __exit vdpasim_net_exit(void)
    318{
    319	vdpa_mgmtdev_unregister(&mgmt_dev);
    320	device_unregister(&vdpasim_net_mgmtdev);
    321}
    322
    323module_init(vdpasim_net_init);
    324module_exit(vdpasim_net_exit);
    325
    326MODULE_VERSION(DRV_VERSION);
    327MODULE_LICENSE(DRV_LICENSE);
    328MODULE_AUTHOR(DRV_AUTHOR);
    329MODULE_DESCRIPTION(DRV_DESC);