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

wol.c (4440B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2
      3#include "netlink.h"
      4#include "common.h"
      5#include "bitset.h"
      6
      7struct wol_req_info {
      8	struct ethnl_req_info		base;
      9};
     10
     11struct wol_reply_data {
     12	struct ethnl_reply_data		base;
     13	struct ethtool_wolinfo		wol;
     14	bool				show_sopass;
     15};
     16
     17#define WOL_REPDATA(__reply_base) \
     18	container_of(__reply_base, struct wol_reply_data, base)
     19
     20const struct nla_policy ethnl_wol_get_policy[] = {
     21	[ETHTOOL_A_WOL_HEADER]		=
     22		NLA_POLICY_NESTED(ethnl_header_policy),
     23};
     24
     25static int wol_prepare_data(const struct ethnl_req_info *req_base,
     26			    struct ethnl_reply_data *reply_base,
     27			    struct genl_info *info)
     28{
     29	struct wol_reply_data *data = WOL_REPDATA(reply_base);
     30	struct net_device *dev = reply_base->dev;
     31	int ret;
     32
     33	if (!dev->ethtool_ops->get_wol)
     34		return -EOPNOTSUPP;
     35
     36	ret = ethnl_ops_begin(dev);
     37	if (ret < 0)
     38		return ret;
     39	dev->ethtool_ops->get_wol(dev, &data->wol);
     40	ethnl_ops_complete(dev);
     41	/* do not include password in notifications */
     42	data->show_sopass = info && (data->wol.supported & WAKE_MAGICSECURE);
     43
     44	return 0;
     45}
     46
     47static int wol_reply_size(const struct ethnl_req_info *req_base,
     48			  const struct ethnl_reply_data *reply_base)
     49{
     50	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
     51	const struct wol_reply_data *data = WOL_REPDATA(reply_base);
     52	int len;
     53
     54	len = ethnl_bitset32_size(&data->wol.wolopts, &data->wol.supported,
     55				  WOL_MODE_COUNT, wol_mode_names, compact);
     56	if (len < 0)
     57		return len;
     58	if (data->show_sopass)
     59		len += nla_total_size(sizeof(data->wol.sopass));
     60
     61	return len;
     62}
     63
     64static int wol_fill_reply(struct sk_buff *skb,
     65			  const struct ethnl_req_info *req_base,
     66			  const struct ethnl_reply_data *reply_base)
     67{
     68	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
     69	const struct wol_reply_data *data = WOL_REPDATA(reply_base);
     70	int ret;
     71
     72	ret = ethnl_put_bitset32(skb, ETHTOOL_A_WOL_MODES, &data->wol.wolopts,
     73				 &data->wol.supported, WOL_MODE_COUNT,
     74				 wol_mode_names, compact);
     75	if (ret < 0)
     76		return ret;
     77	if (data->show_sopass &&
     78	    nla_put(skb, ETHTOOL_A_WOL_SOPASS, sizeof(data->wol.sopass),
     79		    data->wol.sopass))
     80		return -EMSGSIZE;
     81
     82	return 0;
     83}
     84
     85const struct ethnl_request_ops ethnl_wol_request_ops = {
     86	.request_cmd		= ETHTOOL_MSG_WOL_GET,
     87	.reply_cmd		= ETHTOOL_MSG_WOL_GET_REPLY,
     88	.hdr_attr		= ETHTOOL_A_WOL_HEADER,
     89	.req_info_size		= sizeof(struct wol_req_info),
     90	.reply_data_size	= sizeof(struct wol_reply_data),
     91
     92	.prepare_data		= wol_prepare_data,
     93	.reply_size		= wol_reply_size,
     94	.fill_reply		= wol_fill_reply,
     95};
     96
     97/* WOL_SET */
     98
     99const struct nla_policy ethnl_wol_set_policy[] = {
    100	[ETHTOOL_A_WOL_HEADER]		=
    101		NLA_POLICY_NESTED(ethnl_header_policy),
    102	[ETHTOOL_A_WOL_MODES]		= { .type = NLA_NESTED },
    103	[ETHTOOL_A_WOL_SOPASS]		= { .type = NLA_BINARY,
    104					    .len = SOPASS_MAX },
    105};
    106
    107int ethnl_set_wol(struct sk_buff *skb, struct genl_info *info)
    108{
    109	struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
    110	struct ethnl_req_info req_info = {};
    111	struct nlattr **tb = info->attrs;
    112	struct net_device *dev;
    113	bool mod = false;
    114	int ret;
    115
    116	ret = ethnl_parse_header_dev_get(&req_info, tb[ETHTOOL_A_WOL_HEADER],
    117					 genl_info_net(info), info->extack,
    118					 true);
    119	if (ret < 0)
    120		return ret;
    121	dev = req_info.dev;
    122	ret = -EOPNOTSUPP;
    123	if (!dev->ethtool_ops->get_wol || !dev->ethtool_ops->set_wol)
    124		goto out_dev;
    125
    126	rtnl_lock();
    127	ret = ethnl_ops_begin(dev);
    128	if (ret < 0)
    129		goto out_rtnl;
    130
    131	dev->ethtool_ops->get_wol(dev, &wol);
    132	ret = ethnl_update_bitset32(&wol.wolopts, WOL_MODE_COUNT,
    133				    tb[ETHTOOL_A_WOL_MODES], wol_mode_names,
    134				    info->extack, &mod);
    135	if (ret < 0)
    136		goto out_ops;
    137	if (wol.wolopts & ~wol.supported) {
    138		NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_WOL_MODES],
    139				    "cannot enable unsupported WoL mode");
    140		ret = -EINVAL;
    141		goto out_ops;
    142	}
    143	if (tb[ETHTOOL_A_WOL_SOPASS]) {
    144		if (!(wol.supported & WAKE_MAGICSECURE)) {
    145			NL_SET_ERR_MSG_ATTR(info->extack,
    146					    tb[ETHTOOL_A_WOL_SOPASS],
    147					    "magicsecure not supported, cannot set password");
    148			ret = -EINVAL;
    149			goto out_ops;
    150		}
    151		ethnl_update_binary(wol.sopass, sizeof(wol.sopass),
    152				    tb[ETHTOOL_A_WOL_SOPASS], &mod);
    153	}
    154
    155	if (!mod)
    156		goto out_ops;
    157	ret = dev->ethtool_ops->set_wol(dev, &wol);
    158	if (ret)
    159		goto out_ops;
    160	dev->wol_enabled = !!wol.wolopts;
    161	ethtool_notify(dev, ETHTOOL_MSG_WOL_NTF, NULL);
    162
    163out_ops:
    164	ethnl_ops_complete(dev);
    165out_rtnl:
    166	rtnl_unlock();
    167out_dev:
    168	ethnl_parse_header_dev_put(&req_info);
    169	return ret;
    170}