Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'net-flash-modees-firmware' into main

Danielle Ratson says:

====================
Add ability to flash modules' firmware

CMIS compliant modules such as QSFP-DD might be running a firmware that
can be updated in a vendor-neutral way by exchanging messages between
the host and the module as described in section 7.2.2 of revision
4.0 of the CMIS standard.

According to the CMIS standard, the firmware update process is done
using a CDB commands sequence.

CDB (Command Data Block Message Communication) reads and writes are
performed on memory map pages 9Fh-AFh according to the CMIS standard,
section 8.12 of revision 4.0.

Add a pair of new ethtool messages that allow:

* User space to trigger firmware update of transceiver modules

* The kernel to notify user space about the progress of the process

The user interface is designed to be asynchronous in order to avoid RTNL
being held for too long and to allow several modules to be updated
simultaneously. The interface is designed with CMIS compliant modules in
mind, but kept generic enough to accommodate future use cases, if these
arise.

The kernel interface that will implement the firmware update using CDB
command will include 2 layers that will be added under ethtool:

* The upper layer that will be triggered from the module layer, is
cmis_ fw_update.
* The lower one is cmis_cdb.

In the future there might be more operations to implement using CDB
commands. Therefore, the idea is to keep the cmis_cdb interface clean and
the cmis_fw_update specific to the cdb commands handling it.

The communication between the kernel and the driver will be done using
two ethtool operations that enable reading and writing the transceiver
module EEPROM.
The operation ethtool_ops::get_module_eeprom_by_page, that is already
implemented, will be used for reading from the EEPROM the CDB reply,
e.g. reading module setting, state, etc.
The operation ethtool_ops::set_module_eeprom_by_page, that is added in
the current patchset, will be used for writing to the EEPROM the CDB
command such as start firmware image, run firmware image, etc.

Therefore in order for a driver to implement module flashing, that
driver needs to implement the two functions mentioned above.

Patchset overview:
Patch #1-#2: Implement the EEPROM writing in mlxsw.
Patch #3: Define the interface between the kernel and user space.
Patch #4: Add ability to notify the flashing firmware progress.
Patch #5: Veto operations during flashing.
Patch #6: Add extended compliance codes.
Patch #7: Add the cdb layer.
Patch #8: Add the fw_update layer.
Patch #9: Add ability to flash transceiver modules' firmware.

v8:
Patch #7:
* In the ethtool_cmis_wait_for_cond() evaluate the condition once more
to decide if the error code should be -ETIMEDOUT or something else.
* s/netdev_err/netdev_err_once.

v7:
Patch #4:
* Return -ENOMEM instead of PTR_ERR(attr) on
ethnl_module_fw_flash_ntf_put_err().
Patch #9:
* Fix Warning for not unlocking the spin_lock in the error flow
on module_flash_fw_work_list_add().
* Avoid the fall-through on ethnl_sock_priv_destroy().

v6:
* Squash some of the last patch to patch #5 and patch #9.
Patch #3:
* Add paragraph in .rst file.
Patch #4:
* Reserve '1' more place on SKB for NUL terminator in
the error message string.
* Add more prints on error flow, re-write the printing
function and add ethnl_module_fw_flash_ntf_put_err().
* Change the communication method so notification will be
sent in unicast instead of multicast.
* Add new 'struct ethnl_module_fw_flash_ntf_params' that holds
the relevant info for unicast communication and use it to
send notification to the specific socket.
* s/nla_put_u64_64bit/nla_put_uint/
Patch #7:
* In ethtool_cmis_cdb_init(), Use 'const' for the 'params'
parameter.
Patch #8:
* Add a list field to struct ethtool_module_fw_flash for
module_fw_flash_work_list that will be presented in the next
patch.
* Move ethtool_cmis_fw_update() cleaning to a new function that
will be represented in the next patch.
* Move some of the fields in struct ethtool_module_fw_flash to
a separate struct, so ethtool_cmis_fw_update() will get only
the relevant parameters for it.
* Edit the relevant functions to get the relevant params for
them.
* s/CMIS_MODULE_READY_MAX_DURATION_USEC/CMIS_MODULE_READY_MAX_DURATION_MSEC
Patch #9:
* Add a paragraph in the commit message.
* Rename labels in module_flash_fw_schedule().
* Add info to genl_sk_priv_*() and implement the relevant
callbacks, in order to handle properly a scenario of closing
the socket from user space before the work item was ended.
* Add a list the holds all the ethtool_module_fw_flash struct
that corresponds to the in progress work items.
* Add a new enum for the socket types.
* Use both above to identify a flashing socket, add it to the
list and when closing socket affect only the flashing type.
* Create a new function that will get the work item instead of
ethtool_cmis_fw_update().
* Edit the relevant functions to get the relevant params for
them.
* The new function will call the old ethtool_cmis_fw_update(),
and do the cleaning, so the existence of the list should be
completely isolated in module.c.
===================

Signed-off-by: David S. Miller <davem@davemloft.net>

+1963 -11
+55
Documentation/netlink/specs/ethtool.yaml
··· 20 20 name: header-flags 21 21 type: flags 22 22 entries: [ compact-bitsets, omit-reply, stats ] 23 + - 24 + name: module-fw-flash-status 25 + type: enum 26 + entries: [ started, in_progress, completed, error ] 23 27 24 28 attribute-sets: 25 29 - ··· 1008 1004 - 1009 1005 name: burst-tmr 1010 1006 type: u32 1007 + - 1008 + name: module-fw-flash 1009 + attributes: 1010 + - 1011 + name: header 1012 + type: nest 1013 + nested-attributes: header 1014 + - 1015 + name: file-name 1016 + type: string 1017 + - 1018 + name: password 1019 + type: u32 1020 + - 1021 + name: status 1022 + type: u32 1023 + enum: module-fw-flash-status 1024 + - 1025 + name: status-msg 1026 + type: string 1027 + - 1028 + name: done 1029 + type: uint 1030 + - 1031 + name: total 1032 + type: uint 1011 1033 1012 1034 operations: 1013 1035 enum-model: directional ··· 1794 1764 name: mm-ntf 1795 1765 doc: Notification for change in MAC Merge configuration. 1796 1766 notify: mm-get 1767 + - 1768 + name: module-fw-flash-act 1769 + doc: Flash transceiver module firmware. 1770 + 1771 + attribute-set: module-fw-flash 1772 + 1773 + do: 1774 + request: 1775 + attributes: 1776 + - header 1777 + - file-name 1778 + - password 1779 + - 1780 + name: module-fw-flash-ntf 1781 + doc: Notification for firmware flashing progress and status. 1782 + 1783 + attribute-set: module-fw-flash 1784 + 1785 + event: 1786 + attributes: 1787 + - header 1788 + - status 1789 + - status-msg 1790 + - done 1791 + - total
+70
Documentation/networking/ethtool-netlink.rst
··· 228 228 ``ETHTOOL_MSG_PLCA_GET_STATUS`` get PLCA RS status 229 229 ``ETHTOOL_MSG_MM_GET`` get MAC merge layer state 230 230 ``ETHTOOL_MSG_MM_SET`` set MAC merge layer parameters 231 + ``ETHTOOL_MSG_MODULE_FW_FLASH_ACT`` flash transceiver module firmware 231 232 ===================================== ================================= 232 233 233 234 Kernel to userspace: ··· 275 274 ``ETHTOOL_MSG_PLCA_GET_STATUS_REPLY`` PLCA RS status 276 275 ``ETHTOOL_MSG_PLCA_NTF`` PLCA RS parameters 277 276 ``ETHTOOL_MSG_MM_GET_REPLY`` MAC merge layer status 277 + ``ETHTOOL_MSG_MODULE_FW_FLASH_NTF`` transceiver module flash updates 278 278 ======================================== ================================= 279 279 280 280 ``GET`` requests are sent by userspace applications to retrieve device ··· 2043 2041 .. kernel-doc:: include/linux/ethtool.h 2044 2042 :identifiers: ethtool_mm_cfg 2045 2043 2044 + MODULE_FW_FLASH_ACT 2045 + =================== 2046 + 2047 + Flashes transceiver module firmware. 2048 + 2049 + Request contents: 2050 + 2051 + ======================================= ====== =========================== 2052 + ``ETHTOOL_A_MODULE_FW_FLASH_HEADER`` nested request header 2053 + ``ETHTOOL_A_MODULE_FW_FLASH_FILE_NAME`` string firmware image file name 2054 + ``ETHTOOL_A_MODULE_FW_FLASH_PASSWORD`` u32 transceiver module password 2055 + ======================================= ====== =========================== 2056 + 2057 + The firmware update process consists of three logical steps: 2058 + 2059 + 1. Downloading a firmware image to the transceiver module and validating it. 2060 + 2. Running the firmware image. 2061 + 3. Committing the firmware image so that it is run upon reset. 2062 + 2063 + When flash command is given, those three steps are taken in that order. 2064 + 2065 + This message merely schedules the update process and returns immediately 2066 + without blocking. The process then runs asynchronously. 2067 + Since it can take several minutes to complete, during the update process 2068 + notifications are emitted from the kernel to user space updating it about 2069 + the status and progress. 2070 + 2071 + The ``ETHTOOL_A_MODULE_FW_FLASH_FILE_NAME`` attribute encodes the firmware 2072 + image file name. The firmware image is downloaded to the transceiver module, 2073 + validated, run and committed. 2074 + 2075 + The optional ``ETHTOOL_A_MODULE_FW_FLASH_PASSWORD`` attribute encodes a password 2076 + that might be required as part of the transceiver module firmware update 2077 + process. 2078 + 2079 + The firmware update process can take several minutes to complete. Therefore, 2080 + during the update process notifications are emitted from the kernel to user 2081 + space updating it about the status and progress. 2082 + 2083 + 2084 + 2085 + Notification contents: 2086 + 2087 + +---------------------------------------------------+--------+----------------+ 2088 + | ``ETHTOOL_A_MODULE_FW_FLASH_HEADER`` | nested | reply header | 2089 + +---------------------------------------------------+--------+----------------+ 2090 + | ``ETHTOOL_A_MODULE_FW_FLASH_STATUS`` | u32 | status | 2091 + +---------------------------------------------------+--------+----------------+ 2092 + | ``ETHTOOL_A_MODULE_FW_FLASH_STATUS_MSG`` | string | status message | 2093 + +---------------------------------------------------+--------+----------------+ 2094 + | ``ETHTOOL_A_MODULE_FW_FLASH_DONE`` | uint | progress | 2095 + +---------------------------------------------------+--------+----------------+ 2096 + | ``ETHTOOL_A_MODULE_FW_FLASH_TOTAL`` | uint | total | 2097 + +---------------------------------------------------+--------+----------------+ 2098 + 2099 + The ``ETHTOOL_A_MODULE_FW_FLASH_STATUS`` attribute encodes the current status 2100 + of the firmware update process. Possible values are: 2101 + 2102 + .. kernel-doc:: include/uapi/linux/ethtool.h 2103 + :identifiers: ethtool_module_fw_flash_status 2104 + 2105 + The ``ETHTOOL_A_MODULE_FW_FLASH_STATUS_MSG`` attribute encodes a status message 2106 + string. 2107 + 2108 + The ``ETHTOOL_A_MODULE_FW_FLASH_DONE`` and ``ETHTOOL_A_MODULE_FW_FLASH_TOTAL`` 2109 + attributes encode the completed and total amount of work, respectively. 2110 + 2046 2111 Request translation 2047 2112 =================== 2048 2113 ··· 2216 2147 n/a ``ETHTOOL_MSG_PLCA_GET_STATUS`` 2217 2148 n/a ``ETHTOOL_MSG_MM_GET`` 2218 2149 n/a ``ETHTOOL_MSG_MM_SET`` 2150 + n/a ``ETHTOOL_MSG_MODULE_FW_FLASH_ACT`` 2219 2151 =================================== =====================================
+57
drivers/net/ethernet/mellanox/mlxsw/core_env.c
··· 513 513 } 514 514 EXPORT_SYMBOL(mlxsw_env_get_module_eeprom_by_page); 515 515 516 + int 517 + mlxsw_env_set_module_eeprom_by_page(struct mlxsw_core *mlxsw_core, 518 + u8 slot_index, u8 module, 519 + const struct ethtool_module_eeprom *page, 520 + struct netlink_ext_ack *extack) 521 + { 522 + struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); 523 + u32 bytes_written = 0; 524 + u16 device_addr; 525 + int err; 526 + 527 + if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) { 528 + NL_SET_ERR_MSG_MOD(extack, 529 + "Cannot write to EEPROM of a module on an inactive line card"); 530 + return -EIO; 531 + } 532 + 533 + err = mlxsw_env_validate_module_type(mlxsw_core, slot_index, module); 534 + if (err) { 535 + NL_SET_ERR_MSG_MOD(extack, "EEPROM is not equipped on port module type"); 536 + return err; 537 + } 538 + 539 + device_addr = page->offset; 540 + 541 + while (bytes_written < page->length) { 542 + char mcia_pl[MLXSW_REG_MCIA_LEN]; 543 + char eeprom_tmp[128] = {}; 544 + u8 size; 545 + 546 + size = min_t(u8, page->length - bytes_written, 547 + mlxsw_env->max_eeprom_len); 548 + 549 + mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, page->page, 550 + device_addr + bytes_written, size, 551 + page->i2c_address); 552 + mlxsw_reg_mcia_bank_number_set(mcia_pl, page->bank); 553 + memcpy(eeprom_tmp, page->data + bytes_written, size); 554 + mlxsw_reg_mcia_eeprom_memcpy_to(mcia_pl, eeprom_tmp); 555 + 556 + err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcia), mcia_pl); 557 + if (err) { 558 + NL_SET_ERR_MSG_MOD(extack, "Failed to access module's EEPROM"); 559 + return err; 560 + } 561 + 562 + err = mlxsw_env_mcia_status_process(mcia_pl, extack); 563 + if (err) 564 + return err; 565 + 566 + bytes_written += size; 567 + } 568 + 569 + return 0; 570 + } 571 + EXPORT_SYMBOL(mlxsw_env_set_module_eeprom_by_page); 572 + 516 573 static int mlxsw_env_module_reset(struct mlxsw_core *mlxsw_core, u8 slot_index, 517 574 u8 module) 518 575 {
+6
drivers/net/ethernet/mellanox/mlxsw/core_env.h
··· 28 28 const struct ethtool_module_eeprom *page, 29 29 struct netlink_ext_ack *extack); 30 30 31 + int 32 + mlxsw_env_set_module_eeprom_by_page(struct mlxsw_core *mlxsw_core, 33 + u8 slot_index, u8 module, 34 + const struct ethtool_module_eeprom *page, 35 + struct netlink_ext_ack *extack); 36 + 31 37 int mlxsw_env_reset_module(struct net_device *netdev, 32 38 struct mlxsw_core *mlxsw_core, u8 slot_index, 33 39 u8 module, u32 *flags);
+15
drivers/net/ethernet/mellanox/mlxsw/minimal.c
··· 140 140 page, extack); 141 141 } 142 142 143 + static int 144 + mlxsw_m_set_module_eeprom_by_page(struct net_device *netdev, 145 + const struct ethtool_module_eeprom *page, 146 + struct netlink_ext_ack *extack) 147 + { 148 + struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); 149 + struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; 150 + 151 + return mlxsw_env_set_module_eeprom_by_page(core, 152 + mlxsw_m_port->slot_index, 153 + mlxsw_m_port->module, 154 + page, extack); 155 + } 156 + 143 157 static int mlxsw_m_reset(struct net_device *netdev, u32 *flags) 144 158 { 145 159 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); ··· 195 181 .get_module_info = mlxsw_m_get_module_info, 196 182 .get_module_eeprom = mlxsw_m_get_module_eeprom, 197 183 .get_module_eeprom_by_page = mlxsw_m_get_module_eeprom_by_page, 184 + .set_module_eeprom_by_page = mlxsw_m_set_module_eeprom_by_page, 198 185 .reset = mlxsw_m_reset, 199 186 .get_module_power_mode = mlxsw_m_get_module_power_mode, 200 187 .set_module_power_mode = mlxsw_m_set_module_power_mode,
+15
drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c
··· 1068 1068 } 1069 1069 1070 1070 static int 1071 + mlxsw_sp_set_module_eeprom_by_page(struct net_device *dev, 1072 + const struct ethtool_module_eeprom *page, 1073 + struct netlink_ext_ack *extack) 1074 + { 1075 + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 1076 + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 1077 + u8 slot_index = mlxsw_sp_port->mapping.slot_index; 1078 + u8 module = mlxsw_sp_port->mapping.module; 1079 + 1080 + return mlxsw_env_set_module_eeprom_by_page(mlxsw_sp->core, slot_index, 1081 + module, page, extack); 1082 + } 1083 + 1084 + static int 1071 1085 mlxsw_sp_get_ts_info(struct net_device *netdev, struct ethtool_ts_info *info) 1072 1086 { 1073 1087 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev); ··· 1270 1256 .get_module_info = mlxsw_sp_get_module_info, 1271 1257 .get_module_eeprom = mlxsw_sp_get_module_eeprom, 1272 1258 .get_module_eeprom_by_page = mlxsw_sp_get_module_eeprom_by_page, 1259 + .set_module_eeprom_by_page = mlxsw_sp_set_module_eeprom_by_page, 1273 1260 .get_ts_info = mlxsw_sp_get_ts_info, 1274 1261 .get_eth_phy_stats = mlxsw_sp_get_eth_phy_stats, 1275 1262 .get_eth_mac_stats = mlxsw_sp_get_eth_mac_stats,
+12 -8
include/linux/ethtool.h
··· 506 506 #define ETH_MODULE_MAX_I2C_ADDRESS 0x7f 507 507 508 508 /** 509 - * struct ethtool_module_eeprom - EEPROM dump from specified page 510 - * @offset: Offset within the specified EEPROM page to begin read, in bytes. 511 - * @length: Number of bytes to read. 512 - * @page: Page number to read from. 513 - * @bank: Page bank number to read from, if applicable by EEPROM spec. 509 + * struct ethtool_module_eeprom - plug-in module EEPROM read / write parameters 510 + * @offset: When @offset is 0-127, it is used as an address to the Lower Memory 511 + * (@page must be 0). Otherwise, it is used as an address to the 512 + * Upper Memory. 513 + * @length: Number of bytes to read / write. 514 + * @page: Page number. 515 + * @bank: Bank number, if supported by EEPROM spec. 514 516 * @i2c_address: I2C address of a page. Value less than 0x7f expected. Most 515 517 * EEPROMs use 0x50 or 0x51. 516 518 * @data: Pointer to buffer with EEPROM data of @length size. 517 - * 518 - * This can be used to manage pages during EEPROM dump in ethtool and pass 519 - * required information to the driver. 520 519 */ 521 520 struct ethtool_module_eeprom { 522 521 u32 offset; ··· 823 824 * @get_module_eeprom_by_page: Get a region of plug-in module EEPROM data from 824 825 * specified page. Returns a negative error code or the amount of bytes 825 826 * read. 827 + * @set_module_eeprom_by_page: Write to a region of plug-in module EEPROM, 828 + * from kernel space only. Returns a negative error code or zero. 826 829 * @get_eth_phy_stats: Query some of the IEEE 802.3 PHY statistics. 827 830 * @get_eth_mac_stats: Query some of the IEEE 802.3 MAC statistics. 828 831 * @get_eth_ctrl_stats: Query some of the IEEE 802.3 MAC Ctrl statistics. ··· 957 956 int (*set_phy_tunable)(struct net_device *, 958 957 const struct ethtool_tunable *, const void *); 959 958 int (*get_module_eeprom_by_page)(struct net_device *dev, 959 + const struct ethtool_module_eeprom *page, 960 + struct netlink_ext_ack *extack); 961 + int (*set_module_eeprom_by_page)(struct net_device *dev, 960 962 const struct ethtool_module_eeprom *page, 961 963 struct netlink_ext_ack *extack); 962 964 void (*get_eth_phy_stats)(struct net_device *dev,
+3 -1
include/linux/netdevice.h
··· 1990 1990 * 1991 1991 * @threaded: napi threaded mode is enabled 1992 1992 * 1993 + * @module_fw_flash_in_progress: Module firmware flashing is in progress. 1994 + * 1993 1995 * @net_notifier_list: List of per-net netdev notifier block 1994 1996 * that follow this device when it is moved 1995 1997 * to another network namespace. ··· 2376 2374 bool proto_down; 2377 2375 bool threaded; 2378 2376 unsigned wol_enabled:1; 2379 - 2377 + unsigned module_fw_flash_in_progress:1; 2380 2378 struct list_head net_notifier_list; 2381 2379 2382 2380 #if IS_ENABLED(CONFIG_MACSEC)
+6
include/linux/sfp.h
··· 284 284 SFF8024_ID_QSFP_8438 = 0x0c, 285 285 SFF8024_ID_QSFP_8436_8636 = 0x0d, 286 286 SFF8024_ID_QSFP28_8636 = 0x11, 287 + SFF8024_ID_QSFP_DD = 0x18, 288 + SFF8024_ID_OSFP = 0x19, 289 + SFF8024_ID_DSFP = 0x1B, 290 + SFF8024_ID_QSFP_PLUS_CMIS = 0x1E, 291 + SFF8024_ID_SFP_DD_CMIS = 0x1F, 292 + SFF8024_ID_SFP_PLUS_CMIS = 0x20, 287 293 288 294 SFF8024_ENCODING_UNSPEC = 0x00, 289 295 SFF8024_ENCODING_8B10B = 0x01,
+18
include/uapi/linux/ethtool.h
··· 878 878 }; 879 879 880 880 /** 881 + * enum ethtool_module_fw_flash_status - plug-in module firmware flashing status 882 + * @ETHTOOL_MODULE_FW_FLASH_STATUS_STARTED: The firmware flashing process has 883 + * started. 884 + * @ETHTOOL_MODULE_FW_FLASH_STATUS_IN_PROGRESS: The firmware flashing process 885 + * is in progress. 886 + * @ETHTOOL_MODULE_FW_FLASH_STATUS_COMPLETED: The firmware flashing process was 887 + * completed successfully. 888 + * @ETHTOOL_MODULE_FW_FLASH_STATUS_ERROR: The firmware flashing process was 889 + * stopped due to an error. 890 + */ 891 + enum ethtool_module_fw_flash_status { 892 + ETHTOOL_MODULE_FW_FLASH_STATUS_STARTED = 1, 893 + ETHTOOL_MODULE_FW_FLASH_STATUS_IN_PROGRESS, 894 + ETHTOOL_MODULE_FW_FLASH_STATUS_COMPLETED, 895 + ETHTOOL_MODULE_FW_FLASH_STATUS_ERROR, 896 + }; 897 + 898 + /** 881 899 * struct ethtool_gstrings - string set for data tagging 882 900 * @cmd: Command number = %ETHTOOL_GSTRINGS 883 901 * @string_set: String set ID; one of &enum ethtool_stringset
+19
include/uapi/linux/ethtool_netlink.h
··· 57 57 ETHTOOL_MSG_PLCA_GET_STATUS, 58 58 ETHTOOL_MSG_MM_GET, 59 59 ETHTOOL_MSG_MM_SET, 60 + ETHTOOL_MSG_MODULE_FW_FLASH_ACT, 60 61 61 62 /* add new constants above here */ 62 63 __ETHTOOL_MSG_USER_CNT, ··· 110 109 ETHTOOL_MSG_PLCA_NTF, 111 110 ETHTOOL_MSG_MM_GET_REPLY, 112 111 ETHTOOL_MSG_MM_NTF, 112 + ETHTOOL_MSG_MODULE_FW_FLASH_NTF, 113 113 114 114 /* add new constants above here */ 115 115 __ETHTOOL_MSG_KERNEL_CNT, ··· 1018 1016 /* add new constants above here */ 1019 1017 __ETHTOOL_A_MM_CNT, 1020 1018 ETHTOOL_A_MM_MAX = (__ETHTOOL_A_MM_CNT - 1) 1019 + }; 1020 + 1021 + /* MODULE_FW_FLASH */ 1022 + 1023 + enum { 1024 + ETHTOOL_A_MODULE_FW_FLASH_UNSPEC, 1025 + ETHTOOL_A_MODULE_FW_FLASH_HEADER, /* nest - _A_HEADER_* */ 1026 + ETHTOOL_A_MODULE_FW_FLASH_FILE_NAME, /* string */ 1027 + ETHTOOL_A_MODULE_FW_FLASH_PASSWORD, /* u32 */ 1028 + ETHTOOL_A_MODULE_FW_FLASH_STATUS, /* u32 */ 1029 + ETHTOOL_A_MODULE_FW_FLASH_STATUS_MSG, /* string */ 1030 + ETHTOOL_A_MODULE_FW_FLASH_DONE, /* uint */ 1031 + ETHTOOL_A_MODULE_FW_FLASH_TOTAL, /* uint */ 1032 + 1033 + /* add new constants above here */ 1034 + __ETHTOOL_A_MODULE_FW_FLASH_CNT, 1035 + ETHTOOL_A_MODULE_FW_FLASH_MAX = (__ETHTOOL_A_MODULE_FW_FLASH_CNT - 1) 1021 1036 }; 1022 1037 1023 1038 /* generic netlink info */
+1 -1
net/ethtool/Makefile
··· 8 8 linkstate.o debug.o wol.o features.o privflags.o rings.o \ 9 9 channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \ 10 10 tunnels.o fec.o eeprom.o stats.o phc_vclocks.o mm.o \ 11 - module.o pse-pd.o plca.o mm.o 11 + module.o cmis_fw_update.o cmis_cdb.o pse-pd.o plca.o mm.o
+124
net/ethtool/cmis.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + 3 + #define ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH 120 4 + #define ETHTOOL_CMIS_CDB_CMD_PAGE 0x9F 5 + #define ETHTOOL_CMIS_CDB_PAGE_I2C_ADDR 0x50 6 + 7 + /** 8 + * struct ethtool_cmis_cdb - CDB commands parameters 9 + * @cmis_rev: CMIS revision major. 10 + * @read_write_len_ext: Allowable additional number of byte octets to the LPL 11 + * in a READ or a WRITE CDB commands. 12 + * @max_completion_time: Maximum CDB command completion time in msec. 13 + */ 14 + struct ethtool_cmis_cdb { 15 + u8 cmis_rev; 16 + u8 read_write_len_ext; 17 + u16 max_completion_time; 18 + }; 19 + 20 + enum ethtool_cmis_cdb_cmd_id { 21 + ETHTOOL_CMIS_CDB_CMD_QUERY_STATUS = 0x0000, 22 + ETHTOOL_CMIS_CDB_CMD_MODULE_FEATURES = 0x0040, 23 + ETHTOOL_CMIS_CDB_CMD_FW_MANAGMENT_FEATURES = 0x0041, 24 + ETHTOOL_CMIS_CDB_CMD_START_FW_DOWNLOAD = 0x0101, 25 + ETHTOOL_CMIS_CDB_CMD_WRITE_FW_BLOCK_LPL = 0x0103, 26 + ETHTOOL_CMIS_CDB_CMD_COMPLETE_FW_DOWNLOAD = 0x0107, 27 + ETHTOOL_CMIS_CDB_CMD_RUN_FW_IMAGE = 0x0109, 28 + ETHTOOL_CMIS_CDB_CMD_COMMIT_FW_IMAGE = 0x010A, 29 + }; 30 + 31 + /** 32 + * struct ethtool_cmis_cdb_request - CDB commands request fields as decribed in 33 + * the CMIS standard 34 + * @id: Command ID. 35 + * @epl_len: EPL memory length. 36 + * @lpl_len: LPL memory length. 37 + * @chk_code: Check code for the previous field and the payload. 38 + * @resv1: Added to match the CMIS standard request continuity. 39 + * @resv2: Added to match the CMIS standard request continuity. 40 + * @payload: Payload for the CDB commands. 41 + */ 42 + struct ethtool_cmis_cdb_request { 43 + __be16 id; 44 + struct_group(body, 45 + __be16 epl_len; 46 + u8 lpl_len; 47 + u8 chk_code; 48 + u8 resv1; 49 + u8 resv2; 50 + u8 payload[ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH]; 51 + ); 52 + }; 53 + 54 + #define CDB_F_COMPLETION_VALID BIT(0) 55 + #define CDB_F_STATUS_VALID BIT(1) 56 + #define CDB_F_MODULE_STATE_VALID BIT(2) 57 + 58 + /** 59 + * struct ethtool_cmis_cdb_cmd_args - CDB commands execution arguments 60 + * @req: CDB command fields as described in the CMIS standard. 61 + * @max_duration: Maximum duration time for command completion in msec. 62 + * @read_write_len_ext: Allowable additional number of byte octets to the LPL 63 + * in a READ or a WRITE commands. 64 + * @msleep_pre_rpl: Waiting time before checking reply in msec. 65 + * @rpl_exp_len: Expected reply length in bytes. 66 + * @flags: Validation flags for CDB commands. 67 + * @err_msg: Error message to be sent to user space. 68 + */ 69 + struct ethtool_cmis_cdb_cmd_args { 70 + struct ethtool_cmis_cdb_request req; 71 + u16 max_duration; 72 + u8 read_write_len_ext; 73 + u8 msleep_pre_rpl; 74 + u8 rpl_exp_len; 75 + u8 flags; 76 + char *err_msg; 77 + }; 78 + 79 + /** 80 + * struct ethtool_cmis_cdb_rpl_hdr - CDB commands reply header arguments 81 + * @rpl_len: Reply length. 82 + * @rpl_chk_code: Reply check code. 83 + */ 84 + struct ethtool_cmis_cdb_rpl_hdr { 85 + u8 rpl_len; 86 + u8 rpl_chk_code; 87 + }; 88 + 89 + /** 90 + * struct ethtool_cmis_cdb_rpl - CDB commands reply arguments 91 + * @hdr: CDB commands reply header arguments. 92 + * @payload: Payload for the CDB commands reply. 93 + */ 94 + struct ethtool_cmis_cdb_rpl { 95 + struct ethtool_cmis_cdb_rpl_hdr hdr; 96 + u8 payload[ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH]; 97 + }; 98 + 99 + u32 ethtool_cmis_get_max_payload_size(u8 num_of_byte_octs); 100 + 101 + void ethtool_cmis_cdb_compose_args(struct ethtool_cmis_cdb_cmd_args *args, 102 + enum ethtool_cmis_cdb_cmd_id cmd, u8 *pl, 103 + u8 lpl_len, u16 max_duration, 104 + u8 read_write_len_ext, u16 msleep_pre_rpl, 105 + u8 rpl_exp_len, u8 flags); 106 + 107 + void ethtool_cmis_cdb_check_completion_flag(u8 cmis_rev, u8 *flags); 108 + 109 + void ethtool_cmis_page_init(struct ethtool_module_eeprom *page_data, 110 + u8 page, u32 offset, u32 length); 111 + void ethtool_cmis_page_fini(struct ethtool_module_eeprom *page_data); 112 + 113 + struct ethtool_cmis_cdb * 114 + ethtool_cmis_cdb_init(struct net_device *dev, 115 + const struct ethtool_module_fw_flash_params *params, 116 + struct ethnl_module_fw_flash_ntf_params *ntf_params); 117 + void ethtool_cmis_cdb_fini(struct ethtool_cmis_cdb *cdb); 118 + 119 + int ethtool_cmis_wait_for_cond(struct net_device *dev, u8 flags, u8 flag, 120 + u16 max_duration, u32 offset, 121 + bool (*cond_success)(u8), bool (*cond_fail)(u8), u8 *state); 122 + 123 + int ethtool_cmis_cdb_execute_cmd(struct net_device *dev, 124 + struct ethtool_cmis_cdb_cmd_args *args);
+602
net/ethtool/cmis_cdb.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + #include <linux/ethtool.h> 4 + #include <linux/jiffies.h> 5 + 6 + #include "common.h" 7 + #include "module_fw.h" 8 + #include "cmis.h" 9 + 10 + /* For accessing the LPL field on page 9Fh, the allowable length extension is 11 + * min(i, 15) byte octets where i specifies the allowable additional number of 12 + * byte octets in a READ or a WRITE. 13 + */ 14 + u32 ethtool_cmis_get_max_payload_size(u8 num_of_byte_octs) 15 + { 16 + return 8 * (1 + min_t(u8, num_of_byte_octs, 15)); 17 + } 18 + 19 + void ethtool_cmis_cdb_compose_args(struct ethtool_cmis_cdb_cmd_args *args, 20 + enum ethtool_cmis_cdb_cmd_id cmd, u8 *pl, 21 + u8 lpl_len, u16 max_duration, 22 + u8 read_write_len_ext, u16 msleep_pre_rpl, 23 + u8 rpl_exp_len, u8 flags) 24 + { 25 + args->req.id = cpu_to_be16(cmd); 26 + args->req.lpl_len = lpl_len; 27 + if (pl) 28 + memcpy(args->req.payload, pl, args->req.lpl_len); 29 + 30 + args->max_duration = max_duration; 31 + args->read_write_len_ext = 32 + ethtool_cmis_get_max_payload_size(read_write_len_ext); 33 + args->msleep_pre_rpl = msleep_pre_rpl; 34 + args->rpl_exp_len = rpl_exp_len; 35 + args->flags = flags; 36 + args->err_msg = NULL; 37 + } 38 + 39 + void ethtool_cmis_page_init(struct ethtool_module_eeprom *page_data, 40 + u8 page, u32 offset, u32 length) 41 + { 42 + page_data->page = page; 43 + page_data->offset = offset; 44 + page_data->length = length; 45 + page_data->i2c_address = ETHTOOL_CMIS_CDB_PAGE_I2C_ADDR; 46 + } 47 + 48 + #define CMIS_REVISION_PAGE 0x00 49 + #define CMIS_REVISION_OFFSET 0x01 50 + 51 + struct cmis_rev_rpl { 52 + u8 rev; 53 + }; 54 + 55 + static u8 cmis_rev_rpl_major(struct cmis_rev_rpl *rpl) 56 + { 57 + return rpl->rev >> 4; 58 + } 59 + 60 + static int cmis_rev_major_get(struct net_device *dev, u8 *rev_major) 61 + { 62 + const struct ethtool_ops *ops = dev->ethtool_ops; 63 + struct ethtool_module_eeprom page_data = {0}; 64 + struct netlink_ext_ack extack = {}; 65 + struct cmis_rev_rpl rpl = {}; 66 + int err; 67 + 68 + ethtool_cmis_page_init(&page_data, CMIS_REVISION_PAGE, 69 + CMIS_REVISION_OFFSET, sizeof(rpl)); 70 + page_data.data = (u8 *)&rpl; 71 + 72 + err = ops->get_module_eeprom_by_page(dev, &page_data, &extack); 73 + if (err < 0) { 74 + if (extack._msg) 75 + netdev_err(dev, "%s\n", extack._msg); 76 + return err; 77 + } 78 + 79 + *rev_major = cmis_rev_rpl_major(&rpl); 80 + 81 + return 0; 82 + } 83 + 84 + #define CMIS_CDB_ADVERTISEMENT_PAGE 0x01 85 + #define CMIS_CDB_ADVERTISEMENT_OFFSET 0xA3 86 + 87 + /* Based on section 8.4.11 "CDB Messaging Support Advertisement" in CMIS 88 + * standard revision 5.2. 89 + */ 90 + struct cmis_cdb_advert_rpl { 91 + u8 inst_supported; 92 + u8 read_write_len_ext; 93 + u8 resv1; 94 + u8 resv2; 95 + }; 96 + 97 + static u8 cmis_cdb_advert_rpl_inst_supported(struct cmis_cdb_advert_rpl *rpl) 98 + { 99 + return rpl->inst_supported >> 6; 100 + } 101 + 102 + static int cmis_cdb_advertisement_get(struct ethtool_cmis_cdb *cdb, 103 + struct net_device *dev) 104 + { 105 + const struct ethtool_ops *ops = dev->ethtool_ops; 106 + struct ethtool_module_eeprom page_data = {}; 107 + struct cmis_cdb_advert_rpl rpl = {}; 108 + struct netlink_ext_ack extack = {}; 109 + int err; 110 + 111 + ethtool_cmis_page_init(&page_data, CMIS_CDB_ADVERTISEMENT_PAGE, 112 + CMIS_CDB_ADVERTISEMENT_OFFSET, sizeof(rpl)); 113 + page_data.data = (u8 *)&rpl; 114 + 115 + err = ops->get_module_eeprom_by_page(dev, &page_data, &extack); 116 + if (err < 0) { 117 + if (extack._msg) 118 + netdev_err(dev, "%s\n", extack._msg); 119 + return err; 120 + } 121 + 122 + if (!cmis_cdb_advert_rpl_inst_supported(&rpl)) 123 + return -EOPNOTSUPP; 124 + 125 + cdb->read_write_len_ext = rpl.read_write_len_ext; 126 + 127 + return 0; 128 + } 129 + 130 + #define CMIS_PASSWORD_ENTRY_PAGE 0x00 131 + #define CMIS_PASSWORD_ENTRY_OFFSET 0x7A 132 + 133 + struct cmis_password_entry_pl { 134 + __be32 password; 135 + }; 136 + 137 + /* See section 9.3.1 "CMD 0000h: Query Status" in CMIS standard revision 5.2. 138 + * struct cmis_cdb_query_status_pl and struct cmis_cdb_query_status_rpl are 139 + * structured layouts of the flat arrays, 140 + * struct ethtool_cmis_cdb_request::payload and 141 + * struct ethtool_cmis_cdb_rpl::payload respectively. 142 + */ 143 + struct cmis_cdb_query_status_pl { 144 + u16 response_delay; 145 + }; 146 + 147 + struct cmis_cdb_query_status_rpl { 148 + u8 length; 149 + u8 status; 150 + }; 151 + 152 + static int 153 + cmis_cdb_validate_password(struct ethtool_cmis_cdb *cdb, 154 + struct net_device *dev, 155 + const struct ethtool_module_fw_flash_params *params, 156 + struct ethnl_module_fw_flash_ntf_params *ntf_params) 157 + { 158 + const struct ethtool_ops *ops = dev->ethtool_ops; 159 + struct cmis_cdb_query_status_pl qs_pl = {0}; 160 + struct ethtool_module_eeprom page_data = {}; 161 + struct ethtool_cmis_cdb_cmd_args args = {}; 162 + struct cmis_password_entry_pl pe_pl = {}; 163 + struct cmis_cdb_query_status_rpl *rpl; 164 + struct netlink_ext_ack extack = {}; 165 + int err; 166 + 167 + ethtool_cmis_page_init(&page_data, CMIS_PASSWORD_ENTRY_PAGE, 168 + CMIS_PASSWORD_ENTRY_OFFSET, sizeof(pe_pl)); 169 + page_data.data = (u8 *)&pe_pl; 170 + 171 + pe_pl = *((struct cmis_password_entry_pl *)page_data.data); 172 + pe_pl.password = params->password; 173 + err = ops->set_module_eeprom_by_page(dev, &page_data, &extack); 174 + if (err < 0) { 175 + if (extack._msg) 176 + netdev_err(dev, "%s\n", extack._msg); 177 + return err; 178 + } 179 + 180 + ethtool_cmis_cdb_compose_args(&args, ETHTOOL_CMIS_CDB_CMD_QUERY_STATUS, 181 + (u8 *)&qs_pl, sizeof(qs_pl), 0, 182 + cdb->read_write_len_ext, 1000, 183 + sizeof(*rpl), 184 + CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID); 185 + 186 + err = ethtool_cmis_cdb_execute_cmd(dev, &args); 187 + if (err < 0) { 188 + ethnl_module_fw_flash_ntf_err(dev, ntf_params, 189 + "Query Status command failed", 190 + args.err_msg); 191 + return err; 192 + } 193 + 194 + rpl = (struct cmis_cdb_query_status_rpl *)args.req.payload; 195 + if (!rpl->length || !rpl->status) { 196 + ethnl_module_fw_flash_ntf_err(dev, ntf_params, 197 + "Password was not accepted", 198 + NULL); 199 + return -EINVAL; 200 + } 201 + 202 + return 0; 203 + } 204 + 205 + /* Some CDB commands asserts the CDB completion flag only from CMIS 206 + * revision 5. Therefore, check the relevant validity flag only when 207 + * the revision supports it. 208 + */ 209 + void ethtool_cmis_cdb_check_completion_flag(u8 cmis_rev, u8 *flags) 210 + { 211 + *flags |= cmis_rev >= 5 ? CDB_F_COMPLETION_VALID : 0; 212 + } 213 + 214 + #define CMIS_CDB_MODULE_FEATURES_RESV_DATA 34 215 + 216 + /* See section 9.4.1 "CMD 0040h: Module Features" in CMIS standard revision 5.2. 217 + * struct cmis_cdb_module_features_rpl is structured layout of the flat 218 + * array, ethtool_cmis_cdb_rpl::payload. 219 + */ 220 + struct cmis_cdb_module_features_rpl { 221 + u8 resv1[CMIS_CDB_MODULE_FEATURES_RESV_DATA]; 222 + __be16 max_completion_time; 223 + }; 224 + 225 + static u16 226 + cmis_cdb_module_features_completion_time(struct cmis_cdb_module_features_rpl *rpl) 227 + { 228 + return be16_to_cpu(rpl->max_completion_time); 229 + } 230 + 231 + static int cmis_cdb_module_features_get(struct ethtool_cmis_cdb *cdb, 232 + struct net_device *dev, 233 + struct ethnl_module_fw_flash_ntf_params *ntf_params) 234 + { 235 + struct ethtool_cmis_cdb_cmd_args args = {}; 236 + struct cmis_cdb_module_features_rpl *rpl; 237 + u8 flags = CDB_F_STATUS_VALID; 238 + int err; 239 + 240 + ethtool_cmis_cdb_check_completion_flag(cdb->cmis_rev, &flags); 241 + ethtool_cmis_cdb_compose_args(&args, 242 + ETHTOOL_CMIS_CDB_CMD_MODULE_FEATURES, 243 + NULL, 0, 0, cdb->read_write_len_ext, 244 + 1000, sizeof(*rpl), flags); 245 + 246 + err = ethtool_cmis_cdb_execute_cmd(dev, &args); 247 + if (err < 0) { 248 + ethnl_module_fw_flash_ntf_err(dev, ntf_params, 249 + "Module Features command failed", 250 + args.err_msg); 251 + return err; 252 + } 253 + 254 + rpl = (struct cmis_cdb_module_features_rpl *)args.req.payload; 255 + cdb->max_completion_time = 256 + cmis_cdb_module_features_completion_time(rpl); 257 + 258 + return 0; 259 + } 260 + 261 + struct ethtool_cmis_cdb * 262 + ethtool_cmis_cdb_init(struct net_device *dev, 263 + const struct ethtool_module_fw_flash_params *params, 264 + struct ethnl_module_fw_flash_ntf_params *ntf_params) 265 + { 266 + struct ethtool_cmis_cdb *cdb; 267 + int err; 268 + 269 + cdb = kzalloc(sizeof(*cdb), GFP_KERNEL); 270 + if (!cdb) 271 + return ERR_PTR(-ENOMEM); 272 + 273 + err = cmis_rev_major_get(dev, &cdb->cmis_rev); 274 + if (err < 0) 275 + goto err; 276 + 277 + if (cdb->cmis_rev < 4) { 278 + ethnl_module_fw_flash_ntf_err(dev, ntf_params, 279 + "CMIS revision doesn't support module firmware flashing", 280 + NULL); 281 + err = -EOPNOTSUPP; 282 + goto err; 283 + } 284 + 285 + err = cmis_cdb_advertisement_get(cdb, dev); 286 + if (err < 0) 287 + goto err; 288 + 289 + if (params->password_valid) { 290 + err = cmis_cdb_validate_password(cdb, dev, params, ntf_params); 291 + if (err < 0) 292 + goto err; 293 + } 294 + 295 + err = cmis_cdb_module_features_get(cdb, dev, ntf_params); 296 + if (err < 0) 297 + goto err; 298 + 299 + return cdb; 300 + 301 + err: 302 + ethtool_cmis_cdb_fini(cdb); 303 + return ERR_PTR(err); 304 + } 305 + 306 + void ethtool_cmis_cdb_fini(struct ethtool_cmis_cdb *cdb) 307 + { 308 + kfree(cdb); 309 + } 310 + 311 + static bool is_completed(u8 data) 312 + { 313 + return !!(data & 0x40); 314 + } 315 + 316 + #define CMIS_CDB_STATUS_SUCCESS 0x01 317 + 318 + static bool status_success(u8 data) 319 + { 320 + return data == CMIS_CDB_STATUS_SUCCESS; 321 + } 322 + 323 + #define CMIS_CDB_STATUS_FAIL 0x40 324 + 325 + static bool status_fail(u8 data) 326 + { 327 + return data & CMIS_CDB_STATUS_FAIL; 328 + } 329 + 330 + struct cmis_wait_for_cond_rpl { 331 + u8 state; 332 + }; 333 + 334 + static int 335 + ethtool_cmis_module_poll(struct net_device *dev, 336 + struct cmis_wait_for_cond_rpl *rpl, u32 offset, 337 + bool (*cond_success)(u8), bool (*cond_fail)(u8)) 338 + { 339 + const struct ethtool_ops *ops = dev->ethtool_ops; 340 + struct ethtool_module_eeprom page_data = {0}; 341 + struct netlink_ext_ack extack = {}; 342 + int err; 343 + 344 + ethtool_cmis_page_init(&page_data, 0, offset, sizeof(rpl)); 345 + page_data.data = (u8 *)rpl; 346 + 347 + err = ops->get_module_eeprom_by_page(dev, &page_data, &extack); 348 + if (err < 0) { 349 + if (extack._msg) 350 + netdev_err_once(dev, "%s\n", extack._msg); 351 + return -EBUSY; 352 + } 353 + 354 + if ((*cond_success)(rpl->state)) 355 + return 0; 356 + 357 + if (*cond_fail && (*cond_fail)(rpl->state)) 358 + return -EIO; 359 + 360 + return -EBUSY; 361 + } 362 + 363 + int ethtool_cmis_wait_for_cond(struct net_device *dev, u8 flags, u8 flag, 364 + u16 max_duration, u32 offset, 365 + bool (*cond_success)(u8), bool (*cond_fail)(u8), 366 + u8 *state) 367 + { 368 + struct cmis_wait_for_cond_rpl rpl = {}; 369 + unsigned long end; 370 + int err; 371 + 372 + if (!(flags & flag)) 373 + return 0; 374 + 375 + if (max_duration == 0) 376 + max_duration = U16_MAX; 377 + 378 + end = jiffies + msecs_to_jiffies(max_duration); 379 + do { 380 + err = ethtool_cmis_module_poll(dev, &rpl, offset, cond_success, 381 + cond_fail); 382 + if (err != -EBUSY) 383 + goto out; 384 + 385 + msleep(20); 386 + } while (time_before(jiffies, end)); 387 + 388 + err = ethtool_cmis_module_poll(dev, &rpl, offset, cond_success, 389 + cond_fail); 390 + if (err == -EBUSY) 391 + err = -ETIMEDOUT; 392 + 393 + out: 394 + *state = rpl.state; 395 + return err; 396 + } 397 + 398 + #define CMIS_CDB_COMPLETION_FLAG_OFFSET 0x08 399 + 400 + static int cmis_cdb_wait_for_completion(struct net_device *dev, 401 + struct ethtool_cmis_cdb_cmd_args *args) 402 + { 403 + u8 flag; 404 + int err; 405 + 406 + /* Some vendors demand waiting time before checking completion flag 407 + * in some CDB commands. 408 + */ 409 + msleep(args->msleep_pre_rpl); 410 + 411 + err = ethtool_cmis_wait_for_cond(dev, args->flags, 412 + CDB_F_COMPLETION_VALID, 413 + args->max_duration, 414 + CMIS_CDB_COMPLETION_FLAG_OFFSET, 415 + is_completed, NULL, &flag); 416 + if (err < 0) 417 + args->err_msg = "Completion Flag did not set on time"; 418 + 419 + return err; 420 + } 421 + 422 + #define CMIS_CDB_STATUS_OFFSET 0x25 423 + 424 + static void cmis_cdb_status_fail_msg_get(u8 status, char **err_msg) 425 + { 426 + switch (status) { 427 + case 0b10000001: 428 + *err_msg = "CDB Status is in progress: Busy capturing command"; 429 + break; 430 + case 0b10000010: 431 + *err_msg = 432 + "CDB Status is in progress: Busy checking/validating command"; 433 + break; 434 + case 0b10000011: 435 + *err_msg = "CDB Status is in progress: Busy executing"; 436 + break; 437 + case 0b01000000: 438 + *err_msg = "CDB status failed: no specific failure"; 439 + break; 440 + case 0b01000010: 441 + *err_msg = 442 + "CDB status failed: Parameter range error or parameter not supported"; 443 + break; 444 + case 0b01000101: 445 + *err_msg = "CDB status failed: CdbChkCode error"; 446 + break; 447 + default: 448 + *err_msg = "Unknown failure reason"; 449 + } 450 + }; 451 + 452 + static int cmis_cdb_wait_for_status(struct net_device *dev, 453 + struct ethtool_cmis_cdb_cmd_args *args) 454 + { 455 + u8 status; 456 + int err; 457 + 458 + /* Some vendors demand waiting time before checking status in some 459 + * CDB commands. 460 + */ 461 + msleep(args->msleep_pre_rpl); 462 + 463 + err = ethtool_cmis_wait_for_cond(dev, args->flags, CDB_F_STATUS_VALID, 464 + args->max_duration, 465 + CMIS_CDB_STATUS_OFFSET, 466 + status_success, status_fail, &status); 467 + if (err < 0 && !args->err_msg) 468 + cmis_cdb_status_fail_msg_get(status, &args->err_msg); 469 + 470 + return err; 471 + } 472 + 473 + #define CMIS_CDB_REPLY_OFFSET 0x86 474 + 475 + static int cmis_cdb_process_reply(struct net_device *dev, 476 + struct ethtool_module_eeprom *page_data, 477 + struct ethtool_cmis_cdb_cmd_args *args) 478 + { 479 + u8 rpl_hdr_len = sizeof(struct ethtool_cmis_cdb_rpl_hdr); 480 + u8 rpl_exp_len = args->rpl_exp_len + rpl_hdr_len; 481 + const struct ethtool_ops *ops = dev->ethtool_ops; 482 + struct netlink_ext_ack extack = {}; 483 + struct ethtool_cmis_cdb_rpl *rpl; 484 + int err; 485 + 486 + if (!args->rpl_exp_len) 487 + return 0; 488 + 489 + ethtool_cmis_page_init(page_data, ETHTOOL_CMIS_CDB_CMD_PAGE, 490 + CMIS_CDB_REPLY_OFFSET, rpl_exp_len); 491 + page_data->data = kmalloc(page_data->length, GFP_KERNEL); 492 + if (!page_data->data) 493 + return -ENOMEM; 494 + 495 + err = ops->get_module_eeprom_by_page(dev, page_data, &extack); 496 + if (err < 0) { 497 + if (extack._msg) 498 + netdev_err(dev, "%s\n", extack._msg); 499 + goto out; 500 + } 501 + 502 + rpl = (struct ethtool_cmis_cdb_rpl *)page_data->data; 503 + if ((args->rpl_exp_len > rpl->hdr.rpl_len + rpl_hdr_len) || 504 + !rpl->hdr.rpl_chk_code) { 505 + err = -EIO; 506 + goto out; 507 + } 508 + 509 + args->req.lpl_len = rpl->hdr.rpl_len; 510 + memcpy(args->req.payload, rpl->payload, args->req.lpl_len); 511 + 512 + out: 513 + kfree(page_data->data); 514 + return err; 515 + } 516 + 517 + static int 518 + __ethtool_cmis_cdb_execute_cmd(struct net_device *dev, 519 + struct ethtool_module_eeprom *page_data, 520 + u8 page, u32 offset, u32 length, void *data) 521 + { 522 + const struct ethtool_ops *ops = dev->ethtool_ops; 523 + struct netlink_ext_ack extack = {}; 524 + int err; 525 + 526 + ethtool_cmis_page_init(page_data, page, offset, length); 527 + page_data->data = kmemdup(data, page_data->length, GFP_KERNEL); 528 + if (!page_data->data) 529 + return -ENOMEM; 530 + 531 + err = ops->set_module_eeprom_by_page(dev, page_data, &extack); 532 + if (err < 0) { 533 + if (extack._msg) 534 + netdev_err(dev, "%s\n", extack._msg); 535 + } 536 + 537 + kfree(page_data->data); 538 + return err; 539 + } 540 + 541 + static u8 cmis_cdb_calc_checksum(const void *data, size_t size) 542 + { 543 + const u8 *bytes = (const u8 *)data; 544 + u8 checksum = 0; 545 + 546 + for (size_t i = 0; i < size; i++) 547 + checksum += bytes[i]; 548 + 549 + return ~checksum; 550 + } 551 + 552 + #define CMIS_CDB_CMD_ID_OFFSET 0x80 553 + 554 + int ethtool_cmis_cdb_execute_cmd(struct net_device *dev, 555 + struct ethtool_cmis_cdb_cmd_args *args) 556 + { 557 + struct ethtool_module_eeprom page_data = {}; 558 + u32 offset; 559 + int err; 560 + 561 + args->req.chk_code = 562 + cmis_cdb_calc_checksum(&args->req, sizeof(args->req)); 563 + 564 + if (args->req.lpl_len > args->read_write_len_ext) { 565 + args->err_msg = "LPL length is longer than CDB read write length extension allows"; 566 + return -EINVAL; 567 + } 568 + 569 + /* According to the CMIS standard, there are two options to trigger the 570 + * CDB commands. The default option is triggering the command by writing 571 + * the CMDID bytes. Therefore, the command will be split to 2 calls: 572 + * First, with everything except the CMDID field and then the CMDID 573 + * field. 574 + */ 575 + offset = CMIS_CDB_CMD_ID_OFFSET + 576 + offsetof(struct ethtool_cmis_cdb_request, body); 577 + err = __ethtool_cmis_cdb_execute_cmd(dev, &page_data, 578 + ETHTOOL_CMIS_CDB_CMD_PAGE, offset, 579 + sizeof(args->req.body), 580 + &args->req.body); 581 + if (err < 0) 582 + return err; 583 + 584 + offset = CMIS_CDB_CMD_ID_OFFSET + 585 + offsetof(struct ethtool_cmis_cdb_request, id); 586 + err = __ethtool_cmis_cdb_execute_cmd(dev, &page_data, 587 + ETHTOOL_CMIS_CDB_CMD_PAGE, offset, 588 + sizeof(args->req.id), 589 + &args->req.id); 590 + if (err < 0) 591 + return err; 592 + 593 + err = cmis_cdb_wait_for_completion(dev, args); 594 + if (err < 0) 595 + return err; 596 + 597 + err = cmis_cdb_wait_for_status(dev, args); 598 + if (err < 0) 599 + return err; 600 + 601 + return cmis_cdb_process_reply(dev, &page_data, args); 602 + }
+399
net/ethtool/cmis_fw_update.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + #include <linux/ethtool.h> 4 + #include <linux/firmware.h> 5 + 6 + #include "common.h" 7 + #include "module_fw.h" 8 + #include "cmis.h" 9 + 10 + struct cmis_fw_update_fw_mng_features { 11 + u8 start_cmd_payload_size; 12 + u16 max_duration_start; 13 + u16 max_duration_write; 14 + u16 max_duration_complete; 15 + }; 16 + 17 + /* See section 9.4.2 "CMD 0041h: Firmware Management Features" in CMIS standard 18 + * revision 5.2. 19 + * struct cmis_cdb_fw_mng_features_rpl is a structured layout of the flat 20 + * array, ethtool_cmis_cdb_rpl::payload. 21 + */ 22 + struct cmis_cdb_fw_mng_features_rpl { 23 + u8 resv1; 24 + u8 resv2; 25 + u8 start_cmd_payload_size; 26 + u8 resv3; 27 + u8 read_write_len_ext; 28 + u8 write_mechanism; 29 + u8 resv4; 30 + u8 resv5; 31 + __be16 max_duration_start; 32 + __be16 resv6; 33 + __be16 max_duration_write; 34 + __be16 max_duration_complete; 35 + __be16 resv7; 36 + }; 37 + 38 + #define CMIS_CDB_FW_WRITE_MECHANISM_LPL 0x01 39 + 40 + static int 41 + cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb *cdb, 42 + struct net_device *dev, 43 + struct cmis_fw_update_fw_mng_features *fw_mng, 44 + struct ethnl_module_fw_flash_ntf_params *ntf_params) 45 + { 46 + struct ethtool_cmis_cdb_cmd_args args = {}; 47 + struct cmis_cdb_fw_mng_features_rpl *rpl; 48 + u8 flags = CDB_F_STATUS_VALID; 49 + int err; 50 + 51 + ethtool_cmis_cdb_check_completion_flag(cdb->cmis_rev, &flags); 52 + ethtool_cmis_cdb_compose_args(&args, 53 + ETHTOOL_CMIS_CDB_CMD_FW_MANAGMENT_FEATURES, 54 + NULL, 0, cdb->max_completion_time, 55 + cdb->read_write_len_ext, 1000, 56 + sizeof(*rpl), flags); 57 + 58 + err = ethtool_cmis_cdb_execute_cmd(dev, &args); 59 + if (err < 0) { 60 + ethnl_module_fw_flash_ntf_err(dev, ntf_params, 61 + "FW Management Features command failed", 62 + args.err_msg); 63 + return err; 64 + } 65 + 66 + rpl = (struct cmis_cdb_fw_mng_features_rpl *)args.req.payload; 67 + if (!(rpl->write_mechanism == CMIS_CDB_FW_WRITE_MECHANISM_LPL)) { 68 + ethnl_module_fw_flash_ntf_err(dev, ntf_params, 69 + "Write LPL is not supported", 70 + NULL); 71 + return -EOPNOTSUPP; 72 + } 73 + 74 + /* Above, we used read_write_len_ext that we got from CDB 75 + * advertisement. Update it with the value that we got from module 76 + * features query, which is specific for Firmware Management Commands 77 + * (IDs 0100h-01FFh). 78 + */ 79 + cdb->read_write_len_ext = rpl->read_write_len_ext; 80 + fw_mng->start_cmd_payload_size = rpl->start_cmd_payload_size; 81 + fw_mng->max_duration_start = be16_to_cpu(rpl->max_duration_start); 82 + fw_mng->max_duration_write = be16_to_cpu(rpl->max_duration_write); 83 + fw_mng->max_duration_complete = be16_to_cpu(rpl->max_duration_complete); 84 + 85 + return 0; 86 + } 87 + 88 + /* See section 9.7.2 "CMD 0101h: Start Firmware Download" in CMIS standard 89 + * revision 5.2. 90 + * struct cmis_cdb_start_fw_download_pl is a structured layout of the 91 + * flat array, ethtool_cmis_cdb_request::payload. 92 + */ 93 + struct cmis_cdb_start_fw_download_pl { 94 + __struct_group(cmis_cdb_start_fw_download_pl_h, head, /* no attrs */, 95 + __be32 image_size; 96 + __be32 resv1; 97 + ); 98 + u8 vendor_data[ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH - 99 + sizeof(struct cmis_cdb_start_fw_download_pl_h)]; 100 + }; 101 + 102 + static int 103 + cmis_fw_update_start_download(struct ethtool_cmis_cdb *cdb, 104 + struct ethtool_cmis_fw_update_params *fw_update, 105 + struct cmis_fw_update_fw_mng_features *fw_mng) 106 + { 107 + u8 vendor_data_size = fw_mng->start_cmd_payload_size; 108 + struct cmis_cdb_start_fw_download_pl pl = {}; 109 + struct ethtool_cmis_cdb_cmd_args args = {}; 110 + u8 lpl_len; 111 + int err; 112 + 113 + pl.image_size = cpu_to_be32(fw_update->fw->size); 114 + memcpy(pl.vendor_data, fw_update->fw->data, vendor_data_size); 115 + 116 + lpl_len = offsetof(struct cmis_cdb_start_fw_download_pl, 117 + vendor_data[vendor_data_size]); 118 + 119 + ethtool_cmis_cdb_compose_args(&args, 120 + ETHTOOL_CMIS_CDB_CMD_START_FW_DOWNLOAD, 121 + (u8 *)&pl, lpl_len, 122 + fw_mng->max_duration_start, 123 + cdb->read_write_len_ext, 1000, 0, 124 + CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID); 125 + 126 + err = ethtool_cmis_cdb_execute_cmd(fw_update->dev, &args); 127 + if (err < 0) 128 + ethnl_module_fw_flash_ntf_err(fw_update->dev, 129 + &fw_update->ntf_params, 130 + "Start FW download command failed", 131 + args.err_msg); 132 + 133 + return err; 134 + } 135 + 136 + /* See section 9.7.4 "CMD 0103h: Write Firmware Block LPL" in CMIS standard 137 + * revision 5.2. 138 + * struct cmis_cdb_write_fw_block_lpl_pl is a structured layout of the 139 + * flat array, ethtool_cmis_cdb_request::payload. 140 + */ 141 + struct cmis_cdb_write_fw_block_lpl_pl { 142 + __be32 block_address; 143 + u8 fw_block[ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH - sizeof(__be32)]; 144 + }; 145 + 146 + static int 147 + cmis_fw_update_write_image(struct ethtool_cmis_cdb *cdb, 148 + struct ethtool_cmis_fw_update_params *fw_update, 149 + struct cmis_fw_update_fw_mng_features *fw_mng) 150 + { 151 + u8 start = fw_mng->start_cmd_payload_size; 152 + u32 offset, max_block_size, max_lpl_len; 153 + u32 image_size = fw_update->fw->size; 154 + int err; 155 + 156 + max_lpl_len = min_t(u32, 157 + ethtool_cmis_get_max_payload_size(cdb->read_write_len_ext), 158 + ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH); 159 + max_block_size = 160 + max_lpl_len - sizeof_field(struct cmis_cdb_write_fw_block_lpl_pl, 161 + block_address); 162 + 163 + for (offset = start; offset < image_size; offset += max_block_size) { 164 + struct cmis_cdb_write_fw_block_lpl_pl pl = { 165 + .block_address = cpu_to_be32(offset - start), 166 + }; 167 + struct ethtool_cmis_cdb_cmd_args args = {}; 168 + u32 block_size, lpl_len; 169 + 170 + ethnl_module_fw_flash_ntf_in_progress(fw_update->dev, 171 + &fw_update->ntf_params, 172 + offset - start, 173 + image_size); 174 + block_size = min_t(u32, max_block_size, image_size - offset); 175 + memcpy(pl.fw_block, &fw_update->fw->data[offset], block_size); 176 + lpl_len = block_size + 177 + sizeof_field(struct cmis_cdb_write_fw_block_lpl_pl, 178 + block_address); 179 + 180 + ethtool_cmis_cdb_compose_args(&args, 181 + ETHTOOL_CMIS_CDB_CMD_WRITE_FW_BLOCK_LPL, 182 + (u8 *)&pl, lpl_len, 183 + fw_mng->max_duration_write, 184 + cdb->read_write_len_ext, 1, 0, 185 + CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID); 186 + 187 + err = ethtool_cmis_cdb_execute_cmd(fw_update->dev, &args); 188 + if (err < 0) { 189 + ethnl_module_fw_flash_ntf_err(fw_update->dev, 190 + &fw_update->ntf_params, 191 + "Write FW block LPL command failed", 192 + args.err_msg); 193 + return err; 194 + } 195 + } 196 + 197 + return 0; 198 + } 199 + 200 + static int 201 + cmis_fw_update_complete_download(struct ethtool_cmis_cdb *cdb, 202 + struct net_device *dev, 203 + struct cmis_fw_update_fw_mng_features *fw_mng, 204 + struct ethnl_module_fw_flash_ntf_params *ntf_params) 205 + { 206 + struct ethtool_cmis_cdb_cmd_args args = {}; 207 + int err; 208 + 209 + ethtool_cmis_cdb_compose_args(&args, 210 + ETHTOOL_CMIS_CDB_CMD_COMPLETE_FW_DOWNLOAD, 211 + NULL, 0, fw_mng->max_duration_complete, 212 + cdb->read_write_len_ext, 1000, 0, 213 + CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID); 214 + 215 + err = ethtool_cmis_cdb_execute_cmd(dev, &args); 216 + if (err < 0) 217 + ethnl_module_fw_flash_ntf_err(dev, ntf_params, 218 + "Complete FW download command failed", 219 + args.err_msg); 220 + 221 + return err; 222 + } 223 + 224 + static int 225 + cmis_fw_update_download_image(struct ethtool_cmis_cdb *cdb, 226 + struct ethtool_cmis_fw_update_params *fw_update, 227 + struct cmis_fw_update_fw_mng_features *fw_mng) 228 + { 229 + int err; 230 + 231 + err = cmis_fw_update_start_download(cdb, fw_update, fw_mng); 232 + if (err < 0) 233 + return err; 234 + 235 + err = cmis_fw_update_write_image(cdb, fw_update, fw_mng); 236 + if (err < 0) 237 + return err; 238 + 239 + err = cmis_fw_update_complete_download(cdb, fw_update->dev, fw_mng, 240 + &fw_update->ntf_params); 241 + if (err < 0) 242 + return err; 243 + 244 + return 0; 245 + } 246 + 247 + enum { 248 + CMIS_MODULE_LOW_PWR = 1, 249 + CMIS_MODULE_READY = 3, 250 + }; 251 + 252 + static bool module_is_ready(u8 data) 253 + { 254 + u8 state = (data >> 1) & 7; 255 + 256 + return state == CMIS_MODULE_READY || state == CMIS_MODULE_LOW_PWR; 257 + } 258 + 259 + #define CMIS_MODULE_READY_MAX_DURATION_MSEC 1000 260 + #define CMIS_MODULE_STATE_OFFSET 3 261 + 262 + static int 263 + cmis_fw_update_wait_for_module_state(struct net_device *dev, u8 flags) 264 + { 265 + u8 state; 266 + 267 + return ethtool_cmis_wait_for_cond(dev, flags, CDB_F_MODULE_STATE_VALID, 268 + CMIS_MODULE_READY_MAX_DURATION_MSEC, 269 + CMIS_MODULE_STATE_OFFSET, 270 + module_is_ready, NULL, &state); 271 + } 272 + 273 + /* See section 9.7.10 "CMD 0109h: Run Firmware Image" in CMIS standard 274 + * revision 5.2. 275 + * struct cmis_cdb_run_fw_image_pl is a structured layout of the flat 276 + * array, ethtool_cmis_cdb_request::payload. 277 + */ 278 + struct cmis_cdb_run_fw_image_pl { 279 + u8 resv1; 280 + u8 image_to_run; 281 + u16 delay_to_reset; 282 + }; 283 + 284 + static int 285 + cmis_fw_update_run_image(struct ethtool_cmis_cdb *cdb, struct net_device *dev, 286 + struct ethnl_module_fw_flash_ntf_params *ntf_params) 287 + { 288 + struct ethtool_cmis_cdb_cmd_args args = {}; 289 + struct cmis_cdb_run_fw_image_pl pl = {0}; 290 + int err; 291 + 292 + ethtool_cmis_cdb_compose_args(&args, ETHTOOL_CMIS_CDB_CMD_RUN_FW_IMAGE, 293 + (u8 *)&pl, sizeof(pl), 294 + cdb->max_completion_time, 295 + cdb->read_write_len_ext, 1000, 0, 296 + CDB_F_MODULE_STATE_VALID); 297 + 298 + err = ethtool_cmis_cdb_execute_cmd(dev, &args); 299 + if (err < 0) { 300 + ethnl_module_fw_flash_ntf_err(dev, ntf_params, 301 + "Run image command failed", 302 + args.err_msg); 303 + return err; 304 + } 305 + 306 + err = cmis_fw_update_wait_for_module_state(dev, args.flags); 307 + if (err < 0) 308 + ethnl_module_fw_flash_ntf_err(dev, ntf_params, 309 + "Module is not ready on time after reset", 310 + NULL); 311 + 312 + return err; 313 + } 314 + 315 + static int 316 + cmis_fw_update_commit_image(struct ethtool_cmis_cdb *cdb, 317 + struct net_device *dev, 318 + struct ethnl_module_fw_flash_ntf_params *ntf_params) 319 + { 320 + struct ethtool_cmis_cdb_cmd_args args = {}; 321 + int err; 322 + 323 + ethtool_cmis_cdb_compose_args(&args, 324 + ETHTOOL_CMIS_CDB_CMD_COMMIT_FW_IMAGE, 325 + NULL, 0, cdb->max_completion_time, 326 + cdb->read_write_len_ext, 1000, 0, 327 + CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID); 328 + 329 + err = ethtool_cmis_cdb_execute_cmd(dev, &args); 330 + if (err < 0) 331 + ethnl_module_fw_flash_ntf_err(dev, ntf_params, 332 + "Commit image command failed", 333 + args.err_msg); 334 + 335 + return err; 336 + } 337 + 338 + static int cmis_fw_update_reset(struct net_device *dev) 339 + { 340 + __u32 reset_data = ETH_RESET_PHY; 341 + 342 + return dev->ethtool_ops->reset(dev, &reset_data); 343 + } 344 + 345 + void 346 + ethtool_cmis_fw_update(struct ethtool_cmis_fw_update_params *fw_update) 347 + { 348 + struct ethnl_module_fw_flash_ntf_params *ntf_params = 349 + &fw_update->ntf_params; 350 + struct cmis_fw_update_fw_mng_features fw_mng = {0}; 351 + struct net_device *dev = fw_update->dev; 352 + struct ethtool_cmis_cdb *cdb; 353 + int err; 354 + 355 + cdb = ethtool_cmis_cdb_init(dev, &fw_update->params, ntf_params); 356 + if (IS_ERR(cdb)) 357 + goto err_send_ntf; 358 + 359 + ethnl_module_fw_flash_ntf_start(dev, ntf_params); 360 + 361 + err = cmis_fw_update_fw_mng_features_get(cdb, dev, &fw_mng, ntf_params); 362 + if (err < 0) 363 + goto err_cdb_fini; 364 + 365 + err = cmis_fw_update_download_image(cdb, fw_update, &fw_mng); 366 + if (err < 0) 367 + goto err_cdb_fini; 368 + 369 + err = cmis_fw_update_run_image(cdb, dev, ntf_params); 370 + if (err < 0) 371 + goto err_cdb_fini; 372 + 373 + /* The CDB command "Run Firmware Image" resets the firmware, so the new 374 + * one might have different settings. 375 + * Free the old CDB instance, and init a new one. 376 + */ 377 + ethtool_cmis_cdb_fini(cdb); 378 + 379 + cdb = ethtool_cmis_cdb_init(dev, &fw_update->params, ntf_params); 380 + if (IS_ERR(cdb)) 381 + goto err_send_ntf; 382 + 383 + err = cmis_fw_update_commit_image(cdb, dev, ntf_params); 384 + if (err < 0) 385 + goto err_cdb_fini; 386 + 387 + err = cmis_fw_update_reset(dev); 388 + if (err < 0) 389 + goto err_cdb_fini; 390 + 391 + ethnl_module_fw_flash_ntf_complete(dev, ntf_params); 392 + ethtool_cmis_cdb_fini(cdb); 393 + return; 394 + 395 + err_cdb_fini: 396 + ethtool_cmis_cdb_fini(cdb); 397 + err_send_ntf: 398 + ethnl_module_fw_flash_ntf_err(dev, ntf_params, NULL, NULL); 399 + }
+6
net/ethtool/eeprom.c
··· 91 91 { 92 92 const struct ethtool_ops *ops = dev->ethtool_ops; 93 93 94 + if (dev->module_fw_flash_in_progress) { 95 + NL_SET_ERR_MSG(extack, 96 + "Module firmware flashing is in progress"); 97 + return -EBUSY; 98 + } 99 + 94 100 if (dev->sfp_bus) 95 101 return sfp_get_module_eeprom_by_page(dev->sfp_bus, page_data, extack); 96 102
+12
net/ethtool/ioctl.c
··· 658 658 if (!dev->ethtool_ops->get_link_ksettings) 659 659 return -EOPNOTSUPP; 660 660 661 + if (dev->module_fw_flash_in_progress) 662 + return -EBUSY; 663 + 661 664 memset(&link_ksettings, 0, sizeof(link_ksettings)); 662 665 err = dev->ethtool_ops->get_link_ksettings(dev, &link_ksettings); 663 666 if (err < 0) ··· 1451 1448 1452 1449 if (!dev->ethtool_ops->reset) 1453 1450 return -EOPNOTSUPP; 1451 + 1452 + if (dev->module_fw_flash_in_progress) 1453 + return -EBUSY; 1454 1454 1455 1455 if (copy_from_user(&reset, useraddr, sizeof(reset))) 1456 1456 return -EFAULT; ··· 2468 2462 const struct ethtool_ops *ops = dev->ethtool_ops; 2469 2463 struct phy_device *phydev = dev->phydev; 2470 2464 2465 + if (dev->module_fw_flash_in_progress) 2466 + return -EBUSY; 2467 + 2471 2468 if (dev->sfp_bus) 2472 2469 return sfp_get_module_info(dev->sfp_bus, modinfo); 2473 2470 ··· 2507 2498 { 2508 2499 const struct ethtool_ops *ops = dev->ethtool_ops; 2509 2500 struct phy_device *phydev = dev->phydev; 2501 + 2502 + if (dev->module_fw_flash_in_progress) 2503 + return -EBUSY; 2510 2504 2511 2505 if (dev->sfp_bus) 2512 2506 return sfp_get_module_eeprom(dev->sfp_bus, ee, data);
+394
net/ethtool/module.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 3 3 #include <linux/ethtool.h> 4 + #include <linux/firmware.h> 5 + #include <linux/sfp.h> 6 + #include <net/devlink.h> 4 7 5 8 #include "netlink.h" 6 9 #include "common.h" 7 10 #include "bitset.h" 11 + #include "module_fw.h" 8 12 9 13 struct module_req_info { 10 14 struct ethnl_req_info base; ··· 36 32 37 33 if (!ops->get_module_power_mode) 38 34 return 0; 35 + 36 + if (dev->module_fw_flash_in_progress) { 37 + NL_SET_ERR_MSG(extack, 38 + "Module firmware flashing is in progress"); 39 + return -EBUSY; 40 + } 39 41 40 42 return ops->get_module_power_mode(dev, &data->power, extack); 41 43 } ··· 119 109 if (!tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY]) 120 110 return 0; 121 111 112 + if (req_info->dev->module_fw_flash_in_progress) { 113 + NL_SET_ERR_MSG(info->extack, 114 + "Module firmware flashing is in progress"); 115 + return -EBUSY; 116 + } 117 + 122 118 if (!ops->get_module_power_mode || !ops->set_module_power_mode) { 123 119 NL_SET_ERR_MSG_ATTR(info->extack, 124 120 tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY], ··· 174 158 .set = ethnl_set_module, 175 159 .set_ntf_cmd = ETHTOOL_MSG_MODULE_NTF, 176 160 }; 161 + 162 + /* MODULE_FW_FLASH_ACT */ 163 + 164 + const struct nla_policy 165 + ethnl_module_fw_flash_act_policy[ETHTOOL_A_MODULE_FW_FLASH_PASSWORD + 1] = { 166 + [ETHTOOL_A_MODULE_FW_FLASH_HEADER] = 167 + NLA_POLICY_NESTED(ethnl_header_policy), 168 + [ETHTOOL_A_MODULE_FW_FLASH_FILE_NAME] = { .type = NLA_NUL_STRING }, 169 + [ETHTOOL_A_MODULE_FW_FLASH_PASSWORD] = { .type = NLA_U32 }, 170 + }; 171 + 172 + static LIST_HEAD(module_fw_flash_work_list); 173 + static DEFINE_SPINLOCK(module_fw_flash_work_list_lock); 174 + 175 + static int 176 + module_flash_fw_work_list_add(struct ethtool_module_fw_flash *module_fw, 177 + struct genl_info *info) 178 + { 179 + struct ethtool_module_fw_flash *work; 180 + 181 + /* First, check if already registered. */ 182 + spin_lock(&module_fw_flash_work_list_lock); 183 + list_for_each_entry(work, &module_fw_flash_work_list, list) { 184 + if (work->fw_update.ntf_params.portid == info->snd_portid && 185 + work->fw_update.dev == module_fw->fw_update.dev) { 186 + spin_unlock(&module_fw_flash_work_list_lock); 187 + return -EALREADY; 188 + } 189 + } 190 + 191 + list_add_tail(&module_fw->list, &module_fw_flash_work_list); 192 + spin_unlock(&module_fw_flash_work_list_lock); 193 + 194 + return 0; 195 + } 196 + 197 + static void module_flash_fw_work_list_del(struct list_head *list) 198 + { 199 + spin_lock(&module_fw_flash_work_list_lock); 200 + list_del(list); 201 + spin_unlock(&module_fw_flash_work_list_lock); 202 + } 203 + 204 + static void module_flash_fw_work(struct work_struct *work) 205 + { 206 + struct ethtool_module_fw_flash *module_fw; 207 + 208 + module_fw = container_of(work, struct ethtool_module_fw_flash, work); 209 + 210 + ethtool_cmis_fw_update(&module_fw->fw_update); 211 + 212 + module_flash_fw_work_list_del(&module_fw->list); 213 + module_fw->fw_update.dev->module_fw_flash_in_progress = false; 214 + netdev_put(module_fw->fw_update.dev, &module_fw->dev_tracker); 215 + release_firmware(module_fw->fw_update.fw); 216 + kfree(module_fw); 217 + } 218 + 219 + #define MODULE_EEPROM_PHYS_ID_PAGE 0 220 + #define MODULE_EEPROM_PHYS_ID_I2C_ADDR 0x50 221 + 222 + static int module_flash_fw_work_init(struct ethtool_module_fw_flash *module_fw, 223 + struct net_device *dev, 224 + struct netlink_ext_ack *extack) 225 + { 226 + const struct ethtool_ops *ops = dev->ethtool_ops; 227 + struct ethtool_module_eeprom page_data = {}; 228 + u8 phys_id; 229 + int err; 230 + 231 + /* Fetch the SFF-8024 Identifier Value. For all supported standards, it 232 + * is located at I2C address 0x50, byte 0. See section 4.1 in SFF-8024, 233 + * revision 4.9. 234 + */ 235 + page_data.page = MODULE_EEPROM_PHYS_ID_PAGE; 236 + page_data.offset = SFP_PHYS_ID; 237 + page_data.length = sizeof(phys_id); 238 + page_data.i2c_address = MODULE_EEPROM_PHYS_ID_I2C_ADDR; 239 + page_data.data = &phys_id; 240 + 241 + err = ops->get_module_eeprom_by_page(dev, &page_data, extack); 242 + if (err < 0) 243 + return err; 244 + 245 + switch (phys_id) { 246 + case SFF8024_ID_QSFP_DD: 247 + case SFF8024_ID_OSFP: 248 + case SFF8024_ID_DSFP: 249 + case SFF8024_ID_QSFP_PLUS_CMIS: 250 + case SFF8024_ID_SFP_DD_CMIS: 251 + case SFF8024_ID_SFP_PLUS_CMIS: 252 + INIT_WORK(&module_fw->work, module_flash_fw_work); 253 + break; 254 + default: 255 + NL_SET_ERR_MSG(extack, 256 + "Module type does not support firmware flashing"); 257 + return -EOPNOTSUPP; 258 + } 259 + 260 + return 0; 261 + } 262 + 263 + void ethnl_module_fw_flash_sock_destroy(struct ethnl_sock_priv *sk_priv) 264 + { 265 + struct ethtool_module_fw_flash *work; 266 + 267 + spin_lock(&module_fw_flash_work_list_lock); 268 + list_for_each_entry(work, &module_fw_flash_work_list, list) { 269 + if (work->fw_update.dev == sk_priv->dev && 270 + work->fw_update.ntf_params.portid == sk_priv->portid) { 271 + work->fw_update.ntf_params.closed_sock = true; 272 + break; 273 + } 274 + } 275 + spin_unlock(&module_fw_flash_work_list_lock); 276 + } 277 + 278 + static int 279 + module_flash_fw_schedule(struct net_device *dev, const char *file_name, 280 + struct ethtool_module_fw_flash_params *params, 281 + struct sk_buff *skb, struct genl_info *info) 282 + { 283 + struct ethtool_cmis_fw_update_params *fw_update; 284 + struct ethtool_module_fw_flash *module_fw; 285 + int err; 286 + 287 + module_fw = kzalloc(sizeof(*module_fw), GFP_KERNEL); 288 + if (!module_fw) 289 + return -ENOMEM; 290 + 291 + fw_update = &module_fw->fw_update; 292 + fw_update->params = *params; 293 + err = request_firmware_direct(&fw_update->fw, 294 + file_name, &dev->dev); 295 + if (err) { 296 + NL_SET_ERR_MSG(info->extack, 297 + "Failed to request module firmware image"); 298 + goto err_free; 299 + } 300 + 301 + err = module_flash_fw_work_init(module_fw, dev, info->extack); 302 + if (err < 0) 303 + goto err_release_firmware; 304 + 305 + dev->module_fw_flash_in_progress = true; 306 + netdev_hold(dev, &module_fw->dev_tracker, GFP_KERNEL); 307 + fw_update->dev = dev; 308 + fw_update->ntf_params.portid = info->snd_portid; 309 + fw_update->ntf_params.seq = info->snd_seq; 310 + fw_update->ntf_params.closed_sock = false; 311 + 312 + err = ethnl_sock_priv_set(skb, dev, fw_update->ntf_params.portid, 313 + ETHTOOL_SOCK_TYPE_MODULE_FW_FLASH); 314 + if (err < 0) 315 + goto err_release_firmware; 316 + 317 + err = module_flash_fw_work_list_add(module_fw, info); 318 + if (err < 0) 319 + goto err_release_firmware; 320 + 321 + schedule_work(&module_fw->work); 322 + 323 + return 0; 324 + 325 + err_release_firmware: 326 + release_firmware(fw_update->fw); 327 + err_free: 328 + kfree(module_fw); 329 + return err; 330 + } 331 + 332 + static int module_flash_fw(struct net_device *dev, struct nlattr **tb, 333 + struct sk_buff *skb, struct genl_info *info) 334 + { 335 + struct ethtool_module_fw_flash_params params = {}; 336 + const char *file_name; 337 + struct nlattr *attr; 338 + 339 + if (GENL_REQ_ATTR_CHECK(info, ETHTOOL_A_MODULE_FW_FLASH_FILE_NAME)) 340 + return -EINVAL; 341 + 342 + file_name = nla_data(tb[ETHTOOL_A_MODULE_FW_FLASH_FILE_NAME]); 343 + 344 + attr = tb[ETHTOOL_A_MODULE_FW_FLASH_PASSWORD]; 345 + if (attr) { 346 + params.password = cpu_to_be32(nla_get_u32(attr)); 347 + params.password_valid = true; 348 + } 349 + 350 + return module_flash_fw_schedule(dev, file_name, &params, skb, info); 351 + } 352 + 353 + static int ethnl_module_fw_flash_validate(struct net_device *dev, 354 + struct netlink_ext_ack *extack) 355 + { 356 + struct devlink_port *devlink_port = dev->devlink_port; 357 + const struct ethtool_ops *ops = dev->ethtool_ops; 358 + 359 + if (!ops->set_module_eeprom_by_page || 360 + !ops->get_module_eeprom_by_page) { 361 + NL_SET_ERR_MSG(extack, 362 + "Flashing module firmware is not supported by this device"); 363 + return -EOPNOTSUPP; 364 + } 365 + 366 + if (!ops->reset) { 367 + NL_SET_ERR_MSG(extack, 368 + "Reset module is not supported by this device, so flashing is not permitted"); 369 + return -EOPNOTSUPP; 370 + } 371 + 372 + if (dev->module_fw_flash_in_progress) { 373 + NL_SET_ERR_MSG(extack, "Module firmware flashing already in progress"); 374 + return -EBUSY; 375 + } 376 + 377 + if (dev->flags & IFF_UP) { 378 + NL_SET_ERR_MSG(extack, "Netdevice is up, so flashing is not permitted"); 379 + return -EBUSY; 380 + } 381 + 382 + if (devlink_port && devlink_port->attrs.split) { 383 + NL_SET_ERR_MSG(extack, "Can't perform firmware flashing on a split port"); 384 + return -EOPNOTSUPP; 385 + } 386 + 387 + return 0; 388 + } 389 + 390 + int ethnl_act_module_fw_flash(struct sk_buff *skb, struct genl_info *info) 391 + { 392 + struct ethnl_req_info req_info = {}; 393 + struct nlattr **tb = info->attrs; 394 + struct net_device *dev; 395 + int ret; 396 + 397 + ret = ethnl_parse_header_dev_get(&req_info, 398 + tb[ETHTOOL_A_MODULE_FW_FLASH_HEADER], 399 + genl_info_net(info), info->extack, 400 + true); 401 + if (ret < 0) 402 + return ret; 403 + dev = req_info.dev; 404 + 405 + rtnl_lock(); 406 + ret = ethnl_ops_begin(dev); 407 + if (ret < 0) 408 + goto out_rtnl; 409 + 410 + ret = ethnl_module_fw_flash_validate(dev, info->extack); 411 + if (ret < 0) 412 + goto out_rtnl; 413 + 414 + ret = module_flash_fw(dev, tb, skb, info); 415 + 416 + ethnl_ops_complete(dev); 417 + 418 + out_rtnl: 419 + rtnl_unlock(); 420 + ethnl_parse_header_dev_put(&req_info); 421 + return ret; 422 + } 423 + 424 + /* MODULE_FW_FLASH_NTF */ 425 + 426 + static int 427 + ethnl_module_fw_flash_ntf_put_err(struct sk_buff *skb, char *err_msg, 428 + char *sub_err_msg) 429 + { 430 + int err_msg_len, sub_err_msg_len, total_len; 431 + struct nlattr *attr; 432 + 433 + if (!err_msg) 434 + return 0; 435 + 436 + err_msg_len = strlen(err_msg); 437 + total_len = err_msg_len + 2; /* For period and NUL. */ 438 + 439 + if (sub_err_msg) { 440 + sub_err_msg_len = strlen(sub_err_msg); 441 + total_len += sub_err_msg_len + 2; /* For ", ". */ 442 + } 443 + 444 + attr = nla_reserve(skb, ETHTOOL_A_MODULE_FW_FLASH_STATUS_MSG, 445 + total_len); 446 + if (!attr) 447 + return -ENOMEM; 448 + 449 + if (sub_err_msg) 450 + sprintf(nla_data(attr), "%s, %s.", err_msg, sub_err_msg); 451 + else 452 + sprintf(nla_data(attr), "%s.", err_msg); 453 + 454 + return 0; 455 + } 456 + 457 + static void 458 + ethnl_module_fw_flash_ntf(struct net_device *dev, 459 + enum ethtool_module_fw_flash_status status, 460 + struct ethnl_module_fw_flash_ntf_params *ntf_params, 461 + char *err_msg, char *sub_err_msg, 462 + u64 done, u64 total) 463 + { 464 + struct sk_buff *skb; 465 + void *hdr; 466 + int ret; 467 + 468 + if (ntf_params->closed_sock) 469 + return; 470 + 471 + skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 472 + if (!skb) 473 + return; 474 + 475 + hdr = ethnl_unicast_put(skb, ntf_params->portid, ntf_params->seq, 476 + ETHTOOL_MSG_MODULE_FW_FLASH_NTF); 477 + if (!hdr) 478 + goto err_skb; 479 + 480 + ret = ethnl_fill_reply_header(skb, dev, 481 + ETHTOOL_A_MODULE_FW_FLASH_HEADER); 482 + if (ret < 0) 483 + goto err_skb; 484 + 485 + if (nla_put_u32(skb, ETHTOOL_A_MODULE_FW_FLASH_STATUS, status)) 486 + goto err_skb; 487 + 488 + ret = ethnl_module_fw_flash_ntf_put_err(skb, err_msg, sub_err_msg); 489 + if (ret < 0) 490 + goto err_skb; 491 + 492 + if (nla_put_uint(skb, ETHTOOL_A_MODULE_FW_FLASH_DONE, done)) 493 + goto err_skb; 494 + 495 + if (nla_put_uint(skb, ETHTOOL_A_MODULE_FW_FLASH_TOTAL, total)) 496 + goto err_skb; 497 + 498 + genlmsg_end(skb, hdr); 499 + genlmsg_unicast(dev_net(dev), skb, ntf_params->portid); 500 + return; 501 + 502 + err_skb: 503 + nlmsg_free(skb); 504 + } 505 + 506 + void ethnl_module_fw_flash_ntf_err(struct net_device *dev, 507 + struct ethnl_module_fw_flash_ntf_params *params, 508 + char *err_msg, char *sub_err_msg) 509 + { 510 + ethnl_module_fw_flash_ntf(dev, ETHTOOL_MODULE_FW_FLASH_STATUS_ERROR, 511 + params, err_msg, sub_err_msg, 0, 0); 512 + } 513 + 514 + void 515 + ethnl_module_fw_flash_ntf_start(struct net_device *dev, 516 + struct ethnl_module_fw_flash_ntf_params *params) 517 + { 518 + ethnl_module_fw_flash_ntf(dev, ETHTOOL_MODULE_FW_FLASH_STATUS_STARTED, 519 + params, NULL, NULL, 0, 0); 520 + } 521 + 522 + void 523 + ethnl_module_fw_flash_ntf_complete(struct net_device *dev, 524 + struct ethnl_module_fw_flash_ntf_params *params) 525 + { 526 + ethnl_module_fw_flash_ntf(dev, ETHTOOL_MODULE_FW_FLASH_STATUS_COMPLETED, 527 + params, NULL, NULL, 0, 0); 528 + } 529 + 530 + void 531 + ethnl_module_fw_flash_ntf_in_progress(struct net_device *dev, 532 + struct ethnl_module_fw_flash_ntf_params *params, 533 + u64 done, u64 total) 534 + { 535 + ethnl_module_fw_flash_ntf(dev, 536 + ETHTOOL_MODULE_FW_FLASH_STATUS_IN_PROGRESS, 537 + params, NULL, NULL, done, total); 538 + }
+75
net/ethtool/module_fw.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + 3 + #include <uapi/linux/ethtool.h> 4 + #include "netlink.h" 5 + 6 + /** 7 + * struct ethnl_module_fw_flash_ntf_params - module firmware flashing 8 + * notifications parameters 9 + * @portid: Netlink portid of sender. 10 + * @seq: Sequence number of sender. 11 + * @closed_sock: Indicates whether the socket was closed from user space. 12 + */ 13 + struct ethnl_module_fw_flash_ntf_params { 14 + u32 portid; 15 + u32 seq; 16 + bool closed_sock; 17 + }; 18 + 19 + /** 20 + * struct ethtool_module_fw_flash_params - module firmware flashing parameters 21 + * @password: Module password. Only valid when @pass_valid is set. 22 + * @password_valid: Whether the module password is valid or not. 23 + */ 24 + struct ethtool_module_fw_flash_params { 25 + __be32 password; 26 + u8 password_valid:1; 27 + }; 28 + 29 + /** 30 + * struct ethtool_cmis_fw_update_params - CMIS firmware update specific 31 + * parameters 32 + * @dev: Pointer to the net_device to be flashed. 33 + * @params: Module firmware flashing parameters. 34 + * @ntf_params: Module firmware flashing notification parameters. 35 + * @fw: Firmware to flash. 36 + */ 37 + struct ethtool_cmis_fw_update_params { 38 + struct net_device *dev; 39 + struct ethtool_module_fw_flash_params params; 40 + struct ethnl_module_fw_flash_ntf_params ntf_params; 41 + const struct firmware *fw; 42 + }; 43 + 44 + /** 45 + * struct ethtool_module_fw_flash - module firmware flashing 46 + * @list: List node for &module_fw_flash_work_list. 47 + * @dev_tracker: Refcount tracker for @dev. 48 + * @work: The flashing firmware work. 49 + * @fw_update: CMIS firmware update specific parameters. 50 + */ 51 + struct ethtool_module_fw_flash { 52 + struct list_head list; 53 + netdevice_tracker dev_tracker; 54 + struct work_struct work; 55 + struct ethtool_cmis_fw_update_params fw_update; 56 + }; 57 + 58 + void ethnl_module_fw_flash_sock_destroy(struct ethnl_sock_priv *sk_priv); 59 + 60 + void 61 + ethnl_module_fw_flash_ntf_err(struct net_device *dev, 62 + struct ethnl_module_fw_flash_ntf_params *params, 63 + char *err_msg, char *sub_err_msg); 64 + void 65 + ethnl_module_fw_flash_ntf_start(struct net_device *dev, 66 + struct ethnl_module_fw_flash_ntf_params *params); 67 + void 68 + ethnl_module_fw_flash_ntf_complete(struct net_device *dev, 69 + struct ethnl_module_fw_flash_ntf_params *params); 70 + void 71 + ethnl_module_fw_flash_ntf_in_progress(struct net_device *dev, 72 + struct ethnl_module_fw_flash_ntf_params *params, 73 + u64 done, u64 total); 74 + 75 + void ethtool_cmis_fw_update(struct ethtool_cmis_fw_update_params *params);
+56
net/ethtool/netlink.c
··· 4 4 #include <linux/ethtool_netlink.h> 5 5 #include <linux/pm_runtime.h> 6 6 #include "netlink.h" 7 + #include "module_fw.h" 7 8 8 9 static struct genl_family ethtool_genl_family; 9 10 ··· 30 29 [ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32, 31 30 ETHTOOL_FLAGS_STATS), 32 31 }; 32 + 33 + int ethnl_sock_priv_set(struct sk_buff *skb, struct net_device *dev, u32 portid, 34 + enum ethnl_sock_type type) 35 + { 36 + struct ethnl_sock_priv *sk_priv; 37 + 38 + sk_priv = genl_sk_priv_get(&ethtool_genl_family, NETLINK_CB(skb).sk); 39 + if (IS_ERR(sk_priv)) 40 + return PTR_ERR(sk_priv); 41 + 42 + sk_priv->dev = dev; 43 + sk_priv->portid = portid; 44 + sk_priv->type = type; 45 + 46 + return 0; 47 + } 48 + 49 + static void ethnl_sock_priv_destroy(void *priv) 50 + { 51 + struct ethnl_sock_priv *sk_priv = priv; 52 + 53 + switch (sk_priv->type) { 54 + case ETHTOOL_SOCK_TYPE_MODULE_FW_FLASH: 55 + ethnl_module_fw_flash_sock_destroy(sk_priv); 56 + break; 57 + default: 58 + break; 59 + } 60 + } 33 61 34 62 int ethnl_ops_begin(struct net_device *dev) 35 63 { ··· 267 237 { 268 238 return genlmsg_put(skb, 0, ++ethnl_bcast_seq, &ethtool_genl_family, 0, 269 239 cmd); 240 + } 241 + 242 + void *ethnl_unicast_put(struct sk_buff *skb, u32 portid, u32 seq, u8 cmd) 243 + { 244 + return genlmsg_put(skb, portid, seq, &ethtool_genl_family, 0, cmd); 270 245 } 271 246 272 247 int ethnl_multicast(struct sk_buff *skb, struct net_device *dev) ··· 795 760 static int ethnl_netdev_event(struct notifier_block *this, unsigned long event, 796 761 void *ptr) 797 762 { 763 + struct netdev_notifier_info *info = ptr; 764 + struct netlink_ext_ack *extack; 765 + struct net_device *dev; 766 + 767 + dev = netdev_notifier_info_to_dev(info); 768 + extack = netdev_notifier_info_to_extack(info); 769 + 798 770 switch (event) { 799 771 case NETDEV_FEAT_CHANGE: 800 772 ethnl_notify_features(ptr); 801 773 break; 774 + case NETDEV_PRE_UP: 775 + if (dev->module_fw_flash_in_progress) { 776 + NL_SET_ERR_MSG(extack, "Can't set port up while flashing module firmware"); 777 + return NOTIFY_BAD; 778 + } 802 779 } 803 780 804 781 return NOTIFY_DONE; ··· 1172 1125 .policy = ethnl_mm_set_policy, 1173 1126 .maxattr = ARRAY_SIZE(ethnl_mm_set_policy) - 1, 1174 1127 }, 1128 + { 1129 + .cmd = ETHTOOL_MSG_MODULE_FW_FLASH_ACT, 1130 + .flags = GENL_UNS_ADMIN_PERM, 1131 + .doit = ethnl_act_module_fw_flash, 1132 + .policy = ethnl_module_fw_flash_act_policy, 1133 + .maxattr = ARRAY_SIZE(ethnl_module_fw_flash_act_policy) - 1, 1134 + }, 1175 1135 }; 1176 1136 1177 1137 static const struct genl_multicast_group ethtool_nl_mcgrps[] = { ··· 1195 1141 .resv_start_op = ETHTOOL_MSG_MODULE_GET + 1, 1196 1142 .mcgrps = ethtool_nl_mcgrps, 1197 1143 .n_mcgrps = ARRAY_SIZE(ethtool_nl_mcgrps), 1144 + .sock_priv_size = sizeof(struct ethnl_sock_priv), 1145 + .sock_priv_destroy = ethnl_sock_priv_destroy, 1198 1146 }; 1199 1147 1200 1148 /* module setup */
+16
net/ethtool/netlink.h
··· 21 21 void **ehdrp); 22 22 void *ethnl_dump_put(struct sk_buff *skb, struct netlink_callback *cb, u8 cmd); 23 23 void *ethnl_bcastmsg_put(struct sk_buff *skb, u8 cmd); 24 + void *ethnl_unicast_put(struct sk_buff *skb, u32 portid, u32 seq, u8 cmd); 24 25 int ethnl_multicast(struct sk_buff *skb, struct net_device *dev); 25 26 26 27 /** ··· 284 283 int ethnl_ops_begin(struct net_device *dev); 285 284 void ethnl_ops_complete(struct net_device *dev); 286 285 286 + enum ethnl_sock_type { 287 + ETHTOOL_SOCK_TYPE_MODULE_FW_FLASH, 288 + }; 289 + 290 + struct ethnl_sock_priv { 291 + struct net_device *dev; 292 + u32 portid; 293 + enum ethnl_sock_type type; 294 + }; 295 + 296 + int ethnl_sock_priv_set(struct sk_buff *skb, struct net_device *dev, u32 portid, 297 + enum ethnl_sock_type type); 298 + 287 299 /** 288 300 * struct ethnl_request_ops - unified handling of GET and SET requests 289 301 * @request_cmd: command id for request (GET) ··· 455 441 extern const struct nla_policy ethnl_plca_get_status_policy[ETHTOOL_A_PLCA_HEADER + 1]; 456 442 extern const struct nla_policy ethnl_mm_get_policy[ETHTOOL_A_MM_HEADER + 1]; 457 443 extern const struct nla_policy ethnl_mm_set_policy[ETHTOOL_A_MM_MAX + 1]; 444 + extern const struct nla_policy ethnl_module_fw_flash_act_policy[ETHTOOL_A_MODULE_FW_FLASH_PASSWORD + 1]; 458 445 459 446 int ethnl_set_features(struct sk_buff *skb, struct genl_info *info); 460 447 int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info); ··· 463 448 int ethnl_tunnel_info_doit(struct sk_buff *skb, struct genl_info *info); 464 449 int ethnl_tunnel_info_start(struct netlink_callback *cb); 465 450 int ethnl_tunnel_info_dumpit(struct sk_buff *skb, struct netlink_callback *cb); 451 + int ethnl_act_module_fw_flash(struct sk_buff *skb, struct genl_info *info); 466 452 467 453 extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN]; 468 454 extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN];
+2 -1
tools/net/ynl/Makefile.deps
··· 16 16 17 17 CFLAGS_devlink:=$(call get_hdr_inc,_LINUX_DEVLINK_H_,devlink.h) 18 18 CFLAGS_dpll:=$(call get_hdr_inc,_LINUX_DPLL_H,dpll.h) 19 - CFLAGS_ethtool:=$(call get_hdr_inc,_LINUX_ETHTOOL_NETLINK_H_,ethtool_netlink.h) 19 + CFLAGS_ethtool:=$(call get_hdr_inc,_LINUX_ETHTOOL_H,ethtool.h) \ 20 + $(call get_hdr_inc,_LINUX_ETHTOOL_NETLINK_H_,ethtool_netlink.h) 20 21 CFLAGS_handshake:=$(call get_hdr_inc,_LINUX_HANDSHAKE_H,handshake.h) 21 22 CFLAGS_mptcp_pm:=$(call get_hdr_inc,_LINUX_MPTCP_PM_H,mptcp_pm.h) 22 23 CFLAGS_netdev:=$(call get_hdr_inc,_LINUX_NETDEV_H,netdev.h)