1353407d9SIdo Schimmel // SPDX-License-Identifier: GPL-2.0-only
2353407d9SIdo Schimmel
3353407d9SIdo Schimmel #include <linux/ethtool.h>
432b4c8b5SDanielle Ratson #include <linux/firmware.h>
532b4c8b5SDanielle Ratson #include <linux/sfp.h>
632b4c8b5SDanielle Ratson #include <net/devlink.h>
7*8ef890dfSJakub Kicinski #include <net/netdev_lock.h>
8353407d9SIdo Schimmel
9353407d9SIdo Schimmel #include "netlink.h"
10353407d9SIdo Schimmel #include "common.h"
11353407d9SIdo Schimmel #include "bitset.h"
12d7d4cfc4SDanielle Ratson #include "module_fw.h"
13353407d9SIdo Schimmel
14353407d9SIdo Schimmel struct module_req_info {
15353407d9SIdo Schimmel struct ethnl_req_info base;
16353407d9SIdo Schimmel };
17353407d9SIdo Schimmel
18353407d9SIdo Schimmel struct module_reply_data {
19353407d9SIdo Schimmel struct ethnl_reply_data base;
20353407d9SIdo Schimmel struct ethtool_module_power_mode_params power;
21353407d9SIdo Schimmel };
22353407d9SIdo Schimmel
23353407d9SIdo Schimmel #define MODULE_REPDATA(__reply_base) \
24353407d9SIdo Schimmel container_of(__reply_base, struct module_reply_data, base)
25353407d9SIdo Schimmel
26353407d9SIdo Schimmel /* MODULE_GET */
27353407d9SIdo Schimmel
28353407d9SIdo Schimmel const struct nla_policy ethnl_module_get_policy[ETHTOOL_A_MODULE_HEADER + 1] = {
29353407d9SIdo Schimmel [ETHTOOL_A_MODULE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
30353407d9SIdo Schimmel };
31353407d9SIdo Schimmel
module_get_power_mode(struct net_device * dev,struct module_reply_data * data,struct netlink_ext_ack * extack)32353407d9SIdo Schimmel static int module_get_power_mode(struct net_device *dev,
33353407d9SIdo Schimmel struct module_reply_data *data,
34353407d9SIdo Schimmel struct netlink_ext_ack *extack)
35353407d9SIdo Schimmel {
36353407d9SIdo Schimmel const struct ethtool_ops *ops = dev->ethtool_ops;
37353407d9SIdo Schimmel
38353407d9SIdo Schimmel if (!ops->get_module_power_mode)
39353407d9SIdo Schimmel return 0;
40353407d9SIdo Schimmel
41caa93b7cSEdward Cree if (dev->ethtool->module_fw_flash_in_progress) {
4232b4c8b5SDanielle Ratson NL_SET_ERR_MSG(extack,
4332b4c8b5SDanielle Ratson "Module firmware flashing is in progress");
4432b4c8b5SDanielle Ratson return -EBUSY;
4532b4c8b5SDanielle Ratson }
4632b4c8b5SDanielle Ratson
47353407d9SIdo Schimmel return ops->get_module_power_mode(dev, &data->power, extack);
48353407d9SIdo Schimmel }
49353407d9SIdo Schimmel
module_prepare_data(const struct ethnl_req_info * req_base,struct ethnl_reply_data * reply_base,const struct genl_info * info)50353407d9SIdo Schimmel static int module_prepare_data(const struct ethnl_req_info *req_base,
51353407d9SIdo Schimmel struct ethnl_reply_data *reply_base,
52f946270dSJakub Kicinski const struct genl_info *info)
53353407d9SIdo Schimmel {
54353407d9SIdo Schimmel struct module_reply_data *data = MODULE_REPDATA(reply_base);
55353407d9SIdo Schimmel struct net_device *dev = reply_base->dev;
56353407d9SIdo Schimmel int ret;
57353407d9SIdo Schimmel
58353407d9SIdo Schimmel ret = ethnl_ops_begin(dev);
59353407d9SIdo Schimmel if (ret < 0)
60353407d9SIdo Schimmel return ret;
61353407d9SIdo Schimmel
62f946270dSJakub Kicinski ret = module_get_power_mode(dev, data, info->extack);
63353407d9SIdo Schimmel if (ret < 0)
64353407d9SIdo Schimmel goto out_complete;
65353407d9SIdo Schimmel
66353407d9SIdo Schimmel out_complete:
67353407d9SIdo Schimmel ethnl_ops_complete(dev);
68353407d9SIdo Schimmel return ret;
69353407d9SIdo Schimmel }
70353407d9SIdo Schimmel
module_reply_size(const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)71353407d9SIdo Schimmel static int module_reply_size(const struct ethnl_req_info *req_base,
72353407d9SIdo Schimmel const struct ethnl_reply_data *reply_base)
73353407d9SIdo Schimmel {
74353407d9SIdo Schimmel struct module_reply_data *data = MODULE_REPDATA(reply_base);
75353407d9SIdo Schimmel int len = 0;
76353407d9SIdo Schimmel
77353407d9SIdo Schimmel if (data->power.policy)
78353407d9SIdo Schimmel len += nla_total_size(sizeof(u8)); /* _MODULE_POWER_MODE_POLICY */
79353407d9SIdo Schimmel
80353407d9SIdo Schimmel if (data->power.mode)
81353407d9SIdo Schimmel len += nla_total_size(sizeof(u8)); /* _MODULE_POWER_MODE */
82353407d9SIdo Schimmel
83353407d9SIdo Schimmel return len;
84353407d9SIdo Schimmel }
85353407d9SIdo Schimmel
module_fill_reply(struct sk_buff * skb,const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)86353407d9SIdo Schimmel static int module_fill_reply(struct sk_buff *skb,
87353407d9SIdo Schimmel const struct ethnl_req_info *req_base,
88353407d9SIdo Schimmel const struct ethnl_reply_data *reply_base)
89353407d9SIdo Schimmel {
90353407d9SIdo Schimmel const struct module_reply_data *data = MODULE_REPDATA(reply_base);
91353407d9SIdo Schimmel
92353407d9SIdo Schimmel if (data->power.policy &&
93353407d9SIdo Schimmel nla_put_u8(skb, ETHTOOL_A_MODULE_POWER_MODE_POLICY,
94353407d9SIdo Schimmel data->power.policy))
95353407d9SIdo Schimmel return -EMSGSIZE;
96353407d9SIdo Schimmel
97353407d9SIdo Schimmel if (data->power.mode &&
98353407d9SIdo Schimmel nla_put_u8(skb, ETHTOOL_A_MODULE_POWER_MODE, data->power.mode))
99353407d9SIdo Schimmel return -EMSGSIZE;
100353407d9SIdo Schimmel
101353407d9SIdo Schimmel return 0;
102353407d9SIdo Schimmel }
103353407d9SIdo Schimmel
10404007961SJakub Kicinski /* MODULE_SET */
10504007961SJakub Kicinski
10604007961SJakub Kicinski const struct nla_policy ethnl_module_set_policy[ETHTOOL_A_MODULE_POWER_MODE_POLICY + 1] = {
10704007961SJakub Kicinski [ETHTOOL_A_MODULE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
10804007961SJakub Kicinski [ETHTOOL_A_MODULE_POWER_MODE_POLICY] =
10904007961SJakub Kicinski NLA_POLICY_RANGE(NLA_U8, ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH,
11004007961SJakub Kicinski ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO),
11104007961SJakub Kicinski };
11204007961SJakub Kicinski
11304007961SJakub Kicinski static int
ethnl_set_module_validate(struct ethnl_req_info * req_info,struct genl_info * info)11404007961SJakub Kicinski ethnl_set_module_validate(struct ethnl_req_info *req_info,
11504007961SJakub Kicinski struct genl_info *info)
11604007961SJakub Kicinski {
11704007961SJakub Kicinski const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
11804007961SJakub Kicinski struct nlattr **tb = info->attrs;
11904007961SJakub Kicinski
12004007961SJakub Kicinski if (!tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY])
12104007961SJakub Kicinski return 0;
12204007961SJakub Kicinski
123caa93b7cSEdward Cree if (req_info->dev->ethtool->module_fw_flash_in_progress) {
12432b4c8b5SDanielle Ratson NL_SET_ERR_MSG(info->extack,
12532b4c8b5SDanielle Ratson "Module firmware flashing is in progress");
12632b4c8b5SDanielle Ratson return -EBUSY;
12732b4c8b5SDanielle Ratson }
12832b4c8b5SDanielle Ratson
12904007961SJakub Kicinski if (!ops->get_module_power_mode || !ops->set_module_power_mode) {
13004007961SJakub Kicinski NL_SET_ERR_MSG_ATTR(info->extack,
13104007961SJakub Kicinski tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY],
13204007961SJakub Kicinski "Setting power mode policy is not supported by this device");
13304007961SJakub Kicinski return -EOPNOTSUPP;
13404007961SJakub Kicinski }
13504007961SJakub Kicinski
13604007961SJakub Kicinski return 1;
13704007961SJakub Kicinski }
13804007961SJakub Kicinski
13904007961SJakub Kicinski static int
ethnl_set_module(struct ethnl_req_info * req_info,struct genl_info * info)14004007961SJakub Kicinski ethnl_set_module(struct ethnl_req_info *req_info, struct genl_info *info)
14104007961SJakub Kicinski {
14204007961SJakub Kicinski struct ethtool_module_power_mode_params power = {};
14304007961SJakub Kicinski struct ethtool_module_power_mode_params power_new;
14404007961SJakub Kicinski const struct ethtool_ops *ops;
14504007961SJakub Kicinski struct net_device *dev = req_info->dev;
14604007961SJakub Kicinski struct nlattr **tb = info->attrs;
14704007961SJakub Kicinski int ret;
14804007961SJakub Kicinski
14904007961SJakub Kicinski ops = dev->ethtool_ops;
15004007961SJakub Kicinski
15104007961SJakub Kicinski power_new.policy = nla_get_u8(tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY]);
15204007961SJakub Kicinski ret = ops->get_module_power_mode(dev, &power, info->extack);
15304007961SJakub Kicinski if (ret < 0)
15404007961SJakub Kicinski return ret;
15504007961SJakub Kicinski
15604007961SJakub Kicinski if (power_new.policy == power.policy)
15704007961SJakub Kicinski return 0;
15804007961SJakub Kicinski
15904007961SJakub Kicinski ret = ops->set_module_power_mode(dev, &power_new, info->extack);
16004007961SJakub Kicinski return ret < 0 ? ret : 1;
16104007961SJakub Kicinski }
16204007961SJakub Kicinski
163353407d9SIdo Schimmel const struct ethnl_request_ops ethnl_module_request_ops = {
164353407d9SIdo Schimmel .request_cmd = ETHTOOL_MSG_MODULE_GET,
165353407d9SIdo Schimmel .reply_cmd = ETHTOOL_MSG_MODULE_GET_REPLY,
166353407d9SIdo Schimmel .hdr_attr = ETHTOOL_A_MODULE_HEADER,
167353407d9SIdo Schimmel .req_info_size = sizeof(struct module_req_info),
168353407d9SIdo Schimmel .reply_data_size = sizeof(struct module_reply_data),
169353407d9SIdo Schimmel
170353407d9SIdo Schimmel .prepare_data = module_prepare_data,
171353407d9SIdo Schimmel .reply_size = module_reply_size,
172353407d9SIdo Schimmel .fill_reply = module_fill_reply,
17304007961SJakub Kicinski
17404007961SJakub Kicinski .set_validate = ethnl_set_module_validate,
17504007961SJakub Kicinski .set = ethnl_set_module,
17604007961SJakub Kicinski .set_ntf_cmd = ETHTOOL_MSG_MODULE_NTF,
177353407d9SIdo Schimmel };
178d7d4cfc4SDanielle Ratson
17932b4c8b5SDanielle Ratson /* MODULE_FW_FLASH_ACT */
18032b4c8b5SDanielle Ratson
18132b4c8b5SDanielle Ratson const struct nla_policy
18232b4c8b5SDanielle Ratson ethnl_module_fw_flash_act_policy[ETHTOOL_A_MODULE_FW_FLASH_PASSWORD + 1] = {
18332b4c8b5SDanielle Ratson [ETHTOOL_A_MODULE_FW_FLASH_HEADER] =
18432b4c8b5SDanielle Ratson NLA_POLICY_NESTED(ethnl_header_policy),
18532b4c8b5SDanielle Ratson [ETHTOOL_A_MODULE_FW_FLASH_FILE_NAME] = { .type = NLA_NUL_STRING },
18632b4c8b5SDanielle Ratson [ETHTOOL_A_MODULE_FW_FLASH_PASSWORD] = { .type = NLA_U32 },
18732b4c8b5SDanielle Ratson };
18832b4c8b5SDanielle Ratson
18932b4c8b5SDanielle Ratson static LIST_HEAD(module_fw_flash_work_list);
19032b4c8b5SDanielle Ratson static DEFINE_SPINLOCK(module_fw_flash_work_list_lock);
19132b4c8b5SDanielle Ratson
19232b4c8b5SDanielle Ratson static int
module_flash_fw_work_list_add(struct ethtool_module_fw_flash * module_fw,struct genl_info * info)19332b4c8b5SDanielle Ratson module_flash_fw_work_list_add(struct ethtool_module_fw_flash *module_fw,
19432b4c8b5SDanielle Ratson struct genl_info *info)
19532b4c8b5SDanielle Ratson {
19632b4c8b5SDanielle Ratson struct ethtool_module_fw_flash *work;
19732b4c8b5SDanielle Ratson
19832b4c8b5SDanielle Ratson /* First, check if already registered. */
19932b4c8b5SDanielle Ratson spin_lock(&module_fw_flash_work_list_lock);
20032b4c8b5SDanielle Ratson list_for_each_entry(work, &module_fw_flash_work_list, list) {
20132b4c8b5SDanielle Ratson if (work->fw_update.ntf_params.portid == info->snd_portid &&
20232b4c8b5SDanielle Ratson work->fw_update.dev == module_fw->fw_update.dev) {
20332b4c8b5SDanielle Ratson spin_unlock(&module_fw_flash_work_list_lock);
20432b4c8b5SDanielle Ratson return -EALREADY;
20532b4c8b5SDanielle Ratson }
20632b4c8b5SDanielle Ratson }
20732b4c8b5SDanielle Ratson
20832b4c8b5SDanielle Ratson list_add_tail(&module_fw->list, &module_fw_flash_work_list);
20932b4c8b5SDanielle Ratson spin_unlock(&module_fw_flash_work_list_lock);
21032b4c8b5SDanielle Ratson
21132b4c8b5SDanielle Ratson return 0;
21232b4c8b5SDanielle Ratson }
21332b4c8b5SDanielle Ratson
module_flash_fw_work_list_del(struct list_head * list)21432b4c8b5SDanielle Ratson static void module_flash_fw_work_list_del(struct list_head *list)
21532b4c8b5SDanielle Ratson {
21632b4c8b5SDanielle Ratson spin_lock(&module_fw_flash_work_list_lock);
21732b4c8b5SDanielle Ratson list_del(list);
21832b4c8b5SDanielle Ratson spin_unlock(&module_fw_flash_work_list_lock);
21932b4c8b5SDanielle Ratson }
22032b4c8b5SDanielle Ratson
module_flash_fw_work(struct work_struct * work)22132b4c8b5SDanielle Ratson static void module_flash_fw_work(struct work_struct *work)
22232b4c8b5SDanielle Ratson {
22332b4c8b5SDanielle Ratson struct ethtool_module_fw_flash *module_fw;
22432b4c8b5SDanielle Ratson
22532b4c8b5SDanielle Ratson module_fw = container_of(work, struct ethtool_module_fw_flash, work);
22632b4c8b5SDanielle Ratson
22732b4c8b5SDanielle Ratson ethtool_cmis_fw_update(&module_fw->fw_update);
22832b4c8b5SDanielle Ratson
22932b4c8b5SDanielle Ratson module_flash_fw_work_list_del(&module_fw->list);
230caa93b7cSEdward Cree module_fw->fw_update.dev->ethtool->module_fw_flash_in_progress = false;
23132b4c8b5SDanielle Ratson netdev_put(module_fw->fw_update.dev, &module_fw->dev_tracker);
23232b4c8b5SDanielle Ratson release_firmware(module_fw->fw_update.fw);
23332b4c8b5SDanielle Ratson kfree(module_fw);
23432b4c8b5SDanielle Ratson }
23532b4c8b5SDanielle Ratson
23632b4c8b5SDanielle Ratson #define MODULE_EEPROM_PHYS_ID_PAGE 0
23732b4c8b5SDanielle Ratson #define MODULE_EEPROM_PHYS_ID_I2C_ADDR 0x50
23832b4c8b5SDanielle Ratson
module_flash_fw_work_init(struct ethtool_module_fw_flash * module_fw,struct net_device * dev,struct netlink_ext_ack * extack)23932b4c8b5SDanielle Ratson static int module_flash_fw_work_init(struct ethtool_module_fw_flash *module_fw,
24032b4c8b5SDanielle Ratson struct net_device *dev,
24132b4c8b5SDanielle Ratson struct netlink_ext_ack *extack)
24232b4c8b5SDanielle Ratson {
24332b4c8b5SDanielle Ratson const struct ethtool_ops *ops = dev->ethtool_ops;
24432b4c8b5SDanielle Ratson struct ethtool_module_eeprom page_data = {};
24532b4c8b5SDanielle Ratson u8 phys_id;
24632b4c8b5SDanielle Ratson int err;
24732b4c8b5SDanielle Ratson
24832b4c8b5SDanielle Ratson /* Fetch the SFF-8024 Identifier Value. For all supported standards, it
24932b4c8b5SDanielle Ratson * is located at I2C address 0x50, byte 0. See section 4.1 in SFF-8024,
25032b4c8b5SDanielle Ratson * revision 4.9.
25132b4c8b5SDanielle Ratson */
25232b4c8b5SDanielle Ratson page_data.page = MODULE_EEPROM_PHYS_ID_PAGE;
25332b4c8b5SDanielle Ratson page_data.offset = SFP_PHYS_ID;
25432b4c8b5SDanielle Ratson page_data.length = sizeof(phys_id);
25532b4c8b5SDanielle Ratson page_data.i2c_address = MODULE_EEPROM_PHYS_ID_I2C_ADDR;
25632b4c8b5SDanielle Ratson page_data.data = &phys_id;
25732b4c8b5SDanielle Ratson
25832b4c8b5SDanielle Ratson err = ops->get_module_eeprom_by_page(dev, &page_data, extack);
25932b4c8b5SDanielle Ratson if (err < 0)
26032b4c8b5SDanielle Ratson return err;
26132b4c8b5SDanielle Ratson
26232b4c8b5SDanielle Ratson switch (phys_id) {
26332b4c8b5SDanielle Ratson case SFF8024_ID_QSFP_DD:
26432b4c8b5SDanielle Ratson case SFF8024_ID_OSFP:
26532b4c8b5SDanielle Ratson case SFF8024_ID_DSFP:
26632b4c8b5SDanielle Ratson case SFF8024_ID_QSFP_PLUS_CMIS:
26732b4c8b5SDanielle Ratson case SFF8024_ID_SFP_DD_CMIS:
26832b4c8b5SDanielle Ratson case SFF8024_ID_SFP_PLUS_CMIS:
26932b4c8b5SDanielle Ratson INIT_WORK(&module_fw->work, module_flash_fw_work);
27032b4c8b5SDanielle Ratson break;
27132b4c8b5SDanielle Ratson default:
27232b4c8b5SDanielle Ratson NL_SET_ERR_MSG(extack,
27332b4c8b5SDanielle Ratson "Module type does not support firmware flashing");
27432b4c8b5SDanielle Ratson return -EOPNOTSUPP;
27532b4c8b5SDanielle Ratson }
27632b4c8b5SDanielle Ratson
27732b4c8b5SDanielle Ratson return 0;
27832b4c8b5SDanielle Ratson }
27932b4c8b5SDanielle Ratson
ethnl_module_fw_flash_sock_destroy(struct ethnl_sock_priv * sk_priv)28032b4c8b5SDanielle Ratson void ethnl_module_fw_flash_sock_destroy(struct ethnl_sock_priv *sk_priv)
28132b4c8b5SDanielle Ratson {
28232b4c8b5SDanielle Ratson struct ethtool_module_fw_flash *work;
28332b4c8b5SDanielle Ratson
28432b4c8b5SDanielle Ratson spin_lock(&module_fw_flash_work_list_lock);
28532b4c8b5SDanielle Ratson list_for_each_entry(work, &module_fw_flash_work_list, list) {
28632b4c8b5SDanielle Ratson if (work->fw_update.dev == sk_priv->dev &&
28732b4c8b5SDanielle Ratson work->fw_update.ntf_params.portid == sk_priv->portid) {
28832b4c8b5SDanielle Ratson work->fw_update.ntf_params.closed_sock = true;
28932b4c8b5SDanielle Ratson break;
29032b4c8b5SDanielle Ratson }
29132b4c8b5SDanielle Ratson }
29232b4c8b5SDanielle Ratson spin_unlock(&module_fw_flash_work_list_lock);
29332b4c8b5SDanielle Ratson }
29432b4c8b5SDanielle Ratson
29532b4c8b5SDanielle Ratson static int
module_flash_fw_schedule(struct net_device * dev,const char * file_name,struct ethtool_module_fw_flash_params * params,struct sk_buff * skb,struct genl_info * info)29632b4c8b5SDanielle Ratson module_flash_fw_schedule(struct net_device *dev, const char *file_name,
29732b4c8b5SDanielle Ratson struct ethtool_module_fw_flash_params *params,
29832b4c8b5SDanielle Ratson struct sk_buff *skb, struct genl_info *info)
29932b4c8b5SDanielle Ratson {
30032b4c8b5SDanielle Ratson struct ethtool_cmis_fw_update_params *fw_update;
30132b4c8b5SDanielle Ratson struct ethtool_module_fw_flash *module_fw;
30232b4c8b5SDanielle Ratson int err;
30332b4c8b5SDanielle Ratson
30432b4c8b5SDanielle Ratson module_fw = kzalloc(sizeof(*module_fw), GFP_KERNEL);
30532b4c8b5SDanielle Ratson if (!module_fw)
30632b4c8b5SDanielle Ratson return -ENOMEM;
30732b4c8b5SDanielle Ratson
30832b4c8b5SDanielle Ratson fw_update = &module_fw->fw_update;
30932b4c8b5SDanielle Ratson fw_update->params = *params;
31032b4c8b5SDanielle Ratson err = request_firmware_direct(&fw_update->fw,
31132b4c8b5SDanielle Ratson file_name, &dev->dev);
31232b4c8b5SDanielle Ratson if (err) {
31332b4c8b5SDanielle Ratson NL_SET_ERR_MSG(info->extack,
31432b4c8b5SDanielle Ratson "Failed to request module firmware image");
31532b4c8b5SDanielle Ratson goto err_free;
31632b4c8b5SDanielle Ratson }
31732b4c8b5SDanielle Ratson
31832b4c8b5SDanielle Ratson err = module_flash_fw_work_init(module_fw, dev, info->extack);
31932b4c8b5SDanielle Ratson if (err < 0)
32032b4c8b5SDanielle Ratson goto err_release_firmware;
32132b4c8b5SDanielle Ratson
322caa93b7cSEdward Cree dev->ethtool->module_fw_flash_in_progress = true;
32332b4c8b5SDanielle Ratson netdev_hold(dev, &module_fw->dev_tracker, GFP_KERNEL);
32432b4c8b5SDanielle Ratson fw_update->dev = dev;
32532b4c8b5SDanielle Ratson fw_update->ntf_params.portid = info->snd_portid;
32632b4c8b5SDanielle Ratson fw_update->ntf_params.seq = info->snd_seq;
32732b4c8b5SDanielle Ratson fw_update->ntf_params.closed_sock = false;
32832b4c8b5SDanielle Ratson
32932b4c8b5SDanielle Ratson err = ethnl_sock_priv_set(skb, dev, fw_update->ntf_params.portid,
33032b4c8b5SDanielle Ratson ETHTOOL_SOCK_TYPE_MODULE_FW_FLASH);
33132b4c8b5SDanielle Ratson if (err < 0)
33232b4c8b5SDanielle Ratson goto err_release_firmware;
33332b4c8b5SDanielle Ratson
33432b4c8b5SDanielle Ratson err = module_flash_fw_work_list_add(module_fw, info);
33532b4c8b5SDanielle Ratson if (err < 0)
33632b4c8b5SDanielle Ratson goto err_release_firmware;
33732b4c8b5SDanielle Ratson
33832b4c8b5SDanielle Ratson schedule_work(&module_fw->work);
33932b4c8b5SDanielle Ratson
34032b4c8b5SDanielle Ratson return 0;
34132b4c8b5SDanielle Ratson
34232b4c8b5SDanielle Ratson err_release_firmware:
34332b4c8b5SDanielle Ratson release_firmware(fw_update->fw);
34432b4c8b5SDanielle Ratson err_free:
34532b4c8b5SDanielle Ratson kfree(module_fw);
34632b4c8b5SDanielle Ratson return err;
34732b4c8b5SDanielle Ratson }
34832b4c8b5SDanielle Ratson
module_flash_fw(struct net_device * dev,struct nlattr ** tb,struct sk_buff * skb,struct genl_info * info)34932b4c8b5SDanielle Ratson static int module_flash_fw(struct net_device *dev, struct nlattr **tb,
35032b4c8b5SDanielle Ratson struct sk_buff *skb, struct genl_info *info)
35132b4c8b5SDanielle Ratson {
35232b4c8b5SDanielle Ratson struct ethtool_module_fw_flash_params params = {};
35332b4c8b5SDanielle Ratson const char *file_name;
35432b4c8b5SDanielle Ratson struct nlattr *attr;
35532b4c8b5SDanielle Ratson
35632b4c8b5SDanielle Ratson if (GENL_REQ_ATTR_CHECK(info, ETHTOOL_A_MODULE_FW_FLASH_FILE_NAME))
35732b4c8b5SDanielle Ratson return -EINVAL;
35832b4c8b5SDanielle Ratson
35932b4c8b5SDanielle Ratson file_name = nla_data(tb[ETHTOOL_A_MODULE_FW_FLASH_FILE_NAME]);
36032b4c8b5SDanielle Ratson
36132b4c8b5SDanielle Ratson attr = tb[ETHTOOL_A_MODULE_FW_FLASH_PASSWORD];
36232b4c8b5SDanielle Ratson if (attr) {
36332b4c8b5SDanielle Ratson params.password = cpu_to_be32(nla_get_u32(attr));
36432b4c8b5SDanielle Ratson params.password_valid = true;
36532b4c8b5SDanielle Ratson }
36632b4c8b5SDanielle Ratson
36732b4c8b5SDanielle Ratson return module_flash_fw_schedule(dev, file_name, ¶ms, skb, info);
36832b4c8b5SDanielle Ratson }
36932b4c8b5SDanielle Ratson
ethnl_module_fw_flash_validate(struct net_device * dev,struct netlink_ext_ack * extack)37032b4c8b5SDanielle Ratson static int ethnl_module_fw_flash_validate(struct net_device *dev,
37132b4c8b5SDanielle Ratson struct netlink_ext_ack *extack)
37232b4c8b5SDanielle Ratson {
37332b4c8b5SDanielle Ratson struct devlink_port *devlink_port = dev->devlink_port;
37432b4c8b5SDanielle Ratson const struct ethtool_ops *ops = dev->ethtool_ops;
37532b4c8b5SDanielle Ratson
37632b4c8b5SDanielle Ratson if (!ops->set_module_eeprom_by_page ||
37732b4c8b5SDanielle Ratson !ops->get_module_eeprom_by_page) {
37832b4c8b5SDanielle Ratson NL_SET_ERR_MSG(extack,
37932b4c8b5SDanielle Ratson "Flashing module firmware is not supported by this device");
38032b4c8b5SDanielle Ratson return -EOPNOTSUPP;
38132b4c8b5SDanielle Ratson }
38232b4c8b5SDanielle Ratson
38332b4c8b5SDanielle Ratson if (!ops->reset) {
38432b4c8b5SDanielle Ratson NL_SET_ERR_MSG(extack,
38532b4c8b5SDanielle Ratson "Reset module is not supported by this device, so flashing is not permitted");
38632b4c8b5SDanielle Ratson return -EOPNOTSUPP;
38732b4c8b5SDanielle Ratson }
38832b4c8b5SDanielle Ratson
389caa93b7cSEdward Cree if (dev->ethtool->module_fw_flash_in_progress) {
39032b4c8b5SDanielle Ratson NL_SET_ERR_MSG(extack, "Module firmware flashing already in progress");
39132b4c8b5SDanielle Ratson return -EBUSY;
39232b4c8b5SDanielle Ratson }
39332b4c8b5SDanielle Ratson
39432b4c8b5SDanielle Ratson if (dev->flags & IFF_UP) {
39532b4c8b5SDanielle Ratson NL_SET_ERR_MSG(extack, "Netdevice is up, so flashing is not permitted");
39632b4c8b5SDanielle Ratson return -EBUSY;
39732b4c8b5SDanielle Ratson }
39832b4c8b5SDanielle Ratson
39932b4c8b5SDanielle Ratson if (devlink_port && devlink_port->attrs.split) {
40032b4c8b5SDanielle Ratson NL_SET_ERR_MSG(extack, "Can't perform firmware flashing on a split port");
40132b4c8b5SDanielle Ratson return -EOPNOTSUPP;
40232b4c8b5SDanielle Ratson }
40332b4c8b5SDanielle Ratson
40432b4c8b5SDanielle Ratson return 0;
40532b4c8b5SDanielle Ratson }
40632b4c8b5SDanielle Ratson
ethnl_act_module_fw_flash(struct sk_buff * skb,struct genl_info * info)40732b4c8b5SDanielle Ratson int ethnl_act_module_fw_flash(struct sk_buff *skb, struct genl_info *info)
40832b4c8b5SDanielle Ratson {
40932b4c8b5SDanielle Ratson struct ethnl_req_info req_info = {};
41032b4c8b5SDanielle Ratson struct nlattr **tb = info->attrs;
41132b4c8b5SDanielle Ratson struct net_device *dev;
41232b4c8b5SDanielle Ratson int ret;
41332b4c8b5SDanielle Ratson
41432b4c8b5SDanielle Ratson ret = ethnl_parse_header_dev_get(&req_info,
41532b4c8b5SDanielle Ratson tb[ETHTOOL_A_MODULE_FW_FLASH_HEADER],
41632b4c8b5SDanielle Ratson genl_info_net(info), info->extack,
41732b4c8b5SDanielle Ratson true);
41832b4c8b5SDanielle Ratson if (ret < 0)
41932b4c8b5SDanielle Ratson return ret;
42032b4c8b5SDanielle Ratson dev = req_info.dev;
42132b4c8b5SDanielle Ratson
42232b4c8b5SDanielle Ratson rtnl_lock();
4232bcf4772SJakub Kicinski netdev_lock_ops(dev);
42432b4c8b5SDanielle Ratson ret = ethnl_ops_begin(dev);
42532b4c8b5SDanielle Ratson if (ret < 0)
4262bcf4772SJakub Kicinski goto out_unlock;
42732b4c8b5SDanielle Ratson
42832b4c8b5SDanielle Ratson ret = ethnl_module_fw_flash_validate(dev, info->extack);
42932b4c8b5SDanielle Ratson if (ret < 0)
4302bcf4772SJakub Kicinski goto out_unlock;
43132b4c8b5SDanielle Ratson
43232b4c8b5SDanielle Ratson ret = module_flash_fw(dev, tb, skb, info);
43332b4c8b5SDanielle Ratson
43432b4c8b5SDanielle Ratson ethnl_ops_complete(dev);
43532b4c8b5SDanielle Ratson
4362bcf4772SJakub Kicinski out_unlock:
4372bcf4772SJakub Kicinski netdev_unlock_ops(dev);
43832b4c8b5SDanielle Ratson rtnl_unlock();
43932b4c8b5SDanielle Ratson ethnl_parse_header_dev_put(&req_info);
44032b4c8b5SDanielle Ratson return ret;
44132b4c8b5SDanielle Ratson }
44232b4c8b5SDanielle Ratson
443d7d4cfc4SDanielle Ratson /* MODULE_FW_FLASH_NTF */
444d7d4cfc4SDanielle Ratson
445d7d4cfc4SDanielle Ratson static int
ethnl_module_fw_flash_ntf_put_err(struct sk_buff * skb,char * err_msg,char * sub_err_msg)446d7d4cfc4SDanielle Ratson ethnl_module_fw_flash_ntf_put_err(struct sk_buff *skb, char *err_msg,
447d7d4cfc4SDanielle Ratson char *sub_err_msg)
448d7d4cfc4SDanielle Ratson {
449d7d4cfc4SDanielle Ratson int err_msg_len, sub_err_msg_len, total_len;
450d7d4cfc4SDanielle Ratson struct nlattr *attr;
451d7d4cfc4SDanielle Ratson
452d7d4cfc4SDanielle Ratson if (!err_msg)
453d7d4cfc4SDanielle Ratson return 0;
454d7d4cfc4SDanielle Ratson
455d7d4cfc4SDanielle Ratson err_msg_len = strlen(err_msg);
456d7d4cfc4SDanielle Ratson total_len = err_msg_len + 2; /* For period and NUL. */
457d7d4cfc4SDanielle Ratson
458d7d4cfc4SDanielle Ratson if (sub_err_msg) {
459d7d4cfc4SDanielle Ratson sub_err_msg_len = strlen(sub_err_msg);
460d7d4cfc4SDanielle Ratson total_len += sub_err_msg_len + 2; /* For ", ". */
461d7d4cfc4SDanielle Ratson }
462d7d4cfc4SDanielle Ratson
463d7d4cfc4SDanielle Ratson attr = nla_reserve(skb, ETHTOOL_A_MODULE_FW_FLASH_STATUS_MSG,
464d7d4cfc4SDanielle Ratson total_len);
465d7d4cfc4SDanielle Ratson if (!attr)
466d7d4cfc4SDanielle Ratson return -ENOMEM;
467d7d4cfc4SDanielle Ratson
468d7d4cfc4SDanielle Ratson if (sub_err_msg)
469d7d4cfc4SDanielle Ratson sprintf(nla_data(attr), "%s, %s.", err_msg, sub_err_msg);
470d7d4cfc4SDanielle Ratson else
471d7d4cfc4SDanielle Ratson sprintf(nla_data(attr), "%s.", err_msg);
472d7d4cfc4SDanielle Ratson
473d7d4cfc4SDanielle Ratson return 0;
474d7d4cfc4SDanielle Ratson }
475d7d4cfc4SDanielle Ratson
476d7d4cfc4SDanielle Ratson static void
ethnl_module_fw_flash_ntf(struct net_device * dev,enum ethtool_module_fw_flash_status status,struct ethnl_module_fw_flash_ntf_params * ntf_params,char * err_msg,char * sub_err_msg,u64 done,u64 total)477d7d4cfc4SDanielle Ratson ethnl_module_fw_flash_ntf(struct net_device *dev,
478d7d4cfc4SDanielle Ratson enum ethtool_module_fw_flash_status status,
479d7d4cfc4SDanielle Ratson struct ethnl_module_fw_flash_ntf_params *ntf_params,
480d7d4cfc4SDanielle Ratson char *err_msg, char *sub_err_msg,
481d7d4cfc4SDanielle Ratson u64 done, u64 total)
482d7d4cfc4SDanielle Ratson {
483d7d4cfc4SDanielle Ratson struct sk_buff *skb;
484d7d4cfc4SDanielle Ratson void *hdr;
485d7d4cfc4SDanielle Ratson int ret;
486d7d4cfc4SDanielle Ratson
487d7d4cfc4SDanielle Ratson if (ntf_params->closed_sock)
488d7d4cfc4SDanielle Ratson return;
489d7d4cfc4SDanielle Ratson
490d7d4cfc4SDanielle Ratson skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
491d7d4cfc4SDanielle Ratson if (!skb)
492d7d4cfc4SDanielle Ratson return;
493d7d4cfc4SDanielle Ratson
494275a63c9SDanielle Ratson hdr = ethnl_unicast_put(skb, ntf_params->portid, ++ntf_params->seq,
495d7d4cfc4SDanielle Ratson ETHTOOL_MSG_MODULE_FW_FLASH_NTF);
496d7d4cfc4SDanielle Ratson if (!hdr)
497d7d4cfc4SDanielle Ratson goto err_skb;
498d7d4cfc4SDanielle Ratson
499d7d4cfc4SDanielle Ratson ret = ethnl_fill_reply_header(skb, dev,
500d7d4cfc4SDanielle Ratson ETHTOOL_A_MODULE_FW_FLASH_HEADER);
501d7d4cfc4SDanielle Ratson if (ret < 0)
502d7d4cfc4SDanielle Ratson goto err_skb;
503d7d4cfc4SDanielle Ratson
504d7d4cfc4SDanielle Ratson if (nla_put_u32(skb, ETHTOOL_A_MODULE_FW_FLASH_STATUS, status))
505d7d4cfc4SDanielle Ratson goto err_skb;
506d7d4cfc4SDanielle Ratson
507d7d4cfc4SDanielle Ratson ret = ethnl_module_fw_flash_ntf_put_err(skb, err_msg, sub_err_msg);
508d7d4cfc4SDanielle Ratson if (ret < 0)
509d7d4cfc4SDanielle Ratson goto err_skb;
510d7d4cfc4SDanielle Ratson
511d7d4cfc4SDanielle Ratson if (nla_put_uint(skb, ETHTOOL_A_MODULE_FW_FLASH_DONE, done))
512d7d4cfc4SDanielle Ratson goto err_skb;
513d7d4cfc4SDanielle Ratson
514d7d4cfc4SDanielle Ratson if (nla_put_uint(skb, ETHTOOL_A_MODULE_FW_FLASH_TOTAL, total))
515d7d4cfc4SDanielle Ratson goto err_skb;
516d7d4cfc4SDanielle Ratson
517d7d4cfc4SDanielle Ratson genlmsg_end(skb, hdr);
518d7d4cfc4SDanielle Ratson genlmsg_unicast(dev_net(dev), skb, ntf_params->portid);
519d7d4cfc4SDanielle Ratson return;
520d7d4cfc4SDanielle Ratson
521d7d4cfc4SDanielle Ratson err_skb:
522d7d4cfc4SDanielle Ratson nlmsg_free(skb);
523d7d4cfc4SDanielle Ratson }
524d7d4cfc4SDanielle Ratson
ethnl_module_fw_flash_ntf_err(struct net_device * dev,struct ethnl_module_fw_flash_ntf_params * params,char * err_msg,char * sub_err_msg)525d7d4cfc4SDanielle Ratson void ethnl_module_fw_flash_ntf_err(struct net_device *dev,
526d7d4cfc4SDanielle Ratson struct ethnl_module_fw_flash_ntf_params *params,
527d7d4cfc4SDanielle Ratson char *err_msg, char *sub_err_msg)
528d7d4cfc4SDanielle Ratson {
529d7d4cfc4SDanielle Ratson ethnl_module_fw_flash_ntf(dev, ETHTOOL_MODULE_FW_FLASH_STATUS_ERROR,
530d7d4cfc4SDanielle Ratson params, err_msg, sub_err_msg, 0, 0);
531d7d4cfc4SDanielle Ratson }
532d7d4cfc4SDanielle Ratson
533d7d4cfc4SDanielle Ratson void
ethnl_module_fw_flash_ntf_start(struct net_device * dev,struct ethnl_module_fw_flash_ntf_params * params)534d7d4cfc4SDanielle Ratson ethnl_module_fw_flash_ntf_start(struct net_device *dev,
535d7d4cfc4SDanielle Ratson struct ethnl_module_fw_flash_ntf_params *params)
536d7d4cfc4SDanielle Ratson {
537d7d4cfc4SDanielle Ratson ethnl_module_fw_flash_ntf(dev, ETHTOOL_MODULE_FW_FLASH_STATUS_STARTED,
538d7d4cfc4SDanielle Ratson params, NULL, NULL, 0, 0);
539d7d4cfc4SDanielle Ratson }
540d7d4cfc4SDanielle Ratson
541d7d4cfc4SDanielle Ratson void
ethnl_module_fw_flash_ntf_complete(struct net_device * dev,struct ethnl_module_fw_flash_ntf_params * params)542d7d4cfc4SDanielle Ratson ethnl_module_fw_flash_ntf_complete(struct net_device *dev,
543d7d4cfc4SDanielle Ratson struct ethnl_module_fw_flash_ntf_params *params)
544d7d4cfc4SDanielle Ratson {
545d7d4cfc4SDanielle Ratson ethnl_module_fw_flash_ntf(dev, ETHTOOL_MODULE_FW_FLASH_STATUS_COMPLETED,
546d7d4cfc4SDanielle Ratson params, NULL, NULL, 0, 0);
547d7d4cfc4SDanielle Ratson }
548d7d4cfc4SDanielle Ratson
549d7d4cfc4SDanielle Ratson void
ethnl_module_fw_flash_ntf_in_progress(struct net_device * dev,struct ethnl_module_fw_flash_ntf_params * params,u64 done,u64 total)550d7d4cfc4SDanielle Ratson ethnl_module_fw_flash_ntf_in_progress(struct net_device *dev,
551d7d4cfc4SDanielle Ratson struct ethnl_module_fw_flash_ntf_params *params,
552d7d4cfc4SDanielle Ratson u64 done, u64 total)
553d7d4cfc4SDanielle Ratson {
554d7d4cfc4SDanielle Ratson ethnl_module_fw_flash_ntf(dev,
555d7d4cfc4SDanielle Ratson ETHTOOL_MODULE_FW_FLASH_STATUS_IN_PROGRESS,
556d7d4cfc4SDanielle Ratson params, NULL, NULL, done, total);
557d7d4cfc4SDanielle Ratson }
558