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

eeprom.c (7006B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2
      3#include <linux/ethtool.h>
      4#include <linux/sfp.h>
      5#include "netlink.h"
      6#include "common.h"
      7
      8struct eeprom_req_info {
      9	struct ethnl_req_info	base;
     10	u32			offset;
     11	u32			length;
     12	u8			page;
     13	u8			bank;
     14	u8			i2c_address;
     15};
     16
     17struct eeprom_reply_data {
     18	struct ethnl_reply_data base;
     19	u32			length;
     20	u8			*data;
     21};
     22
     23#define MODULE_EEPROM_REQINFO(__req_base) \
     24	container_of(__req_base, struct eeprom_req_info, base)
     25
     26#define MODULE_EEPROM_REPDATA(__reply_base) \
     27	container_of(__reply_base, struct eeprom_reply_data, base)
     28
     29static int fallback_set_params(struct eeprom_req_info *request,
     30			       struct ethtool_modinfo *modinfo,
     31			       struct ethtool_eeprom *eeprom)
     32{
     33	u32 offset = request->offset;
     34	u32 length = request->length;
     35
     36	if (request->page)
     37		offset = request->page * ETH_MODULE_EEPROM_PAGE_LEN + offset;
     38
     39	if (modinfo->type == ETH_MODULE_SFF_8472 &&
     40	    request->i2c_address == 0x51)
     41		offset += ETH_MODULE_EEPROM_PAGE_LEN * 2;
     42
     43	if (offset >= modinfo->eeprom_len)
     44		return -EINVAL;
     45
     46	eeprom->cmd = ETHTOOL_GMODULEEEPROM;
     47	eeprom->len = length;
     48	eeprom->offset = offset;
     49
     50	return 0;
     51}
     52
     53static int eeprom_fallback(struct eeprom_req_info *request,
     54			   struct eeprom_reply_data *reply,
     55			   struct genl_info *info)
     56{
     57	struct net_device *dev = reply->base.dev;
     58	struct ethtool_modinfo modinfo = {0};
     59	struct ethtool_eeprom eeprom = {0};
     60	u8 *data;
     61	int err;
     62
     63	modinfo.cmd = ETHTOOL_GMODULEINFO;
     64	err = ethtool_get_module_info_call(dev, &modinfo);
     65	if (err < 0)
     66		return err;
     67
     68	err = fallback_set_params(request, &modinfo, &eeprom);
     69	if (err < 0)
     70		return err;
     71
     72	data = kmalloc(eeprom.len, GFP_KERNEL);
     73	if (!data)
     74		return -ENOMEM;
     75	err = ethtool_get_module_eeprom_call(dev, &eeprom, data);
     76	if (err < 0)
     77		goto err_out;
     78
     79	reply->data = data;
     80	reply->length = eeprom.len;
     81
     82	return 0;
     83
     84err_out:
     85	kfree(data);
     86	return err;
     87}
     88
     89static int get_module_eeprom_by_page(struct net_device *dev,
     90				     struct ethtool_module_eeprom *page_data,
     91				     struct netlink_ext_ack *extack)
     92{
     93	const struct ethtool_ops *ops = dev->ethtool_ops;
     94
     95	if (dev->sfp_bus)
     96		return sfp_get_module_eeprom_by_page(dev->sfp_bus, page_data, extack);
     97
     98	if (ops->get_module_eeprom_by_page)
     99		return ops->get_module_eeprom_by_page(dev, page_data, extack);
    100
    101	return -EOPNOTSUPP;
    102}
    103
    104static int eeprom_prepare_data(const struct ethnl_req_info *req_base,
    105			       struct ethnl_reply_data *reply_base,
    106			       struct genl_info *info)
    107{
    108	struct eeprom_reply_data *reply = MODULE_EEPROM_REPDATA(reply_base);
    109	struct eeprom_req_info *request = MODULE_EEPROM_REQINFO(req_base);
    110	struct ethtool_module_eeprom page_data = {0};
    111	struct net_device *dev = reply_base->dev;
    112	int ret;
    113
    114	page_data.offset = request->offset;
    115	page_data.length = request->length;
    116	page_data.i2c_address = request->i2c_address;
    117	page_data.page = request->page;
    118	page_data.bank = request->bank;
    119	page_data.data = kmalloc(page_data.length, GFP_KERNEL);
    120	if (!page_data.data)
    121		return -ENOMEM;
    122
    123	ret = ethnl_ops_begin(dev);
    124	if (ret)
    125		goto err_free;
    126
    127	ret = get_module_eeprom_by_page(dev, &page_data, info->extack);
    128	if (ret < 0)
    129		goto err_ops;
    130
    131	reply->length = ret;
    132	reply->data = page_data.data;
    133
    134	ethnl_ops_complete(dev);
    135	return 0;
    136
    137err_ops:
    138	ethnl_ops_complete(dev);
    139err_free:
    140	kfree(page_data.data);
    141
    142	if (ret == -EOPNOTSUPP)
    143		return eeprom_fallback(request, reply, info);
    144	return ret;
    145}
    146
    147static int eeprom_parse_request(struct ethnl_req_info *req_info, struct nlattr **tb,
    148				struct netlink_ext_ack *extack)
    149{
    150	struct eeprom_req_info *request = MODULE_EEPROM_REQINFO(req_info);
    151
    152	if (!tb[ETHTOOL_A_MODULE_EEPROM_OFFSET] ||
    153	    !tb[ETHTOOL_A_MODULE_EEPROM_LENGTH] ||
    154	    !tb[ETHTOOL_A_MODULE_EEPROM_PAGE] ||
    155	    !tb[ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS])
    156		return -EINVAL;
    157
    158	request->i2c_address = nla_get_u8(tb[ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS]);
    159	request->offset = nla_get_u32(tb[ETHTOOL_A_MODULE_EEPROM_OFFSET]);
    160	request->length = nla_get_u32(tb[ETHTOOL_A_MODULE_EEPROM_LENGTH]);
    161
    162	/* The following set of conditions limit the API to only dump 1/2
    163	 * EEPROM page without crossing low page boundary located at offset 128.
    164	 * This means user may only request dumps of length limited to 128 from
    165	 * either low 128 bytes or high 128 bytes.
    166	 * For pages higher than 0 only high 128 bytes are accessible.
    167	 */
    168	request->page = nla_get_u8(tb[ETHTOOL_A_MODULE_EEPROM_PAGE]);
    169	if (request->page && request->offset < ETH_MODULE_EEPROM_PAGE_LEN) {
    170		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_MODULE_EEPROM_PAGE],
    171				    "reading from lower half page is allowed for page 0 only");
    172		return -EINVAL;
    173	}
    174
    175	if (request->offset < ETH_MODULE_EEPROM_PAGE_LEN &&
    176	    request->offset + request->length > ETH_MODULE_EEPROM_PAGE_LEN) {
    177		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_MODULE_EEPROM_LENGTH],
    178				    "reading cross half page boundary is illegal");
    179		return -EINVAL;
    180	} else if (request->offset + request->length > ETH_MODULE_EEPROM_PAGE_LEN * 2) {
    181		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_MODULE_EEPROM_LENGTH],
    182				    "reading cross page boundary is illegal");
    183		return -EINVAL;
    184	}
    185
    186	if (tb[ETHTOOL_A_MODULE_EEPROM_BANK])
    187		request->bank = nla_get_u8(tb[ETHTOOL_A_MODULE_EEPROM_BANK]);
    188
    189	return 0;
    190}
    191
    192static int eeprom_reply_size(const struct ethnl_req_info *req_base,
    193			     const struct ethnl_reply_data *reply_base)
    194{
    195	const struct eeprom_req_info *request = MODULE_EEPROM_REQINFO(req_base);
    196
    197	return nla_total_size(sizeof(u8) * request->length); /* _EEPROM_DATA */
    198}
    199
    200static int eeprom_fill_reply(struct sk_buff *skb,
    201			     const struct ethnl_req_info *req_base,
    202			     const struct ethnl_reply_data *reply_base)
    203{
    204	struct eeprom_reply_data *reply = MODULE_EEPROM_REPDATA(reply_base);
    205
    206	return nla_put(skb, ETHTOOL_A_MODULE_EEPROM_DATA, reply->length, reply->data);
    207}
    208
    209static void eeprom_cleanup_data(struct ethnl_reply_data *reply_base)
    210{
    211	struct eeprom_reply_data *reply = MODULE_EEPROM_REPDATA(reply_base);
    212
    213	kfree(reply->data);
    214}
    215
    216const struct ethnl_request_ops ethnl_module_eeprom_request_ops = {
    217	.request_cmd		= ETHTOOL_MSG_MODULE_EEPROM_GET,
    218	.reply_cmd		= ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY,
    219	.hdr_attr		= ETHTOOL_A_MODULE_EEPROM_HEADER,
    220	.req_info_size		= sizeof(struct eeprom_req_info),
    221	.reply_data_size	= sizeof(struct eeprom_reply_data),
    222
    223	.parse_request		= eeprom_parse_request,
    224	.prepare_data		= eeprom_prepare_data,
    225	.reply_size		= eeprom_reply_size,
    226	.fill_reply		= eeprom_fill_reply,
    227	.cleanup_data		= eeprom_cleanup_data,
    228};
    229
    230const struct nla_policy ethnl_module_eeprom_get_policy[] = {
    231	[ETHTOOL_A_MODULE_EEPROM_HEADER]	= NLA_POLICY_NESTED(ethnl_header_policy),
    232	[ETHTOOL_A_MODULE_EEPROM_OFFSET]	=
    233		NLA_POLICY_MAX(NLA_U32, ETH_MODULE_EEPROM_PAGE_LEN * 2 - 1),
    234	[ETHTOOL_A_MODULE_EEPROM_LENGTH]	=
    235		NLA_POLICY_RANGE(NLA_U32, 1, ETH_MODULE_EEPROM_PAGE_LEN),
    236	[ETHTOOL_A_MODULE_EEPROM_PAGE]		= { .type = NLA_U8 },
    237	[ETHTOOL_A_MODULE_EEPROM_BANK]		= { .type = NLA_U8 },
    238	[ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS]	=
    239		NLA_POLICY_RANGE(NLA_U8, 0, ETH_MODULE_MAX_I2C_ADDRESS),
    240};
    241