brintos

brintos / linux-shallow public Read only

0
0
Text · 13.2 KiB · e63720e Raw
634 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Management Component Transport Protocol (MCTP) - serial transport4 * binding. This driver is an implementation of the DMTF specificiation5 * "DSP0253 - Management Component Transport Protocol (MCTP) Serial Transport6 * Binding", available at:7 *8 *  https://www.dmtf.org/sites/default/files/standards/documents/DSP0253_1.0.0.pdf9 *10 * This driver provides DSP0253-type MCTP-over-serial transport using a Linux11 * tty device, by setting the N_MCTP line discipline on the tty.12 *13 * Copyright (c) 2021 Code Construct14 */15 16#include <linux/idr.h>17#include <linux/if_arp.h>18#include <linux/module.h>19#include <linux/skbuff.h>20#include <linux/tty.h>21#include <linux/workqueue.h>22#include <linux/crc-ccitt.h>23 24#include <linux/mctp.h>25#include <net/mctp.h>26#include <net/pkt_sched.h>27 28#define MCTP_SERIAL_MTU		68 /* base mtu (64) + mctp header */29#define MCTP_SERIAL_FRAME_MTU	(MCTP_SERIAL_MTU + 6) /* + serial framing */30 31#define MCTP_SERIAL_VERSION	0x1 /* DSP0253 defines a single version: 1 */32 33#define BUFSIZE			MCTP_SERIAL_FRAME_MTU34 35#define BYTE_FRAME		0x7e36#define BYTE_ESC		0x7d37 38#define FCS_INIT		0xffff39 40static DEFINE_IDA(mctp_serial_ida);41 42enum mctp_serial_state {43	STATE_IDLE,44	STATE_START,45	STATE_HEADER,46	STATE_DATA,47	STATE_ESCAPE,48	STATE_TRAILER,49	STATE_DONE,50	STATE_ERR,51};52 53struct mctp_serial {54	struct net_device	*netdev;55	struct tty_struct	*tty;56 57	int			idx;58 59	/* protects our rx & tx state machines; held during both paths */60	spinlock_t		lock;61 62	struct work_struct	tx_work;63	enum mctp_serial_state	txstate, rxstate;64	u16			txfcs, rxfcs, rxfcs_rcvd;65	unsigned int		txlen, rxlen;66	unsigned int		txpos, rxpos;67	u8			txbuf[BUFSIZE],68				rxbuf[BUFSIZE];69};70 71static bool needs_escape(u8 c)72{73	return c == BYTE_ESC || c == BYTE_FRAME;74}75 76static unsigned int next_chunk_len(struct mctp_serial *dev)77{78	unsigned int i;79 80	/* either we have no bytes to send ... */81	if (dev->txpos == dev->txlen)82		return 0;83 84	/* ... or the next byte to send is an escaped byte; requiring a85	 * single-byte chunk...86	 */87	if (needs_escape(dev->txbuf[dev->txpos]))88		return 1;89 90	/* ... or we have one or more bytes up to the next escape - this chunk91	 * will be those non-escaped bytes, and does not include the escaped92	 * byte.93	 */94	for (i = 1; i + dev->txpos < dev->txlen; i++) {95		if (needs_escape(dev->txbuf[dev->txpos + i]))96			break;97	}98 99	return i;100}101 102static ssize_t write_chunk(struct mctp_serial *dev, u8 *buf, size_t len)103{104	return dev->tty->ops->write(dev->tty, buf, len);105}106 107static void mctp_serial_tx_work(struct work_struct *work)108{109	struct mctp_serial *dev = container_of(work, struct mctp_serial,110					       tx_work);111	unsigned long flags;112	ssize_t txlen;113	unsigned int len;114	u8 c, buf[3];115 116	spin_lock_irqsave(&dev->lock, flags);117 118	/* txstate represents the next thing to send */119	switch (dev->txstate) {120	case STATE_START:121		dev->txpos = 0;122		fallthrough;123	case STATE_HEADER:124		buf[0] = BYTE_FRAME;125		buf[1] = MCTP_SERIAL_VERSION;126		buf[2] = dev->txlen;127 128		if (!dev->txpos)129			dev->txfcs = crc_ccitt(FCS_INIT, buf + 1, 2);130 131		txlen = write_chunk(dev, buf + dev->txpos, 3 - dev->txpos);132		if (txlen <= 0) {133			dev->txstate = STATE_ERR;134		} else {135			dev->txpos += txlen;136			if (dev->txpos == 3) {137				dev->txstate = STATE_DATA;138				dev->txpos = 0;139			}140		}141		break;142 143	case STATE_ESCAPE:144		buf[0] = dev->txbuf[dev->txpos] & ~0x20;145		txlen = write_chunk(dev, buf, 1);146		if (txlen <= 0) {147			dev->txstate = STATE_ERR;148		} else {149			dev->txpos += txlen;150			if (dev->txpos == dev->txlen) {151				dev->txstate = STATE_TRAILER;152				dev->txpos = 0;153			}154		}155 156		break;157 158	case STATE_DATA:159		len = next_chunk_len(dev);160		if (len) {161			c = dev->txbuf[dev->txpos];162			if (len == 1 && needs_escape(c)) {163				buf[0] = BYTE_ESC;164				buf[1] = c & ~0x20;165				dev->txfcs = crc_ccitt_byte(dev->txfcs, c);166				txlen = write_chunk(dev, buf, 2);167				if (txlen == 2)168					dev->txpos++;169				else if (txlen == 1)170					dev->txstate = STATE_ESCAPE;171				else172					dev->txstate = STATE_ERR;173			} else {174				txlen = write_chunk(dev,175						    dev->txbuf + dev->txpos,176						    len);177				if (txlen <= 0) {178					dev->txstate = STATE_ERR;179				} else {180					dev->txfcs = crc_ccitt(dev->txfcs,181							       dev->txbuf +182							       dev->txpos,183							       txlen);184					dev->txpos += txlen;185				}186			}187			if (dev->txstate == STATE_DATA &&188			    dev->txpos == dev->txlen) {189				dev->txstate = STATE_TRAILER;190				dev->txpos = 0;191			}192			break;193		}194		dev->txstate = STATE_TRAILER;195		dev->txpos = 0;196		fallthrough;197 198	case STATE_TRAILER:199		buf[0] = dev->txfcs >> 8;200		buf[1] = dev->txfcs & 0xff;201		buf[2] = BYTE_FRAME;202		txlen = write_chunk(dev, buf + dev->txpos, 3 - dev->txpos);203		if (txlen <= 0) {204			dev->txstate = STATE_ERR;205		} else {206			dev->txpos += txlen;207			if (dev->txpos == 3) {208				dev->txstate = STATE_DONE;209				dev->txpos = 0;210			}211		}212		break;213	default:214		netdev_err_once(dev->netdev, "invalid tx state %d\n",215				dev->txstate);216	}217 218	if (dev->txstate == STATE_DONE) {219		dev->netdev->stats.tx_packets++;220		dev->netdev->stats.tx_bytes += dev->txlen;221		dev->txlen = 0;222		dev->txpos = 0;223		clear_bit(TTY_DO_WRITE_WAKEUP, &dev->tty->flags);224		dev->txstate = STATE_IDLE;225		spin_unlock_irqrestore(&dev->lock, flags);226 227		netif_wake_queue(dev->netdev);228	} else {229		spin_unlock_irqrestore(&dev->lock, flags);230	}231}232 233static netdev_tx_t mctp_serial_tx(struct sk_buff *skb, struct net_device *ndev)234{235	struct mctp_serial *dev = netdev_priv(ndev);236	unsigned long flags;237 238	WARN_ON(dev->txstate != STATE_IDLE);239 240	if (skb->len > MCTP_SERIAL_MTU) {241		dev->netdev->stats.tx_dropped++;242		goto out;243	}244 245	spin_lock_irqsave(&dev->lock, flags);246	netif_stop_queue(dev->netdev);247	skb_copy_bits(skb, 0, dev->txbuf, skb->len);248	dev->txpos = 0;249	dev->txlen = skb->len;250	dev->txstate = STATE_START;251	spin_unlock_irqrestore(&dev->lock, flags);252 253	set_bit(TTY_DO_WRITE_WAKEUP, &dev->tty->flags);254	schedule_work(&dev->tx_work);255 256out:257	kfree_skb(skb);258	return NETDEV_TX_OK;259}260 261static void mctp_serial_tty_write_wakeup(struct tty_struct *tty)262{263	struct mctp_serial *dev = tty->disc_data;264 265	schedule_work(&dev->tx_work);266}267 268static void mctp_serial_rx(struct mctp_serial *dev)269{270	struct mctp_skb_cb *cb;271	struct sk_buff *skb;272 273	if (dev->rxfcs != dev->rxfcs_rcvd) {274		dev->netdev->stats.rx_dropped++;275		dev->netdev->stats.rx_crc_errors++;276		return;277	}278 279	skb = netdev_alloc_skb(dev->netdev, dev->rxlen);280	if (!skb) {281		dev->netdev->stats.rx_dropped++;282		return;283	}284 285	skb->protocol = htons(ETH_P_MCTP);286	skb_put_data(skb, dev->rxbuf, dev->rxlen);287	skb_reset_network_header(skb);288 289	cb = __mctp_cb(skb);290	cb->halen = 0;291 292	netif_rx(skb);293	dev->netdev->stats.rx_packets++;294	dev->netdev->stats.rx_bytes += dev->rxlen;295}296 297static void mctp_serial_push_header(struct mctp_serial *dev, u8 c)298{299	switch (dev->rxpos) {300	case 0:301		if (c == BYTE_FRAME)302			dev->rxpos++;303		else304			dev->rxstate = STATE_ERR;305		break;306	case 1:307		if (c == MCTP_SERIAL_VERSION) {308			dev->rxpos++;309			dev->rxfcs = crc_ccitt_byte(FCS_INIT, c);310		} else {311			dev->rxstate = STATE_ERR;312		}313		break;314	case 2:315		if (c > MCTP_SERIAL_FRAME_MTU) {316			dev->rxstate = STATE_ERR;317		} else {318			dev->rxlen = c;319			dev->rxpos = 0;320			dev->rxstate = STATE_DATA;321			dev->rxfcs = crc_ccitt_byte(dev->rxfcs, c);322		}323		break;324	}325}326 327static void mctp_serial_push_trailer(struct mctp_serial *dev, u8 c)328{329	switch (dev->rxpos) {330	case 0:331		dev->rxfcs_rcvd = c << 8;332		dev->rxpos++;333		break;334	case 1:335		dev->rxfcs_rcvd |= c;336		dev->rxpos++;337		break;338	case 2:339		if (c != BYTE_FRAME) {340			dev->rxstate = STATE_ERR;341		} else {342			mctp_serial_rx(dev);343			dev->rxlen = 0;344			dev->rxpos = 0;345			dev->rxstate = STATE_IDLE;346		}347		break;348	}349}350 351static void mctp_serial_push(struct mctp_serial *dev, u8 c)352{353	switch (dev->rxstate) {354	case STATE_IDLE:355		dev->rxstate = STATE_HEADER;356		fallthrough;357	case STATE_HEADER:358		mctp_serial_push_header(dev, c);359		break;360 361	case STATE_ESCAPE:362		c |= 0x20;363		fallthrough;364	case STATE_DATA:365		if (dev->rxstate != STATE_ESCAPE && c == BYTE_ESC) {366			dev->rxstate = STATE_ESCAPE;367		} else {368			dev->rxfcs = crc_ccitt_byte(dev->rxfcs, c);369			dev->rxbuf[dev->rxpos] = c;370			dev->rxpos++;371			dev->rxstate = STATE_DATA;372			if (dev->rxpos == dev->rxlen) {373				dev->rxpos = 0;374				dev->rxstate = STATE_TRAILER;375			}376		}377		break;378 379	case STATE_TRAILER:380		mctp_serial_push_trailer(dev, c);381		break;382 383	case STATE_ERR:384		if (c == BYTE_FRAME)385			dev->rxstate = STATE_IDLE;386		break;387 388	default:389		netdev_err_once(dev->netdev, "invalid rx state %d\n",390				dev->rxstate);391	}392}393 394static void mctp_serial_tty_receive_buf(struct tty_struct *tty, const u8 *c,395					const u8 *f, size_t len)396{397	struct mctp_serial *dev = tty->disc_data;398	size_t i;399 400	if (!netif_running(dev->netdev))401		return;402 403	/* we don't (currently) use the flag bytes, just data. */404	for (i = 0; i < len; i++)405		mctp_serial_push(dev, c[i]);406}407 408static void mctp_serial_uninit(struct net_device *ndev)409{410	struct mctp_serial *dev = netdev_priv(ndev);411 412	cancel_work_sync(&dev->tx_work);413}414 415static const struct net_device_ops mctp_serial_netdev_ops = {416	.ndo_start_xmit = mctp_serial_tx,417	.ndo_uninit = mctp_serial_uninit,418};419 420static void mctp_serial_setup(struct net_device *ndev)421{422	ndev->type = ARPHRD_MCTP;423 424	/* we limit at the fixed MTU, which is also the MCTP-standard425	 * baseline MTU, so is also our minimum426	 */427	ndev->mtu = MCTP_SERIAL_MTU;428	ndev->max_mtu = MCTP_SERIAL_MTU;429	ndev->min_mtu = MCTP_SERIAL_MTU;430 431	ndev->hard_header_len = 0;432	ndev->addr_len = 0;433	ndev->tx_queue_len = DEFAULT_TX_QUEUE_LEN;434	ndev->flags = IFF_NOARP;435	ndev->netdev_ops = &mctp_serial_netdev_ops;436	ndev->needs_free_netdev = true;437}438 439static int mctp_serial_open(struct tty_struct *tty)440{441	struct mctp_serial *dev;442	struct net_device *ndev;443	char name[32];444	int idx, rc;445 446	if (!capable(CAP_NET_ADMIN))447		return -EPERM;448 449	if (!tty->ops->write)450		return -EOPNOTSUPP;451 452	idx = ida_alloc(&mctp_serial_ida, GFP_KERNEL);453	if (idx < 0)454		return idx;455 456	snprintf(name, sizeof(name), "mctpserial%d", idx);457	ndev = alloc_netdev(sizeof(*dev), name, NET_NAME_ENUM,458			    mctp_serial_setup);459	if (!ndev) {460		rc = -ENOMEM;461		goto free_ida;462	}463 464	dev = netdev_priv(ndev);465	dev->idx = idx;466	dev->tty = tty;467	dev->netdev = ndev;468	dev->txstate = STATE_IDLE;469	dev->rxstate = STATE_IDLE;470	spin_lock_init(&dev->lock);471	INIT_WORK(&dev->tx_work, mctp_serial_tx_work);472 473	rc = register_netdev(ndev);474	if (rc)475		goto free_netdev;476 477	tty->receive_room = 64 * 1024;478	tty->disc_data = dev;479 480	return 0;481 482free_netdev:483	free_netdev(ndev);484 485free_ida:486	ida_free(&mctp_serial_ida, idx);487	return rc;488}489 490static void mctp_serial_close(struct tty_struct *tty)491{492	struct mctp_serial *dev = tty->disc_data;493	int idx = dev->idx;494 495	unregister_netdev(dev->netdev);496	ida_free(&mctp_serial_ida, idx);497}498 499static struct tty_ldisc_ops mctp_ldisc = {500	.owner		= THIS_MODULE,501	.num		= N_MCTP,502	.name		= "mctp",503	.open		= mctp_serial_open,504	.close		= mctp_serial_close,505	.receive_buf	= mctp_serial_tty_receive_buf,506	.write_wakeup	= mctp_serial_tty_write_wakeup,507};508 509static int __init mctp_serial_init(void)510{511	return tty_register_ldisc(&mctp_ldisc);512}513 514static void __exit mctp_serial_exit(void)515{516	tty_unregister_ldisc(&mctp_ldisc);517}518 519module_init(mctp_serial_init);520module_exit(mctp_serial_exit);521 522MODULE_LICENSE("GPL v2");523MODULE_AUTHOR("Jeremy Kerr <jk@codeconstruct.com.au>");524MODULE_DESCRIPTION("MCTP Serial transport");525 526#if IS_ENABLED(CONFIG_MCTP_SERIAL_TEST)527#include <kunit/test.h>528 529#define MAX_CHUNKS 6530struct test_chunk_tx {531	u8 input_len;532	u8 input[MCTP_SERIAL_MTU];533	u8 chunks[MAX_CHUNKS];534};535 536static void test_next_chunk_len(struct kunit *test)537{538	struct mctp_serial devx;539	struct mctp_serial *dev = &devx;540	int next;541 542	const struct test_chunk_tx *params = test->param_value;543 544	memset(dev, 0x0, sizeof(*dev));545	memcpy(dev->txbuf, params->input, params->input_len);546	dev->txlen = params->input_len;547 548	for (size_t i = 0; i < MAX_CHUNKS; i++) {549		next = next_chunk_len(dev);550		dev->txpos += next;551		KUNIT_EXPECT_EQ(test, next, params->chunks[i]);552 553		if (next == 0) {554			KUNIT_EXPECT_EQ(test, dev->txpos, dev->txlen);555			return;556		}557	}558 559	KUNIT_FAIL_AND_ABORT(test, "Ran out of chunks");560}561 562static struct test_chunk_tx chunk_tx_tests[] = {563	{564		.input_len = 5,565		.input = { 0x00, 0x11, 0x22, 0x7e, 0x80 },566		.chunks = { 3, 1, 1, 0},567	},568	{569		.input_len = 5,570		.input = { 0x00, 0x11, 0x22, 0x7e, 0x7d },571		.chunks = { 3, 1, 1, 0},572	},573	{574		.input_len = 3,575		.input = { 0x7e, 0x11, 0x22, },576		.chunks = { 1, 2, 0},577	},578	{579		.input_len = 3,580		.input = { 0x7e, 0x7e, 0x7d, },581		.chunks = { 1, 1, 1, 0},582	},583	{584		.input_len = 4,585		.input = { 0x7e, 0x7e, 0x00, 0x7d, },586		.chunks = { 1, 1, 1, 1, 0},587	},588	{589		.input_len = 6,590		.input = { 0x7e, 0x7e, 0x00, 0x7d, 0x10, 0x10},591		.chunks = { 1, 1, 1, 1, 2, 0},592	},593	{594		.input_len = 1,595		.input = { 0x7e },596		.chunks = { 1, 0 },597	},598	{599		.input_len = 1,600		.input = { 0x80 },601		.chunks = { 1, 0 },602	},603	{604		.input_len = 3,605		.input = { 0x80, 0x80, 0x00 },606		.chunks = { 3, 0 },607	},608	{609		.input_len = 7,610		.input = { 0x01, 0x00, 0x08, 0xc8, 0x00, 0x80, 0x02 },611		.chunks = { 7, 0 },612	},613	{614		.input_len = 7,615		.input = { 0x01, 0x00, 0x08, 0xc8, 0x7e, 0x80, 0x02 },616		.chunks = { 4, 1, 2, 0 },617	},618};619 620KUNIT_ARRAY_PARAM(chunk_tx, chunk_tx_tests, NULL);621 622static struct kunit_case mctp_serial_test_cases[] = {623	KUNIT_CASE_PARAM(test_next_chunk_len, chunk_tx_gen_params),624};625 626static struct kunit_suite mctp_serial_test_suite = {627	.name = "mctp_serial",628	.test_cases = mctp_serial_test_cases,629};630 631kunit_test_suite(mctp_serial_test_suite);632 633#endif /* CONFIG_MCTP_SERIAL_TEST */634