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

vhci_rx.c (6308B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Copyright (C) 2003-2008 Takahiro Hirofuchi
      4 */
      5
      6#include <linux/kthread.h>
      7#include <linux/slab.h>
      8
      9#include "usbip_common.h"
     10#include "vhci.h"
     11
     12/* get URB from transmitted urb queue. caller must hold vdev->priv_lock */
     13struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum)
     14{
     15	struct vhci_priv *priv, *tmp;
     16	struct urb *urb = NULL;
     17	int status;
     18
     19	list_for_each_entry_safe(priv, tmp, &vdev->priv_rx, list) {
     20		if (priv->seqnum != seqnum)
     21			continue;
     22
     23		urb = priv->urb;
     24		status = urb->status;
     25
     26		usbip_dbg_vhci_rx("find urb seqnum %u\n", seqnum);
     27
     28		switch (status) {
     29		case -ENOENT:
     30			fallthrough;
     31		case -ECONNRESET:
     32			dev_dbg(&urb->dev->dev,
     33				 "urb seq# %u was unlinked %ssynchronously\n",
     34				 seqnum, status == -ENOENT ? "" : "a");
     35			break;
     36		case -EINPROGRESS:
     37			/* no info output */
     38			break;
     39		default:
     40			dev_dbg(&urb->dev->dev,
     41				 "urb seq# %u may be in a error, status %d\n",
     42				 seqnum, status);
     43		}
     44
     45		list_del(&priv->list);
     46		kfree(priv);
     47		urb->hcpriv = NULL;
     48
     49		break;
     50	}
     51
     52	return urb;
     53}
     54
     55static void vhci_recv_ret_submit(struct vhci_device *vdev,
     56				 struct usbip_header *pdu)
     57{
     58	struct vhci_hcd *vhci_hcd = vdev_to_vhci_hcd(vdev);
     59	struct vhci *vhci = vhci_hcd->vhci;
     60	struct usbip_device *ud = &vdev->ud;
     61	struct urb *urb;
     62	unsigned long flags;
     63
     64	spin_lock_irqsave(&vdev->priv_lock, flags);
     65	urb = pickup_urb_and_free_priv(vdev, pdu->base.seqnum);
     66	spin_unlock_irqrestore(&vdev->priv_lock, flags);
     67
     68	if (!urb) {
     69		pr_err("cannot find a urb of seqnum %u max seqnum %d\n",
     70			pdu->base.seqnum,
     71			atomic_read(&vhci_hcd->seqnum));
     72		usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
     73		return;
     74	}
     75
     76	/* unpack the pdu to a urb */
     77	usbip_pack_pdu(pdu, urb, USBIP_RET_SUBMIT, 0);
     78
     79	/* recv transfer buffer */
     80	if (usbip_recv_xbuff(ud, urb) < 0) {
     81		urb->status = -EPROTO;
     82		goto error;
     83	}
     84
     85	/* recv iso_packet_descriptor */
     86	if (usbip_recv_iso(ud, urb) < 0) {
     87		urb->status = -EPROTO;
     88		goto error;
     89	}
     90
     91	/* restore the padding in iso packets */
     92	usbip_pad_iso(ud, urb);
     93
     94error:
     95	if (usbip_dbg_flag_vhci_rx)
     96		usbip_dump_urb(urb);
     97
     98	if (urb->num_sgs)
     99		urb->transfer_flags &= ~URB_DMA_MAP_SG;
    100
    101	usbip_dbg_vhci_rx("now giveback urb %u\n", pdu->base.seqnum);
    102
    103	spin_lock_irqsave(&vhci->lock, flags);
    104	usb_hcd_unlink_urb_from_ep(vhci_hcd_to_hcd(vhci_hcd), urb);
    105	spin_unlock_irqrestore(&vhci->lock, flags);
    106
    107	usb_hcd_giveback_urb(vhci_hcd_to_hcd(vhci_hcd), urb, urb->status);
    108
    109	usbip_dbg_vhci_rx("Leave\n");
    110}
    111
    112static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev,
    113						  struct usbip_header *pdu)
    114{
    115	struct vhci_unlink *unlink, *tmp;
    116	unsigned long flags;
    117
    118	spin_lock_irqsave(&vdev->priv_lock, flags);
    119
    120	list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) {
    121		pr_info("unlink->seqnum %lu\n", unlink->seqnum);
    122		if (unlink->seqnum == pdu->base.seqnum) {
    123			usbip_dbg_vhci_rx("found pending unlink, %lu\n",
    124					  unlink->seqnum);
    125			list_del(&unlink->list);
    126
    127			spin_unlock_irqrestore(&vdev->priv_lock, flags);
    128			return unlink;
    129		}
    130	}
    131
    132	spin_unlock_irqrestore(&vdev->priv_lock, flags);
    133
    134	return NULL;
    135}
    136
    137static void vhci_recv_ret_unlink(struct vhci_device *vdev,
    138				 struct usbip_header *pdu)
    139{
    140	struct vhci_hcd *vhci_hcd = vdev_to_vhci_hcd(vdev);
    141	struct vhci *vhci = vhci_hcd->vhci;
    142	struct vhci_unlink *unlink;
    143	struct urb *urb;
    144	unsigned long flags;
    145
    146	usbip_dump_header(pdu);
    147
    148	unlink = dequeue_pending_unlink(vdev, pdu);
    149	if (!unlink) {
    150		pr_info("cannot find the pending unlink %u\n",
    151			pdu->base.seqnum);
    152		return;
    153	}
    154
    155	spin_lock_irqsave(&vdev->priv_lock, flags);
    156	urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum);
    157	spin_unlock_irqrestore(&vdev->priv_lock, flags);
    158
    159	if (!urb) {
    160		/*
    161		 * I get the result of a unlink request. But, it seems that I
    162		 * already received the result of its submit result and gave
    163		 * back the URB.
    164		 */
    165		pr_info("the urb (seqnum %d) was already given back\n",
    166			pdu->base.seqnum);
    167	} else {
    168		usbip_dbg_vhci_rx("now giveback urb %d\n", pdu->base.seqnum);
    169
    170		/* If unlink is successful, status is -ECONNRESET */
    171		urb->status = pdu->u.ret_unlink.status;
    172		pr_info("urb->status %d\n", urb->status);
    173
    174		spin_lock_irqsave(&vhci->lock, flags);
    175		usb_hcd_unlink_urb_from_ep(vhci_hcd_to_hcd(vhci_hcd), urb);
    176		spin_unlock_irqrestore(&vhci->lock, flags);
    177
    178		usb_hcd_giveback_urb(vhci_hcd_to_hcd(vhci_hcd), urb, urb->status);
    179	}
    180
    181	kfree(unlink);
    182}
    183
    184static int vhci_priv_tx_empty(struct vhci_device *vdev)
    185{
    186	int empty = 0;
    187	unsigned long flags;
    188
    189	spin_lock_irqsave(&vdev->priv_lock, flags);
    190	empty = list_empty(&vdev->priv_rx);
    191	spin_unlock_irqrestore(&vdev->priv_lock, flags);
    192
    193	return empty;
    194}
    195
    196/* recv a pdu */
    197static void vhci_rx_pdu(struct usbip_device *ud)
    198{
    199	int ret;
    200	struct usbip_header pdu;
    201	struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
    202
    203	usbip_dbg_vhci_rx("Enter\n");
    204
    205	memset(&pdu, 0, sizeof(pdu));
    206
    207	/* receive a pdu header */
    208	ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu));
    209	if (ret < 0) {
    210		if (ret == -ECONNRESET)
    211			pr_info("connection reset by peer\n");
    212		else if (ret == -EAGAIN) {
    213			/* ignore if connection was idle */
    214			if (vhci_priv_tx_empty(vdev))
    215				return;
    216			pr_info("connection timed out with pending urbs\n");
    217		} else if (ret != -ERESTARTSYS)
    218			pr_info("xmit failed %d\n", ret);
    219
    220		usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
    221		return;
    222	}
    223	if (ret == 0) {
    224		pr_info("connection closed");
    225		usbip_event_add(ud, VDEV_EVENT_DOWN);
    226		return;
    227	}
    228	if (ret != sizeof(pdu)) {
    229		pr_err("received pdu size is %d, should be %d\n", ret,
    230		       (unsigned int)sizeof(pdu));
    231		usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
    232		return;
    233	}
    234
    235	usbip_header_correct_endian(&pdu, 0);
    236
    237	if (usbip_dbg_flag_vhci_rx)
    238		usbip_dump_header(&pdu);
    239
    240	switch (pdu.base.command) {
    241	case USBIP_RET_SUBMIT:
    242		vhci_recv_ret_submit(vdev, &pdu);
    243		break;
    244	case USBIP_RET_UNLINK:
    245		vhci_recv_ret_unlink(vdev, &pdu);
    246		break;
    247	default:
    248		/* NOT REACHED */
    249		pr_err("unknown pdu %u\n", pdu.base.command);
    250		usbip_dump_header(&pdu);
    251		usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
    252		break;
    253	}
    254}
    255
    256int vhci_rx_loop(void *data)
    257{
    258	struct usbip_device *ud = data;
    259
    260	while (!kthread_should_stop()) {
    261		if (usbip_event_happened(ud))
    262			break;
    263
    264		usbip_kcov_remote_start(ud);
    265		vhci_rx_pdu(ud);
    266		usbip_kcov_remote_stop();
    267	}
    268
    269	return 0;
    270}