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_subset.c (14793B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * f_subset.c -- "CDC Subset" Ethernet link function driver
      4 *
      5 * Copyright (C) 2003-2005,2008 David Brownell
      6 * Copyright (C) 2008 Nokia Corporation
      7 */
      8
      9#include <linux/slab.h>
     10#include <linux/kernel.h>
     11#include <linux/module.h>
     12#include <linux/device.h>
     13#include <linux/etherdevice.h>
     14
     15#include "u_ether.h"
     16#include "u_ether_configfs.h"
     17#include "u_gether.h"
     18
     19/*
     20 * This function packages a simple "CDC Subset" Ethernet port with no real
     21 * control mechanisms; just raw data transfer over two bulk endpoints.
     22 * The data transfer model is exactly that of CDC Ethernet, which is
     23 * why we call it the "CDC Subset".
     24 *
     25 * Because it's not standardized, this has some interoperability issues.
     26 * They mostly relate to driver binding, since the data transfer model is
     27 * so simple (CDC Ethernet).  The original versions of this protocol used
     28 * specific product/vendor IDs:  byteswapped IDs for Digital Equipment's
     29 * SA-1100 "Itsy" board, which could run Linux 2.4 kernels and supported
     30 * daughtercards with USB peripheral connectors.  (It was used more often
     31 * with other boards, using the Itsy identifiers.)  Linux hosts recognized
     32 * this with CONFIG_USB_ARMLINUX; these devices have only one configuration
     33 * and one interface.
     34 *
     35 * At some point, MCCI defined a (nonconformant) CDC MDLM variant called
     36 * "SAFE", which happens to have a mode which is identical to the "CDC
     37 * Subset" in terms of data transfer and lack of control model.  This was
     38 * adopted by later Sharp Zaurus models, and by some other software which
     39 * Linux hosts recognize with CONFIG_USB_NET_ZAURUS.
     40 *
     41 * Because Microsoft's RNDIS drivers are far from robust, we added a few
     42 * descriptors to the CDC Subset code, making this code look like a SAFE
     43 * implementation.  This lets you use MCCI's host side MS-Windows drivers
     44 * if you get fed up with RNDIS.  It also makes it easier for composite
     45 * drivers to work, since they can use class based binding instead of
     46 * caring about specific product and vendor IDs.
     47 */
     48
     49struct f_gether {
     50	struct gether			port;
     51
     52	char				ethaddr[14];
     53};
     54
     55static inline struct f_gether *func_to_geth(struct usb_function *f)
     56{
     57	return container_of(f, struct f_gether, port.func);
     58}
     59
     60/*-------------------------------------------------------------------------*/
     61
     62/*
     63 * "Simple" CDC-subset option is a simple vendor-neutral model that most
     64 * full speed controllers can handle:  one interface, two bulk endpoints.
     65 * To assist host side drivers, we fancy it up a bit, and add descriptors so
     66 * some host side drivers will understand it as a "SAFE" variant.
     67 *
     68 * "SAFE" loosely follows CDC WMC MDLM, violating the spec in various ways.
     69 * Data endpoints live in the control interface, there's no data interface.
     70 * And it's not used to talk to a cell phone radio.
     71 */
     72
     73/* interface descriptor: */
     74
     75static struct usb_interface_descriptor subset_data_intf = {
     76	.bLength =		sizeof subset_data_intf,
     77	.bDescriptorType =	USB_DT_INTERFACE,
     78
     79	/* .bInterfaceNumber = DYNAMIC */
     80	.bAlternateSetting =	0,
     81	.bNumEndpoints =	2,
     82	.bInterfaceClass =      USB_CLASS_COMM,
     83	.bInterfaceSubClass =	USB_CDC_SUBCLASS_MDLM,
     84	.bInterfaceProtocol =	0,
     85	/* .iInterface = DYNAMIC */
     86};
     87
     88static struct usb_cdc_header_desc mdlm_header_desc = {
     89	.bLength =		sizeof mdlm_header_desc,
     90	.bDescriptorType =	USB_DT_CS_INTERFACE,
     91	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
     92
     93	.bcdCDC =		cpu_to_le16(0x0110),
     94};
     95
     96static struct usb_cdc_mdlm_desc mdlm_desc = {
     97	.bLength =		sizeof mdlm_desc,
     98	.bDescriptorType =	USB_DT_CS_INTERFACE,
     99	.bDescriptorSubType =	USB_CDC_MDLM_TYPE,
    100
    101	.bcdVersion =		cpu_to_le16(0x0100),
    102	.bGUID = {
    103		0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6,
    104		0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f,
    105	},
    106};
    107
    108/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we
    109 * can't really use its struct.  All we do here is say that we're using
    110 * the submode of "SAFE" which directly matches the CDC Subset.
    111 */
    112static u8 mdlm_detail_desc[] = {
    113	6,
    114	USB_DT_CS_INTERFACE,
    115	USB_CDC_MDLM_DETAIL_TYPE,
    116
    117	0,	/* "SAFE" */
    118	0,	/* network control capabilities (none) */
    119	0,	/* network data capabilities ("raw" encapsulation) */
    120};
    121
    122static struct usb_cdc_ether_desc ether_desc = {
    123	.bLength =		sizeof ether_desc,
    124	.bDescriptorType =	USB_DT_CS_INTERFACE,
    125	.bDescriptorSubType =	USB_CDC_ETHERNET_TYPE,
    126
    127	/* this descriptor actually adds value, surprise! */
    128	/* .iMACAddress = DYNAMIC */
    129	.bmEthernetStatistics =	cpu_to_le32(0), /* no statistics */
    130	.wMaxSegmentSize =	cpu_to_le16(ETH_FRAME_LEN),
    131	.wNumberMCFilters =	cpu_to_le16(0),
    132	.bNumberPowerFilters =	0,
    133};
    134
    135/* full speed support: */
    136
    137static struct usb_endpoint_descriptor fs_subset_in_desc = {
    138	.bLength =		USB_DT_ENDPOINT_SIZE,
    139	.bDescriptorType =	USB_DT_ENDPOINT,
    140
    141	.bEndpointAddress =	USB_DIR_IN,
    142	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
    143};
    144
    145static struct usb_endpoint_descriptor fs_subset_out_desc = {
    146	.bLength =		USB_DT_ENDPOINT_SIZE,
    147	.bDescriptorType =	USB_DT_ENDPOINT,
    148
    149	.bEndpointAddress =	USB_DIR_OUT,
    150	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
    151};
    152
    153static struct usb_descriptor_header *fs_eth_function[] = {
    154	(struct usb_descriptor_header *) &subset_data_intf,
    155	(struct usb_descriptor_header *) &mdlm_header_desc,
    156	(struct usb_descriptor_header *) &mdlm_desc,
    157	(struct usb_descriptor_header *) &mdlm_detail_desc,
    158	(struct usb_descriptor_header *) &ether_desc,
    159	(struct usb_descriptor_header *) &fs_subset_in_desc,
    160	(struct usb_descriptor_header *) &fs_subset_out_desc,
    161	NULL,
    162};
    163
    164/* high speed support: */
    165
    166static struct usb_endpoint_descriptor hs_subset_in_desc = {
    167	.bLength =		USB_DT_ENDPOINT_SIZE,
    168	.bDescriptorType =	USB_DT_ENDPOINT,
    169
    170	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
    171	.wMaxPacketSize =	cpu_to_le16(512),
    172};
    173
    174static struct usb_endpoint_descriptor hs_subset_out_desc = {
    175	.bLength =		USB_DT_ENDPOINT_SIZE,
    176	.bDescriptorType =	USB_DT_ENDPOINT,
    177
    178	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
    179	.wMaxPacketSize =	cpu_to_le16(512),
    180};
    181
    182static struct usb_descriptor_header *hs_eth_function[] = {
    183	(struct usb_descriptor_header *) &subset_data_intf,
    184	(struct usb_descriptor_header *) &mdlm_header_desc,
    185	(struct usb_descriptor_header *) &mdlm_desc,
    186	(struct usb_descriptor_header *) &mdlm_detail_desc,
    187	(struct usb_descriptor_header *) &ether_desc,
    188	(struct usb_descriptor_header *) &hs_subset_in_desc,
    189	(struct usb_descriptor_header *) &hs_subset_out_desc,
    190	NULL,
    191};
    192
    193/* super speed support: */
    194
    195static struct usb_endpoint_descriptor ss_subset_in_desc = {
    196	.bLength =		USB_DT_ENDPOINT_SIZE,
    197	.bDescriptorType =	USB_DT_ENDPOINT,
    198
    199	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
    200	.wMaxPacketSize =	cpu_to_le16(1024),
    201};
    202
    203static struct usb_endpoint_descriptor ss_subset_out_desc = {
    204	.bLength =		USB_DT_ENDPOINT_SIZE,
    205	.bDescriptorType =	USB_DT_ENDPOINT,
    206
    207	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
    208	.wMaxPacketSize =	cpu_to_le16(1024),
    209};
    210
    211static struct usb_ss_ep_comp_descriptor ss_subset_bulk_comp_desc = {
    212	.bLength =		sizeof ss_subset_bulk_comp_desc,
    213	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
    214
    215	/* the following 2 values can be tweaked if necessary */
    216	/* .bMaxBurst =		0, */
    217	/* .bmAttributes =	0, */
    218};
    219
    220static struct usb_descriptor_header *ss_eth_function[] = {
    221	(struct usb_descriptor_header *) &subset_data_intf,
    222	(struct usb_descriptor_header *) &mdlm_header_desc,
    223	(struct usb_descriptor_header *) &mdlm_desc,
    224	(struct usb_descriptor_header *) &mdlm_detail_desc,
    225	(struct usb_descriptor_header *) &ether_desc,
    226	(struct usb_descriptor_header *) &ss_subset_in_desc,
    227	(struct usb_descriptor_header *) &ss_subset_bulk_comp_desc,
    228	(struct usb_descriptor_header *) &ss_subset_out_desc,
    229	(struct usb_descriptor_header *) &ss_subset_bulk_comp_desc,
    230	NULL,
    231};
    232
    233/* string descriptors: */
    234
    235static struct usb_string geth_string_defs[] = {
    236	[0].s = "CDC Ethernet Subset/SAFE",
    237	[1].s = "",
    238	{  } /* end of list */
    239};
    240
    241static struct usb_gadget_strings geth_string_table = {
    242	.language =		0x0409,	/* en-us */
    243	.strings =		geth_string_defs,
    244};
    245
    246static struct usb_gadget_strings *geth_strings[] = {
    247	&geth_string_table,
    248	NULL,
    249};
    250
    251/*-------------------------------------------------------------------------*/
    252
    253static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
    254{
    255	struct f_gether		*geth = func_to_geth(f);
    256	struct usb_composite_dev *cdev = f->config->cdev;
    257	struct net_device	*net;
    258
    259	/* we know alt == 0, so this is an activation or a reset */
    260
    261	if (geth->port.in_ep->enabled) {
    262		DBG(cdev, "reset cdc subset\n");
    263		gether_disconnect(&geth->port);
    264	}
    265
    266	DBG(cdev, "init + activate cdc subset\n");
    267	if (config_ep_by_speed(cdev->gadget, f, geth->port.in_ep) ||
    268	    config_ep_by_speed(cdev->gadget, f, geth->port.out_ep)) {
    269		geth->port.in_ep->desc = NULL;
    270		geth->port.out_ep->desc = NULL;
    271		return -EINVAL;
    272	}
    273
    274	net = gether_connect(&geth->port);
    275	return PTR_ERR_OR_ZERO(net);
    276}
    277
    278static void geth_disable(struct usb_function *f)
    279{
    280	struct f_gether	*geth = func_to_geth(f);
    281	struct usb_composite_dev *cdev = f->config->cdev;
    282
    283	DBG(cdev, "net deactivated\n");
    284	gether_disconnect(&geth->port);
    285}
    286
    287/*-------------------------------------------------------------------------*/
    288
    289/* serial function driver setup/binding */
    290
    291static int
    292geth_bind(struct usb_configuration *c, struct usb_function *f)
    293{
    294	struct usb_composite_dev *cdev = c->cdev;
    295	struct f_gether		*geth = func_to_geth(f);
    296	struct usb_string	*us;
    297	int			status;
    298	struct usb_ep		*ep;
    299
    300	struct f_gether_opts	*gether_opts;
    301
    302	gether_opts = container_of(f->fi, struct f_gether_opts, func_inst);
    303
    304	/*
    305	 * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
    306	 * configurations are bound in sequence with list_for_each_entry,
    307	 * in each configuration its functions are bound in sequence
    308	 * with list_for_each_entry, so we assume no race condition
    309	 * with regard to gether_opts->bound access
    310	 */
    311	if (!gether_opts->bound) {
    312		mutex_lock(&gether_opts->lock);
    313		gether_set_gadget(gether_opts->net, cdev->gadget);
    314		status = gether_register_netdev(gether_opts->net);
    315		mutex_unlock(&gether_opts->lock);
    316		if (status)
    317			return status;
    318		gether_opts->bound = true;
    319	}
    320
    321	us = usb_gstrings_attach(cdev, geth_strings,
    322				 ARRAY_SIZE(geth_string_defs));
    323	if (IS_ERR(us))
    324		return PTR_ERR(us);
    325
    326	subset_data_intf.iInterface = us[0].id;
    327	ether_desc.iMACAddress = us[1].id;
    328
    329	/* allocate instance-specific interface IDs */
    330	status = usb_interface_id(c, f);
    331	if (status < 0)
    332		goto fail;
    333	subset_data_intf.bInterfaceNumber = status;
    334
    335	status = -ENODEV;
    336
    337	/* allocate instance-specific endpoints */
    338	ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_in_desc);
    339	if (!ep)
    340		goto fail;
    341	geth->port.in_ep = ep;
    342
    343	ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_out_desc);
    344	if (!ep)
    345		goto fail;
    346	geth->port.out_ep = ep;
    347
    348	/* support all relevant hardware speeds... we expect that when
    349	 * hardware is dual speed, all bulk-capable endpoints work at
    350	 * both speeds
    351	 */
    352	hs_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress;
    353	hs_subset_out_desc.bEndpointAddress =
    354		fs_subset_out_desc.bEndpointAddress;
    355
    356	ss_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress;
    357	ss_subset_out_desc.bEndpointAddress =
    358		fs_subset_out_desc.bEndpointAddress;
    359
    360	status = usb_assign_descriptors(f, fs_eth_function, hs_eth_function,
    361			ss_eth_function, ss_eth_function);
    362	if (status)
    363		goto fail;
    364
    365	/* NOTE:  all that is done without knowing or caring about
    366	 * the network link ... which is unavailable to this code
    367	 * until we're activated via set_alt().
    368	 */
    369
    370	DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n",
    371			gadget_is_superspeed(c->cdev->gadget) ? "super" :
    372			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
    373			geth->port.in_ep->name, geth->port.out_ep->name);
    374	return 0;
    375
    376fail:
    377	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
    378
    379	return status;
    380}
    381
    382static inline struct f_gether_opts *to_f_gether_opts(struct config_item *item)
    383{
    384	return container_of(to_config_group(item), struct f_gether_opts,
    385			    func_inst.group);
    386}
    387
    388/* f_gether_item_ops */
    389USB_ETHERNET_CONFIGFS_ITEM(gether);
    390
    391/* f_gether_opts_dev_addr */
    392USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(gether);
    393
    394/* f_gether_opts_host_addr */
    395USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(gether);
    396
    397/* f_gether_opts_qmult */
    398USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(gether);
    399
    400/* f_gether_opts_ifname */
    401USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(gether);
    402
    403static struct configfs_attribute *gether_attrs[] = {
    404	&gether_opts_attr_dev_addr,
    405	&gether_opts_attr_host_addr,
    406	&gether_opts_attr_qmult,
    407	&gether_opts_attr_ifname,
    408	NULL,
    409};
    410
    411static const struct config_item_type gether_func_type = {
    412	.ct_item_ops	= &gether_item_ops,
    413	.ct_attrs	= gether_attrs,
    414	.ct_owner	= THIS_MODULE,
    415};
    416
    417static void geth_free_inst(struct usb_function_instance *f)
    418{
    419	struct f_gether_opts *opts;
    420
    421	opts = container_of(f, struct f_gether_opts, func_inst);
    422	if (opts->bound)
    423		gether_cleanup(netdev_priv(opts->net));
    424	else
    425		free_netdev(opts->net);
    426	kfree(opts);
    427}
    428
    429static struct usb_function_instance *geth_alloc_inst(void)
    430{
    431	struct f_gether_opts *opts;
    432
    433	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
    434	if (!opts)
    435		return ERR_PTR(-ENOMEM);
    436	mutex_init(&opts->lock);
    437	opts->func_inst.free_func_inst = geth_free_inst;
    438	opts->net = gether_setup_default();
    439	if (IS_ERR(opts->net)) {
    440		struct net_device *net = opts->net;
    441		kfree(opts);
    442		return ERR_CAST(net);
    443	}
    444
    445	config_group_init_type_name(&opts->func_inst.group, "",
    446				    &gether_func_type);
    447
    448	return &opts->func_inst;
    449}
    450
    451static void geth_free(struct usb_function *f)
    452{
    453	struct f_gether *eth;
    454
    455	eth = func_to_geth(f);
    456	kfree(eth);
    457}
    458
    459static void geth_unbind(struct usb_configuration *c, struct usb_function *f)
    460{
    461	geth_string_defs[0].id = 0;
    462	usb_free_all_descriptors(f);
    463}
    464
    465static struct usb_function *geth_alloc(struct usb_function_instance *fi)
    466{
    467	struct f_gether	*geth;
    468	struct f_gether_opts *opts;
    469	int status;
    470
    471	/* allocate and initialize one new instance */
    472	geth = kzalloc(sizeof(*geth), GFP_KERNEL);
    473	if (!geth)
    474		return ERR_PTR(-ENOMEM);
    475
    476	opts = container_of(fi, struct f_gether_opts, func_inst);
    477
    478	mutex_lock(&opts->lock);
    479	opts->refcnt++;
    480	/* export host's Ethernet address in CDC format */
    481	status = gether_get_host_addr_cdc(opts->net, geth->ethaddr,
    482					  sizeof(geth->ethaddr));
    483	if (status < 12) {
    484		kfree(geth);
    485		mutex_unlock(&opts->lock);
    486		return ERR_PTR(-EINVAL);
    487	}
    488	geth_string_defs[1].s = geth->ethaddr;
    489
    490	geth->port.ioport = netdev_priv(opts->net);
    491	mutex_unlock(&opts->lock);
    492	geth->port.cdc_filter = DEFAULT_FILTER;
    493
    494	geth->port.func.name = "cdc_subset";
    495	geth->port.func.bind = geth_bind;
    496	geth->port.func.unbind = geth_unbind;
    497	geth->port.func.set_alt = geth_set_alt;
    498	geth->port.func.disable = geth_disable;
    499	geth->port.func.free_func = geth_free;
    500
    501	return &geth->port.func;
    502}
    503
    504DECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc);
    505MODULE_LICENSE("GPL");
    506MODULE_AUTHOR("David Brownell");