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

sparx5_mactable.c (13350B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/* Microchip Sparx5 Switch driver
      3 *
      4 * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
      5 */
      6
      7#include <net/switchdev.h>
      8#include <linux/if_bridge.h>
      9#include <linux/iopoll.h>
     10
     11#include "sparx5_main_regs.h"
     12#include "sparx5_main.h"
     13
     14/* Commands for Mac Table Command register */
     15#define MAC_CMD_LEARN         0 /* Insert (Learn) 1 entry */
     16#define MAC_CMD_UNLEARN       1 /* Unlearn (Forget) 1 entry */
     17#define MAC_CMD_LOOKUP        2 /* Look up 1 entry */
     18#define MAC_CMD_READ          3 /* Read entry at Mac Table Index */
     19#define MAC_CMD_WRITE         4 /* Write entry at Mac Table Index */
     20#define MAC_CMD_SCAN          5 /* Scan (Age or find next) */
     21#define MAC_CMD_FIND_SMALLEST 6 /* Get next entry */
     22#define MAC_CMD_CLEAR_ALL     7 /* Delete all entries in table */
     23
     24/* Commands for MAC_ENTRY_ADDR_TYPE */
     25#define  MAC_ENTRY_ADDR_TYPE_UPSID_PN         0
     26#define  MAC_ENTRY_ADDR_TYPE_UPSID_CPU_OR_INT 1
     27#define  MAC_ENTRY_ADDR_TYPE_GLAG             2
     28#define  MAC_ENTRY_ADDR_TYPE_MC_IDX           3
     29
     30#define TABLE_UPDATE_SLEEP_US 10
     31#define TABLE_UPDATE_TIMEOUT_US 100000
     32
     33struct sparx5_mact_entry {
     34	struct list_head list;
     35	unsigned char mac[ETH_ALEN];
     36	u32 flags;
     37#define MAC_ENT_ALIVE	BIT(0)
     38#define MAC_ENT_MOVED	BIT(1)
     39#define MAC_ENT_LOCK	BIT(2)
     40	u16 vid;
     41	u16 port;
     42};
     43
     44static int sparx5_mact_get_status(struct sparx5 *sparx5)
     45{
     46	return spx5_rd(sparx5, LRN_COMMON_ACCESS_CTRL);
     47}
     48
     49static int sparx5_mact_wait_for_completion(struct sparx5 *sparx5)
     50{
     51	u32 val;
     52
     53	return readx_poll_timeout(sparx5_mact_get_status,
     54		sparx5, val,
     55		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_GET(val) == 0,
     56		TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
     57}
     58
     59static void sparx5_mact_select(struct sparx5 *sparx5,
     60			       const unsigned char mac[ETH_ALEN],
     61			       u16 vid)
     62{
     63	u32 macl = 0, mach = 0;
     64
     65	/* Set the MAC address to handle and the vlan associated in a format
     66	 * understood by the hardware.
     67	 */
     68	mach |= vid    << 16;
     69	mach |= mac[0] << 8;
     70	mach |= mac[1] << 0;
     71	macl |= mac[2] << 24;
     72	macl |= mac[3] << 16;
     73	macl |= mac[4] << 8;
     74	macl |= mac[5] << 0;
     75
     76	spx5_wr(mach, sparx5, LRN_MAC_ACCESS_CFG_0);
     77	spx5_wr(macl, sparx5, LRN_MAC_ACCESS_CFG_1);
     78}
     79
     80int sparx5_mact_learn(struct sparx5 *sparx5, int pgid,
     81		      const unsigned char mac[ETH_ALEN], u16 vid)
     82{
     83	int addr, type, ret;
     84
     85	if (pgid < SPX5_PORTS) {
     86		type = MAC_ENTRY_ADDR_TYPE_UPSID_PN;
     87		addr = pgid % 32;
     88		addr += (pgid / 32) << 5; /* Add upsid */
     89	} else {
     90		type = MAC_ENTRY_ADDR_TYPE_MC_IDX;
     91		addr = pgid - SPX5_PORTS;
     92	}
     93
     94	mutex_lock(&sparx5->lock);
     95
     96	sparx5_mact_select(sparx5, mac, vid);
     97
     98	/* MAC entry properties */
     99	spx5_wr(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_SET(addr) |
    100		LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_SET(type) |
    101		LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_SET(1) |
    102		LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_LOCKED_SET(1),
    103		sparx5, LRN_MAC_ACCESS_CFG_2);
    104	spx5_wr(0, sparx5, LRN_MAC_ACCESS_CFG_3);
    105
    106	/*  Insert/learn new entry */
    107	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LEARN) |
    108		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
    109		sparx5, LRN_COMMON_ACCESS_CTRL);
    110
    111	ret = sparx5_mact_wait_for_completion(sparx5);
    112
    113	mutex_unlock(&sparx5->lock);
    114
    115	return ret;
    116}
    117
    118int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr)
    119{
    120	struct sparx5_port *port = netdev_priv(dev);
    121	struct sparx5 *sparx5 = port->sparx5;
    122
    123	return sparx5_mact_forget(sparx5, addr, port->pvid);
    124}
    125
    126int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr)
    127{
    128	struct sparx5_port *port = netdev_priv(dev);
    129	struct sparx5 *sparx5 = port->sparx5;
    130
    131	return sparx5_mact_learn(sparx5, PGID_CPU, addr, port->pvid);
    132}
    133
    134static int sparx5_mact_get(struct sparx5 *sparx5,
    135			   unsigned char mac[ETH_ALEN],
    136			   u16 *vid, u32 *pcfg2)
    137{
    138	u32 mach, macl, cfg2;
    139	int ret = -ENOENT;
    140
    141	cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2);
    142	if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2)) {
    143		mach = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_0);
    144		macl = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_1);
    145		mac[0] = ((mach >> 8)  & 0xff);
    146		mac[1] = ((mach >> 0)  & 0xff);
    147		mac[2] = ((macl >> 24) & 0xff);
    148		mac[3] = ((macl >> 16) & 0xff);
    149		mac[4] = ((macl >> 8)  & 0xff);
    150		mac[5] = ((macl >> 0)  & 0xff);
    151		*vid = mach >> 16;
    152		*pcfg2 = cfg2;
    153		ret = 0;
    154	}
    155
    156	return ret;
    157}
    158
    159bool sparx5_mact_getnext(struct sparx5 *sparx5,
    160			 unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2)
    161{
    162	u32 cfg2;
    163	int ret;
    164
    165	mutex_lock(&sparx5->lock);
    166
    167	sparx5_mact_select(sparx5, mac, *vid);
    168
    169	spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_IGNORE_LOCKED_ENA_SET(1) |
    170		LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1),
    171		sparx5, LRN_SCAN_NEXT_CFG);
    172	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET
    173		(MAC_CMD_FIND_SMALLEST) |
    174		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
    175		sparx5, LRN_COMMON_ACCESS_CTRL);
    176
    177	ret = sparx5_mact_wait_for_completion(sparx5);
    178	if (ret == 0) {
    179		ret = sparx5_mact_get(sparx5, mac, vid, &cfg2);
    180		if (ret == 0)
    181			*pcfg2 = cfg2;
    182	}
    183
    184	mutex_unlock(&sparx5->lock);
    185
    186	return ret == 0;
    187}
    188
    189bool sparx5_mact_find(struct sparx5 *sparx5,
    190		      const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2)
    191{
    192	int ret;
    193	u32 cfg2;
    194
    195	mutex_lock(&sparx5->lock);
    196
    197	sparx5_mact_select(sparx5, mac, vid);
    198
    199	/* Issue a lookup command */
    200	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LOOKUP) |
    201		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
    202		sparx5, LRN_COMMON_ACCESS_CTRL);
    203
    204	ret = sparx5_mact_wait_for_completion(sparx5);
    205	if (ret == 0) {
    206		cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2);
    207		if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2))
    208			*pcfg2 = cfg2;
    209		else
    210			ret = -ENOENT;
    211	}
    212
    213	mutex_unlock(&sparx5->lock);
    214
    215	return ret;
    216}
    217
    218int sparx5_mact_forget(struct sparx5 *sparx5,
    219		       const unsigned char mac[ETH_ALEN], u16 vid)
    220{
    221	int ret;
    222
    223	mutex_lock(&sparx5->lock);
    224
    225	sparx5_mact_select(sparx5, mac, vid);
    226
    227	/* Issue an unlearn command */
    228	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_UNLEARN) |
    229		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
    230		sparx5, LRN_COMMON_ACCESS_CTRL);
    231
    232	ret = sparx5_mact_wait_for_completion(sparx5);
    233
    234	mutex_unlock(&sparx5->lock);
    235
    236	return ret;
    237}
    238
    239static struct sparx5_mact_entry *alloc_mact_entry(struct sparx5 *sparx5,
    240						  const unsigned char *mac,
    241						  u16 vid, u16 port_index)
    242{
    243	struct sparx5_mact_entry *mact_entry;
    244
    245	mact_entry = devm_kzalloc(sparx5->dev,
    246				  sizeof(*mact_entry), GFP_ATOMIC);
    247	if (!mact_entry)
    248		return NULL;
    249
    250	memcpy(mact_entry->mac, mac, ETH_ALEN);
    251	mact_entry->vid = vid;
    252	mact_entry->port = port_index;
    253	return mact_entry;
    254}
    255
    256static struct sparx5_mact_entry *find_mact_entry(struct sparx5 *sparx5,
    257						 const unsigned char *mac,
    258						 u16 vid, u16 port_index)
    259{
    260	struct sparx5_mact_entry *mact_entry;
    261	struct sparx5_mact_entry *res = NULL;
    262
    263	mutex_lock(&sparx5->mact_lock);
    264	list_for_each_entry(mact_entry, &sparx5->mact_entries, list) {
    265		if (mact_entry->vid == vid &&
    266		    ether_addr_equal(mac, mact_entry->mac) &&
    267		    mact_entry->port == port_index) {
    268			res = mact_entry;
    269			break;
    270		}
    271	}
    272	mutex_unlock(&sparx5->mact_lock);
    273
    274	return res;
    275}
    276
    277static void sparx5_fdb_call_notifiers(enum switchdev_notifier_type type,
    278				      const char *mac, u16 vid,
    279				      struct net_device *dev, bool offloaded)
    280{
    281	struct switchdev_notifier_fdb_info info = {};
    282
    283	info.addr = mac;
    284	info.vid = vid;
    285	info.offloaded = offloaded;
    286	call_switchdev_notifiers(type, dev, &info.info, NULL);
    287}
    288
    289int sparx5_add_mact_entry(struct sparx5 *sparx5,
    290			  struct net_device *dev,
    291			  u16 portno,
    292			  const unsigned char *addr, u16 vid)
    293{
    294	struct sparx5_mact_entry *mact_entry;
    295	int ret;
    296	u32 cfg2;
    297
    298	ret = sparx5_mact_find(sparx5, addr, vid, &cfg2);
    299	if (!ret)
    300		return 0;
    301
    302	/* In case the entry already exists, don't add it again to SW,
    303	 * just update HW, but we need to look in the actual HW because
    304	 * it is possible for an entry to be learn by HW and before the
    305	 * mact thread to start the frame will reach CPU and the CPU will
    306	 * add the entry but without the extern_learn flag.
    307	 */
    308	mact_entry = find_mact_entry(sparx5, addr, vid, portno);
    309	if (mact_entry)
    310		goto update_hw;
    311
    312	/* Add the entry in SW MAC table not to get the notification when
    313	 * SW is pulling again
    314	 */
    315	mact_entry = alloc_mact_entry(sparx5, addr, vid, portno);
    316	if (!mact_entry)
    317		return -ENOMEM;
    318
    319	mutex_lock(&sparx5->mact_lock);
    320	list_add_tail(&mact_entry->list, &sparx5->mact_entries);
    321	mutex_unlock(&sparx5->mact_lock);
    322
    323update_hw:
    324	ret = sparx5_mact_learn(sparx5, portno, addr, vid);
    325
    326	/* New entry? */
    327	if (mact_entry->flags == 0) {
    328		mact_entry->flags |= MAC_ENT_LOCK; /* Don't age this */
    329		sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, addr, vid,
    330					  dev, true);
    331	}
    332
    333	return ret;
    334}
    335
    336int sparx5_del_mact_entry(struct sparx5 *sparx5,
    337			  const unsigned char *addr,
    338			  u16 vid)
    339{
    340	struct sparx5_mact_entry *mact_entry, *tmp;
    341
    342	/* Delete the entry in SW MAC table not to get the notification when
    343	 * SW is pulling again
    344	 */
    345	mutex_lock(&sparx5->mact_lock);
    346	list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries,
    347				 list) {
    348		if ((vid == 0 || mact_entry->vid == vid) &&
    349		    ether_addr_equal(addr, mact_entry->mac)) {
    350			list_del(&mact_entry->list);
    351			devm_kfree(sparx5->dev, mact_entry);
    352
    353			sparx5_mact_forget(sparx5, addr, mact_entry->vid);
    354		}
    355	}
    356	mutex_unlock(&sparx5->mact_lock);
    357
    358	return 0;
    359}
    360
    361static void sparx5_mact_handle_entry(struct sparx5 *sparx5,
    362				     unsigned char mac[ETH_ALEN],
    363				     u16 vid, u32 cfg2)
    364{
    365	struct sparx5_mact_entry *mact_entry;
    366	bool found = false;
    367	u16 port;
    368
    369	if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_GET(cfg2) !=
    370	    MAC_ENTRY_ADDR_TYPE_UPSID_PN)
    371		return;
    372
    373	port = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(cfg2);
    374	if (port >= SPX5_PORTS)
    375		return;
    376
    377	if (!test_bit(port, sparx5->bridge_mask))
    378		return;
    379
    380	mutex_lock(&sparx5->mact_lock);
    381	list_for_each_entry(mact_entry, &sparx5->mact_entries, list) {
    382		if (mact_entry->vid == vid &&
    383		    ether_addr_equal(mac, mact_entry->mac)) {
    384			found = true;
    385			mact_entry->flags |= MAC_ENT_ALIVE;
    386			if (mact_entry->port != port) {
    387				dev_warn(sparx5->dev, "Entry move: %d -> %d\n",
    388					 mact_entry->port, port);
    389				mact_entry->port = port;
    390				mact_entry->flags |= MAC_ENT_MOVED;
    391			}
    392			/* Entry handled */
    393			break;
    394		}
    395	}
    396	mutex_unlock(&sparx5->mact_lock);
    397
    398	if (found && !(mact_entry->flags & MAC_ENT_MOVED))
    399		/* Present, not moved */
    400		return;
    401
    402	if (!found) {
    403		/* Entry not found - now add */
    404		mact_entry = alloc_mact_entry(sparx5, mac, vid, port);
    405		if (!mact_entry)
    406			return;
    407
    408		mact_entry->flags |= MAC_ENT_ALIVE;
    409		mutex_lock(&sparx5->mact_lock);
    410		list_add_tail(&mact_entry->list, &sparx5->mact_entries);
    411		mutex_unlock(&sparx5->mact_lock);
    412	}
    413
    414	/* New or moved entry - notify bridge */
    415	sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
    416				  mac, vid, sparx5->ports[port]->ndev,
    417				  true);
    418}
    419
    420void sparx5_mact_pull_work(struct work_struct *work)
    421{
    422	struct delayed_work *del_work = to_delayed_work(work);
    423	struct sparx5 *sparx5 = container_of(del_work, struct sparx5,
    424					     mact_work);
    425	struct sparx5_mact_entry *mact_entry, *tmp;
    426	unsigned char mac[ETH_ALEN];
    427	u32 cfg2;
    428	u16 vid;
    429	int ret;
    430
    431	/* Reset MAC entry flags */
    432	mutex_lock(&sparx5->mact_lock);
    433	list_for_each_entry(mact_entry, &sparx5->mact_entries, list)
    434		mact_entry->flags &= MAC_ENT_LOCK;
    435	mutex_unlock(&sparx5->mact_lock);
    436
    437	/* MAIN mac address processing loop */
    438	vid = 0;
    439	memset(mac, 0, sizeof(mac));
    440	do {
    441		mutex_lock(&sparx5->lock);
    442		sparx5_mact_select(sparx5, mac, vid);
    443		spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1),
    444			sparx5, LRN_SCAN_NEXT_CFG);
    445		spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET
    446			(MAC_CMD_FIND_SMALLEST) |
    447			LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
    448			sparx5, LRN_COMMON_ACCESS_CTRL);
    449		ret = sparx5_mact_wait_for_completion(sparx5);
    450		if (ret == 0)
    451			ret = sparx5_mact_get(sparx5, mac, &vid, &cfg2);
    452		mutex_unlock(&sparx5->lock);
    453		if (ret == 0)
    454			sparx5_mact_handle_entry(sparx5, mac, vid, cfg2);
    455	} while (ret == 0);
    456
    457	mutex_lock(&sparx5->mact_lock);
    458	list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries,
    459				 list) {
    460		/* If the entry is in HW or permanent, then skip */
    461		if (mact_entry->flags & (MAC_ENT_ALIVE | MAC_ENT_LOCK))
    462			continue;
    463
    464		sparx5_fdb_call_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
    465					  mact_entry->mac, mact_entry->vid,
    466					  sparx5->ports[mact_entry->port]->ndev,
    467					  true);
    468
    469		list_del(&mact_entry->list);
    470		devm_kfree(sparx5->dev, mact_entry);
    471	}
    472	mutex_unlock(&sparx5->mact_lock);
    473
    474	queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work,
    475			   SPX5_MACT_PULL_DELAY);
    476}
    477
    478void sparx5_set_ageing(struct sparx5 *sparx5, int msecs)
    479{
    480	int value = max(1, msecs / 10); /* unit 10 ms */
    481
    482	spx5_rmw(LRN_AUTOAGE_CFG_UNIT_SIZE_SET(2) | /* 10 ms */
    483		 LRN_AUTOAGE_CFG_PERIOD_VAL_SET(value / 2), /* one bit ageing */
    484		 LRN_AUTOAGE_CFG_UNIT_SIZE |
    485		 LRN_AUTOAGE_CFG_PERIOD_VAL,
    486		 sparx5,
    487		 LRN_AUTOAGE_CFG(0));
    488}
    489
    490void sparx5_mact_init(struct sparx5 *sparx5)
    491{
    492	mutex_init(&sparx5->lock);
    493
    494	/*  Flush MAC table */
    495	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_CLEAR_ALL) |
    496		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
    497		sparx5, LRN_COMMON_ACCESS_CTRL);
    498
    499	if (sparx5_mact_wait_for_completion(sparx5) != 0)
    500		dev_warn(sparx5->dev, "MAC flush error\n");
    501
    502	sparx5_set_ageing(sparx5, BR_DEFAULT_AGEING_TIME / HZ * 1000);
    503}