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

privflags.c (5572B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2
      3#include "netlink.h"
      4#include "common.h"
      5#include "bitset.h"
      6
      7struct privflags_req_info {
      8	struct ethnl_req_info		base;
      9};
     10
     11struct privflags_reply_data {
     12	struct ethnl_reply_data		base;
     13	const char			(*priv_flag_names)[ETH_GSTRING_LEN];
     14	unsigned int			n_priv_flags;
     15	u32				priv_flags;
     16};
     17
     18#define PRIVFLAGS_REPDATA(__reply_base) \
     19	container_of(__reply_base, struct privflags_reply_data, base)
     20
     21const struct nla_policy ethnl_privflags_get_policy[] = {
     22	[ETHTOOL_A_PRIVFLAGS_HEADER]		=
     23		NLA_POLICY_NESTED(ethnl_header_policy),
     24};
     25
     26static int ethnl_get_priv_flags_info(struct net_device *dev,
     27				     unsigned int *count,
     28				     const char (**names)[ETH_GSTRING_LEN])
     29{
     30	const struct ethtool_ops *ops = dev->ethtool_ops;
     31	int nflags;
     32
     33	nflags = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
     34	if (nflags < 0)
     35		return nflags;
     36
     37	if (names) {
     38		*names = kcalloc(nflags, ETH_GSTRING_LEN, GFP_KERNEL);
     39		if (!*names)
     40			return -ENOMEM;
     41		ops->get_strings(dev, ETH_SS_PRIV_FLAGS, (u8 *)*names);
     42	}
     43
     44	/* We can pass more than 32 private flags to userspace via netlink but
     45	 * we cannot get more with ethtool_ops::get_priv_flags(). Note that we
     46	 * must not adjust nflags before allocating the space for flag names
     47	 * as the buffer must be large enough for all flags.
     48	 */
     49	if (WARN_ONCE(nflags > 32,
     50		      "device %s reports more than 32 private flags (%d)\n",
     51		      netdev_name(dev), nflags))
     52		nflags = 32;
     53	*count = nflags;
     54
     55	return 0;
     56}
     57
     58static int privflags_prepare_data(const struct ethnl_req_info *req_base,
     59				  struct ethnl_reply_data *reply_base,
     60				  struct genl_info *info)
     61{
     62	struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base);
     63	struct net_device *dev = reply_base->dev;
     64	const char (*names)[ETH_GSTRING_LEN];
     65	const struct ethtool_ops *ops;
     66	unsigned int nflags;
     67	int ret;
     68
     69	ops = dev->ethtool_ops;
     70	if (!ops->get_priv_flags || !ops->get_sset_count || !ops->get_strings)
     71		return -EOPNOTSUPP;
     72	ret = ethnl_ops_begin(dev);
     73	if (ret < 0)
     74		return ret;
     75
     76	ret = ethnl_get_priv_flags_info(dev, &nflags, &names);
     77	if (ret < 0)
     78		goto out_ops;
     79	data->priv_flags = ops->get_priv_flags(dev);
     80	data->priv_flag_names = names;
     81	data->n_priv_flags = nflags;
     82
     83out_ops:
     84	ethnl_ops_complete(dev);
     85	return ret;
     86}
     87
     88static int privflags_reply_size(const struct ethnl_req_info *req_base,
     89				const struct ethnl_reply_data *reply_base)
     90{
     91	const struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base);
     92	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
     93	const u32 all_flags = ~(u32)0 >> (32 - data->n_priv_flags);
     94
     95	return ethnl_bitset32_size(&data->priv_flags, &all_flags,
     96				   data->n_priv_flags,
     97				   data->priv_flag_names, compact);
     98}
     99
    100static int privflags_fill_reply(struct sk_buff *skb,
    101				const struct ethnl_req_info *req_base,
    102				const struct ethnl_reply_data *reply_base)
    103{
    104	const struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base);
    105	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
    106	const u32 all_flags = ~(u32)0 >> (32 - data->n_priv_flags);
    107
    108	return ethnl_put_bitset32(skb, ETHTOOL_A_PRIVFLAGS_FLAGS,
    109				  &data->priv_flags, &all_flags,
    110				  data->n_priv_flags, data->priv_flag_names,
    111				  compact);
    112}
    113
    114static void privflags_cleanup_data(struct ethnl_reply_data *reply_data)
    115{
    116	struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_data);
    117
    118	kfree(data->priv_flag_names);
    119}
    120
    121const struct ethnl_request_ops ethnl_privflags_request_ops = {
    122	.request_cmd		= ETHTOOL_MSG_PRIVFLAGS_GET,
    123	.reply_cmd		= ETHTOOL_MSG_PRIVFLAGS_GET_REPLY,
    124	.hdr_attr		= ETHTOOL_A_PRIVFLAGS_HEADER,
    125	.req_info_size		= sizeof(struct privflags_req_info),
    126	.reply_data_size	= sizeof(struct privflags_reply_data),
    127
    128	.prepare_data		= privflags_prepare_data,
    129	.reply_size		= privflags_reply_size,
    130	.fill_reply		= privflags_fill_reply,
    131	.cleanup_data		= privflags_cleanup_data,
    132};
    133
    134/* PRIVFLAGS_SET */
    135
    136const struct nla_policy ethnl_privflags_set_policy[] = {
    137	[ETHTOOL_A_PRIVFLAGS_HEADER]		=
    138		NLA_POLICY_NESTED(ethnl_header_policy),
    139	[ETHTOOL_A_PRIVFLAGS_FLAGS]		= { .type = NLA_NESTED },
    140};
    141
    142int ethnl_set_privflags(struct sk_buff *skb, struct genl_info *info)
    143{
    144	const char (*names)[ETH_GSTRING_LEN] = NULL;
    145	struct ethnl_req_info req_info = {};
    146	struct nlattr **tb = info->attrs;
    147	const struct ethtool_ops *ops;
    148	struct net_device *dev;
    149	unsigned int nflags;
    150	bool mod = false;
    151	bool compact;
    152	u32 flags;
    153	int ret;
    154
    155	if (!tb[ETHTOOL_A_PRIVFLAGS_FLAGS])
    156		return -EINVAL;
    157	ret = ethnl_bitset_is_compact(tb[ETHTOOL_A_PRIVFLAGS_FLAGS], &compact);
    158	if (ret < 0)
    159		return ret;
    160	ret = ethnl_parse_header_dev_get(&req_info,
    161					 tb[ETHTOOL_A_PRIVFLAGS_HEADER],
    162					 genl_info_net(info), info->extack,
    163					 true);
    164	if (ret < 0)
    165		return ret;
    166	dev = req_info.dev;
    167	ops = dev->ethtool_ops;
    168	ret = -EOPNOTSUPP;
    169	if (!ops->get_priv_flags || !ops->set_priv_flags ||
    170	    !ops->get_sset_count || !ops->get_strings)
    171		goto out_dev;
    172
    173	rtnl_lock();
    174	ret = ethnl_ops_begin(dev);
    175	if (ret < 0)
    176		goto out_rtnl;
    177	ret = ethnl_get_priv_flags_info(dev, &nflags, compact ? NULL : &names);
    178	if (ret < 0)
    179		goto out_ops;
    180	flags = ops->get_priv_flags(dev);
    181
    182	ret = ethnl_update_bitset32(&flags, nflags,
    183				    tb[ETHTOOL_A_PRIVFLAGS_FLAGS], names,
    184				    info->extack, &mod);
    185	if (ret < 0 || !mod)
    186		goto out_free;
    187	ret = ops->set_priv_flags(dev, flags);
    188	if (ret < 0)
    189		goto out_free;
    190	ethtool_notify(dev, ETHTOOL_MSG_PRIVFLAGS_NTF, NULL);
    191
    192out_free:
    193	kfree(names);
    194out_ops:
    195	ethnl_ops_complete(dev);
    196out_rtnl:
    197	rtnl_unlock();
    198out_dev:
    199	ethnl_parse_header_dev_put(&req_info);
    200	return ret;
    201}