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

net.c (12660B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * net.c - Networking component for Mostcore
      4 *
      5 * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG
      6 */
      7
      8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
      9
     10#include <linux/module.h>
     11#include <linux/netdevice.h>
     12#include <linux/etherdevice.h>
     13#include <linux/slab.h>
     14#include <linux/init.h>
     15#include <linux/list.h>
     16#include <linux/wait.h>
     17#include <linux/kobject.h>
     18#include <linux/most.h>
     19
     20#define MEP_HDR_LEN 8
     21#define MDP_HDR_LEN 16
     22#define MAMAC_DATA_LEN (1024 - MDP_HDR_LEN)
     23
     24#define PMHL 5
     25
     26#define PMS_TELID_UNSEGM_MAMAC	0x0A
     27#define PMS_FIFONO_MDP		0x01
     28#define PMS_FIFONO_MEP		0x04
     29#define PMS_MSGTYPE_DATA	0x04
     30#define PMS_DEF_PRIO		0
     31#define MEP_DEF_RETRY		15
     32
     33#define PMS_FIFONO_MASK		0x07
     34#define PMS_FIFONO_SHIFT	3
     35#define PMS_RETRY_SHIFT		4
     36#define PMS_TELID_MASK		0x0F
     37#define PMS_TELID_SHIFT		4
     38
     39#define HB(value)		((u8)((u16)(value) >> 8))
     40#define LB(value)		((u8)(value))
     41
     42#define EXTRACT_BIT_SET(bitset_name, value) \
     43	(((value) >> bitset_name##_SHIFT) & bitset_name##_MASK)
     44
     45#define PMS_IS_MEP(buf, len) \
     46	((len) > MEP_HDR_LEN && \
     47	 EXTRACT_BIT_SET(PMS_FIFONO, (buf)[3]) == PMS_FIFONO_MEP)
     48
     49static inline bool pms_is_mamac(char *buf, u32 len)
     50{
     51	return (len > MDP_HDR_LEN &&
     52		EXTRACT_BIT_SET(PMS_FIFONO, buf[3]) == PMS_FIFONO_MDP &&
     53		EXTRACT_BIT_SET(PMS_TELID, buf[14]) == PMS_TELID_UNSEGM_MAMAC);
     54}
     55
     56struct net_dev_channel {
     57	bool linked;
     58	int ch_id;
     59};
     60
     61struct net_dev_context {
     62	struct most_interface *iface;
     63	bool is_mamac;
     64	struct net_device *dev;
     65	struct net_dev_channel rx;
     66	struct net_dev_channel tx;
     67	struct list_head list;
     68};
     69
     70static LIST_HEAD(net_devices);
     71static DEFINE_MUTEX(probe_disc_mt); /* ch->linked = true, most_nd_open */
     72static DEFINE_SPINLOCK(list_lock); /* list_head, ch->linked = false, dev_hold */
     73static struct most_component comp;
     74
     75static int skb_to_mamac(const struct sk_buff *skb, struct mbo *mbo)
     76{
     77	u8 *buff = mbo->virt_address;
     78	static const u8 broadcast[] = { 0x03, 0xFF };
     79	const u8 *dest_addr = skb->data + 4;
     80	const u8 *eth_type = skb->data + 12;
     81	unsigned int payload_len = skb->len - ETH_HLEN;
     82	unsigned int mdp_len = payload_len + MDP_HDR_LEN;
     83
     84	if (mdp_len < skb->len) {
     85		pr_err("drop: too large packet! (%u)\n", skb->len);
     86		return -EINVAL;
     87	}
     88
     89	if (mbo->buffer_length < mdp_len) {
     90		pr_err("drop: too small buffer! (%d for %d)\n",
     91		       mbo->buffer_length, mdp_len);
     92		return -EINVAL;
     93	}
     94
     95	if (skb->len < ETH_HLEN) {
     96		pr_err("drop: too small packet! (%d)\n", skb->len);
     97		return -EINVAL;
     98	}
     99
    100	if (dest_addr[0] == 0xFF && dest_addr[1] == 0xFF)
    101		dest_addr = broadcast;
    102
    103	*buff++ = HB(mdp_len - 2);
    104	*buff++ = LB(mdp_len - 2);
    105
    106	*buff++ = PMHL;
    107	*buff++ = (PMS_FIFONO_MDP << PMS_FIFONO_SHIFT) | PMS_MSGTYPE_DATA;
    108	*buff++ = PMS_DEF_PRIO;
    109	*buff++ = dest_addr[0];
    110	*buff++ = dest_addr[1];
    111	*buff++ = 0x00;
    112
    113	*buff++ = HB(payload_len + 6);
    114	*buff++ = LB(payload_len + 6);
    115
    116	/* end of FPH here */
    117
    118	*buff++ = eth_type[0];
    119	*buff++ = eth_type[1];
    120	*buff++ = 0;
    121	*buff++ = 0;
    122
    123	*buff++ = PMS_TELID_UNSEGM_MAMAC << 4 | HB(payload_len);
    124	*buff++ = LB(payload_len);
    125
    126	memcpy(buff, skb->data + ETH_HLEN, payload_len);
    127	mbo->buffer_length = mdp_len;
    128	return 0;
    129}
    130
    131static int skb_to_mep(const struct sk_buff *skb, struct mbo *mbo)
    132{
    133	u8 *buff = mbo->virt_address;
    134	unsigned int mep_len = skb->len + MEP_HDR_LEN;
    135
    136	if (mep_len < skb->len) {
    137		pr_err("drop: too large packet! (%u)\n", skb->len);
    138		return -EINVAL;
    139	}
    140
    141	if (mbo->buffer_length < mep_len) {
    142		pr_err("drop: too small buffer! (%d for %d)\n",
    143		       mbo->buffer_length, mep_len);
    144		return -EINVAL;
    145	}
    146
    147	*buff++ = HB(mep_len - 2);
    148	*buff++ = LB(mep_len - 2);
    149
    150	*buff++ = PMHL;
    151	*buff++ = (PMS_FIFONO_MEP << PMS_FIFONO_SHIFT) | PMS_MSGTYPE_DATA;
    152	*buff++ = (MEP_DEF_RETRY << PMS_RETRY_SHIFT) | PMS_DEF_PRIO;
    153	*buff++ = 0;
    154	*buff++ = 0;
    155	*buff++ = 0;
    156
    157	memcpy(buff, skb->data, skb->len);
    158	mbo->buffer_length = mep_len;
    159	return 0;
    160}
    161
    162static int most_nd_set_mac_address(struct net_device *dev, void *p)
    163{
    164	struct net_dev_context *nd = netdev_priv(dev);
    165	int err = eth_mac_addr(dev, p);
    166
    167	if (err)
    168		return err;
    169
    170	nd->is_mamac =
    171		(dev->dev_addr[0] == 0 && dev->dev_addr[1] == 0 &&
    172		 dev->dev_addr[2] == 0 && dev->dev_addr[3] == 0);
    173
    174	/*
    175	 * Set default MTU for the given packet type.
    176	 * It is still possible to change MTU using ip tools afterwards.
    177	 */
    178	dev->mtu = nd->is_mamac ? MAMAC_DATA_LEN : ETH_DATA_LEN;
    179
    180	return 0;
    181}
    182
    183static void on_netinfo(struct most_interface *iface,
    184		       unsigned char link_stat, unsigned char *mac_addr);
    185
    186static int most_nd_open(struct net_device *dev)
    187{
    188	struct net_dev_context *nd = netdev_priv(dev);
    189	int ret = 0;
    190
    191	mutex_lock(&probe_disc_mt);
    192
    193	if (most_start_channel(nd->iface, nd->rx.ch_id, &comp)) {
    194		netdev_err(dev, "most_start_channel() failed\n");
    195		ret = -EBUSY;
    196		goto unlock;
    197	}
    198
    199	if (most_start_channel(nd->iface, nd->tx.ch_id, &comp)) {
    200		netdev_err(dev, "most_start_channel() failed\n");
    201		most_stop_channel(nd->iface, nd->rx.ch_id, &comp);
    202		ret = -EBUSY;
    203		goto unlock;
    204	}
    205
    206	netif_carrier_off(dev);
    207	if (is_valid_ether_addr(dev->dev_addr))
    208		netif_dormant_off(dev);
    209	else
    210		netif_dormant_on(dev);
    211	netif_wake_queue(dev);
    212	if (nd->iface->request_netinfo)
    213		nd->iface->request_netinfo(nd->iface, nd->tx.ch_id, on_netinfo);
    214
    215unlock:
    216	mutex_unlock(&probe_disc_mt);
    217	return ret;
    218}
    219
    220static int most_nd_stop(struct net_device *dev)
    221{
    222	struct net_dev_context *nd = netdev_priv(dev);
    223
    224	netif_stop_queue(dev);
    225	if (nd->iface->request_netinfo)
    226		nd->iface->request_netinfo(nd->iface, nd->tx.ch_id, NULL);
    227	most_stop_channel(nd->iface, nd->rx.ch_id, &comp);
    228	most_stop_channel(nd->iface, nd->tx.ch_id, &comp);
    229
    230	return 0;
    231}
    232
    233static netdev_tx_t most_nd_start_xmit(struct sk_buff *skb,
    234				      struct net_device *dev)
    235{
    236	struct net_dev_context *nd = netdev_priv(dev);
    237	struct mbo *mbo;
    238	int ret;
    239
    240	mbo = most_get_mbo(nd->iface, nd->tx.ch_id, &comp);
    241
    242	if (!mbo) {
    243		netif_stop_queue(dev);
    244		dev->stats.tx_fifo_errors++;
    245		return NETDEV_TX_BUSY;
    246	}
    247
    248	if (nd->is_mamac)
    249		ret = skb_to_mamac(skb, mbo);
    250	else
    251		ret = skb_to_mep(skb, mbo);
    252
    253	if (ret) {
    254		most_put_mbo(mbo);
    255		dev->stats.tx_dropped++;
    256		kfree_skb(skb);
    257		return NETDEV_TX_OK;
    258	}
    259
    260	most_submit_mbo(mbo);
    261	dev->stats.tx_packets++;
    262	dev->stats.tx_bytes += skb->len;
    263	kfree_skb(skb);
    264	return NETDEV_TX_OK;
    265}
    266
    267static const struct net_device_ops most_nd_ops = {
    268	.ndo_open = most_nd_open,
    269	.ndo_stop = most_nd_stop,
    270	.ndo_start_xmit = most_nd_start_xmit,
    271	.ndo_set_mac_address = most_nd_set_mac_address,
    272};
    273
    274static void most_nd_setup(struct net_device *dev)
    275{
    276	ether_setup(dev);
    277	dev->netdev_ops = &most_nd_ops;
    278}
    279
    280static struct net_dev_context *get_net_dev(struct most_interface *iface)
    281{
    282	struct net_dev_context *nd;
    283
    284	list_for_each_entry(nd, &net_devices, list)
    285		if (nd->iface == iface)
    286			return nd;
    287	return NULL;
    288}
    289
    290static struct net_dev_context *get_net_dev_hold(struct most_interface *iface)
    291{
    292	struct net_dev_context *nd;
    293	unsigned long flags;
    294
    295	spin_lock_irqsave(&list_lock, flags);
    296	nd = get_net_dev(iface);
    297	if (nd && nd->rx.linked && nd->tx.linked)
    298		dev_hold(nd->dev);
    299	else
    300		nd = NULL;
    301	spin_unlock_irqrestore(&list_lock, flags);
    302	return nd;
    303}
    304
    305static int comp_probe_channel(struct most_interface *iface, int channel_idx,
    306			      struct most_channel_config *ccfg, char *name,
    307			      char *args)
    308{
    309	struct net_dev_context *nd;
    310	struct net_dev_channel *ch;
    311	struct net_device *dev;
    312	unsigned long flags;
    313	int ret = 0;
    314
    315	if (!iface)
    316		return -EINVAL;
    317
    318	if (ccfg->data_type != MOST_CH_ASYNC)
    319		return -EINVAL;
    320
    321	mutex_lock(&probe_disc_mt);
    322	nd = get_net_dev(iface);
    323	if (!nd) {
    324		dev = alloc_netdev(sizeof(struct net_dev_context), "meth%d",
    325				   NET_NAME_UNKNOWN, most_nd_setup);
    326		if (!dev) {
    327			ret = -ENOMEM;
    328			goto unlock;
    329		}
    330
    331		nd = netdev_priv(dev);
    332		nd->iface = iface;
    333		nd->dev = dev;
    334
    335		spin_lock_irqsave(&list_lock, flags);
    336		list_add(&nd->list, &net_devices);
    337		spin_unlock_irqrestore(&list_lock, flags);
    338
    339		ch = ccfg->direction == MOST_CH_TX ? &nd->tx : &nd->rx;
    340	} else {
    341		ch = ccfg->direction == MOST_CH_TX ? &nd->tx : &nd->rx;
    342		if (ch->linked) {
    343			pr_err("direction is allocated\n");
    344			ret = -EINVAL;
    345			goto unlock;
    346		}
    347
    348		if (register_netdev(nd->dev)) {
    349			pr_err("register_netdev() failed\n");
    350			ret = -EINVAL;
    351			goto unlock;
    352		}
    353	}
    354	ch->ch_id = channel_idx;
    355	ch->linked = true;
    356
    357unlock:
    358	mutex_unlock(&probe_disc_mt);
    359	return ret;
    360}
    361
    362static int comp_disconnect_channel(struct most_interface *iface,
    363				   int channel_idx)
    364{
    365	struct net_dev_context *nd;
    366	struct net_dev_channel *ch;
    367	unsigned long flags;
    368	int ret = 0;
    369
    370	mutex_lock(&probe_disc_mt);
    371	nd = get_net_dev(iface);
    372	if (!nd) {
    373		ret = -EINVAL;
    374		goto unlock;
    375	}
    376
    377	if (nd->rx.linked && channel_idx == nd->rx.ch_id) {
    378		ch = &nd->rx;
    379	} else if (nd->tx.linked && channel_idx == nd->tx.ch_id) {
    380		ch = &nd->tx;
    381	} else {
    382		ret = -EINVAL;
    383		goto unlock;
    384	}
    385
    386	if (nd->rx.linked && nd->tx.linked) {
    387		spin_lock_irqsave(&list_lock, flags);
    388		ch->linked = false;
    389		spin_unlock_irqrestore(&list_lock, flags);
    390
    391		/*
    392		 * do not call most_stop_channel() here, because channels are
    393		 * going to be closed in ndo_stop() after unregister_netdev()
    394		 */
    395		unregister_netdev(nd->dev);
    396	} else {
    397		spin_lock_irqsave(&list_lock, flags);
    398		list_del(&nd->list);
    399		spin_unlock_irqrestore(&list_lock, flags);
    400
    401		free_netdev(nd->dev);
    402	}
    403
    404unlock:
    405	mutex_unlock(&probe_disc_mt);
    406	return ret;
    407}
    408
    409static int comp_resume_tx_channel(struct most_interface *iface,
    410				  int channel_idx)
    411{
    412	struct net_dev_context *nd;
    413
    414	nd = get_net_dev_hold(iface);
    415	if (!nd)
    416		return 0;
    417
    418	if (nd->tx.ch_id != channel_idx)
    419		goto put_nd;
    420
    421	netif_wake_queue(nd->dev);
    422
    423put_nd:
    424	dev_put(nd->dev);
    425	return 0;
    426}
    427
    428static int comp_rx_data(struct mbo *mbo)
    429{
    430	const u32 zero = 0;
    431	struct net_dev_context *nd;
    432	char *buf = mbo->virt_address;
    433	u32 len = mbo->processed_length;
    434	struct sk_buff *skb;
    435	struct net_device *dev;
    436	unsigned int skb_len;
    437	int ret = 0;
    438
    439	nd = get_net_dev_hold(mbo->ifp);
    440	if (!nd)
    441		return -EIO;
    442
    443	if (nd->rx.ch_id != mbo->hdm_channel_id) {
    444		ret = -EIO;
    445		goto put_nd;
    446	}
    447
    448	dev = nd->dev;
    449
    450	if (nd->is_mamac) {
    451		if (!pms_is_mamac(buf, len)) {
    452			ret = -EIO;
    453			goto put_nd;
    454		}
    455
    456		skb = dev_alloc_skb(len - MDP_HDR_LEN + 2 * ETH_ALEN + 2);
    457	} else {
    458		if (!PMS_IS_MEP(buf, len)) {
    459			ret = -EIO;
    460			goto put_nd;
    461		}
    462
    463		skb = dev_alloc_skb(len - MEP_HDR_LEN);
    464	}
    465
    466	if (!skb) {
    467		dev->stats.rx_dropped++;
    468		pr_err_once("drop packet: no memory for skb\n");
    469		goto out;
    470	}
    471
    472	skb->dev = dev;
    473
    474	if (nd->is_mamac) {
    475		/* dest */
    476		ether_addr_copy(skb_put(skb, ETH_ALEN), dev->dev_addr);
    477
    478		/* src */
    479		skb_put_data(skb, &zero, 4);
    480		skb_put_data(skb, buf + 5, 2);
    481
    482		/* eth type */
    483		skb_put_data(skb, buf + 10, 2);
    484
    485		buf += MDP_HDR_LEN;
    486		len -= MDP_HDR_LEN;
    487	} else {
    488		buf += MEP_HDR_LEN;
    489		len -= MEP_HDR_LEN;
    490	}
    491
    492	skb_put_data(skb, buf, len);
    493	skb->protocol = eth_type_trans(skb, dev);
    494	skb_len = skb->len;
    495	if (netif_rx(skb) == NET_RX_SUCCESS) {
    496		dev->stats.rx_packets++;
    497		dev->stats.rx_bytes += skb_len;
    498	} else {
    499		dev->stats.rx_dropped++;
    500	}
    501
    502out:
    503	most_put_mbo(mbo);
    504
    505put_nd:
    506	dev_put(nd->dev);
    507	return ret;
    508}
    509
    510static struct most_component comp = {
    511	.mod = THIS_MODULE,
    512	.name = "net",
    513	.probe_channel = comp_probe_channel,
    514	.disconnect_channel = comp_disconnect_channel,
    515	.tx_completion = comp_resume_tx_channel,
    516	.rx_completion = comp_rx_data,
    517};
    518
    519static int __init most_net_init(void)
    520{
    521	int err;
    522
    523	err = most_register_component(&comp);
    524	if (err)
    525		return err;
    526	err = most_register_configfs_subsys(&comp);
    527	if (err) {
    528		most_deregister_component(&comp);
    529		return err;
    530	}
    531	return 0;
    532}
    533
    534static void __exit most_net_exit(void)
    535{
    536	most_deregister_configfs_subsys(&comp);
    537	most_deregister_component(&comp);
    538}
    539
    540/**
    541 * on_netinfo - callback for HDM to be informed about HW's MAC
    542 * @iface: most interface instance
    543 * @link_stat: link status
    544 * @mac_addr: MAC address
    545 */
    546static void on_netinfo(struct most_interface *iface,
    547		       unsigned char link_stat, unsigned char *mac_addr)
    548{
    549	struct net_dev_context *nd;
    550	struct net_device *dev;
    551	const u8 *m = mac_addr;
    552
    553	nd = get_net_dev_hold(iface);
    554	if (!nd)
    555		return;
    556
    557	dev = nd->dev;
    558
    559	if (link_stat)
    560		netif_carrier_on(dev);
    561	else
    562		netif_carrier_off(dev);
    563
    564	if (m && is_valid_ether_addr(m)) {
    565		if (!is_valid_ether_addr(dev->dev_addr)) {
    566			netdev_info(dev, "set mac %pM\n", m);
    567			eth_hw_addr_set(dev, m);
    568			netif_dormant_off(dev);
    569		} else if (!ether_addr_equal(dev->dev_addr, m)) {
    570			netdev_warn(dev, "reject mac %pM\n", m);
    571		}
    572	}
    573
    574	dev_put(nd->dev);
    575}
    576
    577module_init(most_net_init);
    578module_exit(most_net_exit);
    579MODULE_LICENSE("GPL");
    580MODULE_AUTHOR("Andrey Shvetsov <andrey.shvetsov@k2l.de>");
    581MODULE_DESCRIPTION("Networking Component Module for Mostcore");