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

cabletest.c (9872B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2
      3#include <linux/phy.h>
      4#include <linux/ethtool_netlink.h>
      5#include "netlink.h"
      6#include "common.h"
      7
      8/* 802.3 standard allows 100 meters for BaseT cables. However longer
      9 * cables might work, depending on the quality of the cables and the
     10 * PHY. So allow testing for up to 150 meters.
     11 */
     12#define MAX_CABLE_LENGTH_CM (150 * 100)
     13
     14const struct nla_policy ethnl_cable_test_act_policy[] = {
     15	[ETHTOOL_A_CABLE_TEST_HEADER]		=
     16		NLA_POLICY_NESTED(ethnl_header_policy),
     17};
     18
     19static int ethnl_cable_test_started(struct phy_device *phydev, u8 cmd)
     20{
     21	struct sk_buff *skb;
     22	int err = -ENOMEM;
     23	void *ehdr;
     24
     25	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
     26	if (!skb)
     27		goto out;
     28
     29	ehdr = ethnl_bcastmsg_put(skb, cmd);
     30	if (!ehdr) {
     31		err = -EMSGSIZE;
     32		goto out;
     33	}
     34
     35	err = ethnl_fill_reply_header(skb, phydev->attached_dev,
     36				      ETHTOOL_A_CABLE_TEST_NTF_HEADER);
     37	if (err)
     38		goto out;
     39
     40	err = nla_put_u8(skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS,
     41			 ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED);
     42	if (err)
     43		goto out;
     44
     45	genlmsg_end(skb, ehdr);
     46
     47	return ethnl_multicast(skb, phydev->attached_dev);
     48
     49out:
     50	nlmsg_free(skb);
     51	phydev_err(phydev, "%s: Error %pe\n", __func__, ERR_PTR(err));
     52
     53	return err;
     54}
     55
     56int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info)
     57{
     58	struct ethnl_req_info req_info = {};
     59	const struct ethtool_phy_ops *ops;
     60	struct nlattr **tb = info->attrs;
     61	struct net_device *dev;
     62	int ret;
     63
     64	ret = ethnl_parse_header_dev_get(&req_info,
     65					 tb[ETHTOOL_A_CABLE_TEST_HEADER],
     66					 genl_info_net(info), info->extack,
     67					 true);
     68	if (ret < 0)
     69		return ret;
     70
     71	dev = req_info.dev;
     72	if (!dev->phydev) {
     73		ret = -EOPNOTSUPP;
     74		goto out_dev_put;
     75	}
     76
     77	rtnl_lock();
     78	ops = ethtool_phy_ops;
     79	if (!ops || !ops->start_cable_test) {
     80		ret = -EOPNOTSUPP;
     81		goto out_rtnl;
     82	}
     83
     84	ret = ethnl_ops_begin(dev);
     85	if (ret < 0)
     86		goto out_rtnl;
     87
     88	ret = ops->start_cable_test(dev->phydev, info->extack);
     89
     90	ethnl_ops_complete(dev);
     91
     92	if (!ret)
     93		ethnl_cable_test_started(dev->phydev,
     94					 ETHTOOL_MSG_CABLE_TEST_NTF);
     95
     96out_rtnl:
     97	rtnl_unlock();
     98out_dev_put:
     99	ethnl_parse_header_dev_put(&req_info);
    100	return ret;
    101}
    102
    103int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd)
    104{
    105	int err = -ENOMEM;
    106
    107	/* One TDR sample occupies 20 bytes. For a 150 meter cable,
    108	 * with four pairs, around 12K is needed.
    109	 */
    110	phydev->skb = genlmsg_new(SZ_16K, GFP_KERNEL);
    111	if (!phydev->skb)
    112		goto out;
    113
    114	phydev->ehdr = ethnl_bcastmsg_put(phydev->skb, cmd);
    115	if (!phydev->ehdr) {
    116		err = -EMSGSIZE;
    117		goto out;
    118	}
    119
    120	err = ethnl_fill_reply_header(phydev->skb, phydev->attached_dev,
    121				      ETHTOOL_A_CABLE_TEST_NTF_HEADER);
    122	if (err)
    123		goto out;
    124
    125	err = nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS,
    126			 ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED);
    127	if (err)
    128		goto out;
    129
    130	phydev->nest = nla_nest_start(phydev->skb,
    131				      ETHTOOL_A_CABLE_TEST_NTF_NEST);
    132	if (!phydev->nest) {
    133		err = -EMSGSIZE;
    134		goto out;
    135	}
    136
    137	return 0;
    138
    139out:
    140	nlmsg_free(phydev->skb);
    141	phydev->skb = NULL;
    142	return err;
    143}
    144EXPORT_SYMBOL_GPL(ethnl_cable_test_alloc);
    145
    146void ethnl_cable_test_free(struct phy_device *phydev)
    147{
    148	nlmsg_free(phydev->skb);
    149	phydev->skb = NULL;
    150}
    151EXPORT_SYMBOL_GPL(ethnl_cable_test_free);
    152
    153void ethnl_cable_test_finished(struct phy_device *phydev)
    154{
    155	nla_nest_end(phydev->skb, phydev->nest);
    156
    157	genlmsg_end(phydev->skb, phydev->ehdr);
    158
    159	ethnl_multicast(phydev->skb, phydev->attached_dev);
    160}
    161EXPORT_SYMBOL_GPL(ethnl_cable_test_finished);
    162
    163int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result)
    164{
    165	struct nlattr *nest;
    166	int ret = -EMSGSIZE;
    167
    168	nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_NEST_RESULT);
    169	if (!nest)
    170		return -EMSGSIZE;
    171
    172	if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_PAIR, pair))
    173		goto err;
    174	if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_CODE, result))
    175		goto err;
    176
    177	nla_nest_end(phydev->skb, nest);
    178	return 0;
    179
    180err:
    181	nla_nest_cancel(phydev->skb, nest);
    182	return ret;
    183}
    184EXPORT_SYMBOL_GPL(ethnl_cable_test_result);
    185
    186int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm)
    187{
    188	struct nlattr *nest;
    189	int ret = -EMSGSIZE;
    190
    191	nest = nla_nest_start(phydev->skb,
    192			      ETHTOOL_A_CABLE_NEST_FAULT_LENGTH);
    193	if (!nest)
    194		return -EMSGSIZE;
    195
    196	if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR, pair))
    197		goto err;
    198	if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_CM, cm))
    199		goto err;
    200
    201	nla_nest_end(phydev->skb, nest);
    202	return 0;
    203
    204err:
    205	nla_nest_cancel(phydev->skb, nest);
    206	return ret;
    207}
    208EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length);
    209
    210struct cable_test_tdr_req_info {
    211	struct ethnl_req_info		base;
    212};
    213
    214static const struct nla_policy cable_test_tdr_act_cfg_policy[] = {
    215	[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]	= { .type = NLA_U32 },
    216	[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]	= { .type = NLA_U32 },
    217	[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]	= { .type = NLA_U32 },
    218	[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]	= { .type = NLA_U8 },
    219};
    220
    221const struct nla_policy ethnl_cable_test_tdr_act_policy[] = {
    222	[ETHTOOL_A_CABLE_TEST_TDR_HEADER]	=
    223		NLA_POLICY_NESTED(ethnl_header_policy),
    224	[ETHTOOL_A_CABLE_TEST_TDR_CFG]		= { .type = NLA_NESTED },
    225};
    226
    227/* CABLE_TEST_TDR_ACT */
    228static int ethnl_act_cable_test_tdr_cfg(const struct nlattr *nest,
    229					struct genl_info *info,
    230					struct phy_tdr_config *cfg)
    231{
    232	struct nlattr *tb[ARRAY_SIZE(cable_test_tdr_act_cfg_policy)];
    233	int ret;
    234
    235	cfg->first = 100;
    236	cfg->step = 100;
    237	cfg->last = MAX_CABLE_LENGTH_CM;
    238	cfg->pair = PHY_PAIR_ALL;
    239
    240	if (!nest)
    241		return 0;
    242
    243	ret = nla_parse_nested(tb,
    244			       ARRAY_SIZE(cable_test_tdr_act_cfg_policy) - 1,
    245			       nest, cable_test_tdr_act_cfg_policy,
    246			       info->extack);
    247	if (ret < 0)
    248		return ret;
    249
    250	if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST])
    251		cfg->first = nla_get_u32(
    252			tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]);
    253
    254	if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST])
    255		cfg->last = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]);
    256
    257	if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP])
    258		cfg->step = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]);
    259
    260	if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]) {
    261		cfg->pair = nla_get_u8(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]);
    262		if (cfg->pair > ETHTOOL_A_CABLE_PAIR_D) {
    263			NL_SET_ERR_MSG_ATTR(
    264				info->extack,
    265				tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR],
    266				"invalid pair parameter");
    267			return -EINVAL;
    268		}
    269	}
    270
    271	if (cfg->first > MAX_CABLE_LENGTH_CM) {
    272		NL_SET_ERR_MSG_ATTR(info->extack,
    273				    tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST],
    274				    "invalid first parameter");
    275		return -EINVAL;
    276	}
    277
    278	if (cfg->last > MAX_CABLE_LENGTH_CM) {
    279		NL_SET_ERR_MSG_ATTR(info->extack,
    280				    tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST],
    281				    "invalid last parameter");
    282		return -EINVAL;
    283	}
    284
    285	if (cfg->first > cfg->last) {
    286		NL_SET_ERR_MSG(info->extack, "invalid first/last parameter");
    287		return -EINVAL;
    288	}
    289
    290	if (!cfg->step) {
    291		NL_SET_ERR_MSG_ATTR(info->extack,
    292				    tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP],
    293				    "invalid step parameter");
    294		return -EINVAL;
    295	}
    296
    297	if (cfg->step > (cfg->last - cfg->first)) {
    298		NL_SET_ERR_MSG_ATTR(info->extack,
    299				    tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP],
    300				    "step parameter too big");
    301		return -EINVAL;
    302	}
    303
    304	return 0;
    305}
    306
    307int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info)
    308{
    309	struct ethnl_req_info req_info = {};
    310	const struct ethtool_phy_ops *ops;
    311	struct nlattr **tb = info->attrs;
    312	struct phy_tdr_config cfg;
    313	struct net_device *dev;
    314	int ret;
    315
    316	ret = ethnl_parse_header_dev_get(&req_info,
    317					 tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER],
    318					 genl_info_net(info), info->extack,
    319					 true);
    320	if (ret < 0)
    321		return ret;
    322
    323	dev = req_info.dev;
    324	if (!dev->phydev) {
    325		ret = -EOPNOTSUPP;
    326		goto out_dev_put;
    327	}
    328
    329	ret = ethnl_act_cable_test_tdr_cfg(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG],
    330					   info, &cfg);
    331	if (ret)
    332		goto out_dev_put;
    333
    334	rtnl_lock();
    335	ops = ethtool_phy_ops;
    336	if (!ops || !ops->start_cable_test_tdr) {
    337		ret = -EOPNOTSUPP;
    338		goto out_rtnl;
    339	}
    340
    341	ret = ethnl_ops_begin(dev);
    342	if (ret < 0)
    343		goto out_rtnl;
    344
    345	ret = ops->start_cable_test_tdr(dev->phydev, info->extack, &cfg);
    346
    347	ethnl_ops_complete(dev);
    348
    349	if (!ret)
    350		ethnl_cable_test_started(dev->phydev,
    351					 ETHTOOL_MSG_CABLE_TEST_TDR_NTF);
    352
    353out_rtnl:
    354	rtnl_unlock();
    355out_dev_put:
    356	ethnl_parse_header_dev_put(&req_info);
    357	return ret;
    358}
    359 
    360int ethnl_cable_test_amplitude(struct phy_device *phydev,
    361			       u8 pair, s16 mV)
    362{
    363	struct nlattr *nest;
    364	int ret = -EMSGSIZE;
    365
    366	nest = nla_nest_start(phydev->skb,
    367			      ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE);
    368	if (!nest)
    369		return -EMSGSIZE;
    370
    371	if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_PAIR, pair))
    372		goto err;
    373	if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_mV, mV))
    374		goto err;
    375
    376	nla_nest_end(phydev->skb, nest);
    377	return 0;
    378
    379err:
    380	nla_nest_cancel(phydev->skb, nest);
    381	return ret;
    382}
    383EXPORT_SYMBOL_GPL(ethnl_cable_test_amplitude);
    384
    385int ethnl_cable_test_pulse(struct phy_device *phydev, u16 mV)
    386{
    387	struct nlattr *nest;
    388	int ret = -EMSGSIZE;
    389
    390	nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_PULSE);
    391	if (!nest)
    392		return -EMSGSIZE;
    393
    394	if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_PULSE_mV, mV))
    395		goto err;
    396
    397	nla_nest_end(phydev->skb, nest);
    398	return 0;
    399
    400err:
    401	nla_nest_cancel(phydev->skb, nest);
    402	return ret;
    403}
    404EXPORT_SYMBOL_GPL(ethnl_cable_test_pulse);
    405
    406int ethnl_cable_test_step(struct phy_device *phydev, u32 first, u32 last,
    407			  u32 step)
    408{
    409	struct nlattr *nest;
    410	int ret = -EMSGSIZE;
    411
    412	nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_STEP);
    413	if (!nest)
    414		return -EMSGSIZE;
    415
    416	if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE,
    417			first))
    418		goto err;
    419
    420	if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_LAST_DISTANCE, last))
    421		goto err;
    422
    423	if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_STEP_DISTANCE, step))
    424		goto err;
    425
    426	nla_nest_end(phydev->skb, nest);
    427	return 0;
    428
    429err:
    430	nla_nest_cancel(phydev->skb, nest);
    431	return ret;
    432}
    433EXPORT_SYMBOL_GPL(ethnl_cable_test_step);