* [PATCH v5 fwctl 1/3] fwctl: Add driver_data field to fwctl_rpc
2026-07-07 10:00 [PATCH v5 fwctl 0/3] fwctl/bnxt: DMA buffer support for HWRM commands Pavan Chebbi
@ 2026-07-07 10:00 ` Pavan Chebbi
2026-07-07 10:00 ` [PATCH v5 fwctl 2/3] bnxt_en: Update bnxt firmware spec Pavan Chebbi
2026-07-07 10:00 ` [PATCH v5 fwctl 3/3] fwctl/bnxt: add DMA buffer support for HWRM commands Pavan Chebbi
2 siblings, 0 replies; 4+ messages in thread
From: Pavan Chebbi @ 2026-07-07 10:00 UTC (permalink / raw)
To: jgg, michael.chan
Cc: linux-kernel, dave.jiang, saeedm, jic23, gospo, selvin.xavier,
leon, kalesh-anakkur.purayil, Pavan Chebbi
Add a driver_data field to struct fwctl_rpc in the UAPI and thread the
value through the fw_rpc() ops callback as a new __u64 parameter. The
field is opaque and driver-defined.
Drivers that do not define a driver_data format may return -EOPNOTSUPP.
Update the existing drivers' fw_rpc implementations to enforce this.
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
---
drivers/cxl/core/features.c | 6 +++++-
drivers/fwctl/bnxt/main.c | 6 +++++-
drivers/fwctl/main.c | 3 ++-
drivers/fwctl/mlx5/main.c | 6 +++++-
drivers/fwctl/pds/main.c | 6 +++++-
include/linux/fwctl.h | 8 ++++++--
include/uapi/fwctl/fwctl.h | 3 +++
7 files changed, 31 insertions(+), 7 deletions(-)
diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c
index 85185af46b72..7a3809b1722b 100644
--- a/drivers/cxl/core/features.c
+++ b/drivers/cxl/core/features.c
@@ -643,7 +643,8 @@ static void *cxlctl_handle_commands(struct cxl_features_state *cxlfs,
}
static void *cxlctl_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope,
- void *in, size_t in_len, size_t *out_len)
+ void *in, size_t in_len, size_t *out_len,
+ __u64 driver_data)
{
struct fwctl_device *fwctl_dev = uctx->fwctl;
struct cxl_memdev *cxlmd = fwctl_to_memdev(fwctl_dev);
@@ -651,6 +652,9 @@ static void *cxlctl_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope,
const struct fwctl_rpc_cxl *rpc_in = in;
u16 opcode = rpc_in->opcode;
+ if (driver_data)
+ return ERR_PTR(-EOPNOTSUPP);
+
if (!cxlctl_validate_hw_command(cxlfs, rpc_in, scope, opcode))
return ERR_PTR(-EINVAL);
diff --git a/drivers/fwctl/bnxt/main.c b/drivers/fwctl/bnxt/main.c
index 951c8ac2e0a1..5eb3651a1784 100644
--- a/drivers/fwctl/bnxt/main.c
+++ b/drivers/fwctl/bnxt/main.c
@@ -172,7 +172,8 @@ static unsigned int bnxtctl_get_timeout(struct input *req)
static void *bnxtctl_fw_rpc(struct fwctl_uctx *uctx,
enum fwctl_rpc_scope scope,
- void *in, size_t in_len, size_t *out_len)
+ void *in, size_t in_len, size_t *out_len,
+ __u64 driver_data)
{
struct bnxtctl_dev *bnxtctl =
container_of(uctx->fwctl, struct bnxtctl_dev, fwctl);
@@ -180,6 +181,9 @@ static void *bnxtctl_fw_rpc(struct fwctl_uctx *uctx,
struct bnxt_fw_msg rpc_in = {0};
int rc;
+ if (driver_data)
+ return ERR_PTR(-EOPNOTSUPP);
+
if (in_len < sizeof(struct input) || in_len > HWRM_MAX_REQ_LEN)
return ERR_PTR(-EINVAL);
diff --git a/drivers/fwctl/main.c b/drivers/fwctl/main.c
index 098c3824ad75..32c4c66dd7d5 100644
--- a/drivers/fwctl/main.c
+++ b/drivers/fwctl/main.c
@@ -122,7 +122,8 @@ static int fwctl_cmd_rpc(struct fwctl_ucmd *ucmd)
out_len = cmd->out_len;
void *outbuf __free(kvfree) = fwctl->ops->fw_rpc(
- ucmd->uctx, cmd->scope, inbuf, cmd->in_len, &out_len);
+ ucmd->uctx, cmd->scope, inbuf, cmd->in_len, &out_len,
+ cmd->driver_data);
if (IS_ERR(outbuf))
return PTR_ERR(outbuf);
if (outbuf == inbuf) {
diff --git a/drivers/fwctl/mlx5/main.c b/drivers/fwctl/mlx5/main.c
index e86ab703c767..2a5105769126 100644
--- a/drivers/fwctl/mlx5/main.c
+++ b/drivers/fwctl/mlx5/main.c
@@ -304,7 +304,8 @@ static bool mlx5ctl_validate_rpc(const void *in, enum fwctl_rpc_scope scope)
}
static void *mlx5ctl_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope,
- void *rpc_in, size_t in_len, size_t *out_len)
+ void *rpc_in, size_t in_len, size_t *out_len,
+ __u64 driver_data)
{
struct mlx5ctl_dev *mcdev =
container_of(uctx->fwctl, struct mlx5ctl_dev, fwctl);
@@ -313,6 +314,9 @@ static void *mlx5ctl_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope,
void *rpc_out;
int ret;
+ if (driver_data)
+ return ERR_PTR(-EOPNOTSUPP);
+
if (in_len < MLX5_ST_SZ_BYTES(mbox_in_hdr) ||
*out_len < MLX5_ST_SZ_BYTES(mbox_out_hdr))
return ERR_PTR(-EMSGSIZE);
diff --git a/drivers/fwctl/pds/main.c b/drivers/fwctl/pds/main.c
index 68fe254dd10a..f036a3e3f5cd 100644
--- a/drivers/fwctl/pds/main.c
+++ b/drivers/fwctl/pds/main.c
@@ -348,7 +348,8 @@ static int pdsfc_validate_rpc(struct pdsfc_dev *pdsfc,
}
static void *pdsfc_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope,
- void *in, size_t in_len, size_t *out_len)
+ void *in, size_t in_len, size_t *out_len,
+ __u64 driver_data)
{
struct pdsfc_dev *pdsfc = container_of(uctx->fwctl, struct pdsfc_dev, fwctl);
struct device *dev = &uctx->fwctl->dev;
@@ -365,6 +366,9 @@ static void *pdsfc_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope,
if (in_len < sizeof(*rpc))
return ERR_PTR(-EINVAL);
+ if (driver_data)
+ return ERR_PTR(-EOPNOTSUPP);
+
err = pdsfc_validate_rpc(pdsfc, rpc, scope);
if (err)
return ERR_PTR(err);
diff --git a/include/linux/fwctl.h b/include/linux/fwctl.h
index 5d61fc8a6871..afb7f72d0cb1 100644
--- a/include/linux/fwctl.h
+++ b/include/linux/fwctl.h
@@ -51,10 +51,14 @@ struct fwctl_ops {
* @fw_rpc: Implement FWCTL_RPC. Deliver rpc_in/in_len to the FW and
* return the response and set out_len. rpc_in can be returned as the
* response pointer. Otherwise the returned pointer is freed with
- * kvfree().
+ * kvfree(). driver_data is the opaque value from fwctl_rpc, passed
+ * verbatim from userspace. The driver is responsible for interpreting
+ * and validating it. Drivers that do not define a driver_data format
+ * must return -EOPNOTSUPP if driver_data is non-zero.
*/
void *(*fw_rpc)(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope,
- void *rpc_in, size_t in_len, size_t *out_len);
+ void *rpc_in, size_t in_len, size_t *out_len,
+ __u64 driver_data);
};
/**
diff --git a/include/uapi/fwctl/fwctl.h b/include/uapi/fwctl/fwctl.h
index 2d6d4049c205..2e07425402a3 100644
--- a/include/uapi/fwctl/fwctl.h
+++ b/include/uapi/fwctl/fwctl.h
@@ -120,6 +120,8 @@ enum fwctl_rpc_scope {
* @out_len: Length of the out memory
* @in: Request message in device specific format
* @out: Response message in device specific format
+ * @driver_data: Opaque userspace pointer passed verbatim to the driver.
+ * Must be 0 for drivers that do not define a driver_data format.
*
* Deliver a Remote Procedure Call to the device FW and return the response. The
* call's parameters and return are marshaled into linear buffers of memory. Any
@@ -136,6 +138,7 @@ struct fwctl_rpc {
__u32 out_len;
__aligned_u64 in;
__aligned_u64 out;
+ __aligned_u64 driver_data;
};
#define FWCTL_RPC _IO(FWCTL_TYPE, FWCTL_CMD_RPC)
--
2.52.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH v5 fwctl 3/3] fwctl/bnxt: add DMA buffer support for HWRM commands
2026-07-07 10:00 [PATCH v5 fwctl 0/3] fwctl/bnxt: DMA buffer support for HWRM commands Pavan Chebbi
2026-07-07 10:00 ` [PATCH v5 fwctl 1/3] fwctl: Add driver_data field to fwctl_rpc Pavan Chebbi
2026-07-07 10:00 ` [PATCH v5 fwctl 2/3] bnxt_en: Update bnxt firmware spec Pavan Chebbi
@ 2026-07-07 10:00 ` Pavan Chebbi
2 siblings, 0 replies; 4+ messages in thread
From: Pavan Chebbi @ 2026-07-07 10:00 UTC (permalink / raw)
To: jgg, michael.chan
Cc: linux-kernel, dave.jiang, saeedm, jic23, gospo, selvin.xavier,
leon, kalesh-anakkur.purayil, Pavan Chebbi
Several HWRM commands carry __le64 DMA address fields in their input
structures; firmware reads from or writes to the memory those addresses
point to. Because the kernel must own all DMA mappings, userspace
shall describe the indirect buffers via fwctl_rpc.driver_data and the
driver shall map and patch the commands before sending to firmware.
Introduce UAPI types for bnxt_fwctl that help userspace describe the
DMA buffers. Have a static per-command descriptor table in the driver
that records the details of the DMA fields in each supported HWRM
input struct. When a DMA-bearing HWRM command arrives, the driver
validates it using the descriptor table. Scope-gated allow-list and
timeout value list are updated with the new the commands.
Signed-off-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
---
drivers/fwctl/bnxt/main.c | 483 +++++++++++++++++++++++++++++++++++++-
include/uapi/fwctl/bnxt.h | 35 +++
2 files changed, 512 insertions(+), 6 deletions(-)
diff --git a/drivers/fwctl/bnxt/main.c b/drivers/fwctl/bnxt/main.c
index 5eb3651a1784..23cac9baa7bb 100644
--- a/drivers/fwctl/bnxt/main.c
+++ b/drivers/fwctl/bnxt/main.c
@@ -4,6 +4,7 @@
*/
#include <linux/auxiliary_bus.h>
+#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/fwctl.h>
@@ -31,7 +32,8 @@ static int bnxtctl_open_uctx(struct fwctl_uctx *uctx)
bnxtctl_uctx->uctx_caps = BIT(FWCTL_BNXT_INLINE_COMMANDS) |
BIT(FWCTL_BNXT_QUERY_COMMANDS) |
- BIT(FWCTL_BNXT_SEND_COMMANDS);
+ BIT(FWCTL_BNXT_SEND_COMMANDS) |
+ BIT(FWCTL_BNXT_DMA_COMMANDS);
return 0;
}
@@ -55,6 +57,371 @@ static void *bnxtctl_info(struct fwctl_uctx *uctx, size_t *length)
return info;
}
+struct bnxtctl_dma_field {
+ size_t offset; /* offsetof(hwrm_xxx_input, addr_field) */
+ u8 dir;
+ size_t len_offset; /* offsetof(hwrm_xxx_input, len_field); 0 if the
+ * command carries no transfer-length field
+ */
+ u8 len_width; /* byte width of the length field: 2 or 4 */
+ u8 len_unit; /* bytes represented by one unit of the length field */
+ u32 buf_len; /* for commands with no length in payload */
+};
+
+struct bnxtctl_cmd_dma_desc {
+ u16 req_type;
+ u8 num_fields;
+ u8 scope_min;
+ size_t req_size; /* sizeof(struct hwrm_xxx_input) */
+ struct bnxtctl_dma_field fields[FWCTL_BNXT_MAX_BUFS];
+};
+
+/*
+ * Per-command DMA buffer descriptor table for HWRM commands that
+ * carry __le64 DMA address fields in their input
+ */
+static const struct bnxtctl_cmd_dma_desc bnxtctl_dma_cmds[] = {
+ { HWRM_NVM_SET_VARIABLE, 1, FWCTL_RPC_CONFIGURATION,
+ sizeof(struct hwrm_nvm_set_variable_input),
+ {{ .offset = offsetof(struct hwrm_nvm_set_variable_input, src_data_addr),
+ .dir = FWCTL_BNXT_BUF_TO_DEVICE,
+ .len_offset = offsetof(struct hwrm_nvm_set_variable_input, data_len),
+ .len_width = 2, .len_unit = 1 }} },
+ { HWRM_NVM_GET_VARIABLE, 1, FWCTL_RPC_CONFIGURATION,
+ sizeof(struct hwrm_nvm_get_variable_input),
+ {{ .offset = offsetof(struct hwrm_nvm_get_variable_input, dest_data_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_nvm_get_variable_input, data_len),
+ .len_width = 2, .len_unit = 1 }} },
+ { HWRM_NVM_READ, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_nvm_read_input),
+ {{ .offset = offsetof(struct hwrm_nvm_read_input, host_dest_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_nvm_read_input, len),
+ .len_width = 4, .len_unit = 1 }} },
+ { HWRM_NVM_GET_DIR_ENTRIES, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_nvm_get_dir_entries_input),
+ {{ .offset = offsetof(struct hwrm_nvm_get_dir_entries_input, host_dest_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE, .buf_len = FWCTL_BNXT_MAX_DMABUF }} },
+ { HWRM_NVM_WRITE, 1, FWCTL_RPC_DEBUG_WRITE,
+ sizeof(struct hwrm_nvm_write_input),
+ {{ .offset = offsetof(struct hwrm_nvm_write_input, host_src_addr),
+ .dir = FWCTL_BNXT_BUF_TO_DEVICE,
+ .len_offset = offsetof(struct hwrm_nvm_write_input, dir_data_length),
+ .len_width = 4, .len_unit = 1 }} },
+ { HWRM_NVM_MODIFY, 1, FWCTL_RPC_DEBUG_WRITE,
+ sizeof(struct hwrm_nvm_modify_input),
+ {{ .offset = offsetof(struct hwrm_nvm_modify_input, host_src_addr),
+ .dir = FWCTL_BNXT_BUF_TO_DEVICE,
+ .len_offset = offsetof(struct hwrm_nvm_modify_input, len),
+ .len_width = 4, .len_unit = 1 }} },
+ { HWRM_NVM_RAW_WRITE_BLK, 1, FWCTL_RPC_DEBUG_WRITE_FULL,
+ sizeof(struct hwrm_nvm_raw_write_blk_input),
+ {{ .offset = offsetof(struct hwrm_nvm_raw_write_blk_input, host_src_addr),
+ .dir = FWCTL_BNXT_BUF_TO_DEVICE,
+ .len_offset = offsetof(struct hwrm_nvm_raw_write_blk_input, len),
+ .len_width = 4, .len_unit = 1 }} },
+ { HWRM_NVM_RAW_DUMP, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_nvm_raw_dump_input),
+ {{ .offset = offsetof(struct hwrm_nvm_raw_dump_input, host_dest_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_nvm_raw_dump_input, len),
+ .len_width = 4, .len_unit = 1 }} },
+
+ { HWRM_FW_GET_STRUCTURED_DATA, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_fw_get_structured_data_input),
+ {{ .offset = offsetof(struct hwrm_fw_get_structured_data_input, dest_data_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_fw_get_structured_data_input, data_len),
+ .len_width = 2, .len_unit = 1 }} },
+ { HWRM_FW_SET_STRUCTURED_DATA, 1, FWCTL_RPC_DEBUG_WRITE,
+ sizeof(struct hwrm_fw_set_structured_data_input),
+ {{ .offset = offsetof(struct hwrm_fw_set_structured_data_input, src_data_addr),
+ .dir = FWCTL_BNXT_BUF_TO_DEVICE,
+ .len_offset = offsetof(struct hwrm_fw_set_structured_data_input, data_len),
+ .len_width = 2, .len_unit = 1 }} },
+ { HWRM_FW_LIVEPATCH, 1, FWCTL_RPC_DEBUG_WRITE_FULL,
+ sizeof(struct hwrm_fw_livepatch_input),
+ {{ .offset = offsetof(struct hwrm_fw_livepatch_input, host_addr),
+ .dir = FWCTL_BNXT_BUF_TO_DEVICE,
+ .len_offset = offsetof(struct hwrm_fw_livepatch_input, patch_len),
+ .len_width = 4, .len_unit = 1 }} },
+
+ { HWRM_DBG_COREDUMP_LIST, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_dbg_coredump_list_input),
+ {{ .offset = offsetof(struct hwrm_dbg_coredump_list_input, host_dest_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_dbg_coredump_list_input, host_buf_len),
+ .len_width = 4, .len_unit = 1 }} },
+ { HWRM_DBG_COREDUMP_RETRIEVE, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_dbg_coredump_retrieve_input),
+ {{ .offset = offsetof(struct hwrm_dbg_coredump_retrieve_input, host_dest_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_dbg_coredump_retrieve_input, host_buf_len),
+ .len_width = 4, .len_unit = 1 }} },
+ /* read_len32 counts 32-bit words, not bytes (see bnxt_dbg_hwrm_rd_reg()). */
+ { HWRM_DBG_READ_DIRECT, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_dbg_read_direct_input),
+ {{ .offset = offsetof(struct hwrm_dbg_read_direct_input, host_dest_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_dbg_read_direct_input, read_len32),
+ .len_width = 4, .len_unit = 4 }} },
+ { HWRM_DBG_READ_INDIRECT, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_dbg_read_indirect_input),
+ {{ .offset = offsetof(struct hwrm_dbg_read_indirect_input, host_dest_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_dbg_read_indirect_input, host_dest_addr_len),
+ .len_width = 4, .len_unit = 1 }} },
+ { HWRM_DBG_SERDES_TEST, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_dbg_serdes_test_input),
+ {{ .offset = offsetof(struct hwrm_dbg_serdes_test_input, resp_data_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_dbg_serdes_test_input, data_len),
+ .len_width = 2, .len_unit = 1 }} },
+ { HWRM_DBG_TOKEN_CFG, 1, FWCTL_RPC_DEBUG_WRITE_FULL,
+ sizeof(struct hwrm_dbg_token_cfg_input),
+ {{ .offset = offsetof(struct hwrm_dbg_token_cfg_input, host_src_addr),
+ .dir = FWCTL_BNXT_BUF_TO_DEVICE,
+ .len_offset = offsetof(struct hwrm_dbg_token_cfg_input, dbg_token_len),
+ .len_width = 4, .len_unit = 1 }} },
+
+ { HWRM_QUEUE_DSCP2PRI_QCFG, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_queue_dscp2pri_qcfg_input),
+ {{ .offset = offsetof(struct hwrm_queue_dscp2pri_qcfg_input, dest_data_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_queue_dscp2pri_qcfg_input,
+ dest_data_buffer_size),
+ .len_width = 2, .len_unit = 1 }} },
+
+ { HWRM_PORT_QSTATS, 2, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_port_qstats_input),
+ {{ .offset = offsetof(struct hwrm_port_qstats_input, tx_stat_host_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .buf_len = sizeof(struct tx_port_stats) },
+ { .offset = offsetof(struct hwrm_port_qstats_input, rx_stat_host_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .buf_len = sizeof(struct rx_port_stats) }} },
+ { HWRM_PORT_QSTATS_EXT, 2, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_port_qstats_ext_input),
+ {{ .offset = offsetof(struct hwrm_port_qstats_ext_input, tx_stat_host_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_port_qstats_ext_input, tx_stat_size),
+ .len_width = 2, .len_unit = 1 },
+ { .offset = offsetof(struct hwrm_port_qstats_ext_input, rx_stat_host_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_port_qstats_ext_input, rx_stat_size),
+ .len_width = 2, .len_unit = 1 }} },
+ { HWRM_PORT_QSTATS_EXT_PFC_ADV, 2, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_port_qstats_ext_pfc_adv_input),
+ {{ .offset = offsetof(struct hwrm_port_qstats_ext_pfc_adv_input,
+ tx_pfc_adv_stat_host_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_port_qstats_ext_pfc_adv_input,
+ pfc_adv_stat_size),
+ .len_width = 2, .len_unit = 1 },
+ { .offset = offsetof(struct hwrm_port_qstats_ext_pfc_adv_input,
+ rx_pfc_adv_stat_host_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_port_qstats_ext_pfc_adv_input,
+ pfc_adv_stat_size),
+ .len_width = 2, .len_unit = 1 }} },
+ { HWRM_PCIE_QSTATS, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_pcie_qstats_input),
+ {{ .offset = offsetof(struct hwrm_pcie_qstats_input, pcie_stat_host_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_pcie_qstats_input, pcie_stat_size),
+ .len_width = 2, .len_unit = 1 }} },
+ { HWRM_STAT_GENERIC_QSTATS, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_stat_generic_qstats_input),
+ {{ .offset = offsetof(struct hwrm_stat_generic_qstats_input,
+ generic_stat_host_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_stat_generic_qstats_input,
+ generic_stat_size),
+ .len_width = 2, .len_unit = 1 }} },
+ { HWRM_STAT_QUERY_ROCE_STATS, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_stat_query_roce_stats_input),
+ {{ .offset = offsetof(struct hwrm_stat_query_roce_stats_input,
+ roce_stat_host_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_stat_query_roce_stats_input,
+ roce_stat_size),
+ .len_width = 2, .len_unit = 1 }} },
+ { HWRM_STAT_QUERY_ROCE_STATS_EXT, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_stat_query_roce_stats_ext_input),
+ {{ .offset = offsetof(struct hwrm_stat_query_roce_stats_ext_input,
+ roce_stat_host_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_stat_query_roce_stats_ext_input,
+ roce_stat_size),
+ .len_width = 2, .len_unit = 1 }} },
+
+ { HWRM_PORT_EVENTS_LOG, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_port_events_log_input),
+ {{ .offset = offsetof(struct hwrm_port_events_log_input, host_dest_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_port_events_log_input, host_dest_addr_len),
+ .len_width = 4, .len_unit = 1 }} },
+ { HWRM_PORT_PRBS_TEST, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_port_prbs_test_input),
+ {{ .offset = offsetof(struct hwrm_port_prbs_test_input, resp_data_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_port_prbs_test_input, data_len),
+ .len_width = 2, .len_unit = 1 }} },
+ { HWRM_PORT_DSC_DUMP, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_port_dsc_dump_input),
+ {{ .offset = offsetof(struct hwrm_port_dsc_dump_input, resp_data_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_port_dsc_dump_input, data_len),
+ .len_width = 2, .len_unit = 1 }} },
+
+ { HWRM_SCH_GRP_CFG, 1, FWCTL_RPC_DEBUG_WRITE,
+ sizeof(struct hwrm_sch_grp_cfg_input),
+ {{ .offset = offsetof(struct hwrm_sch_grp_cfg_input, fid_table_addr),
+ .dir = FWCTL_BNXT_BUF_TO_DEVICE,
+ .len_offset = offsetof(struct hwrm_sch_grp_cfg_input, num_fids),
+ .len_width = 2, .len_unit = 2 }} },
+ { HWRM_SCH_GRP_QCFG, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_sch_grp_qcfg_input),
+ {{ .offset = offsetof(struct hwrm_sch_grp_qcfg_input, fid_table_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_sch_grp_qcfg_input, fid_table_len),
+ .len_width = 2, .len_unit = 1 }} },
+
+ { HWRM_SELFTEST_RETRIEVE_SERDES_DATA, 1, FWCTL_RPC_DEBUG_READ_ONLY,
+ sizeof(struct hwrm_selftest_retrieve_serdes_data_input),
+ {{ .offset = offsetof(struct hwrm_selftest_retrieve_serdes_data_input,
+ resp_data_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_selftest_retrieve_serdes_data_input,
+ data_len),
+ .len_width = 2, .len_unit = 1 }} },
+
+ { HWRM_DBG_PTRACE, 2, FWCTL_RPC_DEBUG_WRITE,
+ sizeof(struct hwrm_dbg_ptrace_input),
+ {{ .offset = offsetof(struct hwrm_dbg_ptrace_input, pdi_cmd_buf_addr),
+ .dir = FWCTL_BNXT_BUF_TO_DEVICE,
+ .len_offset = offsetof(struct hwrm_dbg_ptrace_input, pdi_req_buf_len),
+ .len_width = 4, .len_unit = 1 },
+ { .offset = offsetof(struct hwrm_dbg_ptrace_input, pdi_resp_buf_addr),
+ .dir = FWCTL_BNXT_BUF_FROM_DEVICE,
+ .len_offset = offsetof(struct hwrm_dbg_ptrace_input, pdi_req_buf_len),
+ .len_width = 4, .len_unit = 1 }} },
+};
+
+static const struct bnxtctl_cmd_dma_desc *
+bnxtctl_find_dma_desc(u16 req_type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bnxtctl_dma_cmds); i++)
+ if (bnxtctl_dma_cmds[i].req_type == req_type)
+ return &bnxtctl_dma_cmds[i];
+ return NULL;
+}
+
+static void bnxtctl_zero_dma_fields(void *cmd,
+ const struct bnxtctl_cmd_dma_desc *desc)
+{
+ int i;
+
+ for (i = 0; i < desc->num_fields; i++) {
+ __le64 *field = cmd + desc->fields[i].offset;
+
+ *field = 0;
+ }
+}
+
+static u32 bnxtctl_read_len_field(void *cmd, const struct bnxtctl_dma_field *f)
+{
+ if (f->len_width == 2)
+ return le16_to_cpup((__le16 *)(cmd + f->len_offset));
+ return le32_to_cpup((__le32 *)(cmd + f->len_offset));
+}
+
+static int bnxtctl_check_dma_lens(void *cmd, const struct bnxtctl_cmd_dma_desc *desc,
+ const struct fwctl_bnxt_driver_data *dd, u32 *lens)
+{
+ unsigned int i;
+
+ for (i = 0; i < dd->num_bufs; i++) {
+ const struct bnxtctl_dma_field *f = &desc->fields[i];
+ u64 len;
+
+ if (dd->bufs[i].dir != f->dir)
+ return -EINVAL;
+
+ if (f->len_offset)
+ len = (u64)bnxtctl_read_len_field(cmd, f) * f->len_unit;
+ else
+ len = f->buf_len;
+
+ if (!len || len > FWCTL_BNXT_MAX_DMABUF)
+ return -EINVAL;
+
+ lens[i] = len;
+ }
+ return 0;
+}
+
+static int bnxtctl_map_dma_bufs(struct device *dev, void *cmd,
+ const struct bnxtctl_cmd_dma_desc *desc,
+ const struct fwctl_bnxt_driver_data *dd,
+ const u32 *lens, void **kbufs,
+ dma_addr_t *dma_addrs, unsigned int *num_mapped)
+{
+ unsigned int i;
+
+ *num_mapped = 0;
+ for (i = 0; i < dd->num_bufs; i++) {
+ enum dma_data_direction dma_dir;
+ __le64 *field;
+
+ dma_dir = (dd->bufs[i].dir == FWCTL_BNXT_BUF_TO_DEVICE) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE;
+
+ kbufs[i] = dma_alloc_coherent(dev, lens[i],
+ &dma_addrs[i], GFP_KERNEL);
+ if (!kbufs[i])
+ return -ENOMEM;
+
+ if (dma_dir == DMA_TO_DEVICE &&
+ copy_from_user(kbufs[i],
+ u64_to_user_ptr(dd->bufs[i].addr),
+ lens[i])) {
+ dma_free_coherent(dev, lens[i], kbufs[i],
+ dma_addrs[i]);
+ kbufs[i] = NULL;
+ return -EFAULT;
+ }
+
+ (*num_mapped)++;
+
+ field = cmd + desc->fields[i].offset;
+ *field = cpu_to_le64(dma_addrs[i]);
+ }
+ return 0;
+}
+
+static int bnxtctl_unmap_dma_bufs(struct device *dev,
+ const struct fwctl_bnxt_driver_data *dd,
+ const u32 *lens, void **kbufs,
+ dma_addr_t *dma_addrs, unsigned int num_mapped)
+{
+ unsigned int i;
+ int rc = 0;
+
+ for (i = 0; i < num_mapped; i++) {
+ if (dd->bufs[i].dir == FWCTL_BNXT_BUF_FROM_DEVICE &&
+ copy_to_user(u64_to_user_ptr(dd->bufs[i].addr),
+ kbufs[i], lens[i]))
+ rc = -EFAULT;
+
+ dma_free_coherent(dev, lens[i], kbufs[i], dma_addrs[i]);
+ }
+ return rc;
+}
+
/* Caller must hold edev->en_dev_lock */
static bool bnxtctl_validate_rpc(struct bnxt_en_dev *edev,
struct bnxt_fw_msg *hwrm_in,
@@ -82,6 +449,8 @@ static bool bnxtctl_validate_rpc(struct bnxt_en_dev *edev,
case HWRM_NVM_ERASE_DIR_ENTRY:
case HWRM_NVM_MOD_DIR_ENTRY:
case HWRM_NVM_FIND_DIR_ENTRY:
+ case HWRM_NVM_SET_VARIABLE:
+ case HWRM_NVM_GET_VARIABLE:
return scope >= FWCTL_RPC_CONFIGURATION;
case HWRM_VER_GET:
@@ -138,12 +507,45 @@ static bool bnxtctl_validate_rpc(struct bnxt_en_dev *edev,
case HWRM_NVM_GET_DEV_INFO:
case HWRM_NVM_GET_DIR_INFO:
case HWRM_SELFTEST_QLIST:
+ case HWRM_NVM_READ:
+ case HWRM_NVM_GET_DIR_ENTRIES:
+ case HWRM_FW_GET_STRUCTURED_DATA:
+ case HWRM_DBG_COREDUMP_LIST:
+ case HWRM_DBG_COREDUMP_RETRIEVE:
+ case HWRM_DBG_COREDUMP_INITIATE:
+ case HWRM_DBG_READ_DIRECT:
+ case HWRM_QUEUE_DSCP2PRI_QCFG:
+ case HWRM_PORT_QSTATS:
+ case HWRM_PORT_QSTATS_EXT:
+ case HWRM_PORT_QSTATS_EXT_PFC_ADV:
+ case HWRM_PCIE_QSTATS:
+ case HWRM_STAT_GENERIC_QSTATS:
+ case HWRM_STAT_QUERY_ROCE_STATS:
+ case HWRM_STAT_QUERY_ROCE_STATS_EXT:
+ case HWRM_PORT_EVENTS_LOG:
+ case HWRM_PORT_PRBS_TEST:
+ case HWRM_PORT_DSC_DUMP:
+ case HWRM_DBG_READ_INDIRECT:
+ case HWRM_DBG_SERDES_TEST:
+ case HWRM_SCH_GRP_QCFG:
+ case HWRM_SELFTEST_RETRIEVE_SERDES_DATA:
+ case HWRM_NVM_RAW_DUMP:
return scope >= FWCTL_RPC_DEBUG_READ_ONLY;
case HWRM_PORT_PHY_I2C_WRITE:
case HWRM_PORT_PHY_MDIO_WRITE:
+ case HWRM_NVM_WRITE:
+ case HWRM_NVM_MODIFY:
+ case HWRM_FW_SET_STRUCTURED_DATA:
+ case HWRM_SCH_GRP_CFG:
+ case HWRM_DBG_PTRACE:
return scope >= FWCTL_RPC_DEBUG_WRITE;
+ case HWRM_FW_LIVEPATCH:
+ case HWRM_NVM_RAW_WRITE_BLK:
+ case HWRM_DBG_TOKEN_CFG:
+ return scope >= FWCTL_RPC_DEBUG_WRITE_FULL;
+
default:
return false;
}
@@ -162,6 +564,15 @@ static unsigned int bnxtctl_get_timeout(struct input *req)
case HWRM_NVM_VERIFY_UPDATE:
case HWRM_NVM_ERASE_DIR_ENTRY:
case HWRM_NVM_MOD_DIR_ENTRY:
+ case HWRM_NVM_WRITE:
+ case HWRM_FW_SYNC:
+ case HWRM_DBG_COREDUMP_LIST:
+ case HWRM_DBG_COREDUMP_RETRIEVE:
+ case HWRM_DBG_COREDUMP_INITIATE:
+ case HWRM_SELFTEST_RETRIEVE_SERDES_DATA:
+ case HWRM_DBG_SERDES_TEST:
+ case HWRM_NVM_RAW_WRITE_BLK:
+ case HWRM_FW_HEALTH_CHECK:
return BNXTCTL_HWRM_CMD_TIMEOUT_LONG;
case HWRM_FUNC_RESET:
return BNXTCTL_HWRM_CMD_TIMEOUT_MEDM;
@@ -178,21 +589,59 @@ static void *bnxtctl_fw_rpc(struct fwctl_uctx *uctx,
struct bnxtctl_dev *bnxtctl =
container_of(uctx->fwctl, struct bnxtctl_dev, fwctl);
struct bnxt_en_dev *edev = bnxtctl->aux_priv->edev;
+ dma_addr_t dma_addrs[FWCTL_BNXT_MAX_BUFS];
+ const struct bnxtctl_cmd_dma_desc *desc;
+ void *kbufs[FWCTL_BNXT_MAX_BUFS] = {0};
+ struct fwctl_bnxt_driver_data dd = {0};
+ struct device *dev = &edev->pdev->dev;
+ u32 dma_lens[FWCTL_BNXT_MAX_BUFS];
struct bnxt_fw_msg rpc_in = {0};
+ unsigned int num_mapped = 0;
+ struct input *req = in;
int rc;
- if (driver_data)
- return ERR_PTR(-EOPNOTSUPP);
-
if (in_len < sizeof(struct input) || in_len > HWRM_MAX_REQ_LEN)
return ERR_PTR(-EINVAL);
if (*out_len < sizeof(struct output))
return ERR_PTR(-EINVAL);
+ desc = bnxtctl_find_dma_desc(le16_to_cpu(req->req_type));
+
+ if (desc) {
+ if (in_len < desc->req_size)
+ return ERR_PTR(-EINVAL);
+
+ if (!driver_data)
+ return ERR_PTR(-EINVAL);
+
+ if (copy_from_user(&dd, u64_to_user_ptr(driver_data),
+ sizeof(dd)))
+ return ERR_PTR(-EFAULT);
+
+ if (dd.rsvd || dd.num_bufs != desc->num_fields)
+ return ERR_PTR(-EINVAL);
+
+ for (unsigned int i = 0; i < dd.num_bufs; i++) {
+ if (memchr_inv(dd.bufs[i].rsvd, 0,
+ sizeof(dd.bufs[i].rsvd)))
+ return ERR_PTR(-EINVAL);
+ }
+
+ rc = bnxtctl_check_dma_lens(in, desc, &dd, dma_lens);
+ if (rc)
+ return ERR_PTR(rc);
+
+ bnxtctl_zero_dma_fields(in, desc);
+ } else {
+ /* Non-DMA command: driver_data must be absent. */
+ if (driver_data)
+ return ERR_PTR(-EOPNOTSUPP);
+ }
+
rpc_in.msg = in;
rpc_in.msg_len = in_len;
- rpc_in.resp = kzalloc(*out_len, GFP_KERNEL);
+ rpc_in.resp = kvzalloc(*out_len, GFP_KERNEL);
if (!rpc_in.resp)
return ERR_PTR(-ENOMEM);
@@ -202,10 +651,21 @@ static void *bnxtctl_fw_rpc(struct fwctl_uctx *uctx,
guard(mutex)(&edev->en_dev_lock);
if (!bnxtctl_validate_rpc(edev, &rpc_in, scope)) {
- kfree(rpc_in.resp);
+ kvfree(rpc_in.resp);
return ERR_PTR(-EPERM);
}
+ if (desc) {
+ rc = bnxtctl_map_dma_bufs(dev, in, desc, &dd, dma_lens,
+ kbufs, dma_addrs, &num_mapped);
+ if (rc) {
+ bnxtctl_unmap_dma_bufs(dev, &dd, dma_lens,
+ kbufs, dma_addrs, num_mapped);
+ kvfree(rpc_in.resp);
+ return ERR_PTR(rc);
+ }
+ }
+
rc = bnxt_send_msg(edev, &rpc_in);
if (rc) {
struct output *resp = rpc_in.resp;
@@ -220,6 +680,17 @@ static void *bnxtctl_fw_rpc(struct fwctl_uctx *uctx,
resp->error_code = cpu_to_le16(rc);
}
+ if (desc) {
+ int unmap_rc;
+
+ unmap_rc = bnxtctl_unmap_dma_bufs(dev, &dd, dma_lens, kbufs,
+ dma_addrs, num_mapped);
+ if (unmap_rc) {
+ kvfree(rpc_in.resp);
+ return ERR_PTR(unmap_rc);
+ }
+ }
+
return rpc_in.resp;
}
diff --git a/include/uapi/fwctl/bnxt.h b/include/uapi/fwctl/bnxt.h
index 32e0bfb9a836..1aedae47f5c5 100644
--- a/include/uapi/fwctl/bnxt.h
+++ b/include/uapi/fwctl/bnxt.h
@@ -12,6 +12,7 @@ enum fwctl_bnxt_commands {
FWCTL_BNXT_INLINE_COMMANDS = 0,
FWCTL_BNXT_QUERY_COMMANDS,
FWCTL_BNXT_SEND_COMMANDS,
+ FWCTL_BNXT_DMA_COMMANDS,
};
/**
@@ -23,4 +24,38 @@ enum fwctl_bnxt_commands {
struct fwctl_info_bnxt {
__u32 uctx_caps;
};
+
+enum fwctl_bnxt_buf_dir {
+ FWCTL_BNXT_BUF_TO_DEVICE = 0,
+ FWCTL_BNXT_BUF_FROM_DEVICE = 1,
+};
+
+/**
+ * struct fwctl_bnxt_buf - one indirect DMA buffer descriptor
+ * @addr: Userspace pointer to the payload data.
+ * @dir: One of enum fwctl_bnxt_buf_dir.
+ * @rsvd: Must be zero.
+ */
+struct fwctl_bnxt_buf {
+ __aligned_u64 addr;
+ __u32 dir;
+ __u32 rsvd[3];
+};
+
+#define FWCTL_BNXT_MAX_BUFS 4
+#define FWCTL_BNXT_MAX_DMABUF 0x10000 /* 64 KiB */
+
+/**
+ * struct fwctl_bnxt_driver_data - pointed to by fwctl_rpc::driver_data for bnxt
+ * @num_bufs: Number of valid entries in @bufs. Must be non-zero and no greater
+ * than the number of DMA address fields the specific HWRM command supports
+ * @rsvd: Must be zero.
+ * @bufs: Array of buffer descriptors.
+ */
+struct fwctl_bnxt_driver_data {
+ __u32 num_bufs;
+ __u32 rsvd;
+ struct fwctl_bnxt_buf bufs[FWCTL_BNXT_MAX_BUFS];
+};
+
#endif
--
2.52.0
^ permalink raw reply related [flat|nested] 4+ messages in thread