lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Message-Id: <20180428003237.1536-4-f.fainelli@gmail.com> Date: Fri, 27 Apr 2018 17:32:33 -0700 From: Florian Fainelli <f.fainelli@...il.com> To: netdev@...r.kernel.org Cc: Florian Fainelli <f.fainelli@...il.com>, Andrew Lunn <andrew@...n.ch>, Russell King <rmk@...linux.org.uk>, linux-kernel@...r.kernel.org (open list), davem@...emloft.net, cphealy@...il.com, nikita.yoush@...entembedded.com, vivien.didelot@...oirfairelinux.com, Nisar.Sayed@...rochip.com, UNGLinuxDriver@...rochip.com Subject: [RFC net-next 3/5] net: ethtool: Add plumbing to get/set PHY test modes Implement the core ethtool changes to get/set PHY test modes, no driver implements that yet, but the internal API is defined and now allows it. We also provide the required helpers in PHYLIB in order to call the appropriate functions within the drivers. Signed-off-by: Florian Fainelli <f.fainelli@...il.com> --- include/linux/phy.h | 63 ++++++++++++++++++++++++++++++++++++++++-- net/core/ethtool.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 135 insertions(+), 7 deletions(-) diff --git a/include/linux/phy.h b/include/linux/phy.h index deba0c11647f..449afde7ca7c 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -659,6 +659,14 @@ struct phy_driver { struct ethtool_tunable *tuna, const void *data); int (*set_loopback)(struct phy_device *dev, bool enable); + + /* Get and Set PHY test modes */ + int (*get_test_len)(struct phy_device *dev, u32 mode); + int (*get_test)(struct phy_device *dev, + struct ethtool_phy_test *test, u8 *data); + int (*set_test)(struct phy_device *dev, + struct ethtool_phy_test *test, + const u8 *data); }; #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ struct phy_driver, mdiodrv) @@ -1090,9 +1098,11 @@ static inline int phy_ethtool_get_sset_count(struct phy_device *phydev, if (!phydev->drv) return -EIO; - if (phydev->drv->get_sset_count && - phydev->drv->get_strings && - phydev->drv->get_stats) { + if (!phydev->drv->get_sset_count || !phydev->drv->get_strings) + return -EOPNOTSUPP; + + if (phydev->drv->get_stats || phydev->drv->get_test_len || + phydev->drv->get_test || phydev->drv->set_test) { mutex_lock(&phydev->lock); ret = phydev->drv->get_sset_count(phydev, sset); mutex_unlock(&phydev->lock); @@ -1116,6 +1126,53 @@ static inline int phy_ethtool_get_stats(struct phy_device *phydev, return 0; } +static inline int phy_ethtool_get_test_len(struct phy_device *phydev, + u32 mode) +{ + int ret; + + if (!phydev->drv) + return -EIO; + + mutex_lock(&phydev->lock); + ret = phydev->drv->get_test_len(phydev, mode); + mutex_unlock(&phydev->lock); + + return ret; +} + +static inline int phy_ethtool_get_test(struct phy_device *phydev, + struct ethtool_phy_test *test, + u8 *data) +{ + int ret; + + if (!phydev->drv) + return -EIO; + + mutex_lock(&phydev->lock); + ret = phydev->drv->get_test(phydev, test, data); + mutex_unlock(&phydev->lock); + + return ret; +} + +static inline int phy_ethtool_set_test(struct phy_device *phydev, + struct ethtool_phy_test *test, + const u8 *data) +{ + int ret; + + if (!phydev->drv) + return -EIO; + + mutex_lock(&phydev->lock); + ret = phydev->drv->set_test(phydev, test, data); + mutex_unlock(&phydev->lock); + + return ret; +} + extern struct bus_type mdio_bus_type; struct mdio_board_info { diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 0b9e2a44e1d1..52d2c9bc49b4 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -227,8 +227,9 @@ static int __ethtool_get_sset_count(struct net_device *dev, int sset) if (sset == ETH_SS_PHY_TUNABLES) return ARRAY_SIZE(phy_tunable_strings); - if (sset == ETH_SS_PHY_STATS && dev->phydev && - !ops->get_ethtool_phy_stats) + if ((sset == ETH_SS_PHY_STATS && dev->phydev && + !ops->get_ethtool_phy_stats) || + (sset == ETH_SS_PHY_TESTS && dev->phydev)) return phy_ethtool_get_sset_count(dev->phydev, sset); if (ops->get_sset_count && ops->get_strings) @@ -252,8 +253,9 @@ static void __ethtool_get_strings(struct net_device *dev, memcpy(data, tunable_strings, sizeof(tunable_strings)); else if (stringset == ETH_SS_PHY_TUNABLES) memcpy(data, phy_tunable_strings, sizeof(phy_tunable_strings)); - else if (stringset == ETH_SS_PHY_STATS && dev->phydev && - !ops->get_ethtool_phy_stats) + else if ((stringset == ETH_SS_PHY_STATS && dev->phydev && + !ops->get_ethtool_phy_stats) || + (stringset == ETH_SS_PHY_TESTS && dev->phydev)) phy_ethtool_get_strings(dev->phydev, stringset, data); else /* ops->get_strings is valid because checked earlier */ @@ -2016,6 +2018,68 @@ static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr) return ret; } +static int ethtool_get_phy_test(struct net_device *dev, void __user *useraddr) +{ + struct phy_device *phydev = dev->phydev; + struct ethtool_phy_test test; + int ret, test_len; + void *data; + + if (!phydev) + return -EOPNOTSUPP; + + if (copy_from_user(&test, useraddr, sizeof(test))) + return -EFAULT; + + test_len = phy_ethtool_get_test_len(phydev, test.mode); + if (test_len < 0) + return test_len; + + test.len = test_len; + data = kmalloc(test_len, GFP_USER); + if (test_len && !data) + return -ENOMEM; + + ret = phy_ethtool_get_test(phydev, &test, data); + if (ret < 0) + goto out; + + ret = -EFAULT; + if (copy_to_user(useraddr, &test, sizeof(test))) + goto out; + useraddr += sizeof(test); + if (test_len && copy_to_user(useraddr, data, test_len)) + goto out; + ret = 0; +out: + kfree(data); + return ret; +} + +static int ethtool_set_phy_test(struct net_device *dev, void __user *useraddr) +{ + struct phy_device *phydev = dev->phydev; + struct ethtool_phy_test test; + void *data; + int ret; + + if (!phydev) + return -EOPNOTSUPP; + + if (copy_from_user(&test, useraddr, sizeof(test))) + return -EFAULT; + + useraddr += sizeof(test); + data = memdup_user(useraddr, test.len); + if (IS_ERR(data)) + return PTR_ERR(data); + + ret = phy_ethtool_set_test(phydev, &test, data); + + kfree(data); + return ret; +} + static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr) { struct ethtool_perm_addr epaddr; @@ -2637,6 +2701,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_PHY_GTUNABLE: case ETHTOOL_GLINKSETTINGS: case ETHTOOL_GFECPARAM: + case ETHTOOL_GPHYTEST: break; default: if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) @@ -2852,6 +2917,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_SFECPARAM: rc = ethtool_set_fecparam(dev, useraddr); break; + case ETHTOOL_GPHYTEST: + rc = ethtool_get_phy_test(dev, useraddr); + break; + case ETHTOOL_SPHYTEST: + rc = ethtool_set_phy_test(dev, useraddr); + break; default: rc = -EOPNOTSUPP; } -- 2.14.1
Powered by blists - more mailing lists