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

f_loopback.c (14328B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * f_loopback.c - USB peripheral loopback configuration driver
      4 *
      5 * Copyright (C) 2003-2008 David Brownell
      6 * Copyright (C) 2008 by Nokia Corporation
      7 */
      8
      9/* #define VERBOSE_DEBUG */
     10
     11#include <linux/slab.h>
     12#include <linux/kernel.h>
     13#include <linux/device.h>
     14#include <linux/module.h>
     15#include <linux/err.h>
     16#include <linux/usb/composite.h>
     17
     18#include "g_zero.h"
     19#include "u_f.h"
     20
     21/*
     22 * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,
     23 *
     24 * This takes messages of various sizes written OUT to a device, and loops
     25 * them back so they can be read IN from it.  It has been used by certain
     26 * test applications.  It supports limited testing of data queueing logic.
     27 */
     28struct f_loopback {
     29	struct usb_function	function;
     30
     31	struct usb_ep		*in_ep;
     32	struct usb_ep		*out_ep;
     33
     34	unsigned                qlen;
     35	unsigned                buflen;
     36};
     37
     38static inline struct f_loopback *func_to_loop(struct usb_function *f)
     39{
     40	return container_of(f, struct f_loopback, function);
     41}
     42
     43/*-------------------------------------------------------------------------*/
     44
     45static struct usb_interface_descriptor loopback_intf = {
     46	.bLength =		sizeof(loopback_intf),
     47	.bDescriptorType =	USB_DT_INTERFACE,
     48
     49	.bNumEndpoints =	2,
     50	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
     51	/* .iInterface = DYNAMIC */
     52};
     53
     54/* full speed support: */
     55
     56static struct usb_endpoint_descriptor fs_loop_source_desc = {
     57	.bLength =		USB_DT_ENDPOINT_SIZE,
     58	.bDescriptorType =	USB_DT_ENDPOINT,
     59
     60	.bEndpointAddress =	USB_DIR_IN,
     61	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
     62};
     63
     64static struct usb_endpoint_descriptor fs_loop_sink_desc = {
     65	.bLength =		USB_DT_ENDPOINT_SIZE,
     66	.bDescriptorType =	USB_DT_ENDPOINT,
     67
     68	.bEndpointAddress =	USB_DIR_OUT,
     69	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
     70};
     71
     72static struct usb_descriptor_header *fs_loopback_descs[] = {
     73	(struct usb_descriptor_header *) &loopback_intf,
     74	(struct usb_descriptor_header *) &fs_loop_sink_desc,
     75	(struct usb_descriptor_header *) &fs_loop_source_desc,
     76	NULL,
     77};
     78
     79/* high speed support: */
     80
     81static struct usb_endpoint_descriptor hs_loop_source_desc = {
     82	.bLength =		USB_DT_ENDPOINT_SIZE,
     83	.bDescriptorType =	USB_DT_ENDPOINT,
     84
     85	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
     86	.wMaxPacketSize =	cpu_to_le16(512),
     87};
     88
     89static struct usb_endpoint_descriptor hs_loop_sink_desc = {
     90	.bLength =		USB_DT_ENDPOINT_SIZE,
     91	.bDescriptorType =	USB_DT_ENDPOINT,
     92
     93	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
     94	.wMaxPacketSize =	cpu_to_le16(512),
     95};
     96
     97static struct usb_descriptor_header *hs_loopback_descs[] = {
     98	(struct usb_descriptor_header *) &loopback_intf,
     99	(struct usb_descriptor_header *) &hs_loop_source_desc,
    100	(struct usb_descriptor_header *) &hs_loop_sink_desc,
    101	NULL,
    102};
    103
    104/* super speed support: */
    105
    106static struct usb_endpoint_descriptor ss_loop_source_desc = {
    107	.bLength =		USB_DT_ENDPOINT_SIZE,
    108	.bDescriptorType =	USB_DT_ENDPOINT,
    109
    110	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
    111	.wMaxPacketSize =	cpu_to_le16(1024),
    112};
    113
    114static struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = {
    115	.bLength =		USB_DT_SS_EP_COMP_SIZE,
    116	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
    117	.bMaxBurst =		0,
    118	.bmAttributes =		0,
    119	.wBytesPerInterval =	0,
    120};
    121
    122static struct usb_endpoint_descriptor ss_loop_sink_desc = {
    123	.bLength =		USB_DT_ENDPOINT_SIZE,
    124	.bDescriptorType =	USB_DT_ENDPOINT,
    125
    126	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
    127	.wMaxPacketSize =	cpu_to_le16(1024),
    128};
    129
    130static struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = {
    131	.bLength =		USB_DT_SS_EP_COMP_SIZE,
    132	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
    133	.bMaxBurst =		0,
    134	.bmAttributes =		0,
    135	.wBytesPerInterval =	0,
    136};
    137
    138static struct usb_descriptor_header *ss_loopback_descs[] = {
    139	(struct usb_descriptor_header *) &loopback_intf,
    140	(struct usb_descriptor_header *) &ss_loop_source_desc,
    141	(struct usb_descriptor_header *) &ss_loop_source_comp_desc,
    142	(struct usb_descriptor_header *) &ss_loop_sink_desc,
    143	(struct usb_descriptor_header *) &ss_loop_sink_comp_desc,
    144	NULL,
    145};
    146
    147/* function-specific strings: */
    148
    149static struct usb_string strings_loopback[] = {
    150	[0].s = "loop input to output",
    151	{  }			/* end of list */
    152};
    153
    154static struct usb_gadget_strings stringtab_loop = {
    155	.language	= 0x0409,	/* en-us */
    156	.strings	= strings_loopback,
    157};
    158
    159static struct usb_gadget_strings *loopback_strings[] = {
    160	&stringtab_loop,
    161	NULL,
    162};
    163
    164/*-------------------------------------------------------------------------*/
    165
    166static int loopback_bind(struct usb_configuration *c, struct usb_function *f)
    167{
    168	struct usb_composite_dev *cdev = c->cdev;
    169	struct f_loopback	*loop = func_to_loop(f);
    170	int			id;
    171	int ret;
    172
    173	/* allocate interface ID(s) */
    174	id = usb_interface_id(c, f);
    175	if (id < 0)
    176		return id;
    177	loopback_intf.bInterfaceNumber = id;
    178
    179	id = usb_string_id(cdev);
    180	if (id < 0)
    181		return id;
    182	strings_loopback[0].id = id;
    183	loopback_intf.iInterface = id;
    184
    185	/* allocate endpoints */
    186
    187	loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);
    188	if (!loop->in_ep) {
    189autoconf_fail:
    190		ERROR(cdev, "%s: can't autoconfigure on %s\n",
    191			f->name, cdev->gadget->name);
    192		return -ENODEV;
    193	}
    194
    195	loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);
    196	if (!loop->out_ep)
    197		goto autoconf_fail;
    198
    199	/* support high speed hardware */
    200	hs_loop_source_desc.bEndpointAddress =
    201		fs_loop_source_desc.bEndpointAddress;
    202	hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
    203
    204	/* support super speed hardware */
    205	ss_loop_source_desc.bEndpointAddress =
    206		fs_loop_source_desc.bEndpointAddress;
    207	ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
    208
    209	ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs,
    210			ss_loopback_descs, ss_loopback_descs);
    211	if (ret)
    212		return ret;
    213
    214	DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
    215	    (gadget_is_superspeed(c->cdev->gadget) ? "super" :
    216	     (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
    217			f->name, loop->in_ep->name, loop->out_ep->name);
    218	return 0;
    219}
    220
    221static void lb_free_func(struct usb_function *f)
    222{
    223	struct f_lb_opts *opts;
    224
    225	opts = container_of(f->fi, struct f_lb_opts, func_inst);
    226
    227	mutex_lock(&opts->lock);
    228	opts->refcnt--;
    229	mutex_unlock(&opts->lock);
    230
    231	usb_free_all_descriptors(f);
    232	kfree(func_to_loop(f));
    233}
    234
    235static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
    236{
    237	struct f_loopback	*loop = ep->driver_data;
    238	struct usb_composite_dev *cdev = loop->function.config->cdev;
    239	int			status = req->status;
    240
    241	switch (status) {
    242	case 0:				/* normal completion? */
    243		if (ep == loop->out_ep) {
    244			/*
    245			 * We received some data from the host so let's
    246			 * queue it so host can read the from our in ep
    247			 */
    248			struct usb_request *in_req = req->context;
    249
    250			in_req->zero = (req->actual < req->length);
    251			in_req->length = req->actual;
    252			ep = loop->in_ep;
    253			req = in_req;
    254		} else {
    255			/*
    256			 * We have just looped back a bunch of data
    257			 * to host. Now let's wait for some more data.
    258			 */
    259			req = req->context;
    260			ep = loop->out_ep;
    261		}
    262
    263		/* queue the buffer back to host or for next bunch of data */
    264		status = usb_ep_queue(ep, req, GFP_ATOMIC);
    265		if (status == 0) {
    266			return;
    267		} else {
    268			ERROR(cdev, "Unable to loop back buffer to %s: %d\n",
    269			      ep->name, status);
    270			goto free_req;
    271		}
    272
    273		/* "should never get here" */
    274	default:
    275		ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
    276				status, req->actual, req->length);
    277		fallthrough;
    278
    279	/* NOTE:  since this driver doesn't maintain an explicit record
    280	 * of requests it submitted (just maintains qlen count), we
    281	 * rely on the hardware driver to clean up on disconnect or
    282	 * endpoint disable.
    283	 */
    284	case -ECONNABORTED:		/* hardware forced ep reset */
    285	case -ECONNRESET:		/* request dequeued */
    286	case -ESHUTDOWN:		/* disconnect from host */
    287free_req:
    288		usb_ep_free_request(ep == loop->in_ep ?
    289				    loop->out_ep : loop->in_ep,
    290				    req->context);
    291		free_ep_req(ep, req);
    292		return;
    293	}
    294}
    295
    296static void disable_loopback(struct f_loopback *loop)
    297{
    298	struct usb_composite_dev	*cdev;
    299
    300	cdev = loop->function.config->cdev;
    301	disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL);
    302	VDBG(cdev, "%s disabled\n", loop->function.name);
    303}
    304
    305static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len)
    306{
    307	return alloc_ep_req(ep, len);
    308}
    309
    310static int alloc_requests(struct usb_composite_dev *cdev,
    311			  struct f_loopback *loop)
    312{
    313	struct usb_request *in_req, *out_req;
    314	int i;
    315	int result = 0;
    316
    317	/*
    318	 * allocate a bunch of read buffers and queue them all at once.
    319	 * we buffer at most 'qlen' transfers; We allocate buffers only
    320	 * for out transfer and reuse them in IN transfers to implement
    321	 * our loopback functionality
    322	 */
    323	for (i = 0; i < loop->qlen && result == 0; i++) {
    324		result = -ENOMEM;
    325
    326		in_req = usb_ep_alloc_request(loop->in_ep, GFP_ATOMIC);
    327		if (!in_req)
    328			goto fail;
    329
    330		out_req = lb_alloc_ep_req(loop->out_ep, loop->buflen);
    331		if (!out_req)
    332			goto fail_in;
    333
    334		in_req->complete = loopback_complete;
    335		out_req->complete = loopback_complete;
    336
    337		in_req->buf = out_req->buf;
    338		/* length will be set in complete routine */
    339		in_req->context = out_req;
    340		out_req->context = in_req;
    341
    342		result = usb_ep_queue(loop->out_ep, out_req, GFP_ATOMIC);
    343		if (result) {
    344			ERROR(cdev, "%s queue req --> %d\n",
    345					loop->out_ep->name, result);
    346			goto fail_out;
    347		}
    348	}
    349
    350	return 0;
    351
    352fail_out:
    353	free_ep_req(loop->out_ep, out_req);
    354fail_in:
    355	usb_ep_free_request(loop->in_ep, in_req);
    356fail:
    357	return result;
    358}
    359
    360static int enable_endpoint(struct usb_composite_dev *cdev,
    361			   struct f_loopback *loop, struct usb_ep *ep)
    362{
    363	int					result;
    364
    365	result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
    366	if (result)
    367		goto out;
    368
    369	result = usb_ep_enable(ep);
    370	if (result < 0)
    371		goto out;
    372	ep->driver_data = loop;
    373	result = 0;
    374
    375out:
    376	return result;
    377}
    378
    379static int
    380enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
    381{
    382	int					result = 0;
    383
    384	result = enable_endpoint(cdev, loop, loop->in_ep);
    385	if (result)
    386		goto out;
    387
    388	result = enable_endpoint(cdev, loop, loop->out_ep);
    389	if (result)
    390		goto disable_in;
    391
    392	result = alloc_requests(cdev, loop);
    393	if (result)
    394		goto disable_out;
    395
    396	DBG(cdev, "%s enabled\n", loop->function.name);
    397	return 0;
    398
    399disable_out:
    400	usb_ep_disable(loop->out_ep);
    401disable_in:
    402	usb_ep_disable(loop->in_ep);
    403out:
    404	return result;
    405}
    406
    407static int loopback_set_alt(struct usb_function *f,
    408		unsigned intf, unsigned alt)
    409{
    410	struct f_loopback	*loop = func_to_loop(f);
    411	struct usb_composite_dev *cdev = f->config->cdev;
    412
    413	/* we know alt is zero */
    414	disable_loopback(loop);
    415	return enable_loopback(cdev, loop);
    416}
    417
    418static void loopback_disable(struct usb_function *f)
    419{
    420	struct f_loopback	*loop = func_to_loop(f);
    421
    422	disable_loopback(loop);
    423}
    424
    425static struct usb_function *loopback_alloc(struct usb_function_instance *fi)
    426{
    427	struct f_loopback	*loop;
    428	struct f_lb_opts	*lb_opts;
    429
    430	loop = kzalloc(sizeof *loop, GFP_KERNEL);
    431	if (!loop)
    432		return ERR_PTR(-ENOMEM);
    433
    434	lb_opts = container_of(fi, struct f_lb_opts, func_inst);
    435
    436	mutex_lock(&lb_opts->lock);
    437	lb_opts->refcnt++;
    438	mutex_unlock(&lb_opts->lock);
    439
    440	loop->buflen = lb_opts->bulk_buflen;
    441	loop->qlen = lb_opts->qlen;
    442	if (!loop->qlen)
    443		loop->qlen = 32;
    444
    445	loop->function.name = "loopback";
    446	loop->function.bind = loopback_bind;
    447	loop->function.set_alt = loopback_set_alt;
    448	loop->function.disable = loopback_disable;
    449	loop->function.strings = loopback_strings;
    450
    451	loop->function.free_func = lb_free_func;
    452
    453	return &loop->function;
    454}
    455
    456static inline struct f_lb_opts *to_f_lb_opts(struct config_item *item)
    457{
    458	return container_of(to_config_group(item), struct f_lb_opts,
    459			    func_inst.group);
    460}
    461
    462static void lb_attr_release(struct config_item *item)
    463{
    464	struct f_lb_opts *lb_opts = to_f_lb_opts(item);
    465
    466	usb_put_function_instance(&lb_opts->func_inst);
    467}
    468
    469static struct configfs_item_operations lb_item_ops = {
    470	.release		= lb_attr_release,
    471};
    472
    473static ssize_t f_lb_opts_qlen_show(struct config_item *item, char *page)
    474{
    475	struct f_lb_opts *opts = to_f_lb_opts(item);
    476	int result;
    477
    478	mutex_lock(&opts->lock);
    479	result = sprintf(page, "%d\n", opts->qlen);
    480	mutex_unlock(&opts->lock);
    481
    482	return result;
    483}
    484
    485static ssize_t f_lb_opts_qlen_store(struct config_item *item,
    486				    const char *page, size_t len)
    487{
    488	struct f_lb_opts *opts = to_f_lb_opts(item);
    489	int ret;
    490	u32 num;
    491
    492	mutex_lock(&opts->lock);
    493	if (opts->refcnt) {
    494		ret = -EBUSY;
    495		goto end;
    496	}
    497
    498	ret = kstrtou32(page, 0, &num);
    499	if (ret)
    500		goto end;
    501
    502	opts->qlen = num;
    503	ret = len;
    504end:
    505	mutex_unlock(&opts->lock);
    506	return ret;
    507}
    508
    509CONFIGFS_ATTR(f_lb_opts_, qlen);
    510
    511static ssize_t f_lb_opts_bulk_buflen_show(struct config_item *item, char *page)
    512{
    513	struct f_lb_opts *opts = to_f_lb_opts(item);
    514	int result;
    515
    516	mutex_lock(&opts->lock);
    517	result = sprintf(page, "%d\n", opts->bulk_buflen);
    518	mutex_unlock(&opts->lock);
    519
    520	return result;
    521}
    522
    523static ssize_t f_lb_opts_bulk_buflen_store(struct config_item *item,
    524				    const char *page, size_t len)
    525{
    526	struct f_lb_opts *opts = to_f_lb_opts(item);
    527	int ret;
    528	u32 num;
    529
    530	mutex_lock(&opts->lock);
    531	if (opts->refcnt) {
    532		ret = -EBUSY;
    533		goto end;
    534	}
    535
    536	ret = kstrtou32(page, 0, &num);
    537	if (ret)
    538		goto end;
    539
    540	opts->bulk_buflen = num;
    541	ret = len;
    542end:
    543	mutex_unlock(&opts->lock);
    544	return ret;
    545}
    546
    547CONFIGFS_ATTR(f_lb_opts_, bulk_buflen);
    548
    549static struct configfs_attribute *lb_attrs[] = {
    550	&f_lb_opts_attr_qlen,
    551	&f_lb_opts_attr_bulk_buflen,
    552	NULL,
    553};
    554
    555static const struct config_item_type lb_func_type = {
    556	.ct_item_ops    = &lb_item_ops,
    557	.ct_attrs	= lb_attrs,
    558	.ct_owner       = THIS_MODULE,
    559};
    560
    561static void lb_free_instance(struct usb_function_instance *fi)
    562{
    563	struct f_lb_opts *lb_opts;
    564
    565	lb_opts = container_of(fi, struct f_lb_opts, func_inst);
    566	kfree(lb_opts);
    567}
    568
    569static struct usb_function_instance *loopback_alloc_instance(void)
    570{
    571	struct f_lb_opts *lb_opts;
    572
    573	lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL);
    574	if (!lb_opts)
    575		return ERR_PTR(-ENOMEM);
    576	mutex_init(&lb_opts->lock);
    577	lb_opts->func_inst.free_func_inst = lb_free_instance;
    578	lb_opts->bulk_buflen = GZERO_BULK_BUFLEN;
    579	lb_opts->qlen = GZERO_QLEN;
    580
    581	config_group_init_type_name(&lb_opts->func_inst.group, "",
    582				    &lb_func_type);
    583
    584	return  &lb_opts->func_inst;
    585}
    586DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc);
    587
    588int __init lb_modinit(void)
    589{
    590	return usb_function_register(&Loopbackusb_func);
    591}
    592
    593void __exit lb_modexit(void)
    594{
    595	usb_function_unregister(&Loopbackusb_func);
    596}
    597
    598MODULE_LICENSE("GPL");